Skip to main content

No-JVM ThetaData Terminal — native Rust SDK for direct market data access (Python bindings)

Project description

thetadatadx (Python)

Python bindings over the Rust core. Every call crosses the PyO3 boundary into Rust: gRPC communication, protobuf parsing, zstd decompression, FIT tick decoding, and TCP streaming run inside the thetadatadx crate.

Surface coverage: the Python binding exposes all three ThetaData surfaces — MDDS (historical), FPSS (streaming), and FLATFILES (whole-universe daily blobs). Flat files land via tdx.flat_files.*() with .to_arrow(), .to_polars(), .to_pandas(), and .to_list() terminals plus a flatfile_to_path(...) raw-bytes helper — see the Flat Files section for the full method list.

Installation

pip install thetadatadx

# With pandas DataFrame support (Arrow-backed, zero-copy on pandas 2.x)
pip install thetadatadx[pandas]

# With polars DataFrame support
pip install thetadatadx[polars]

# Raw Arrow (pyarrow.Table for DuckDB / Arrow-Flight / cuDF / polars-arrow)
pip install thetadatadx[arrow]

# All optional adapters
pip install thetadatadx[all]

Or build from source (requires Rust toolchain):

pip install "maturin>=1.9.4,<2.0"
maturin develop --release

Binary wheels use CPython's stable ABI (abi3) with a minimum Python version of 3.9, so one wheel per platform supports Python 3.9+.

Quick Start

from thetadatadx import Credentials, Config, ThetaDataDxClient

# Authenticate and connect
creds = Credentials.from_file("creds.txt")
# Or inline: creds = Credentials("user@example.com", "your-password")
tdx = ThetaDataDxClient(creds, Config.production())

# Fetch end-of-day data
eod = tdx.stock_history_eod("AAPL", "20240101", "20240301")
for tick in eod:
    print(f"{tick.date}: O={tick.open:.2f} H={tick.high:.2f} "
          f"L={tick.low:.2f} C={tick.close:.2f} V={tick.volume}")

# Intraday 1-minute OHLC bars (shorthand or milliseconds)
bars = tdx.stock_history_ohlc("AAPL", "20240315", "1m")
print(f"{len(bars)} bars")

# Option chain
exps = tdx.option_list_expirations("SPY")
strikes = tdx.option_list_strikes("SPY", exps[0])

Greeks Calculator

Full Black-Scholes calculator with 23 Greeks, running in Rust:

from thetadatadx import all_greeks, implied_volatility

# All Greeks at once
g = all_greeks(
    spot=450.0, strike=455.0, rate=0.05, div_yield=0.015,
    tte=30/365, option_price=8.50, right="C"
)
print(f"IV={g.iv:.4f} Delta={g.delta:.4f} Gamma={g.gamma:.6f}")

# Just IV
iv, err = implied_volatility(450.0, 455.0, 0.05, 0.015, 30/365, 8.50, "C")

API

Credentials

  • Credentials(email, password) - direct construction
  • Credentials.from_file(path) - load from creds.txt

Config

  • Config.production() - ThetaData NJ production servers
  • Config.dev() - Dev FPSS servers (port 20200, infinite historical replay)
  • Config.stage() - Stage FPSS servers (port 20100, testing, unstable)

ThetaDataDxClient(creds, config)

Every historical endpoint is available. The tick-returning endpoints return typed <TickName>List wrappers (e.g. EodTickList, TradeTickList, QuoteTickList, ...), each implementing the Python sequence protocol (len(...), for tick in ..., lst[0], negative indexing) with typed-pyclass elements exposed via attribute access — tick.close, tick.price — with IDE completion and typo-loud AttributeError on misuse. Every wrapper also exposes the chainable DataFrame terminals covered in Chained DataFrame terminals. List-of-string endpoints (*_list_symbols, *_list_dates, option_list_expirations, option_list_strikes) return a StringList wrapper with the same chainable terminals; the single output column is named by the endpoint metadata (symbol, date, expiration, ...). option_list_contracts returns an OptionContractList; calendar_on_date / calendar_year return a CalendarDayList.

Stock Methods (14)

