You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(server): return ThetaData stream-verification values from the websocket bridge (#807)
* fix(server): return ThetaData stream-verification values from the websocket bridge
The bundled WebSocket bridge acknowledged every stream request with `OK`, which is not one of the stream-request verification values ThetaData clients match on. A successful subscribe, unsubscribe, or stop now acknowledges with `SUBSCRIBED`.
It also returned `OK` when a subscribe arrived before streaming had started, even though nothing was installed. That was a false-positive acknowledgement: the client believed it was subscribed while no feed existed. That path now returns `ERROR` with a message saying streaming is not started.
The acknowledgement value is built from a small internal enum so the wire strings live in one place. ThetaData also documents `MAX_STREAMS_REACHED` and `INVALID_PERMS`, but the upstream FPSS error type does not carry enough to tell those apart from a generic failure, so both currently fall through to `ERROR` with a descriptive message; the specific values can be emitted once the error type surfaces the category.
Closes#801
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(server): the websocket strike field is in dollars, not thousandths
The websocket bridge parses the option strike as dollars, but the docs still showed thousandths (570000) and called it the one exception to the dollars-everywhere rule, so a client following the docs would subscribe to the wrong contract. Show the strike as a dollar number.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: the websocket strike envelope is dollars across the streaming docs
The strike-dollars change made the bundled server's websocket subscribe envelope take dollars, but the per-endpoint streaming option docs and the symbology page still showed thousandths (570000) and called the websocket the one exception to dollars-everywhere. Correct the examples and prose so a websocket client sends the dollar value.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(docs): emit the websocket strike envelope in dollars from the generator
The streaming-options doc generator hard-coded the websocket subscribe
example with the strike as a scaled integer (570000) and prose calling
it thousandths, so a client copying the published envelope subscribed
to a $570,000 strike instead of the $570 option. The server's own
subscribe path takes dollars, so the docs contradicted the code.
Emit the strike in dollars (570) from the three option stream specs and
rewrite the envelope note to match. Invert the docs-consistency guard so
it bans the thousandths vocabulary and the 570000 literal on every page
that carries a websocket subscribe envelope, instead of whitelisting the
thousandths note as an allowed exception.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(server): publish stream-verification tokens and make WS STOP apply
The WebSocket REQ_RESPONSE and DISCONNECTED frames rendered their outcome via the Rust Debug form, putting internal variant identifiers (`Subscribed`, `MaxStreamsReached`, `InvalidPerms`, and the `RemoveReason` names) onto the wire where clients expect the documented stream-verification vocabulary. Give `StreamResponseType` and `RemoveReason` an explicit `as_wire_str` mapping (the single source of `SUBSCRIBED` / `ERROR` / `MAX_STREAMS_REACHED` / `INVALID_PERMS` and the SCREAMING_SNAKE disconnect tokens) and emit that, so the variant identifier can never reach a client. `StreamResponseType` also gains a `Display` impl routed through the same mapping.
STOP previously acknowledged `SUBSCRIBED` unconditionally — even when streaming was never started or no stream existed — which told the client its feed was gone when nothing changed. STOP is a removal, not a status query, so it now snapshots the live subscription set, removes it, and acknowledges success only when the removal actually applied; an inactive session or a failed unsubscribe surfaces `ERROR` with a diagnostic, matching the subscribe path's discipline.
Correct the index price streaming doc example, which subscribed `Contract.stock("SPX")` for an index; SPX is an index, so the example now uses `Contract.index("SPX")` to match the other language rows. Regenerate the checked-in docs page from the corrected source.
Unit tests assert each enum variant maps to its exact wire token (the regression guard against the Debug leak), the STOP error/success token contract, and that no variant identifier appears in the serialized frame.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -69,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
69
69
- The CLI's raw OHLC output (`--format json-raw` / `csv`) emits the `vwap` value in its own column. The raw value row was one field short of its header set, so `vwap` was dropped and `date`, `expiration`, `strike`, and `right` rendered under the wrong column names. The typed SDK surfaces and the CLI's presentation `json` were unaffected.
70
70
- Python — the standalone `StreamingClient` streaming connect, reconnect, and subscribe / unsubscribe paths release the GIL across their blocking I/O (the TLS connect and handshake, and the per-subscription wire write), so other Python threads keep running while a connect or subscribe is in flight; the typed exception raised on failure is unchanged.
71
71
- The standalone Python and TypeScript streaming clients now forward the full streaming and reconnect config, so every tuning knob — including the wait strategy and consumer-core affinity, host selection, watchdog and keepalive cadences, and the reconnect backoff and replay pacing — is honored, matching the unified client and the C ABI.
72
+
- The bundled WebSocket server acknowledges stream requests with ThetaData's stream-verification values: a successful subscribe, unsubscribe, or stop now returns `SUBSCRIBED` instead of `OK`, and a subscribe that arrives before streaming has started returns `ERROR` with a descriptive message instead of a false-positive `OK` that claimed success while installing nothing.
prose:"Streams every index value update. Indices publish price prints through the trade feed, so each update delivers a `Trade` event whose `price` field carries the index value. Indices have no full-stream broadcast; subscribe per index.",
Copy file name to clipboardExpand all lines: docs-site/docs/articles/symbology.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,7 @@ An option contract is always the four-tuple **(symbol, expiration, strike, right
22
22
23
23
Strikes are **human dollars everywhere on the SDK surface** — never scaled integers. Endpoints that take an optional `strike` / `right` treat omission as a wildcard: all strikes, both rights. Wildcard responses identify each row's contract via the `expiration`, `strike`, and `right` response fields.
24
24
25
-
The one place a scaled strike survives: the server's [WebSocket subscribe envelope](/server/websocket)takes thousandths of a dollar (`570000` = $570.00), matching the upstream wire convention.
25
+
Strikes are dollars across every surface, including the bundled server's [WebSocket subscribe envelope](/server/websocket)(`570` = $570.00). The scaled-integer form only exists on the raw upstream wire, which the SDK and the server both hide.
Copy file name to clipboardExpand all lines: docs-site/docs/changelog.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -69,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
69
69
- The CLI's raw OHLC output (`--format json-raw` / `csv`) emits the `vwap` value in its own column. The raw value row was one field short of its header set, so `vwap` was dropped and `date`, `expiration`, `strike`, and `right` rendered under the wrong column names. The typed SDK surfaces and the CLI's presentation `json` were unaffected.
70
70
- Python — the standalone `StreamingClient` streaming connect, reconnect, and subscribe / unsubscribe paths release the GIL across their blocking I/O (the TLS connect and handshake, and the per-subscription wire write), so other Python threads keep running while a connect or subscribe is in flight; the typed exception raised on failure is unchanged.
71
71
- The standalone Python and TypeScript streaming clients now forward the full streaming and reconnect config, so every tuning knob — including the wait strategy and consumer-core affinity, host selection, watchdog and keepalive cadences, and the reconnect backoff and replay pacing — is honored, matching the unified client and the C ABI.
72
+
- The bundled WebSocket server acknowledges stream requests with ThetaData's stream-verification values: a successful subscribe, unsubscribe, or stop now returns `SUBSCRIBED` instead of `OK`, and a subscribe that arrives before streaming has started returns `ERROR` with a descriptive message instead of a false-positive `OK` that claimed success while installing nothing.
Copy file name to clipboardExpand all lines: docs-site/docs/server/websocket.md
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,19 +28,19 @@ The server bridges [streaming](/streaming/) onto a local WebSocket at `ws://127.
28
28
|`id`| Your request id; echoed in the acknowledgement |
29
29
|`contract`| Omit for `FULL_*` streams |
30
30
31
-
Option contracts carry the four-tuple, with the strike in **thousandths of a dollar** (the one wire-format exception to the dollars-everywhere rule — see [Symbology](/articles/symbology)):
31
+
Option contracts carry the four-tuple, with the strike in **dollars** (a JSON number, e.g. `570` or `570.0`):
`{"msg_type": "STOP", "id": 2}` removes every active stream at once. Each command is acknowledged:
37
+
`{"msg_type": "STOP", "id": 2}` removes every active stream at once. Each command is acknowledged with a stream-request verification value in the `response` field:
Invalid commands answer with `"response": "ERROR"` and a message naming the offending field.
43
+
`SUBSCRIBED` confirms the request was accepted; it acknowledges subscribe (`add: true`), unsubscribe (`add: false`), and `STOP`, since there is no removal-specific value. Rejected commands answer with `"response": "ERROR"` and an `error` field naming the cause — a bad envelope, an offending field, or a subscribe sent before streaming has started (which installs nothing, so it is never acknowledged as a success). The values `MAX_STREAMS_REACHED` and `INVALID_PERMS` are also part of the verification vocabulary.
44
44
45
45
## Event messages
46
46
@@ -50,7 +50,7 @@ Events arrive as JSON with a `header.type` of `QUOTE`, `TRADE`, `OHLC`, or `OPEN
0 commit comments