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
docs: comprehensive documentation audit -- 14 fixes across 13 files
Findings and fixes:
- SECURITY.md: version table said 4.5.x current, updated to 5.2.x with
accurate support matrix including Go price formula bug note
- CHANGELOG.md: missing [5.1.1] release entry (link ref existed but no
section) -- added entry documenting tdbe 0.2.0 crates.io publish fix
- Go streaming docs: 4 files still showed PriceToF64() on pre-decoded
float64 fields (would produce wrong results since v5.2). Updated
streaming.md, streaming/index.md, streaming/reconnection.md examples
to use pre-decoded fields directly
- options.md: wildcard docs said right must be "C" or "P" -- added
"both" as valid value (from normalize_right)
- Go/C++ SDK READMEs: Config section listed mixed-language syntax
(Config.stage()/StageConfig()/Config::stage()) -- each now shows
only its own language
- Go/C++ SDK READMEs: GreeksTick type missing Vera field (present in
code since v4.5.0) -- added
- C++ README: Greeks standalone struct missing vera field -- added
- docs/architecture.md, jvm-deviations.md, reverse-engineering.md,
api-reference.md: macro name define_endpoint! renamed to
parsed_endpoint! (actual current macro name)
- streaming/latency.md: replaced ASCII diagram with Mermaid sequence
diagram and latency breakdown flowchart. Added network physics
section with fiber optic speed-of-light calculations for 8 locations.
Added dev server warning (historical replay timestamps are from the
past, not valid for latency measurement)
- streaming/index.md: replaced ASCII architecture diagram with Mermaid
- option/index.md: "At-Time OHLC" link text corrected to "At-Time
Quote" (file contains option_at_time_quote, not OHLC)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When you pass `"0"` for `expiration` or `strike`, the server returns data across all matching contracts. Each tick includes contract identification fields (`expiration`, `strike`, `right`, `strike_price_type`) so you can distinguish which contract each tick belongs to.
143
143
144
144
::: warning
145
-
The `right` parameter does **not**support wildcards. You must specify `"C"` (call) or `"P"` (put). Only `expiration` and `strike` accept `"0"` as a wildcard.
145
+
The `right` parameter does **not**accept `"0"` as a wildcard. Use `"C"` (call), `"P"` (put), or `"both"` (calls and puts). Only `expiration` and `strike` accept `"0"` as a wildcard.
Copy file name to clipboardExpand all lines: docs-site/docs/streaming.md
+6-7Lines changed: 6 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ Each SDK exposes FPSS differently:
11
11
12
12
-**Rust** -- Fully synchronous callback model. Events dispatched through an LMAX Disruptor ring buffer. No Tokio on the streaming hot path.
13
13
-**Python** -- Polling model with `next_event()`. Events returned as Python dicts with all fields.
14
-
-**Go** -- Polling model with `NextEvent()`. Events returned as typed `*FpssEvent` structs. Use `PriceToF64()` for price decoding.
14
+
-**Go** -- Polling model with `NextEvent()`. Events returned as typed `*FpssEvent` structs. Price fields are pre-decoded to `float64`; raw integers available as `*Raw` fields.
15
15
-**C++** -- Polling model with `next_event()`. Events returned as `FpssEventPtr` (`unique_ptr<TdxFpssEvent>`, RAII). `#[repr(C)]` layout-compatible structs.
Helper: `PriceToF64(value int32, priceType int32) float64` -- decode raw integer prices. Note: FPSS event price fields are pre-decoded to `float64` as of v5.2; this helper is for custom use cases or raw field decoding.
B -->|"TLS/TCP"| C["SDK I/O Thread<br/>(FIT decode)"]
16
+
C -->|"Disruptor<br/>ring buffer"| D["Your Application<br/>(callback / poll)"]
15
17
```
16
18
17
19
Events are decoded from the FIT wire format and delta-decompressed on an I/O thread, then dispatched through an LMAX Disruptor ring buffer to your callback (Rust) or polling queue (Python/Go/C++). Every data event carries a `received_at_ns` nanosecond timestamp captured at frame decode time.
@@ -22,7 +24,7 @@ Events are decoded from the FIT wire format and delta-decompressed on an I/O thr
22
24
|-----|-------|------------|---------|
23
25
|**Rust**| Synchronous callback |`&FpssEvent` enum | Disruptor ring buffer dispatch. No Tokio on the hot path. |
24
26
|**Python**| Polling |`dict`|`next_event()` returns events as Python dicts with all fields. |
25
-
|**Go**| Polling |`*FpssEvent` struct |`NextEvent()` returns typed Go structs. `PriceToF64()` for price decoding. |
27
+
|**Go**| Polling |`*FpssEvent` struct |`NextEvent()` returns typed Go structs. Price fields pre-decoded to `float64`. |
Copy file name to clipboardExpand all lines: docs-site/docs/streaming/latency.md
+58-7Lines changed: 58 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,17 +11,41 @@ Combined with the exchange's `ms_of_day` timestamp on each tick, this gives you
11
11
12
12
## How it works
13
13
14
-
```
15
-
Exchange (NJ) ──── ThetaData FPSS server ──── TLS/TCP ──── Your application
16
-
| |
17
-
ms_of_day received_at_ns
18
-
(exchange clock) (your clock)
19
-
20
-
latency = received_at_ns - exchange_timestamp_ns
14
+
```mermaid
15
+
sequenceDiagram
16
+
participant Exchange as Exchange (NYSE/NASDAQ)
17
+
participant FPSS as ThetaData FPSS (NJ)
18
+
participant SDK as ThetaDataDx SDK
19
+
participant App as User Application
20
+
21
+
Exchange->>FPSS: Market data feed
22
+
Note over FPSS: FIT encode + delta compress
23
+
FPSS->>SDK: TLS/TCP frame
24
+
Note over SDK: received_at_ns captured
25
+
SDK->>SDK: FIT decode + delta decompress
26
+
SDK->>App: Callback (FpssEvent)
27
+
Note over App: latency = received_at_ns - exchange_ns
21
28
```
22
29
23
30
The exchange stamps each quote/trade with `ms_of_day` (milliseconds since midnight ET). Your application stamps `received_at_ns` (nanoseconds since UNIX epoch). The difference is your total latency: exchange -> ThetaData -> network -> TLS -> decode -> your callback.
B --> C["Network Transit<br/>(physics: distance/c)"]
36
+
C --> D["SDK Decode<br/>(< 1 us)"]
37
+
D --> E["User Callback"]
38
+
39
+
style C fill:#ff9999,color:#000
40
+
style D fill:#99ff99,color:#000
41
+
```
42
+
43
+
The network transit segment (red) dominates total latency. The SDK decode time (green) is sub-microsecond and negligible.
44
+
45
+
::: danger Production only
46
+
Latency can only be measured meaningfully on the **production** FPSS server (`DirectConfig::production()`, port 20000) **during live market hours** (9:30 AM - 4:00 PM ET). The dev server (port 20200) replays historical data from a past trading day at maximum speed -- the exchange timestamps are from the past, so `received_at_ns` minus the event's original timestamp produces values that are months or years, not real latency. The dev server is for functional testing only, not latency benchmarking.
47
+
:::
48
+
25
49
## `tdbe::latency::latency_ns()`
26
50
27
51
The `tdbe` crate provides a DST-aware helper that converts the exchange `ms_of_day` + `date` into epoch nanoseconds and computes the delta:
@@ -135,6 +159,33 @@ For the absolute lowest latency:
135
159
136
160
3.**Use the Rust SDK directly** -- Python, Go, and C++ add an mpsc channel hop between the Disruptor and `next_event()`.
137
161
162
+
## Network Physics: Minimum Achievable Latency
163
+
164
+
ThetaData's FPSS servers are located in New Jersey (NJ datacenter). The speed of light in fiber optic cable is approximately 200,000 km/s (about 2/3 of the vacuum speed of light, due to the refractive index of glass). This sets an absolute physical floor on latency that no software optimization can overcome.
165
+
166
+
The formula: `minimum_round_trip = distance_km / (300,000 * 0.67) * 2 * 1000` (in milliseconds).
167
+
168
+
| Your Location | Distance to NJ | Minimum Round-Trip | Typical Observed |
| AWS us-east-1 (Virginia) |~350 km |~3.5 ms | 2-5 ms |
171
+
| NJ/NYC datacenter | <50 km | <0.5 ms | <1 ms |
172
+
| Chicago |~1,200 km |~12 ms | 10-15 ms |
173
+
| Los Angeles |~3,900 km |~39 ms | 35-50 ms |
174
+
| London |~5,600 km |~56 ms | 55-70 ms |
175
+
| Frankfurt |~6,200 km |~62 ms | 60-80 ms |
176
+
| Tokyo |~10,800 km |~108 ms | 105-130 ms |
177
+
| Sydney |~16,000 km |~160 ms | 155-180 ms |
178
+
179
+
If you are seeing 60-80ms latency from Europe, that is not a bug -- it is the speed of light in fiber. No SDK, no protocol change, no configuration tweak can make photons travel faster.
180
+
181
+
The SDK's own overhead (`received_at_ns` capture, FIT decode, Disruptor dispatch, callback invocation) is sub-microsecond and entirely negligible compared to network physics.
182
+
183
+
For latency-sensitive applications:
184
+
185
+
1.**Colocate near NJ** -- AWS us-east-1 (N. Virginia) or any NJ/NYC-area datacenter gets sub-5ms
186
+
2.**`FpssFlushMode::Immediate`** reduces software batching latency by up to 100ms, but cannot beat physics
187
+
3.**Use the Rust SDK directly** -- eliminates the FFI channel hop present in Python/Go/C++ (adds <1ms)
Copy file name to clipboardExpand all lines: docs/api-reference.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
@@ -657,7 +657,7 @@ Nexus HTTP responses with status 401 (Unauthorized) or 404 (Not Found) are treat
657
657
658
658
### Endpoint Count
659
659
660
-
ThetaDataDx exposes **61 typed methods** (plus 4 `_stream` variants) covering all 60 gRPC RPCs in `BetaThetaTerminal` plus 1 convenience range-query variant (`stock_history_ohlc_range`). Historical methods are provided via `Deref<Target = DirectClient>` (an internal implementation detail) and generated by the `define_endpoint!` macro in `direct.rs`.
660
+
ThetaDataDx exposes **61 typed methods** (plus 4 `_stream` variants) covering all 60 gRPC RPCs in `BetaThetaTerminal` plus 1 convenience range-query variant (`stock_history_ohlc_range`). Historical methods are provided via `Deref<Target = DirectClient>` (an internal implementation detail) and generated by the `parsed_endpoint!` macro in `direct.rs`.
0 commit comments