If you build precious metals market monitoring or gold futures quantitative strategies, you'll eventually walk the same path: writing Python data collectors that connect to a real-time market data API and pull international gold prices and COMEX gold futures data into your own system. The direction itself has no real barrier — the barrier lives in the integration details. A single WebSocket connection's auth sequencing, heartbeat mechanism, and timestamp handling can silently kill your data stream at 3 AM.

A friend recently sent me a gold futures quote-collection script he'd written, saying "sometimes the data just stops updating, with no error at all." I went through the code line by line — there's more than one problem. This article isn't a tutorial; it's a real code review. Here's the original code, the issues I found, and the fixes written against the official docs. If you're writing futures market-data collectors, real-time quote pipelines, or precious metals data infrastructure, you'll probably recognize some of these.

The Original Code

import json import time import requests import websocket API_BASE = "https://api.itick.org" TOKEN = "your_token_here" headers = {"accept": "application/json", "token": TOKEN} def get_quote(): resp = requests.get( f"{API_BASE}/future/quote", headers=headers, params={"region": "US", "code": "GC"} ) return resp.json()["data"] def on_message(ws, message): payload = json.loads(message) data = payload.get("data") or {} if data.get("type") == "quote": ts = data["t"] // 1000 print(f"{data['s']}: {data['last']} ({data['chp']}%) time={ts}") def on_open(ws): # Subscribe immediately after connection  ws.send(json.dumps({ "ac": "subscribe", "params": "GC$US", "types": "quote" })) ws = websocket.WebSocketApp( "wss://api.itick.org/future", header=[f"token: {TOKEN}"], on_message=on_message, on_open=on_open ) ws.run_forever() 

This code runs. At first glance nothing looks wrong. But going through it carefully, I found six issues — three of which explain the "silently disappearing data."

Review Findings: Six Issues

Issue 1: Subscribing Before Authentication Completes

on_open firing only means the TCP and WebSocket handshake succeeded — it does not mean the server has authenticated your token. The docs state clearly: wait for a resAc:"auth" message with code:1 before sending subscribe. This code subscribes immediately, when the server may still be validating the token. The subscription gets rejected without an error — a classic "no data but the program keeps running" cause.

This trap is extremely common with real-time WebSocket streaming APIs. REST is request-response: when something is wrong, you get an error code back. WebSocket is a long-lived connection — many integration problems surface no error at all, and you can only detect them by watching message types. When integrating a real-time quote API, treat "authenticated" as an explicit state transition, never assume a successful connect means you're ready to use.

Fix: add an authenticated state flag; only subscribe after auth succeeds.

authenticated = False def on_message(ws, message): global authenticated payload = json.loads(message) if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated: authenticated = True ws.send(json.dumps({ "ac": "subscribe", "params": "GC$US", "types": "quote" })) return 

Issue 2: No Heartbeat, Connection Killed by the Server

The futures WebSocket requires a heartbeat every 30 seconds ({"ac":"ping","params":"timestamp"}). If you don't consume messages or send heartbeats, the server will drop the connection. This code has zero heartbeat logic — after a few hours the server reaps the connection, and the program neither exits nor reconnects. It just looks like "the data vanished."

Gold futures differ from stocks mainly in trading hours: roughly 23 hours a day, with only about a one-hour maintenance break. In this scenario, long-lived connections are the norm, so heartbeat isn't optional — it's mandatory. Many people building precious metals monitoring systems skip the heartbeat initially and don't realize the data stopped until an hour or two later.

Fix: start a heartbeat thread after auth succeeds.

import threading def heartbeat(ws): while True: time.sleep(30) ws.send(json.dumps({ "ac": "ping", "params": str(int(time.time() * 1000)) })) # Start inside the auth-success branch threading.Thread(target=heartbeat, args=(ws,), daemon=True).start() 

Issue 3: Timestamp Treated as Seconds

data["t"] is a millisecond timestamp. The code does // 1000 to convert to seconds, then prints it raw without timezone conversion. The US gold session maps to Beijing evening hours; showing raw UTC timestamps makes every "what time did the alert fire" answer wrong, and breaks trading-session checks entirely.

Timestamp issues get worse when you handle cross-market, cross-instrument data. Gold, silver, platinum, and palladium trade across the Asian, European, and US sessions; a monitoring system often processes several markets at once. Time baselines must be unified and conversions explicit. Milliseconds-to-seconds, UTC-to-local, and DST handling should be isolated into utility functions, not scattered as inline divisions in business code.

Fix: convert explicitly to local time.

from datetime import datetime ts = data["t"] / 1000 local_time = datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") print(f"{data['s']}: {data['ld']} ({data['chp']}%) time={local_time}") 

Issue 4: Wrong Field Name

The code reads data['last'], but the actual quote field is ld (latest price). The result: the program runs, the connection is alive, data keeps flowing — but you never get the correct price. This is the most dangerous kind of bug because nothing reports it.

Field naming varies wildly across data sources. Some APIs use last, others price or close; "percent change" might be chp, change_pct, or percent_change. The first step of integrating any financial data API is reading the response field docs cover to cover and writing a field-mapping table before touching code. For futures quote APIs, the quote fields are s/ld/chp/o/h/l/v/tu/ts; K-line fields are t/o/h/l/c/v/tu. Memorizing these saves hours of debugging.

Fix: check field names against the docs — quote fields are s/ld/chp/o/h/l/v/tu/ts; K-line fields are t/o/h/l/c/v/tu.

