Rust SDK for ThetaData market data — single Rust core, four language surfaces (Rust, Python, TypeScript, C++).
thetadatadx is a native Rust SDK for ThetaData market data. It connects directly to ThetaData's three public surfaces — MDDS (historical request/response over gRPC), FPSS (real-time streaming over TCP), and FLATFILES (whole-universe daily blobs over the legacy MDDS port) — decodes ticks in-process, and exposes a typed API across Rust, Python, TypeScript, and C++ from a single Rust core. No JVM, no subprocess, no IPC serialization. Third-party C consumers can integrate against the unchanged C ABI in ffi/ — header at sdks/cpp/include/thetadx.h, all FFI types and free functions exported as tdx_* symbols.
Important
A valid ThetaData subscription is required. The SDK authenticates against ThetaData's Nexus API using your account credentials.
- Rust 1.88 or newer. Declared as
rust-version = "1.88"on every workspace[package]; the Linux Lint job in CI is pinned to this floor so dependency bumps that raise the rustc requirement surface before release. - A valid ThetaData subscription for the live endpoints.
- Typed everywhere. 61 ThetaData endpoints exposed as typed methods across all four SDKs; no raw JSON or protobuf on the public surface.
- Arrow-backed DataFrames. Python
to_arrow()/to_pandas()/to_polars()pipe through shared Arrow buffers. - SPKI-pinned FPSS TLS. Public-key pinning on the FPSS streaming handshake.
- FIT decoder + SPSC ring buffer on the FPSS path. Decode cost is measured in the benchmarks under
crates/thetadatadx/benches/. - Shared FFI layer. C++ and Node.js go through the same
extern "C"layer; the Python wheel uses PyO3 ABI3 directly. The C ABI is also the supported integration path for any third-party C / C++ consumer. - Covers all three public surfaces. MDDS gRPC endpoints, FPSS wire format with reconnect semantics, and the FLATFILES daily-blob protocol — every transport speaks directly to ThetaData's production servers from a single client. See the vendor flat-file reference.
- FLATFILES daily blobs. Pull whole-universe
(sec_type, req_type, date)blobs over the legacy MDDS port; decode to vendor-byte CSV, JSONL, or a typedVec<FlatFileRow>in memory. Cross-language coverage is tracked under the binding issues; the Rust core is shipped today.
Tip
Credentials can be supplied as a creds.txt file (email on line 1, password on line 2), inline via Credentials::new("email", "password"), or through the THETADATA_EMAIL / THETADATA_PASSWORD environment variables.
[dependencies]
thetadatadx = "9"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }use thetadatadx::{ThetaDataDxClient, Credentials, DirectConfig};
#[tokio::main]
async fn main() -> Result<(), thetadatadx::Error> {
let creds = Credentials::from_file("creds.txt")?;
let tdx = ThetaDataDxClient::connect(&creds, DirectConfig::production()).await?;
let eod = tdx.stock_history_eod("AAPL", "20240101", "20240301").await?;
for tick in &eod {
println!("{}: O={} H={} L={} C={} V={}",
tick.date, tick.open, tick.high, tick.low, tick.close, tick.volume);
}
Ok(())
}Opt into chainable DataFrame ergonomics by enabling the polars and/or arrow features. Both stay out of the default dep graph:
[dependencies]
thetadatadx = { version = "9", features = ["polars"] }use thetadatadx::frames::TicksPolarsExt;
let eod = tdx.stock_history_eod("AAPL", "20240101", "20240301").await?;
let df = eod.as_slice().to_polars()?;The arrow feature exposes a TicksArrowExt::to_arrow that materialises an arrow_array::RecordBatch with the same schema the Python .to_polars() / .to_arrow() terminal produces. features = ["frames"] pulls both in.
pip install thetadatadxfrom thetadatadx import Credentials, Config, ThetaDataDxClient
tdx = ThetaDataDxClient(Credentials.from_file("creds.txt"), Config.production())
for tick in tdx.stock_history_eod("AAPL", "20240101", "20240301"):
print(f"{tick.date}: O={tick.open:.2f} H={tick.high:.2f} "
f"L={tick.low:.2f} C={tick.close:.2f} V={tick.volume}")npm install thetadatadximport { ThetaDataDxClient } from 'thetadatadx';
const tdx = await ThetaDataDxClient.connectFromFile('creds.txt');
for (const t of tdx.stockHistoryEOD('AAPL', '20240101', '20240301')) {
console.log(`${t.date}: O=${t.open} H=${t.high} L=${t.low} C=${t.close} V=${t.volume}`);
}#include <thetadx.hpp>
#include <cstdio>
int main() {
auto tdx = thetadatadx::ThetaDataDxClient::connect_from_file("creds.txt");
for (const auto& t : tdx.stock_history_eod("AAPL", "20240101", "20240301")) {
std::printf("%d: O=%.2f H=%.2f L=%.2f C=%.2f V=%lld\n",
t.date, t.open, t.high, t.low, t.close, (long long)t.volume);
}
}One connection, one auth. Historical queries are available immediately; streaming connects lazily on first subscription. The client auto-reconnects and re-subscribes all active contracts on involuntary disconnect.
The primary streaming surface is the fluent contract-first API —
Contract::stock("AAPL").quote() returns a typed Subscription value
that the polymorphic client.subscribe(...) accepts directly:
use thetadatadx::fpss::{FpssData, FpssEvent};
use thetadatadx::prelude::*;
tdx.start_streaming(|event: &FpssEvent| {
match event {
FpssEvent::Data(FpssData::Quote { contract, bid, ask, .. }) => {
println!("Quote: {} bid={bid} ask={ask}", contract.symbol);
}
FpssEvent::Data(FpssData::Trade { contract, price, size, .. }) => {
println!("Trade: {} @ {price} x {size}", contract.symbol);
}
_ => {}
}
})?;
let stock = Contract::stock("AAPL");
let option = Contract::option("SPY", "20260620", "550", "C")?;
tdx.subscribe(stock.quote())?;
tdx.subscribe(option.trade())?;
tdx.subscribe(SecType::Option.full_open_interest())?;
// Bulk install:
tdx.subscribe_many(vec![stock.quote(), option.quote()])?;All prices (bid, ask, price, open, high, low, close) are f64, decoded during parsing.
All prices (bid, ask, price, open, high, low, close) are f64, decoded during parsing.
61 registry/REST endpoints plus 4 SDK-only historical stream variants, FPSS real-time streaming, and a full Black-Scholes Greeks calculator.
| Category | Endpoints | Examples |
|---|---|---|
| Stock | 14 | EOD, OHLC, trades, quotes, snapshots, at-time |
| Option | 34 | Same as stock + 5 Greeks tiers, open interest, contracts |
| Index | 9 | EOD, OHLC, price, snapshots |
| Calendar | 3 | Market open/close, holiday schedule |
| Interest Rate | 1 | EOD rate history |
All endpoints return fully typed data in every language. See the API Reference for the complete method list.
Additional surfaces (not REST/gRPC endpoints): FPSS real-time streaming (7 subscribe/unsubscribe methods per contract and per full-stream type) and a local Greeks calculator (22 Black-Scholes Greeks plus an IV solver, callable individually or batched).
flowchart TB
subgraph core["Rust core"]
direction TB
thetadatadx["<b>thetadatadx</b><br/>auth · MDDS gRPC · FPSS TCP · decode"]
tdbe["<b>tdbe</b><br/>types · FIT / FIE codec · Greeks · Price"]
thetadatadx --> tdbe
end
ffi["<b>ffi</b><br/>stable C ABI · panic boundary"]
core --> ffi
core -->|PyO3 / maturin| python["Python SDK<br/>(pyo3 · Arrow)"]
ffi -->|napi-rs| ts["TypeScript SDK<br/>(N-API · BigInt)"]
ffi -->|extern C| cpp["C++ SDK<br/>(RAII header-only)"]
core -->|tonic| rust["Rust consumer<br/>(direct crate)"]
classDef coreStyle fill:#1e3a8a,stroke:#0c1e5c,color:#fff
classDef ffiStyle fill:#7c2d12,stroke:#450a0a,color:#fff
classDef sdkStyle fill:#14532d,stroke:#052e16,color:#fff
class thetadatadx,tdbe coreStyle
class ffi ffiStyle
class python,ts,cpp,rust sdkStyle
| Layer | Crate / package | Purpose |
|---|---|---|
| Encoding / types | crates/tdbe |
Tick structs, FIT/FIE codecs, Greeks, Price |
| Core SDK | crates/thetadatadx |
MDDS gRPC client, FPSS streaming, auth |
| C FFI | ffi/ |
Stable extern "C" layer consumed by C++, Node.js, and any third-party C / C++ consumer |
| Python | sdks/python |
PyO3 / maturin wheel with Arrow DataFrame adapter |
| TypeScript | sdks/typescript |
napi-rs prebuilt binary |
| C++ | sdks/cpp |
RAII header-only wrapper |
| CLI | tools/cli |
tdx CLI — every generated historical endpoint from the command line |
| MCP | tools/mcp |
MCP server - gives clients access to every generated historical endpoint plus offline tools over JSON-RPC |
| Server | tools/server |
REST + WebSocket server exposing the /v3/* route surface |
| Docs | docs/ |
API reference, architecture, attribution |
| Website | docs-site/ |
VitePress documentation site (deployed to GitHub Pages) |
| Notebooks | notebooks/ |
7 Jupyter notebooks (101-107) |
| Document | Description |
|---|---|
| API Reference | All typed methods, streaming builders, generated tick types, and configuration options |
| Architecture | System design, wire protocols, TOML codegen pipeline |
| Endpoint Schema | TOML codegen format for adding new types/columns |
| Proto Maintenance | Guide for updating proto files |
| Roadmap | Per-binding coverage status |
| Changelog | Release notes with breaking changes, features, and fixes |
Contributions are welcome. See CONTRIBUTING.md for development setup, pre-commit checks, and pull-request process. Community discussion happens on the ThetaData Discord.
Licensed under the Apache License, Version 2.0. See LICENSE.