DEV Community

kalos
kalos

Posted on

Avoid Frequent Reconnections: Dynamic Stock Subscription via WebSocket for Quantitative Trading

Introduction

For quantitative strategy development, backtesting and live market data collection, continuous and reliable real-time stock quotes are the fundamental guarantee for model operation and signal generation. WebSocket has become the mainstream solution for accessing real-time financial data due to its full-duplex persistent connection capability.

The static subscription mode, which sets stock symbols once when establishing a connection, works well for simple scenarios. However, when running multi-symbol rotation strategies, dynamic stock selection models or parallel portfolio monitoring tasks, the subscription list needs to be adjusted frequently. Reconnecting WebSocket every time the symbol list changes will cause data gaps, undermine backtesting accuracy and signal continuity, and also bring extra network overhead.

This article presents a complete implementation of dynamic subscription management based on a single persistent WebSocket connection with AllTick API. I will elaborate on design logic, production-ready code, exception handling and practical value for quantitative scenarios, for developers and quantitative researchers to reference.

Basic Concept

Dynamic WebSocket subscription for stocks means adjusting subscribed symbols by sending dedicated protocol commands over an established persistent connection, instead of rebuilding network connections.

Different from one-time static subscription during connection initialization, this pattern adapts to scenarios with frequent changes to the watchlist. It maintains complete data streams and meets the strict requirements of quantitative strategies for data integrity.

Requirements & Drawbacks of Traditional Solutions

Core Requirements

For quantitative data acquisition, live strategy execution and batch symbol monitoring, the data access layer needs to satisfy two key requirements:

  1. Support adding and removing single or multiple stock symbols flexibly to match dynamic stock selection and multi-pool rotation strategies.
  2. Ensure the data stream of existing subscribed symbols remains uninterrupted during subscription adjustment, so that indicator sampling and strategy calculation will not be affected.

Defects of Static Subscription with Reconnection

The traditional workflow — disconnect, reconnect and re-subscribe after modifying the symbol list — has obvious limitations in quantitative applications:

  1. Frequent connection creation and destruction increases network load on both client and server, which becomes more prominent when collecting data from a large number of symbols.
  2. Data interruption occurs during reconnection, leading to missing quote samples. This directly distorts backtesting results and interferes with signal output of live strategies.
  3. Repeating connection authentication and subscription initialization results in redundant code, which is not conducive to iteration and maintenance of quantitative tools.

The core optimization idea is to reuse one long-lived WebSocket connection and manage subscriptions via in-channel commands to stabilize the data link.

Implementation Logic

The solution consists of two core modules: local subscription state management and WebSocket command interaction, with concise logic that can be easily integrated into quantitative data frameworks.

  1. Use a Set structure locally to store all subscribed stock codes to prevent duplicate subscription requests.
  2. Follow the unified rule: update local status first, then send control commands to the server, to keep local records consistent with server-side data push scope.
  3. According to AllTick specifications, its WebSocket interface allows delivering multiple control commands during the entire connection lifecycle, which supports the implementation of dynamic subscription.

Workflow of Core Operations

Add new subscriptions

Check whether the target symbols exist in the local collection. Add unsubscribed symbols to the set, then construct and send a standard subscribe command. The server will start pushing market data for these symbols afterwards.

Cancel existing subscriptions

Verify the target symbols are included in the local collection, then remove them from the set. Send an unsubscribe command to stop the server-side data push for the specified symbols.

Modify subscriptions in batches

Refresh the entire local symbol list, combine all target codes into one single request and send it in batch. This approach reduces network requests and improves efficiency when managing large quantities of symbols.

Full Python Implementation

The code fully complies with AllTick interface specifications. It adopts the official frame format with cmd_id=22004 and uses the code field to transmit stock symbols. Heartbeat detection, null value validation and exception handling are embedded, so it can be directly integrated into data collectors and quote monitoring tools for quantitative trading.

import websocket
import json

# Stock WebSocket endpoint: AllTick official API Docs (WebSocket address specification)
WS_STOCK_URL = "wss://quote.alltick.co/quote-stock-b-ws-api?token=YOUR_TOKEN"
# Local set: manage subscribed symbols and avoid duplicate requests
stock_subscribe_set = set()

def on_open(ws):
    """Callback after connection establishment: initialize subscription list"""
    init_codes = ["stock_a", "stock_b"]
    global stock_subscribe_set
    stock_subscribe_set.update(init_codes)
    # Construct message following official frame rules
    init_msg = {
        "cmd_id": 22004,
        "action": "subscribe",
        "code": init_codes
    }
    ws.send(json.dumps(init_msg))

def on_message(ws, message):
    """Callback for receiving market data with error protection"""
    if not message:
        return
    try:
        data = json.loads(message)
        # Extend logic: data parsing, indicator calculation, signal triggering, local storage
        print(data)
    except json.JSONDecodeError:
        return

def on_error(ws, error):
    """Callback for connection exceptions, used for log recording and troubleshooting"""
    print(f"WebSocket connection error: {error}")

def on_close(ws, close_status_code, close_msg):
    """Callback for connection closure, can add alert and status recording logic"""
    print("WebSocket connection closed")

def add_subscribe(ws, code_list):
    """Dynamically add symbol subscriptions for dynamic stock selection and temporary pool access"""
    global stock_subscribe_set
    valid_codes = [code for code in code_list if code not in stock_subscribe_set]
    if not valid_codes:
        return
    stock_subscribe_set.update(valid_codes)
    req_msg = {
        "cmd_id": 22004,
        "action": "subscribe",
        "code": valid_codes
    }
    ws.send(json.dumps(req_msg))

def remove_subscribe(ws, code_list):
    """Dynamically cancel subscriptions for symbol elimination and strategy offline"""
    global stock_subscribe_set
    valid_codes = [code for code in code_list if code in stock_subscribe_set]
    if not valid_codes:
        return
    for code in valid_codes:
        stock_subscribe_set.discard(code)
    req_msg = {
        "cmd_id": 22004,
        "action": "unsubscribe",
        "code": valid_codes
    }
    ws.send(json.dumps(req_msg))

if __name__ == "__main__":
    # Initialize WebSocket client, 10s ping interval to maintain persistent connection
    ws_app = websocket.WebSocketApp(
        WS_STOCK_URL,
        on_open=on_open,
        on_message=on_message,
        on_error=on_error,
        on_close=on_close
    )
    ws_app.run_forever(ping_interval=10)
Enter fullscreen mode Exit fullscreen mode

Value for Quantitative Trading Scenarios

Compared with the traditional reconnection mode, this solution brings practical improvements to quantitative development and live trading:

  1. Enhanced data continuity: Reuse a single persistent connection without data gaps during subscription adjustment. Complete quote samples improve the credibility of backtesting results and stability of live strategies.
  2. Reduced resource consumption: Cut down repeated connection creation and authentication operations, lowering computing and network overhead. It works well for architectures running multiple symbols and strategies in parallel.
  3. High scalability: Subscription and unsubscription logic are encapsulated as independent functions. It can be seamlessly connected with symbol pool management, automatic stock selection and portfolio rebalancing modules for easy iteration.
  4. Adapt to high-frequency scenarios: Support both single-symbol and batch operations, matching the requirements of rotation strategies and short-term trading models that need frequent symbol adjustment.

Common Issues & Troubleshooting

Summarized from long-term operation of quantitative programs, here are three frequent problems and corresponding solutions:

  1. Issue: No market data received after sending subscription commands
    Check: Verify consistency between the code field and local stock codes, confirm cmd_id is set to 22004
    Fix: Synchronize the local symbol list and resend the standard subscription command.

  2. Issue: Data is still received after canceling subscription
    Check: Confirm the target symbol is removed from the local set and the unsubscription command is sent normally
    Fix: Add data filtering logic based on the local subscription list to block invalid data and avoid interference with strategy calculation.

  3. Issue: Abnormal subscription status of partial symbols after batch command delivery
    Check: Split the batch list and verify subscription functions for each symbol individually
    Fix: Send commands symbol by symbol temporarily to avoid batch protocol compatibility issues.

Limitations

This solution is developed based on the official stock WebSocket protocol of AllTick. It only supports adding and removing symbols within a single connection.

It cannot synchronize subscription status across multiple devices or connections, and custom commands beyond the official protocol are not supported. Please follow this boundary when designing the overall quantitative system architecture.

Top comments (0)