Skip to content

userFRM/ThetaDataDx

ThetaDataDx

High-performance market-data SDKs for ThetaData, in Python, TypeScript, C++, and Rust — one Rust engine under all four. Pull US stock, option, index, and rate data three ways: point-in-time history, real-time streaming, and whole-universe flat files, all from a single authenticated client. Connects straight to ThetaData — no Java terminal, no JVM.

Rust CI Python SDK TypeScript SDK Deploy Docs

Crates.io PyPI npm docs.rs

License Rust Python Node C++ Discord

Important

A valid ThetaData subscription is required. The SDK authenticates against ThetaData's Nexus API using your account credentials.

Features

  • Complete coverage — stocks, options, indices, and rates across 61 typed endpoints.
  • Three access modes, one client — point-in-time history, real-time streaming, and bulk flat-file downloads.
  • DataFrames built in — every result chains straight to Polars, pandas, or Arrow over a zero-copy boundary.
  • Greeks without a round-trip — 23 Black-Scholes Greeks and an implied-volatility solver, computed locally.
  • The same surface in every language — identical methods and identical typed errors, Python through Rust.
  • No terminal to run — a direct connection to ThetaData; nothing to install and babysit locally.

Install

pip install thetadatadx        # Python
npm install thetadatadx        # TypeScript / Node.js
cargo add thetadatadx          # Rust

C++ ships as a single header plus a prebuilt library — see the C++ guide.

Quick start

Tip

Credentials can come from a creds.txt file (email on line 1, password on line 2), an inline Credentials, or the THETADATA_EMAIL / THETADATA_PASSWORD environment variables.

Python

from thetadatadx import ThetaDataDxClient, Credentials, Config

tdx = ThetaDataDxClient(Credentials.from_file("creds.txt"), Config.production())

# First-order Greeks for every strike on SPY's 2026-06-19 expiry, as of 2024-03-15
greeks = tdx.option_history_greeks_first_order("SPY", "20260619", "20240315")

df = greeks.to_polars()
print(df.select(["strike", "right", "delta", "gamma", "theta", "vega"]).head())

Stream live quotes and trades through the same client. The callback matches on typed event classes:

import time
from thetadatadx import Contract, Quote, Trade

def format_contract(contract):
    parts = [contract.symbol]
    if contract.expiration is not None:
        parts.append(str(contract.expiration))
    if contract.strike is not None:
        parts.append(f"{contract.strike:g}")
    if contract.right is not None:
        parts.append(contract.right)
    return " ".join(parts)

def on_event(event):
    match event:
        case Trade(price=px, size=sz, exchange=ex, ms_of_day=ms, sequence=seq, condition=cond, contract=c):
            print(
                f"{format_contract(c)} trade price={px:.2f} size={sz} "
                f"exchange={ex} ms_of_day={ms} sequence={seq} condition={cond}"
            )
        case Quote(bid=b, ask=a, bid_size=bs, ask_size=asz, bid_exchange=bx, ask_exchange=ax, ms_of_day=ms, contract=c):
            print(
                f"{format_contract(c)} quote bid={b:.2f} ask={a:.2f} "
                f"bid_size={bs} ask_size={asz} bid_exchange={bx} "
                f"ask_exchange={ax} ms_of_day={ms}"
            )

spy_call = Contract.option("SPY", expiration="20260619", strike="550", right="C")

with tdx.streaming(on_event) as session:
    session.subscribe_many([spy_call.quote(), spy_call.trade(), spy_call.market_value()])
    time.sleep(60)   # park the main thread while events flow into on_event

TypeScript

import { Contract, ThetaDataDxClient } from 'thetadatadx';

const client = ThetaDataDxClient.connectFromFile('creds.txt');
const formatContract = (contract: {
  symbol: string;
  expiration?: number;
  strike?: number;
  right?: string;
}) => [contract.symbol, contract.expiration, contract.strike, contract.right]
  .filter((value) => value != null)
  .join(' ');

client.startStreaming((event) => {
  if (event.kind === 'trade' && event.trade) {
    const { contract, price, size, exchange, msOfDay, sequence, condition } = event.trade;
    console.log(
      `${formatContract(contract)} trade price=${price} size=${size} ` +
      `exchange=${exchange} ms_of_day=${msOfDay} sequence=${sequence} condition=${condition}`,
    );
  } else if (event.kind === 'quote' && event.quote) {
    const { contract, bid, ask, bidSize, askSize, bidExchange, askExchange, msOfDay } = event.quote;
    console.log(
      `${formatContract(contract)} quote bid=${bid} ask=${ask} ` +
      `bid_size=${bidSize} ask_size=${askSize} bid_exchange=${bidExchange} ` +
      `ask_exchange=${askExchange} ms_of_day=${msOfDay}`,
    );
  }
});

