When I first tackled real-time market data for the entire A-share market, I did what many devs do: I wrote a loop that polled a REST endpoint for each stock. It was slow. It was fragile. And it was constantly hitting rate limits.
The solution? Tear down the polling loop and put up a WebSocket subscription. This post walks through why you should too, and shows you the code to get started.
The Problem with Polling 5000+ Instruments
Polling is a synchronous ask-reply model. You request, you wait, you get a response. When multiplied by thousands of stocks and the high tick frequency of A-shares (hundreds of trades per second during peak), you end up with a stream of delayed snapshots, not a real-time feed.
WebSocket push turns the model on its head. The server sends you data only when something happens, over a persistent connection. This means:
- Millisecond-level latency
- No wasted requests or rate limits
- One TCP connection regardless of how many symbols you track
Two Common Subscription Message Formats
Different APIs accept slightly different shapes. Usually it's either:
| Format | Sample | Use Case |
|---|---|---|
| Array | ["000001","000002","600036"] |
Great for dynamic watchlists |
| String | "000001,000002,600036" |
Compact, easy for URL params |
Some providers also let you use a wildcard to subscribe to all listed A-shares — amazing for scanners, but you’ll need to get the appropriate access tier.
Code: One WebSocket, Ten Stocks, Zero Polling
Here’s a basic Python script using websocket-client. It connects to a push endpoint (think AllTick-style APIs) and subscribes to a batch of symbols.
import websocket
import json
# WebSocket endpoint that streams real-time trade ticks
url = "wss://apis.alltick.co/websocket-api/stock-websocket-interface-api/transaction-quote-subscription"
def on_message(ws, message):
# Decode the incoming JSON
data = json.loads(message)
# Iterate over all tick updates
for tick in data.get("ticks", []):
print(f"Code:{tick['code']} Price:{tick['price']} Time:{tick['time']}")
def on_open(ws):
# Batch subscription request for 10 A-shares
sub_msg = {
"action": "subscribe",
"symbols": ["000001", "000002", "600036", "600519", "000858",
"002415", "300750", "601318", "000333", "002594"]
}
ws.send(json.dumps(sub_msg))
ws = websocket.WebSocketApp(url, on_message=on_message)
ws.on_open = on_open
ws.run_forever()
Run this, and every time any of those stocks trades, you’ll see the price, code, and timestamp instantly.
Surviving the Data Firehose
Once you scale to the full market, you might see hundreds of ticks per second. Here’s how I handle it in production:
- Sharded processing: Hash by stock code and feed separate worker queues.
-
Snapshot cache: Keep a
dictof latest prices for O(1) read access. - Batch DB inserts: Accumulate ticks and flush in micro-batches (e.g., 100 records or every 200ms).
- Threshold-based UI updates: Only push price changes larger than a set tick size to front-end clients.
Preparing for All-Market Subscription
If you’re going to scan the entire market, make sure:
- Your network and CPU can handle peak loads (I’ve logged ~300–400 ticks/sec during openings).
- You introduce a buffer (like Redis Streams or Kafka) when processing can’t keep up.
- You enable server-side filters that send ticks only for stocks with actual trades.
Wrapping Up
Stop polling. Start pushing. A single WebSocket with batch subscription is the most efficient way I’ve found to get full A-share tick data. When evaluating a provider, focus on latency, batch support, and stability. Then start small, stress-test, and scale gradually.
Your future self — and your servers — will thank you.

Top comments (0)