From 5a0af663c165e399d809ceb530144f6f5677c70d Mon Sep 17 00:00:00 2001 From: harwinder Date: Wed, 8 Jul 2026 21:31:25 -0400 Subject: [PATCH] Add WebSocket dual-connection test script Probes whether Twelve Data allows two simultaneous WS connections for one symbol (SDK-based, 30s run, prints per-connection verdict). Co-Authored-By: Claude Fable 5 --- test_ws_dual.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test_ws_dual.py diff --git a/test_ws_dual.py b/test_ws_dual.py new file mode 100644 index 0000000..8ac9544 --- /dev/null +++ b/test_ws_dual.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Test whether Twelve Data allows 2 simultaneous WebSocket connections for EUR/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 + +API_KEY = "f563b22ffdfd4924955decbe1a8c8701" +SYMBOL = "EUR/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()