WebSocket streams

Real-time market data and per-session event channels.

A single WebSocket endpoint streams live market data and per-session events. It's what keeps the live trading screen and the strategy results progress current in real time.

Endpoint

wss://api.0dtespx.com/__ws

Handshake

After connecting, you have 5 seconds to send an auth message — either anonymous or token-authenticated:

{"auth": "public"}
{"token": "8a4f3e21d6c94b0e9f2a1c5b7d8e4f60"}

A freshly authenticated connection has no subscriptions; you must explicitly subscribe to each channel you want. channels (array) and channel (single) are both accepted on subscribe and unsubscribe:

{"action": "subscribe",   "channels": ["live_aggregate_data", "live_option_chain"]}
{"action": "unsubscribe", "channel":  "live_option_chain"}
Connection lifecycle: connect → auth (≤5 s) → subscribe → frames client wss://api.0dtespx.com/__ws {"token": "…"} — within 5 s of connecting {"action":"subscribe","channels":["live_aggregate_data","live_option_chain"]} {"channel":"live_aggregate_data","payload":{…}} every second {"channel":"live_option_chain","payload":[…]} every second {"channel":"session_events","id":42,"type":"order_update",…} on change

Wire format

Every server-sent message is a JSON object with a top-level channel field — dispatch on that, never on payload shape. Market-data channels nest their data under payload; event channels put id, type/data as siblings of channel:

{"channel": "live_aggregate_data", "payload": { … }}
{"channel": "live_option_chain",   "payload": [{"strike": 5950, "call": {"bid": 4.2, "ask": 4.4, "delta": 0.523}, "put": {"bid": 3.1, "ask": 3.3, "delta": 0.477}}, … ]}
{"channel": "session_events",      "id": 42, "session_id": "<uuid>", "account_id": "<uuid>", "type": "order_update", "payload": { … }}
{"channel": "end_of_data"}

Channels

Channel Cadence Access
live_aggregate_data 1-second Public + authenticated
live_option_chain 1-second Token-authenticated only
session_events Event-driven Token-authenticated, per session
backtest_events Event-driven Token-authenticated, per strategy (source hash)
assistant_draft_events Event-driven Token-authenticated, per draft (owner only)
end_of_data Terminal
  • live_aggregate_data — the 1-second aggregate market feed: SPX, VIX, the expected move, and the two chain-wide premium totals (summed OTM bids and total extrinsic value) — the same series the historical endpoint serves for past sessions.
  • live_option_chain — the latest SPX option-chain snapshot. Each strike's call and put carry bid, ask, and delta — the same fields the REST snapshot endpoint GET /market-data/option-chain-snapshots/{timestamp} returns. (Second-order Greeks, IV, and theoretical price are no longer provided.)
  • session_events — discrete state changes for a live session: order_update (status transitions), financials_update (cash, buying power, realized P&L), ended, and settlement_failed. Subscribe per session with {"action":"subscribe","channel":"session_events","session_id":"<uuid>"}; each envelope carries top-level session_id and account_id so clients route to per-session state. Per-tick continuous values (P&L, delta) aren't pushed here — compute them from live_option_chain.
  • backtest_eventsstrategy results progress for one strategy's backtest. Subscribe per strategy with its source_hash: {"action":"subscribe","channel":"backtest_events","source_hash":"<hex>"} (authorized against a saved strategy of yours with that source, or your live builder preview). Each frame carries the full fee-overlaid results snapshot under data.snapshot (the same shape GET /strategies/preview/{source_hash}/results returns), folded for you — render straight from it, no per-tick refetch. data.type is one of started, day_completed, completed, stopped, failed. The HTTP results endpoint stays the source of truth for the initial load and reconnect. A dropped socket never stops a run; reconnect and re-subscribe.
  • assistant_draft_events — the AI assistant chat stream for one of your drafts. Subscribe with the draft id: {"action":"subscribe","channel":"assistant_draft_events","draft_id":"<uuid>"} (authorized against your ownership of the draft). data.type is one of delta (assistant text chunk), thinking (a chunk of the model's streamed reasoning while it works — display-only, not part of the final message), tool_status, ask_user, message (the finalized assistant message), preview ({source_hash, description, risks} — the live preview moved to a new hash and the strategy description + risk scenarios were updated with it; re-key your backtest_events subscription to it), turn_complete, error. A reconnect may supply last_event_by_draft (draft id → highest seen id) to replay the gap.

Event ordering and recovery

Event-channel envelopes carry a monotonically increasing id. backtest_events deliberately has no replay — but it needs none: each frame carries the full current snapshot, so a missed frame self-heals on the next one, and the HTTP results endpoint backs the initial load and reconnect. For live-session state after a disconnect, refetch positions, orders, and transactions over REST to be safe.

Connection limits

  • Anonymous ({"auth":"public"}) connections stream for at most 5 minutes, after which the server sends {"channel":"end_of_data"} and closes. They may subscribe to live_aggregate_data but not live_option_chain.
  • Token-authenticated accounts have no concurrency limit and no time cap.

Quick test

( echo '{"auth":"public"}'; cat ) | websocat 'wss://api.0dtespx.com/__ws'

Subscribe messages sent before auth completes are ignored, and unknown channel names are silently dropped.