Skip to content

Commit d3ec11b

Browse files
userFRMclaude
andauthored
v5.1.0: contract IDs, 8-field trades, repr(C) FPSS, zero-JSON FFI
Closes #82 Closes #84 Closes #86 Closes #92 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f3118b3 commit d3ec11b

14 files changed

Lines changed: 41 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
### Changed
10+
## [5.1.0] - 2026-04-03
11+
12+
### Breaking Changes
1113

1214
- **FPSS FFI events now use `#[repr(C)]` typed structs** instead of JSON serialization. `tdx_fpss_next_event` and `tdx_unified_next_event` return `*mut TdxFpssEvent` (a flat tagged struct with quote, trade, open interest, OHLCVC, control, and raw_data variants). Free with `tdx_fpss_event_free`. (#82)
1315
- C++ SDK: `FpssClient::next_event()` returns `FpssEventPtr` (RAII unique_ptr to `TdxFpssEvent`).
1416
- Go SDK: `FpssClient.NextEvent()` returns `*FpssEvent` with typed Go structs.
1517
- Streaming event prices are now raw integers with `price_type` (matching the wire format). Callers decode with `Price::new(value, price_type).to_f64()` or `tdx::price_to_f64(value, price_type)`.
18+
- `serde_json` removed from FFI crate dependencies -- zero JSON crosses the FFI boundary.
19+
20+
### Added
21+
22+
- **Contract identification on all 10 option tick types** -- `expiration`, `strike`, `right`, `strike_price_type` fields populated by the server on wildcard queries. Helper methods `strike_price()`, `is_call()`, `is_put()`, `has_contract_id()` on all 10 tick types via `impl_contract_id!` macro. (#84)
23+
- **8-field trade tick support** -- FPSS dev server sends abbreviated 8-field trade ticks; production sends 16-field. `decode_tick()` now auto-detects the field count from the first absolute tick per contract and dispatches to the correct index mapping. (#86)
24+
- **`#[repr(C)]` FPSS event structs** in all SDKs -- `TdxFpssQuote`, `TdxFpssTrade`, `TdxFpssOpenInterest`, `TdxFpssOhlcvc`, `TdxFpssControl`, `TdxFpssRawData` with tagged `TdxFpssEvent` wrapper. (#82)
25+
- `FfiBufferedEvent` with owned backing storage for safe cross-thread `Send` of pointer-containing structs.
26+
- Go SDK: `FpssQuote`, `FpssTrade`, `FpssOpenInterestData`, `FpssOhlcvc`, `FpssControlData` Go structs mirroring Rust `#[repr(C)]` layout.
27+
- C++ SDK: `FpssClient` class with RAII `FpssEventPtr` for streaming.
28+
- Python SDK: `greeks_tick_to_dict` now emits all 24 fields (was 8). (#92)
29+
- `tdbe`: contract ID fields and `impl_contract_id!` macro on all 10 tick types.
30+
31+
### Fixed
32+
33+
- **9 stale JSON references** in FFI doc comments, FFI README, Go README, docs-site API reference, and macro guide -- all now correctly describe typed structs. (#92)
34+
- Python SDK `greeks_tick_to_dict` missing 16 fields (vanna, charm, vomma, veta, speed, zomma, color, ultima, d1, d2, dual_delta, dual_gamma, epsilon, lambda, vera, date). (#92)
35+
- Go SDK README documented `ActiveSubscriptions()` return type as `json.RawMessage` -- actually returns `[]Subscription`. (#92)
36+
- docs-site Go streaming example said "returns json.RawMessage or nil" -- now says "*FpssEvent or nil".
1637

1738
## [5.0.2] - 2026-04-03
1839

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/thetadatadx/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thetadatadx"
3-
version = "5.0.2"
3+
version = "5.1.0"
44
edition = "2021"
55
rust-version = "1.85"
66
authors = ["userFRM"]

docs-site/docs/api-reference.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,7 +2217,7 @@ if event:
22172217
print(event)
22182218
```
22192219
```go [Go]
2220-
event, err := fpss.NextEvent(5000) // returns json.RawMessage or nil
2220+
event, err := fpss.NextEvent(5000) // returns *FpssEvent or nil
22212221
```
22222222
```cpp [C++]
22232223
std::string event = fpss.next_event(5000); // empty string on timeout
@@ -2247,7 +2247,7 @@ fpss.shutdown();
22472247
|--------|---------|-------------|
22482248
| `is_streaming` | bool | Check if the streaming connection is live |
22492249
| `contract_map` / `contract_lookup` | map/string | Look up server-assigned contract IDs |
2250-
| `active_subscriptions` | list/JSON | Get list of active subscriptions |
2250+
| `active_subscriptions` | list/typed structs | Get list of active subscriptions |
22512251

22522252
### FpssEvent Types
22532253

docs/macro-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ The FFI layer uses its own macro set to wrap the Rust builders into
224224
| `tick_array_free!` | Generates the `extern "C"` free function for a tick array |
225225
| `ffi_typed_endpoint!` | Wraps a typed endpoint with C string params |
226226
| `ffi_typed_endpoint_no_params!`| Wraps a typed endpoint with no params |
227-
| `ffi_typed_snapshot_endpoint!` | Wraps a snapshot endpoint (takes JSON array of symbols)|
227+
| `ffi_typed_snapshot_endpoint!` | Wraps a snapshot endpoint (takes C string array of symbols)|
228228
| `ffi_list_endpoint!` | Wraps a list endpoint with C string params |
229229
| `ffi_list_endpoint_no_params!` | Wraps a list endpoint with no params |
230230

ffi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thetadatadx-ffi"
3-
version = "5.0.2"
3+
version = "5.1.0"
44
edition = "2021"
55
description = "C FFI layer for thetadatadx — used by Go and C++ SDKs"
66
license = "GPL-3.0-or-later"

ffi/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ All 61 endpoints are available as `tdx_stock_*`, `tdx_option_*`, `tdx_index_*`,
5151
| `tdx_unified_unsubscribe_full_open_interest` | Unsubscribe from firehose open interest stream |
5252
| `tdx_unified_is_streaming` | Check if FPSS connection is live |
5353
| `tdx_unified_contract_lookup` | Look up contract by ID |
54-
| `tdx_unified_active_subscriptions` | List active subscriptions (JSON) |
55-
| `tdx_unified_next_event` | Poll for next event (JSON, blocks with timeout) |
54+
| `tdx_unified_active_subscriptions` | List active subscriptions (typed `TdxSubscriptionArray`) |
55+
| `tdx_unified_next_event` | Poll for next event (`*mut TdxFpssEvent`, blocks with timeout) |
5656
| `tdx_unified_stop_streaming` | Stop streaming, historical stays alive |
5757
| `tdx_unified_free` | Free the unified handle |
5858

ffi/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub struct TdxUnified {
106106
///
107107
/// Uses the same pattern as the Python SDK: an internal mpsc channel buffering
108108
/// events from the Disruptor callback, and `tdx_fpss_next_event` polls it with
109-
/// a timeout, returning JSON.
109+
/// a timeout, returning a `*mut TdxFpssEvent` typed struct.
110110
pub struct TdxFpssHandle {
111111
inner: Arc<Mutex<Option<thetadatadx::fpss::FpssClient>>>,
112112
rx: Arc<Mutex<std::sync::mpsc::Receiver<FfiBufferedEvent>>>,
@@ -2296,7 +2296,7 @@ pub unsafe extern "C" fn tdx_unified_is_streaming(handle: *const TdxUnified) ->
22962296
}
22972297
}
22982298

2299-
/// Look up a contract by ID. Returns JSON string or null.
2299+
/// Look up a contract by ID. Returns a Display-formatted C string or null.
23002300
#[no_mangle]
23012301
pub unsafe extern "C" fn tdx_unified_contract_lookup(
23022302
handle: *const TdxUnified,
@@ -2934,7 +2934,7 @@ pub unsafe extern "C" fn tdx_fpss_is_authenticated(handle: *const TdxFpssHandle)
29342934

29352935
/// Look up a single contract by its server-assigned ID.
29362936
///
2937-
/// Returns a JSON string representation of the contract, or NULL if not found.
2937+
/// Returns a Display-formatted C string representation of the contract, or NULL if not found.
29382938
/// Caller must free the returned string with `tdx_string_free`.
29392939
#[no_mangle]
29402940
pub unsafe extern "C" fn tdx_fpss_contract_lookup(

sdks/go/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ Prices in streaming events are raw integers with a `PriceType` field. Decode usi
340340
| `UnsubscribeFullOpenInterest(secType)` | `(int, error)` | Unsubscribe from all OI for a security type |
341341
| `IsAuthenticated()` | `bool` | Check if FPSS client is authenticated |
342342
| `ContractLookup(id)` | `(string, error)` | Look up contract by server-assigned ID |
343-
| `ActiveSubscriptions()` | `(json.RawMessage, error)` | List currently active subscriptions |
343+
| `ActiveSubscriptions()` | `([]Subscription, error)` | List currently active subscriptions |
344344
| `NextEvent(timeoutMs)` | `(*FpssEvent, error)` | Poll next event as typed struct (nil on timeout) |
345345
| `Shutdown()` | | Graceful shutdown of streaming |
346346
| `Close()` | | Free the FPSS handle (call after Shutdown) |

sdks/python/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "thetadatadx-py"
3-
version = "5.0.2"
3+
version = "5.1.0"
44
edition = "2021"
55
description = "Python bindings for thetadatadx — native ThetaData SDK powered by Rust"
66

0 commit comments

Comments
 (0)