Skip to content

v12.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 11 Jun 16:52
· 160 commits to main since this release
ce160db

ThetaDataDx 12.0.0

Major release. The FPSS streaming surface is reshaped around a fluent builder, a typed FpssError enum, and an iterator on the client itself. The three earlier delivery modes (push callback over a queue, pull iterator over a queue, direct ring poller) collapse to one primitive. The companion tdbe crate is unchanged at 0.14.0.

Public surface

let client = FpssClient::builder(&creds, &hosts)
    .ring_size(8192)
    .read_timeout_ms(15_000)
    .build()?;

client.subscribe(Contract::stock("AAPL").quote())?;

for event in &client {
    match event? {
        FpssEvent::Data(data)       => { /* ... */ }
        FpssEvent::Control(control) => { /* ... */ }
    }
}

Drain can also be driven via client.next_event() (blocking), client.try_next_event() (non-blocking), client.poll_batch(|event| ...) (non-blocking single batch through a closure), or client.for_each(|event| ...) (blocking loop until the ring shuts down).

Breaking changes

  • FpssClient::builder(creds, hosts) is the sole public constructor. The previous FpssClient::connect(args) entry point and the FpssConnectArgs struct-literal arg bundle are crate-internal.
  • Drain primitives live on FpssClient itself: next_event, try_next_event, poll_batch, for_each, and Iterator for &FpssClient. The FpssEventPoller type and its run / poll_batch methods are removed.
  • New typed FpssError enum (#[non_exhaustive]) returned by the builder, the polling methods, and the iterator. From<FpssError> for Error maps each variant losslessly into the umbrella Error type (auth → Error::Auth, config → Error::Config, io → Error::Io, dispatcher → tagged Error::Fpss); the docstring on FpssError lists every row of the table plus the two known sources of information loss (io::ErrorKind collapse, Config.field regeneration).
  • The unified ThetaDataDxClient::start_streaming(callback) and the Python / TypeScript / FFI bindings preserve the push-callback shape. Each binding owns its own internal dispatcher thread and panic-detection flag; no supervisor handle is exposed on the FPSS public surface.
  • ThetaDataDxClient::start_streaming_iter, start_streaming_iter_with_wake, and start_streaming_iter_with_wake_policy are removed.
  • Python: EventIterator, StreamingIterSession, StreamingAsyncSession, StreamingAsyncBatchesSession, and BackpressurePolicy pyclasses removed. streaming_iter(), streaming_async(), and streaming_async_batches() on ThetaDataDxClient and the standalone FpssClient pyclass are gone. The start_streaming(callback) push surface and the streaming(callback) context manager remain.
  • TypeScript: EventIterator napi class removed. client.startStreamingIter() is gone. client.startStreaming(cb) is preserved.
  • C ABI: TdxFpssEventIterator, tdx_unified_start_streaming_iter, tdx_fpss_event_iter_next, tdx_fpss_event_iter_close, and tdx_fpss_event_iter_free removed. tdx_fpss_set_callback and tdx_unified_set_callback remain.
  • Crate dependency removed: crossbeam-queue.

Streaming

  • Contract.symbol type changed from String to Arc<str>. The field derefs to &str transparently, so existing call sites continue to work. The Rust decode path interns the symbol bytes inside the per-session contract cache, so a session that streams a sustained subscription set allocates once per unique symbol for the lifetime of the session instead of once per event on the Rust side. Python and TypeScript bindings still copy the string at the language boundary — that copy is unavoidable for the runtime's own string ownership — so SDK-surface Contract.symbol types remain String and are populated via .to_string() on each delivered event.

Migration

Rust direct callers:

// Earlier (legacy):
let (client, iter) = FpssClient::connect_iter(args)?;
for event in iter { handle(event); }

// 12.0.0 — iterator on the client itself:
let client = FpssClient::builder(&creds, &hosts).build()?;
for event in &client {
    let event = event?;
    handle(&event);
}

// 12.0.0 — non-blocking batch drain:
let client = FpssClient::builder(&creds, &hosts).build()?;
loop {
    match client.poll_batch(|event| handle(event)) {
        PollOutcome::Drained(_) => { /* re-poll or yield */ }
        PollOutcome::Shutdown   => break,
    }
}

// 12.0.0 — blocking dedicated thread:
let client = FpssClient::builder(&creds, &hosts).build()?;
let client_for_dispatch = std::sync::Arc::new(client);
let arc_for_thread = std::sync::Arc::clone(&client_for_dispatch);
std::thread::spawn(move || {
    for event in &*arc_for_thread {
        match event {
            Ok(event) => handle(&event),
            Err(_)    => break,
        }
    }
});

Embedders that exposed a push-callback over the legacy poller.run(...) shape now spawn their own dispatcher thread that drains for event in &*client_arc { ... } and invokes the user callback per event. Catch panics inside the dispatcher; surface the failed state through whatever observability flag the binding already exposes.

Internals

  • io_loop is structurally simpler: single producer wiring through build_poller_producer. The earlier build_consumer_producer / ConsumerProducerArgs machinery and the push_with_block queue helper are gone.
  • Connect path validates configuration up front and returns the fully-assembled FpssClient (no separate poller handle).
  • The earlier queue-backed soak suite is retired; the poller-backed drain semantics are covered by the workspace cargo test gauntlet.

Removed

  • REST fallback escape hatch (Config::with_rest_fallback, FallbackPolicy, option_history_*_with_fallback). The library now speaks ThetaData's historical gRPC endpoint, streaming TCP feed, and the native flat-file distribution directly — no HTTP fallback path.

Unchanged

  • tdbe stays at 0.14.0; no API or wire-schema change.
  • Historical pipeline, FLATFILES, auth, and config surfaces are unchanged.

What's Changed

  • feat!: curated Rust SDK + FPSS streaming reshape + cross-binding parity sweep by @aukaost in #652
  • docs: correct release-notes naming and refresh streaming docstrings by @userFRM in #655
  • fix(tools): install the rustls provider at CLI and MCP startup by @userFRM in #658
  • fix(server): legacy-terminal behavior parity for the HTTP and WebSocket surfaces by @userFRM in #656
  • feat(fpss): connection-resilience hardening for multi-minute upstream outages by @userFRM in #659
  • bench(grpc): closed-loop transport comparison harness and measured results by @userFRM in #661
  • feat(grpc)!: migrate the MDDS transport to the reference Rust gRPC stack by @userFRM in #664
  • chore(deps-dev): bump vue from 3.5.34 to 3.5.38 in /docs-site in the patch-minor group across 1 directory by @dependabot[bot] in #622
  • chore(deps-dev): bump @napi-rs/cli from 3.6.2 to 3.7.1 in /sdks/typescript in the patch-minor group across 1 directory by @dependabot[bot] in #653
  • chore(deps): bump the patch-minor group across 1 directory with 11 updates by @dependabot[bot] in #660
  • perf(mdds): schema-validated bulk column extraction in the decode path by @userFRM in #665
  • feat(fpss): ring occupancy and capacity observability across bindings by @userFRM in #666

Full Changelog: v11.0.1...v12.0.0