Method Description
stock_list_symbols() All stock symbols
stock_list_dates(request_type, symbol) Available dates by request type
stock_snapshot_ohlc(symbols) Latest OHLC snapshot
stock_snapshot_trade(symbols) Latest trade snapshot
stock_snapshot_quote(symbols) Latest NBBO quote snapshot
stock_snapshot_market_value(symbols) Latest market value snapshot
stock_history_eod(symbol, start_date, end_date) End-of-day data
stock_history_ohlc(symbol, date, interval) Intraday OHLC bars. interval accepts ms ("60000") or shorthand ("1m").
stock_history_ohlc_range(symbol, start_date, end_date, interval) OHLC bars across date range. interval accepts ms or shorthand.
stock_history_trade(symbol, date) All trades for a date
stock_history_quote(symbol, date, interval) NBBO quotes. interval accepts ms or shorthand.
stock_history_trade_quote(symbol, date) Combined trade+quote ticks
stock_at_time_trade(symbol, start_date, end_date, time) Trade at specific time across dates
stock_at_time_quote(symbol, start_date, end_date, time) Quote at specific time across dates

Option Methods (34)

Method Description
option_list_symbols() Option underlying symbols
option_list_dates(request_type, symbol, expiration, strike, right) Available dates for a contract
option_list_expirations(symbol) Expiration dates
option_list_strikes(symbol, expiration) Strike prices
option_list_contracts(request_type, symbol, date) All contracts for a date
option_snapshot_ohlc(symbol, expiration, strike, right) Latest OHLC snapshot
option_snapshot_trade(symbol, expiration, strike, right) Latest trade snapshot
option_snapshot_quote(symbol, expiration, strike, right) Latest quote snapshot
option_snapshot_open_interest(symbol, expiration, strike, right) Latest open interest
option_snapshot_market_value(symbol, expiration, strike, right) Latest market value
option_snapshot_greeks_implied_volatility(symbol, expiration, strike, right) IV snapshot
option_snapshot_greeks_all(symbol, expiration, strike, right) All Greeks snapshot
option_snapshot_greeks_first_order(symbol, expiration, strike, right) First-order Greeks
option_snapshot_greeks_second_order(symbol, expiration, strike, right) Second-order Greeks
option_snapshot_greeks_third_order(symbol, expiration, strike, right) Third-order Greeks
option_history_eod(symbol, expiration, strike, right, start_date, end_date) EOD option data
option_history_ohlc(symbol, expiration, strike, right, date, interval) Intraday OHLC bars
option_history_trade(symbol, expiration, strike, right, date) All trades
option_history_quote(symbol, expiration, strike, right, date, interval) NBBO quotes
option_history_trade_quote(symbol, expiration, strike, right, date) Combined trade+quote
option_history_open_interest(symbol, expiration, strike, right, date) Open interest history
option_history_greeks_eod(symbol, expiration, strike, right, start_date, end_date, *, annual_dividend=None, rate_type=None, rate_value=None, version=None, underlyer_use_nbbo=None, max_dte=None, strike_range=None) EOD Greeks
option_history_greeks_all(symbol, expiration, strike, right, date, interval) All Greeks history
option_history_trade_greeks_all(symbol, expiration, strike, right, date) Greeks on each trade
option_history_greeks_first_order(symbol, expiration, strike, right, date, interval) First-order Greeks history
option_history_trade_greeks_first_order(symbol, expiration, strike, right, date) First-order on each trade
option_history_greeks_second_order(symbol, expiration, strike, right, date, interval) Second-order Greeks history
option_history_trade_greeks_second_order(symbol, expiration, strike, right, date) Second-order on each trade
option_history_greeks_third_order(symbol, expiration, strike, right, date, interval) Third-order Greeks history
option_history_trade_greeks_third_order(symbol, expiration, strike, right, date) Third-order on each trade
option_history_greeks_implied_volatility(symbol, expiration, strike, right, date, interval) IV history
option_history_trade_greeks_implied_volatility(symbol, expiration, strike, right, date) IV on each trade
option_at_time_trade(symbol, expiration, strike, right, start_date, end_date, time) Trade at specific time
option_at_time_quote(symbol, expiration, strike, right, start_date, end_date, time) Quote at specific time

Index Methods (9)

