2d3e524f1c
Claude-Session: https://claude.ai/code/session_01VPNF37MP9Kuv62B1VBrZ1b
111 lines
3.0 KiB
Python
111 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Test whether Twelve Data allows 2 simultaneous WebSocket connections for XAU/USD.
|
|
|
|
Uses the twelvedata SDK which handles the WS protocol correctly.
|
|
Runs connection 1, then adds connection 2 after 10s, monitors for 30s total.
|
|
"""
|
|
|
|
import threading
|
|
import time
|
|
|
|
from twelvedata import TDClient
|
|
|
|
from app_config import provider_api_key
|
|
|
|
API_KEY = provider_api_key()
|
|
SYMBOL = "XAU/USD"
|
|
DURATION = 30
|
|
|
|
results = {
|
|
"WS-1": {"ticks": 0, "errors": [], "alive": False},
|
|
"WS-2": {"ticks": 0, "errors": [], "alive": False},
|
|
}
|
|
|
|
|
|
def make_handlers(label):
|
|
def on_event(event):
|
|
if event.get("event") == "price":
|
|
results[label]["ticks"] += 1
|
|
price = event.get("price", "?")
|
|
print(f"[{label}] tick #{results[label]['ticks']:3d} price={price}")
|
|
|
|
def on_error(error):
|
|
results[label]["errors"].append(str(error))
|
|
print(f"[{label}] ERROR: {error}")
|
|
|
|
def on_close(status):
|
|
results[label]["alive"] = False
|
|
print(f"[{label}] CLOSED (status={status})")
|
|
|
|
def on_connect():
|
|
results[label]["alive"] = True
|
|
print(f"[{label}] CONNECTED — subscribed to {SYMBOL}")
|
|
|
|
return on_event, on_error, on_close, on_connect
|
|
|
|
|
|
def run_ws(label):
|
|
td = TDClient(apikey=API_KEY)
|
|
on_event, on_error, on_close, on_connect = make_handlers(label)
|
|
ws = td.websocket(
|
|
symbols=SYMBOL,
|
|
on_event=on_event,
|
|
on_error=on_error,
|
|
on_close=on_close,
|
|
on_connect=on_connect,
|
|
)
|
|
ws.connect()
|
|
return ws
|
|
|
|
|
|
def main():
|
|
print(f"Testing dual WebSocket for {SYMBOL} ({DURATION}s)\n")
|
|
|
|
print("Starting WS-1...")
|
|
ws1 = run_ws("WS-1")
|
|
|
|
time.sleep(10)
|
|
print(f"\nWS-1 ticks so far: {results['WS-1']['ticks']}")
|
|
print("Starting WS-2...\n")
|
|
ws2 = run_ws("WS-2")
|
|
|
|
remaining = DURATION - 10
|
|
time.sleep(remaining)
|
|
|
|
print("\nStopping connections...")
|
|
try:
|
|
ws1.disconnect()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
ws2.disconnect()
|
|
except Exception:
|
|
pass
|
|
|
|
time.sleep(1)
|
|
|
|
print("\n" + "=" * 50)
|
|
print("RESULTS")
|
|
print("=" * 50)
|
|
for label, info in sorted(results.items()):
|
|
status = "alive" if info["alive"] else "dead"
|
|
errs = info["errors"] or ["none"]
|
|
print(f" {label}: {status} ticks={info['ticks']} errors={errs[0]}")
|
|
|
|
ws1_ok = results["WS-1"]["ticks"] > 0 and not results["WS-1"]["errors"]
|
|
ws2_ok = results["WS-2"]["ticks"] > 0 and not results["WS-2"]["errors"]
|
|
ws1_died = results["WS-1"]["alive"] is False and results["WS-1"]["ticks"] > 0
|
|
|
|
if ws1_ok and ws2_ok:
|
|
print("\n✓ Both connections worked — dual WS is supported")
|
|
elif ws2_ok and ws1_died:
|
|
print("\n✗ WS-2 replaced WS-1 — only 1 connection allowed at a time")
|
|
elif ws1_ok and not ws2_ok:
|
|
print("\n✗ WS-2 was rejected — only 1 connection allowed")
|
|
else:
|
|
print("\n? Inconclusive — check errors above")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|