Python WebSocket + Hong Kong Real-Time Stock API: Detecting Sequence Gaps in Tick Data
If you're building a trading or analytics tool that consumes real-time Hong Kong stock ticks, you've probably dealt with WebSocket data. But have you ever checked whether your tick stream is actually complete? I didn't—until I found a subtle bug that was skewing our trade volume stats.
Let's dive into how I detected and handled sequence number gaps in a Hong Kong real-time stock API using Python.
The Setup
Our stack is Python + WebSocket. We connect to a Hong Kong real-time stock API to receive tick-by-tick trades. Each message includes a seq field—an incrementing integer that should be consecutive. In a perfect world, receiving seq=60003 means the next message will be seq=60004.
But as you'll see, the world isn't perfect.
The Problem: Missing Ticks
After running in production for a while, we noticed that cumulative trade volume for some stocks didn't match exchange data. The gap was small but consistent. I checked price parsing, data types, timezone handling—everything looked fine. Then I dumped the raw seq values and saw this:
| Sequence Number | Status |
|---|---|
| 60001 | Received normally |
| 60002 | Received normally |
| 60003 | Received normally |
| 60005 | Gap detected |
Message 60004 never arrived.
Why does this happen? Network jitter, client-side processing delays, or a WebSocket reconnect that missed a few messages can all cause gaps. The API isn't necessarily broken—but your data is incomplete.
The Solution: Validate at the Ingress Point
The fix is simple: don't trust raw WebSocket messages. Add a validation layer before data enters your business logic.
Here's the core idea:
- Track the last sequence number.
- For each new message, compare its
seqtolast_seq + 1. - If they match, process normally.
- If there's a jump, log the gap.
- If the sequence is lower or equal, handle duplicates or out-of-order messages.
Here's the Python code:
import json
import websocket
last_seq = None
def on_message(ws, message):
global last_seq
data = json.loads(message)
seq = data.get("seq")
if last_seq is not None:
if seq != last_seq + 1:
print(f"Sequence gap detected: {last_seq} -> {seq}")
last_seq = seq
print(data)
ws = websocket.WebSocketApp(
"wss://api.alltick.co/stock/websocket",
on_message=on_message
)
ws.run_forever()
This is the starting point. In production, I also capture:
- Stock symbol
- Trade timestamp
- Missing sequence range
That extra data makes recovery possible.
Other Things to Watch Out For
Sequence gaps aren't the only pitfall. Here are a few more lessons from my experience:
- Sequence order doesn't guarantee time order. Network latency can reorder messages. I always record both the exchange trade time and the local receive time to reconstruct the true market sequence.
- Reconnections are tricky. After a WebSocket disconnect and reconnect, you can't assume the new stream starts exactly where the old one ended. Always re-validate the first message after a reconnect.
- API choice matters, but client-side validation is still required. We use AllTick's API for Hong Kong real-time ticks. It's been reliable, but network-level gaps can happen with any provider. So validate on your end.
Data Recovery
If you only need live monitoring, logging gaps is enough. But if your data feeds backtesting or strategy calculations, you need a recovery mechanism.
My approach:
- Save the missing sequence range
- Save the stock code
- Save the time window
Then, fetch the missing ticks from a historical data endpoint and merge them back into your local store. This keeps your final dataset complete even if the live connection drops a few messages.
Summary
Working with a Hong Kong real-time stock API has taught me that real-time data systems fail in subtle ways. The hardest part isn't getting the data—it's maintaining data integrity over time. Tick data is high-frequency and unforgiving.
Add sequence validation, log anomalies, and build a recovery process. Your future self will thank you.
Have you dealt with sequence gaps in real-time data? Let's discuss in the comments! 🚀

Top comments (0)