Method Description
index_list_symbols() All index symbols
index_list_dates(symbol) Available dates for an index
index_snapshot_ohlc(symbols) Latest OHLC snapshot
index_snapshot_price(symbols) Latest price snapshot
index_snapshot_market_value(symbols) Latest market value snapshot
index_history_eod(symbol, start_date, end_date) End-of-day index data
index_history_ohlc(symbol, start_date, end_date, interval) Intraday OHLC bars
index_history_price(symbol, date, interval) Intraday price history
index_at_time_price(symbol, start_date, end_date, time) Price at specific time

Calendar Methods (3)

Method Description
calendar_open_today() Is the market open today?
calendar_on_date(date) Calendar info for a date
calendar_year(year) Calendar for an entire year

Rate Methods (1)

Method Description
interest_rate_history_eod(symbol, start_date, end_date) Interest rate EOD history

Streaming — fluent contract-first API (primary)

Real-time streaming is accessed through the same ThetaDataDxClient instance. The primary surface is the fluent contract / subscription model:

from thetadatadx import Contract, SecType

stock  = Contract.stock("AAPL")
option = Contract.option("SPY", expiration="20260620", strike="550", right="C")

with client.streaming(on_event) as session:
    session.subscribe(stock.quote())
    session.subscribe(option.trade())
    session.subscribe(option.open_interest())
    session.subscribe(SecType.OPTION.full_trades())

    # Bulk install for many contracts:
    session.subscribe_many([stock.quote(), option.quote()])

The Subscription value returned by Contract.quote() / Contract.trade() / Contract.open_interest() is typed and homogeneous — full-stream subscriptions returned by SecType.OPTION.full_trades() mix into the same subscribe_many([...]) list, no string flags, no kwarg gymnastics.

Fluent surface

Method Description
Contract.stock(symbol) Stock contract
Contract.option(symbol, *, expiration, strike, right) Option contract
contract.quote() / .trade() / .open_interest() Per-contract Subscription
SecType.OPTION.full_trades() / .full_open_interest() Full-stream Subscription
client.subscribe(sub) Install one Subscription
client.subscribe_many([sub, ...]) Install many Subscription values
client.unsubscribe(sub) / unsubscribe_many([...]) Drop subscriptions

Full trade stream behavior: when subscribed via client.subscribe(SecType.OPTION.full_trades()), the ThetaData FPSS server sends a bundle for every trade across ALL option contracts:

  1. Pre-trade NBBO quote
  2. OHLC bar for the traded contract
  3. The trade itself
  4. Two post-trade NBBO quotes

Events arrive as typed pyclass instances — Quote, Trade, Ohlcvc, OpenInterest for market data; one typed class per control variant (LoginSuccess, ContractAssigned, ReqResponse, MarketOpen, MarketClose, ServerError, Disconnected, Reconnecting, Reconnected, Error, UnknownFrame, Connected, Ping, ReconnectedServer, Restart, UnknownControl). Each class mirrors its FpssControl::* / FpssData::* Rust variant one-for-one, so dispatch is a structural match — exactly the same shape Rust consumers use. Truncated / unrecognised wire frames are filtered before the callback fires and accounted on the thetadatadx.fpss.decode_failures metric counter; they never surface on the public event stream.

from thetadatadx import (
    Quote, Trade, Ohlcvc, OpenInterest,
    LoginSuccess, ContractAssigned, Disconnected,
    Reconnecting, Reconnected, Restart,
)

# Build a contract ID -> symbol map as assignments arrive
contracts = {}


def on_event(event):
    match event:
        # Control / lifecycle events
        case LoginSuccess(permissions=p):
            print(f"logged in: bundle={p}")
        case ContractAssigned(id=cid, contract=c):
            contracts[cid] = c.symbol
        case Disconnected(reason=r):
            print(f"disconnected: reason={r}")
        case Reconnecting(attempt=n, delay_ms=ms):
            print(f"reconnect attempt {n} in {ms}ms")
        case Reconnected() | Restart():
            print("stream live again")

        # Market-data events — every variant carries its parsed `contract`.
        case Trade(price=px, size=sz, contract=c):
            print(f"[{c.symbol}] TRADE {px:.2f} x {sz}")
        case Quote(bid=b, ask=a, contract=c):
            print(f"[{c.symbol}] QUOTE bid={b:.2f} ask={a:.2f}")

        # Skip everything else (Ohlcvc bars, Ping heartbeats, ...)
        case _:
            pass


from thetadatadx import SecType

with tdx.streaming(on_event) as session:
    session.subscribe(SecType.OPTION.full_trades())

    # `on_event` runs on the LMAX Disruptor consumer thread under the
    # GIL, wrapped in `catch_unwind` so a Python exception is reported
    # via `tracing::error!` and `panic_count()` rather than tearing
    # down the consumer. Park the main thread while events flow.
    import time
    time.sleep(60)
# `__exit__` calls `stop_streaming()` and then blocks on
# `await_drain(5_000)` so the consumer thread has finished firing the
# callback before control returns. If the drain barrier times out, a
# `RuntimeWarning` is emitted but the `with` block still exits cleanly.

The with tdx.streaming(callback) context manager is the recommended API. Subscription methods on the bound session proxy through to the underlying ThetaDataDxClient via StreamingSession.__getattr__ -- the streaming surface is a single source of truth rooted in the Rust crate, with no hand-listed wrapper to drift.

For the lower-level escape hatch (e.g. cross-process lifecycle management, custom shutdown ordering), call the lifecycle methods explicitly:

tdx.start_streaming(callback=on_event)
tdx.subscribe(SecType.OPTION.full_trades())
import time
time.sleep(60)
tdx.stop_streaming()
# Drain barrier: by the time `await_drain(5000)` returns, the consumer
# thread is guaranteed to have finished firing `on_event`, so the
# closure stack the callback closed over can be released without a
# use-after-free race against the LMAX Disruptor consumer.
tdx.await_drain(5_000)

You can also subscribe to per-contract streams if you only need specific symbols rather than the full-stream subscription.

Pull-iter delivery — for event in tdx.streaming_iter() (high-throughput drain)

Push-callback (tdx.streaming(callback) above) is the recommended default for low-latency single-event reaction. Pull-iter is the sibling delivery mode for high-throughput batch processing where the dominant cost is per-event Python work (tuple build, deque append, DataFrame ingest) rather than per-event vendor latency:

from thetadatadx import Contract, SecType

with tdx.streaming_iter() as iterator:
    iterator.subscribe(SecType.OPTION.full_trades())
    iterator.subscribe(Contract.stock("AAPL").quote())
    for event in iterator:
        match event:
            case Trade(price=p, size=s, contract=c):
                buf.append((c.symbol, p, s))
            case _:
                pass

tdx.streaming_iter() is a context manager that opens an FPSS session in pull-iter mode on enter and pairs stop_streaming() + await_drain(5_000) on exit, mirroring tdx.streaming(callback). The bound iterator is also an EventIterator you can drain with for event in iterator:; subscribe(...) / unsubscribe(...) forward to the underlying ThetaDataDxClient through the same __getattr__ proxy the callback session uses.

The Disruptor consumer thread pushes each event into a per-client bounded queue; the for event in iterator: loop drains the queue under one GIL acquire across the whole batch instead of one GIL acquire per event. For the same per-event Python work (5-field tuple build + deque.append), the included streaming_throughput bench measures ~4.6 Melem/s for pull-iter vs. ~1.1 Melem/s for push-callback — a 4.1× win.

Trade-off: pull-iter pays one queue hop of per-event latency for the batched-GIL throughput win. Push-callback remains the recommended default for sub-millisecond reaction loops; pick pull-iter when the integrator's per-event work dominates.

Mode is chosen at start. Push and pull are mutually exclusive on a given client; switch by calling stop_streaming() first. Backpressure surfaces on the same dropped_event_count() counter as the callback path.

Streaming buffering — Pattern A (collections.deque) vs Pattern B (queue.Queue)

Both patterns drain the SDK callback into a Python-native data structure so business logic runs off the GIL-acquisition hot path. Pattern A is the institutional default — it matches the direct-callback shape used by every major market-data vendor's native API and runs ~2-5x faster than Pattern B because deque.append / popleft are GIL-atomic single ops with no condition-variable wake-up. Pattern B is the right pick when the consumer needs cross-thread blocking get() semantics.

# Pattern A — collections.deque (lowest overhead, ring-buffer drop-oldest)
from collections import deque

buf = deque(maxlen=100_000)


def cb(event):
    buf.append(event)


tdx.start_streaming(cb)
# Consumer thread reads via buf.popleft() with retry-on-IndexError;
# `maxlen` enforces drop-oldest semantics inside the deque itself.
# Pattern B — queue.Queue (cross-thread blocking get(), drop-newest backpressure)
import queue

q = queue.Queue(maxsize=100_000)


def cb(event):
    try:
        q.put_nowait(event)
    except queue.Full:
        pass  # explicit drop on overflow — the SDK's own dropped_event_count()
              # already counts ring-buffer overflow at the Rust layer.


tdx.start_streaming(cb)
# Consumer thread reads via q.get() (blocks until next event).

Trade-off: deque is the fastest queue in the standard library and loses the oldest event on overflow; queue.Queue is slower but gives you get() blocking semantics and an explicit drop-newest decision. Pick deque by default; reach for queue.Queue only when you need the blocking get().

State & lifecycle

Method Description
active_subscriptions() Get list of active subscriptions (list of dicts with "kind" and "contract")
streaming(callback) Open a context-managed streaming session. with tdx.streaming(callback) as session: registers callback via start_streaming on enter and pairs stop_streaming() + await_drain(5_000) on exit, mirroring the C++ RAII destructor. Subscription methods on the bound session proxy through to the underlying ThetaDataDxClient via StreamingSession.__getattr__ -- single source of truth.
streaming_iter() Open a context-managed pull-iter streaming session. with tdx.streaming_iter() as it: opens FPSS in pull-iter delivery mode on enter and pairs stop_streaming() + await_drain(5_000) on exit. The bound it is an EventIteratorfor event in it: drains the per-client queue under one GIL acquire per batch, ~4.1× faster than push-callback on tuple-build / deque-append integrators. Mutually exclusive with start_streaming(callback) on the same client.
start_streaming_iter() Lower-level pull-iter entry point. Returns a bare EventIterator; the caller is responsible for stop_streaming() + await_drain() on shutdown. Prefer streaming_iter() unless you need explicit lifecycle control.
start_streaming(callback) Register a callable; the LMAX Disruptor consumer thread invokes callback(event) under the GIL for every typed FPSS event. event is a Quote / Trade / Ohlcvc / OpenInterest for market data or one of the typed control classes (LoginSuccess, ContractAssigned, Disconnected, Reconnecting, ...) for lifecycle events — dispatch via Python match. Truncated / unrecognised wire frames are filtered before the callback fires (counted on thetadatadx.fpss.decode_failures). Each invocation is wrapped in catch_unwind. event.kind carries the same discriminator tag as the TypeScript SDK's FpssEvent.kind.
await_drain(timeout_ms) Block until the previous streaming session's consumer thread has finished firing the registered callback. Returns True if the drain completed within timeout_ms, False otherwise. Use after stop_streaming() or reconnect() from a thread other than the callback thread to confirm the callback closure can be safely released. The with tdx.streaming(...) context manager calls this for you with a 5_000 ms timeout.
dropped_event_count() Cumulative count of events the FPSS reader could not publish into the Disruptor ring because the consumer fell behind and the ring was full. Resets to 0 on reconnect() (which rebuilds the FPSS client) and reads 0 after stop_streaming(). Snapshot the value before reconnect if you need to accumulate drops across session boundaries.
reconnect() Reconnect streaming and re-register the previously installed callback; restores all subscriptions.
shutdown() Graceful shutdown — drops the registered callback.

Chained DataFrame terminals

Every historical endpoint returns a typed list wrapper (EodTickList, OhlcTickList, StringList, ...) whose terminals convert straight into columnar form:

Terminal Returns Extra
.to_list() Plain list[TickClass]
.to_arrow() pyarrow.Table pip install thetadatadx[arrow]
.to_pandas() pandas.DataFrame pip install thetadatadx[pandas]
.to_polars() polars.DataFrame pip install thetadatadx[polars]

The .to_arrow() terminal hands the RecordBatch off to pyarrow via the Arrow C Data Interface — zero-copy at the pyo3 boundary, the underlying Arrow buffers are the same ones Rust just filled. .to_pandas() and .to_polars() chain through the Arrow table for the final dtype.

