Intro
I build backend infrastructure for quantitative trading systems, supporting backtesting engines, live trading bots, and market data dashboards all at once.
My original WebSocket implementation was quick to write but flawed: every time users add or remove stock tickers, I closed the existing socket and created a brand-new connection, using REST polling as a backup. This worked fine for small local test watchlists, but broke hard in production.
When loading full stock pools or switching tickers in bulk around market open/resumption, massive handshake floods triggered API rate limits and created gaps in time-series tick data. Worse, blank zero-volume data from trading halts broke moving average, volatility and other core indicators, generating false buy/sell signals that ruined backtest reliability.
To fix connection instability, broken subscriptions and missing market data, I rebuilt our market feed layer around a single persistent WebSocket connection with dynamic ticker add/remove functionality. Below you’ll find fully runnable Python code, reproducible API rules, and production-grade fixes for common quant backend bugs.
Core Business Requirements for Market Data Services
Our team serves internal quant strategy research and external market visualization tools, so we defined three non-negotiable standards for the market API layer:
- Maintain one long-lived WebSocket connection. Add/remove stock codes without tearing down sockets to erase connection spikes during bulk ticker switches.
- Separate dedicated WebSocket endpoints for stocks, forex and crypto, unify subscription command formats, and use a local ticker set to auto-deduplicate requests to avoid ghost subscriptions that waste bandwidth and server resources.
- Support both historical backtesting and live trading pipelines. Use the
volumefield from API responses to auto-detect halt blank data and correct metric cycle offsets to keep backtests trustworthy.
Four Persistent Production Data Bugs (Before Refactor)
All these reproducible issues stemmed from oversimplified stock API subscription logic:
- Frequent reconnections fragment market data. Without dedicated halt flags, indicator cycles shift wildly, creating backtest profit curves drastically different from real market performance, leading teams to misjudge strategy quality.
- Bulk ticker pool swaps trigger out-of-order async subscribe/unsubscribe commands. Duplicate tick data floods clients, spikes CPU usage on client and server, and clogs message callback queues to block the main thread.
- No standardized local subscription state tracking. Duplicate requests for the same stock code trigger silent API failures with zero clear error logs, blowing up incident debugging time.
- Reusing a single endpoint for stocks and other assets breaks all batch subscriptions. The system cannot tell stock halts apart from zero-volume commodity sessions; filling gaps with pre-halt close prices creates fake price trends that render backtests useless.
What Is Dynamic Subscription?
Dynamic subscription uses a single active WebSocket long connection. Standard subscription commands carry an operation type and list of ticker codes to add, delete, or fully clear tracked instruments — no socket close/reconnect required.
This is fundamentally different from REST polling or naive implementations that rebuild connections for every watchlist change.
Key engineering benefit: cut concurrent connection volume drastically and centralize subscription state management, perfectly matching high-frequency tick streams from stock market APIs, and widely adopted in production quant systems.
Scenario Verification Table (For Local Testing & Debugging)
| Use Case | Production Pain Point | API Dynamic Params (cmd_id / action / code) | Validation Standard |
|---|---|---|---|
| Bulk load stock pool on startup | Subscribing to dozens of tickers on launch triggers rate limits via repeated new connections | Fixed cmd_id, action="add", bulk stock code array passed | Only one connection created; logs show one open event with no duplicate handshakes |
| Manually add tickers to watchlist | Users receive no market data for tens of seconds after adding securities | Fixed cmd_id, action="add", single stock code passed | Local subscription set auto-deduplicates; identical tickers never send duplicate requests |
| Remove unused tracked tickers | Unwanted tick data continues streaming post-deletion, wasting memory & egress bandwidth | Fixed cmd_id, action="del", target ticker code passed | Local set removes the code; real-time data for that ticker stops entirely |
| Clear entire watchlist | Switch industry ticker pools and reset all subscriptions | Fixed cmd_id, action="clear", empty code array passed | All stock market streams stop; local subscription set fully cleared |
| Edge Case: Repeated identical add requests | Frontend rapid clicks flood channels with redundant subscription commands | Fixed cmd_id, action="add", duplicate ticker codes passed | Client-side pre-deduplication; each unique code only sends one request |
| Edge Case: Add operation with empty list | Business logic bugs pass an empty ticker array | Fixed cmd_id, action="add", empty code array passed | Client blocks invalid requests early; no API error logs generated |
Production Pitfall Breakdown: Root Cause + Detection + Fix
1. High-frequency tick inflow blocks callback thread
Symptom: Market open / post-resumption high volatility brings thousands of ticks per second. Callbacks without async buffering block the main thread, mess up data timestamps, and delay halt data detection.
Detection: Monitor local subscription size and message queue backlog; trigger alerts when per-ticker tick throughput crosses your threshold.
Fix: Build an async consumer queue to buffer ticks before processing. Check the volume field on every tick; mark halt status and skip indicator/signal logic if volume equals 0.
2. Network jitter creates false-live WebSockets (no immediate on_close)
Symptom: Flaky intranet or ISP outages don’t fire the on_close callback instantly. The connection looks alive but stops sending data, and backtests keep consuming invalid blank data.
Detection: 10-second heartbeat ping/pong cycle; mark connection as false-live after three missed pong responses.
Fix: Force close and rebuild connection on heartbeat timeout. After reconnection, reload the full local subscription set and send bulk add commands to restore all market feeds.
3. Rapid ticker add/remove triggers subscription race conditions
Symptom: Fast switching between ticker groups scrambles async command order, creating mismatches between local subscription state and server-side active tickers, leaving unremovable ghost subscriptions.
Detection: Periodically cross-check local ticker set against incoming tick codes; flag unseen tickers as race condition anomalies.
Fix: Wrap subscription command dispatch with a thread lock — only one subscription change can run per connection at a time. Sync the local ticker set immediately after every operation.
4. Mismatched endpoints / ticker formats cause silent subscription failures
Symptom: Routing stocks to the general asset WebSocket endpoint or omitting market prefixes from ticker codes results in zero API errors, with no real-time data delivered.
Detection: If no matching ticks arrive 30s after subscription, search logs for missing ticker stream records.
Fix: Strictly separate two standard WebSocket endpoints: dedicated stock channel, shared channel for forex, crypto, commodities. Force market namespace prefixes on all stock codes and validate formatting with regex before sending requests.
Functional Boundaries
The standard dynamic subscription cmd_id only supports ticker add/remove operations within a single active connection. It cannot sync subscription states across multiple WebSocket connections, pull historical tick data retroactively, or manage custom private API instructions outside this subscription framework.
Full Working Python Code (Copy-Paste Ready)
import websocket
import json
import threading
import time
# Source: Official market API WebSocket docs - dedicated stock endpoint
STOCK_WSS_URL = "wss://quote.alltick.co/quote-stock-b-ws-api?token=YOUR_TOKEN"
# Source: Official market API WebSocket docs - general asset endpoint
COMMON_WSS_URL = "wss://quote.alltick.co/quote-b-ws-api?token=YOUR_TOKEN"
# Local subscription storage with automatic deduplication
subscriptions = set()
# Thread lock to eliminate race conditions on subscription edits
sub_lock = threading.Lock()
def send_sub_cmd(ws, action, code_list):
"""Send dynamic subscription command using standard API cmd_id"""
with sub_lock:
cmd = {
"cmd_id": 22004,
"action": action,
"code": code_list
}
ws.send(json.dumps(cmd))
# Sync local subscription set after command execution
if action == "add":
for c in code_list:
subscriptions.add(c)
elif action == "del":
for c in code_list:
if c in subscriptions:
subscriptions.remove(c)
elif action == "clear":
subscriptions.clear()
def on_open(ws):
print("WebSocket connected, initializing base stock subscriptions")
init_codes = ["HK:00700", "NASDAQ:AAPL"]
send_sub_cmd(ws, "add", init_codes)
def on_message(ws, message):
try:
data = json.loads(message)
tick_data = data.get("data", {})
code = tick_data.get("code", "")
volume = tick_data.get("volume", 0)
close = tick_data.get("close", 0)
# Guard clauses to filter empty messages / invalid tickers
if not code or not tick_data:
return
# Flag halted instruments with zero trading volume
suspend_flag = volume == 0
if suspend_flag:
print(f"Ticker {code} has zero volume, marked halted, skipping signal logic")
return
# Core tick processing logic
print(f"Live Tick | {code} | Close: {close} | Volume: {volume}")
except Exception as e:
print(f"Tick parse error: {str(e)}")
def on_error(ws, error):
print(f"WebSocket connection error: {error}")
def on_close(ws, close_code, close_msg):
print(f"Connection closed | Code: {close_code} | Message: {close_msg}")
if __name__ == "__main__":
ws_app = websocket.WebSocketApp(
STOCK_WSS_URL,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
# 10s heartbeat to detect false-live sockets
ws_app.run_forever(ping_interval=10)
Wrap Up
This dynamic subscription refactor is built on standard WebSocket endpoints for generic stock market APIs. Every URL, command field and interaction pattern can be cross-referenced against official API docs — no opaque black-box logic.
Single-connection dynamic ticker management eliminates connection spikes at the source, layered with three safeguards: local subscription state tracking, halt blank data labeling, and heartbeat-based connection recovery. Combined, these fix two major quant backend pain points: distorted backtest results and frequent live market feed disconnections. You can validate service stability across three layers: runtime logs, raw API response payloads, and application source code.
I fully tested this architecture on AllTick API, which provides clean endpoint segmentation and well-defined multi-asset routing rules. Individual quant developers and institutional quant teams can reuse this codebase to rebuild their internal market data services.

Top comments (0)