Issue 5: No Reconnect Logic

When run_forever() exits after a connection error, the program just ends. Gold futures trade 23 hours a day; a disconnect at 3 AM with nobody watching means the data pipeline is dead until someone restarts it. Production code needs automatic reconnection.

One subtle point in reconnect logic: reset authenticated after reconnecting. A new connection requires fresh authentication; if the flag stays True from the previous session, subscribe will fire before auth completes — exactly Issue 1 again. That's why well-built real-time market data clients place this flag outside the connection loop and reset it on every connect.

Fix: wrap in a loop; on disconnect, reset authenticated (a new connection needs fresh auth) and retry.

while True: authenticated = False ws = websocket.WebSocketApp( "wss://api.itick.org/future", header=[f"token: {TOKEN}"], on_message=on_message ) try: ws.run_forever() except Exception: pass print("Connection lost, reconnecting in 5s...") time.sleep(5) 

Issue 6: Treating Market Closure as an API Outage

COMEX gold closes for about one hour daily, and there's no trade data in that window. Without any trading-status check, the first instinct when data goes quiet is "the API is broken." Quotes actually include a ts field for trading status — check the status before trusting the data.

Precious metals trading rules differ from stocks: stocks have fixed sessions with long closures and everyone expects them; near-24-hour products like gold have short closure windows that get overlooked. Add the ts trading-status check to your monitor, and distinguish "maintenance window" from "real outage" — that alone prevents many 3 AM false alarms.

Fix:

if data.get("type") == "quote": if data.get("ts") == 0: print("Market closed, skipping") return # process quote normally 

Review Checklist at a Glance

Here's the six issues condensed into a self-check table to run after writing your code:

# Check item Failure symptom Correct approach
1 Auth sequencing No data but program runs Wait for resAc:"auth" + code:1 before subscribe
2 Heartbeat Connection reaped after hours Send {"ac":"ping","params":"timestamp"} every 30s
3 Timestamp unit Wrong times, broken session checks ms-to-seconds + explicit local timezone
4 Field names Wrong prices, zero errors Check docs: quote ld, K-line c
5 Reconnect Program silently ends after 3 AM drop Loop + reset auth flag
6 Trading status Closures mistaken for outages Check ts before processing

This table applies to real-time quote integration for other products too — stocks, forex, indices, futures share the same integration framework; the differences are only in code formats and trading sessions.

The Fixed, Complete Code

import json import threading import time from datetime import datetime import websocket WS_URL = "wss://api.itick.org/future" TOKEN = "your_token_here" authenticated = False def on_message(ws, message): global authenticated payload = json.loads(message) if payload.get("resAc") == "auth" and payload.get("code") == 1 and not authenticated: authenticated = True ws.send(json.dumps({ "ac": "subscribe", "params": "GC$US", "types": "quote" })) threading.Thread(target=heartbeat, args=(ws,), daemon=True).start() return if payload.get("resAc") == "pong": return data = payload.get("data") or {} if data.get("type") == "quote": if data.get("ts") == 0: return # market closed  ts = datetime.fromtimestamp(data["t"] / 1000).strftime("%H:%M:%S") print(f"{data['s']}: {data['ld']} ({data['chp']}%) {ts}") def heartbeat(ws): while True: time.sleep(30) ws.send(json.dumps({"ac": "ping", "params": str(int(time.time() * 1000))})) while True: authenticated = False ws = websocket.WebSocketApp( WS_URL, header=[f"token: {TOKEN}"], on_message=on_message ) try: ws.run_forever() except Exception: pass print("Connection lost, reconnecting in 5s...") time.sleep(5) 

Where to Take This Code Next

Reviewing is only the first step. Dropping this code into a real gold market monitoring system usually means adding three more pieces:

Multi-instrument expansion. Watching only GC is rarely enough — silver, platinum, and palladium usually need to be tracked together. The WebSocket subscription supports comma-separated instruments (GC$US,SI$US), so one connection covers your whole precious metals portfolio without opening multiple sockets. On the REST side, the batch quote endpoint /future/quotes takes codes (plural) with comma-separated product codes — one request returns snapshots for many instruments, far more efficient than looping over single-quote calls.

Historical K-line coverage. Monitoring needs real-time quotes; backtesting and moving-average math need historical data. The REST /future/kline endpoint uses kType for period (1=1min, 2=5min, 5=1hour, 8=daily, 9=weekly, 10=monthly) and limit for bar count. Pull daily candles into a local database, incrementally update after close, and monitoring and backtesting share one data source.

Alert triggering. Once real-time quotes arrive, whatever logic you attach is easy: moving-average breakouts, price-range monitoring, relative-strength calculations. With the data layer solved, you can finally focus on strategy.

Review Takeaways

These problems are very representative: most "data mysteriously disappeared" incidents aren't API outages — they're small integration issues. Auth sequencing, heartbeat, field names, timezone, reconnection. Each issue looks trivial on its own, but stacked together you get "program running, data dropping, you hunting for the cause."

When writing market-data collectors, I now run a five-point self-check when I finish: did I wait for auth? Is the heartbeat running? Do field names match the docs? Are timestamps converted? Will it reconnect? Once those five are done, most integration traps are avoided.

For field and parameter details, I keep the official API docs open while writing code — way more reliable than trying to remember.

GitHub:https://github.com/itick-org/