import duckdb
table = tdx.stock_history_eod("AAPL", "20240101", "20240301").to_arrow()
con = duckdb.connect()
con.register("eod", table)                  # zero-copy into DuckDB
con.sql("SELECT AVG(close) FROM eod").show()

The list wrapper itself behaves like a Python sequence:

eod = tdx.stock_history_eod("AAPL", "20240101", "20240301")

len(eod)                                   # row count
bool(eod)                                  # False when empty
eod[0]                                     # first EodTick
eod[-1]                                    # last row, negative indexing supported
for tick in eod:                           # iterate in decode order
    process(tick)

On empty results the wrapper still has __len__ == 0, bool(...) == False, and the .to_arrow() / .to_pandas() / .to_polars() terminals produce a zero-row frame with the full typed column schema — no hint= kwarg needed.

all_greeks(spot, strike, rate, div_yield, tte, option_price, right)

right accepts "C"/"P" or "call"/"put" case-insensitively. Returns AllGreeks pyclass with 22 attribute fields (g.iv, g.delta, g.gamma, ...). All fields are float (f64). Consult help(thetadatadx.AllGreeks) for the full list.

implied_volatility(spot, strike, rate, div_yield, tte, option_price, right)

right accepts "C"/"P" or "call"/"put" case-insensitively. Returns (iv, error) tuple.

Flat Files

Whole-universe daily snapshots over the legacy MDDS port (port 12000). Available for (SecType, ReqType) pairs spanning option / stock data across Quote, Trade, TradeQuote, Ohlc, OpenInterest, Eod.

The decoded schema is determined at runtime by the request type, so the binding follows the same <List> -> Arrow -> {pandas, polars} pipeline as the typed historical endpoints, with the Arrow schema inferred from the first row by flatfiles::arrow::rows_to_arrow.

from thetadatadx import Credentials, Config, ThetaDataDxClient

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

# Decoded -> polars / pandas / pyarrow
rows = tdx.flat_files.option_quote(date="20260428")
print(len(rows))
df = rows.to_polars()           # or .to_pandas() / .to_arrow() / .to_list()

# Generic dispatcher when sec_type / req_type come from config
oi = tdx.flat_files.request("OPTION", "OPEN_INTEREST", "20260428")

# Raw vendor CSV / JSONL straight to disk (no decode, no row materialise)
path = tdx.flatfile_to_path("OPTION", "QUOTE", "20260428",
                            "/tmp/option-quote", format="csv")

Available flat_files.* methods: option_quote, option_trade, option_trade_quote, option_ohlc, option_open_interest, option_eod, stock_quote, stock_trade, stock_trade_quote, stock_eod, plus request(sec_type, req_type, date).

Architecture

graph TD
    A["Python code"] - "PyO3 FFI" --> B["thetadatadx Rust crate"]
    B - "tonic gRPC / TLS TCP" --> C["ThetaData servers"]

No HTTP middleware, no Java terminal, no subprocess. Direct wire-protocol access from the Rust core.

FPSS Streaming

Real-time market data via ThetaData's FPSS servers:

from thetadatadx import Credentials, Config, ThetaDataDxClient

creds = Credentials.from_file("creds.txt")
# Or inline: creds = Credentials("user@example.com", "your-password")
tdx = ThetaDataDxClient(creds, Config.production())

# Register a callback (typed pyclasses: `Quote`, `Trade`, `Ohlcvc`, ...).
# The LMAX Disruptor consumer thread acquires the GIL to invoke `on_event`,
# with each invocation wrapped in `catch_unwind` so a Python exception
# is counted on `panic_count()` rather than tearing down the consumer.
def on_event(event):
    if event.kind == "quote":
        print(f"Quote: {event.contract.symbol} bid={event.bid} ask={event.ask}")
    elif event.kind == "trade":
        print(f"Trade: {event.contract.symbol} price={event.price} size={event.size}")


with tdx.streaming(on_event) as session:
    session.subscribe(Contract.stock("AAPL").quote())
    session.subscribe(Contract.stock("SPY").trade())

    # Park the main thread while events flow.
    import time
    time.sleep(60)