const leg = { expiration: '20260619', strike: '550', right: 'C' };
client.subscribeMany([
  Contract.option('SPY', leg).quote(),
  Contract.option('SPY', leg).trade(),
]);

C++

#include <thetadx.hpp>
#include <cstdio>

int main() {
    auto client = tdx::Client::connect(
        tdx::Credentials::from_file("creds.txt"),
        tdx::Config::production());

    auto greeks = client.option_history_greeks_first_order("SPY", "20260619", "20240315");
    for (const auto& t : greeks) {
        std::printf("K=%.2f %c delta=%+.4f gamma=%+.4f\n",
                    t.strike, t.right, t.delta, t.gamma);
    }
}

Rust

[dependencies]
thetadatadx = "12"
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 greeks = tdx
        .option_history_greeks_eod("SPY", "20260619", "20240101", "20240331")
        .await?;

    for t in greeks.iter().take(5) {
        println!("{} K={:.2} {} delta={:+.4}", t.date, t.strike, t.right, t.delta);
    }
    Ok(())
}

DataFrames

Every historical result is a typed list that converts directly to a dataframe — no row-by-row iteration:

greeks.to_polars()   # polars.DataFrame
greeks.to_pandas()   # pandas.DataFrame   (pip install thetadatadx[pandas])
greeks.to_arrow()    # pyarrow.Table      (zero-copy)

The same .to_polars() / .to_pandas() / .to_arrow() terminals are available on flat-file results. For multi-day backfills, stream the response in chunks instead of buffering it — see Bulk backfill.

Streaming

One connection, one authentication. Historical queries work immediately; the streaming transport connects on the first subscription. Subscribe specific contracts with the fluent Contract API, or take a whole-market feed — every option trade across the universe, no per-contract setup:

with tdx.streaming(on_event) as session:
    session.subscribe(SecType.OPTION.full_trades())
    time.sleep(60)   # the callback runs on the streaming thread — keep it fast

Tip

On an involuntary disconnect the client recovers on its own — exponential backoff with jitter, automatic host failover, then a paced re-subscribe of every active contract. Read liveness directly off the stream with connection_status() and the last-event timestamp; no separate health poll needed.

Endpoint coverage

61 typed endpoints across stocks, options, indices, the market calendar, and interest rates, plus real-time streaming and a local Greeks calculator.

Category Endpoints Examples
Stock 14 EOD, OHLC, trades, quotes, snapshots, at-time
Option 34 Every stock surface plus five Greeks tiers, open interest, contract lists
Index 9 EOD, OHLC, price, snapshots
Calendar 3 Market open/close, holidays, early closes
Interest rate 1 EOD rate history

The full per-language method list lives in the API Reference.

Errors

Every binding raises the same typed hierarchy, so the same cases are catchable in any language — AuthenticationError, RateLimitError, NotFoundError, DeadlineExceededError, InvalidParameterError, and the rest, all under a common ThetaDataError base.

Repository layout

Path Package Purpose
crates/thetadatadx thetadatadx (crates.io) The Rust SDK — tick types, Greeks, price math, and the network client in one crate
sdks/python thetadatadx (PyPI) Python package with DataFrame adapters
sdks/typescript thetadatadx (npm) TypeScript / Node.js package, prebuilt binaries
sdks/cpp header + prebuilt library C++ wrapper over the C ABI
ffi/ release artifacts C ABI for embedders
tools/cli tdx Command-line client
tools/server thetadatadx-server Local HTTP / WebSocket server
tools/mcp thetadatadx-mcp MCP server exposing every historical endpoint to AI clients
docs-site Documentation site (GitHub Pages)

Documentation

Contributing

See CONTRIBUTING.md for development setup and the pull-request process. Community discussion happens on the ThetaData Discord.

License

Licensed under the Apache License, Version 2.0. See LICENSE.

About

Market data SDKs for ThetaData — Rust, Python, TypeScript, and C++. Historical, real-time streaming, and bulk flat-file access through one authenticated client. No JVM, no local terminal.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors