DEV Community

EmilyL
EmilyL

Posted on

# Hong Kong Stock Prices API: How to Get Real-Time HK Stock Quotes and Solve Data Latency?

1. Concept: What matters for HK stock quotes

A Hong Kong stock prices API gives programmatic access to HKEX-listed securities such as 700.HK, 9988.HK, and 3690.HK.

For latency-sensitive applications, the key distinction is:

  • REST API — request/response. Good for snapshots, backfill, and reconciliation.
  • WebSocket API — persistent streaming. Required for real-time HK quotes.

Latency is best measured as:

client_receive_time - exchange_event_time
Enter fullscreen mode Exit fullscreen mode

It is caused by polling intervals, network round trips, JSON parsing, event-loop blocking, and unsynchronised clocks. The practical goal is not zero latency; it is stable, measurable, low p95/p99 latency.

AllTick provides both REST and WebSocket endpoints. This article uses the AllTick API pattern:

  • REST base: https://quote.alltick.io/quote-stock-b-api
  • WebSocket: wss://quote.alltick.io/quote-stock-b-ws-api?token=YOUR_TOKEN
  • Command IDs: 22000 heartbeat, 22002 subscribe, 22999 push

2. Practical: Get real-time HK quotes with AllTick

2.1 Install dependencies

pip install websockets requests
Enter fullscreen mode Exit fullscreen mode

2.2 Bootstrap a recent HK quote snapshot via REST

Use REST only to seed state or recover after a disconnect. Do not poll it for real-time trading.

import json
import requests

TOKEN = "YOUR_ALLTICK_TOKEN"
BASE = "https://quote.alltick.io/quote-stock-b-api"

query = {
    "symbol_list": [
        {"code": "700.HK"},
        {"code": "9988.HK"},
        {"code": "3690.HK"},
    ]
}

resp = requests.get(
    f"{BASE}/trade-tick",
    params={
        "token": TOKEN,
        "query": json.dumps(query, separators=(",", ":")),
    },
    timeout=5,
)

resp.raise_for_status()
print(resp.json())
Enter fullscreen mode Exit fullscreen mode

This gives a recent trade-tick snapshot. For live updates, switch to WebSocket.


2.3 Stream real-time HK quotes over WebSocket

The following example subscribes to HK symbols, sends AllTick heartbeats, measures latency from the exchange timestamp, and reconnects automatically.

import asyncio
import json
import time
from collections import deque

import websockets

TOKEN = "YOUR_ALLTICK_TOKEN"
WS_URL = f"wss://quote.alltick.io/quote-stock-b-ws-api?token={TOKEN}"

SYMBOLS = [
    {"code": "700.HK"},
    {"code": "9988.HK"},
    {"code": "3690.HK"},
]

SUBSCRIBE_CMD = {
    "cmd_id": 22002,
    "seq_id": 1,
    "trace": "hk-quote-sub-1",
    "data": {"symbol_list": SYMBOLS},
}

HEARTBEAT_CMD = {
    "cmd_id": 22000,
    "seq_id": 2,
    "trace": "hk-heartbeat",
    "data": {},
}

latencies = deque(maxlen=5000)


def to_ms(ts):
    """AllTick timestamps may be seconds or milliseconds. Normalise to ms."""
    ts = int(ts)
    return ts * 1000 if ts < 10_000_000_000 else ts


def handle_push(msg):
    # 22999 is the AllTick data-push command.
    if msg.get("cmd_id") != 22999:
        return

    data = msg.get("data", {})
    ticks = data if isinstance(data, list) else [data]

    for tick in ticks:
        code = tick.get("code") or tick.get("symbol")
        price = tick.get("price")
        volume = tick.get("volume")
        ts = tick.get("time") or tick.get("trade_time")

        if ts is None:
            continue

        exchange_ms = to_ms(ts)
        recv_ms = time.time_ns() // 1_000_000
        latency_ms = recv_ms - exchange_ms

        latencies.append(latency_ms)

        print(
            f"{code} price={price} volume={volume} "
            f"latency={latency_ms}ms"
        )

        if len(latencies) >= 100 and len(latencies) % 100 == 0:
            ordered = sorted(latencies)
            p50 = ordered[len(ordered) // 2]
            p95 = ordered[int(len(ordered) * 0.95) - 1]
            print(
                f"latency p50={p50}ms p95={p95}ms "
                f"samples={len(ordered)}"
            )


async def heartbeat(ws):
    seq = 2
    while True:
        await asyncio.sleep(20)
        hb = dict(HEARTBEAT_CMD)
        hb["seq_id"] = seq
        seq += 1
        await ws.send(json.dumps(hb, separators=(",", ":")))


async def main():
    while True:
        try:
            async with websockets.connect(
                WS_URL,
                ping_interval=None,
                max_size=2**20,
                close_timeout=5,
            ) as ws:
                await ws.send(json.dumps(SUBSCRIBE_CMD, separators=(",", ":")))
                asyncio.create_task(heartbeat(ws))

                async for raw in ws:
                    msg = json.loads(raw)
                    handle_push(msg)

        except Exception as exc:
            print(f"WebSocket disconnected: {exc}. Reconnecting in 1s...")
            await asyncio.sleep(1)


if __name__ == "__main__":
    asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

3. How to solve data latency in production

The code above gives you a real-time HK quote stream. To reduce and control latency:

3.1 Use WebSocket, not polling

Polling every second adds an average delay of 500 ms before network RTT. WebSocket push removes that interval entirely.

3.2 Deploy close to the data source

Run your service in a Hong Kong region, such as AWS ap-east-1, GCP asia-east2, or Alibaba Cloud Hong Kong. This reduces network RTT to AllTick and HKEX-related infrastructure.

3.3 Synchronise clocks

Latency measurement depends on accurate time. Use NTP or PTP on the host. On Linux:

chronyc tracking
Enter fullscreen mode Exit fullscreen mode

Without clock sync, client_receive_time - exchange_event_time can be misleading.

3.4 Keep the event loop non-blocking

Do not perform database writes, HTTP calls, or heavy computation inside handle_push. Push ticks into an asyncio.Queue and process them in a separate worker.

3.5 Subscribe only to needed symbols

Every extra symbol increases JSON volume, parsing cost, and queue pressure. Subscribe to the HK codes your strategy actually uses.

3.6 Monitor p95 and p99, not average latency

Average latency hides spikes. Track:

  • p50 — normal conditions
  • p95 — degraded network or vendor load
  • p99 — risk events and market open/close volatility

3.7 Reconnect with jitter and detect sequence gaps

Use exponential backoff with jitter to avoid reconnect storms. Check AllTick seq_id for gaps and reconcile with a REST snapshot when a gap occurs.


4. Conclusion

For Hong Kong stock prices, REST is useful for bootstrap and recovery, but real-time HK quotes require WebSocket streaming. With AllTick, the practical pattern is:

  1. Seed state with /trade-tick.
  2. Subscribe over quote-stock-b-ws-api.
  3. Measure latency from the exchange timestamp.
  4. Deploy in Hong Kong, synchronise clocks, and keep the event loop non-blocking.
  5. Monitor p95/p99 and reconnect safely.

This gives you a real-time HK quote pipeline that is both fast and measurable. Always verify the latest AllTick command IDs, endpoints, and field names against the official documentation at alltick.co before production deployment.

——————————————————————————————————————————————————————

API Docs:https://alltick.co/apis/en

GitHub:https://github.com/alltick/alltick-realtime-forex-crypto-stock-tick-finance-websocket-api

Top comments (0)