# `__exit__` runs `stop_streaming()` + `await_drain(5_000)` so the
# consumer thread has finished firing `on_event` before this scope
# returns. Mirrors the C++ RAII destructor lifecycle.

DataFrame Conversion (Arrow-Backed)

Every DataFrame entry point goes through a single Arrow columnar pipeline: Rust Vec<Tick> -> arrow::RecordBatch -> pyarrow.Table via the Arrow C Data Interface (zero-copy) -> pandas / polars / raw Arrow. On pandas 2.x the numeric DataFrame columns alias the Arrow buffers in place.

from thetadatadx import Credentials, Config, ThetaDataDxClient

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

# Pandas -- zero-copy numeric columns on pandas 2.x.
df  = tdx.stock_history_eod("AAPL", "20240101", "20240301").to_pandas()

# Polars via polars.from_arrow -- zero-copy at the Arrow boundary.
pdf = tdx.stock_history_eod("AAPL", "20240101", "20240301").to_polars()

# Raw Arrow -- plug straight into DuckDB / Arrow-Flight / cuDF.
tbl = tdx.stock_history_eod("AAPL", "20240101", "20240301").to_arrow()

# Same recipe for any tick-returning historical endpoint.
df  = tdx.stock_history_ohlc("AAPL", "20240315", "1m").to_pandas()
df  = tdx.option_history_trade("SPY", "20240620", "550", "C", "20240315").to_pandas()

List endpoints (stock_list_symbols, option_list_expirations, ...) return a StringList wrapper with the same chainable terminals; the DataFrame column header matches the semantic name:

symbols = tdx.stock_list_symbols().to_polars()          # `symbol` column
expirations = tdx.option_list_expirations("AAPL").to_pandas()

See the Arrow project docs for the C Data Interface (how the zero-copy handoff works) and pyarrow Table docs for consumer APIs.

Install with:

  • pip install thetadatadx[pandas] — pandas + pyarrow
  • pip install thetadatadx[polars] — polars + pyarrow
  • pip install thetadatadx[arrow] — pyarrow only

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

thetadatadx-10.0.0.tar.gz (695.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

thetadatadx-10.0.0-cp39-abi3-win_amd64.whl (8.2 MB view details)

Uploaded CPython 3.9+Windows x86-64

thetadatadx-10.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.5 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

thetadatadx-10.0.0-cp39-abi3-macosx_11_0_arm64.whl (8.7 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

File details

Details for the file thetadatadx-10.0.0.tar.gz.

File metadata

  • Download URL: thetadatadx-10.0.0.tar.gz
  • Upload date:
  • Size: 695.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thetadatadx-10.0.0.tar.gz
Algorithm Hash digest
SHA256 399125dbe02fa7e0e540aec5464f9a1b256913fb3dbeda31cdb55c469cccc2af
MD5 a9e428e1c36e5dd1687e5b6723fb9339
BLAKE2b-256 6b8577161248a136db850b94c7f4370ed218431c9ecf55f8df3851312ad4578b

See more details on using hashes here.

File details

Details for the file thetadatadx-10.0.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: thetadatadx-10.0.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 8.2 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thetadatadx-10.0.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 44c389f9b2316e4073b34b57f25dbb4904520dd9d20610daefff993ca96c6d1e
MD5 e1ea35d45c6a0c9a07652b39c2bf6ed1
BLAKE2b-256 f58d5db8611e6c581d18f742b4eb5a928f9ef5df623a2a98c2d1d73412ebb184

See more details on using hashes here.

File details

Details for the file thetadatadx-10.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for thetadatadx-10.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2238e1d0771dc67f87538f53296acc04f0af29c7a7397f5bd43f81af178ad085
MD5 cc60e696271d1278c2f6eaefb1d46c99
BLAKE2b-256 f5e0343f14cb153840389a9364e14008b7124f137a8639625d59adddc639c3dc

See more details on using hashes here.

File details

Details for the file thetadatadx-10.0.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for thetadatadx-10.0.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9081f9625d9caf6fc71266c7d78fb4f21fd6e3165f4022bb13f054855f8b1a12
MD5 e8a70b8cb2361f4ec6677f53641d39e6
BLAKE2b-256 9d4f2adb62148a8bfefdcba117829f0e7feda7f0fff448df7d34aa6259887f89

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page