Skip to main content

Python wrapper and DuckDB cache for ThetaData option and streaming endpoints.

Project description

ThetaDataRS

ThetaDataRS is a Python wrapper around the thetadata package. It adds a convenience facade, local DuckDB caching, typed option strategy screeners, structured errors, and cache-aware batch helpers for watchlist-scale workflows.

Installation

This project uses Python 3.12 and uv.

uv sync

Create a local .env file with your ThetaData credentials:

EMAIL=you@example.com
PASSWD=your-password

Credentials are loaded by thetadatars.client.create_client() through python-dotenv.

Quick Start

Use ThetaDataRS when you want one object that wraps the existing endpoint functions and automatically supplies the ThetaData client.

from thetadatars.thetadata import ThetaDataRS

theta = ThetaDataRS()

contracts = theta.get_options_contract_list(
    "AAPL",
    "2026-04-24",
)

print(contracts)

The first call fetches from ThetaData when local data is missing or stale, writes the result to DuckDB, and returns a Polars DataFrame. Later calls read from the local cache until the endpoint's stale_threshold is exceeded.

Option List Examples

from thetadatars.thetadata import ThetaDataRS

theta = ThetaDataRS()

symbols = theta.get_options_symbols_list()
expirations = theta.get_options_expiration_list("AAPL")
strikes = theta.get_options_strike_list("AAPL", "2026-04-24")
contracts = theta.get_options_contract_list("AAPL", "2026-04-24")

Snapshot Example

from thetadatars.thetadata import ThetaDataRS

theta = ThetaDataRS()

quotes = theta.get_snapshot_quote(
    ticker="AAPL",
    expiration="2026-04-24",
    strike="*",
    right="both",
)

print(quotes.head())

Snapshot endpoints default to a shorter cache window than historical endpoints because the data changes during the trading day.

Snapshot quote, first-order Greek, and option-expiration list helpers also support explicit cache policies:

quotes = theta.get_snapshot_quote(
    ticker="AAPL",
    expiration="2026-04-24",
    strike="*",
    right="put",
    strike_range=20,
    cache_policy="prefer_cache",
)

Supported cache policies:

  • prefer_cache: use fresh matching cache data, otherwise fetch from ThetaData and store it.
  • cache_only: read only from DuckDB and raise CacheMissError if no fresh matching cache entry exists.
  • refresh: fetch from ThetaData even when cache data exists, then update DuckDB.
  • no_cache: fetch from ThetaData without reading or writing DuckDB.

Cache matching is parameter-aware. For example, a cached right="put" request will not satisfy right="call", and a narrow strike_range=10 fetch will not satisfy a broader unbounded request. Cache fetch metadata is stored in the cache_fetches DuckDB table with endpoint, request params, status, row count, and fetch timing.

Wildcard strategy screens use the same cache policy for expiration discovery and snapshot data. cache_only will not fetch missing expirations, and no_cache will not read or write the expiration cache.

Batch Snapshot Data

Use cache-aware batch helpers when you need the same snapshot request across a watchlist without running a strategy screen.

from thetadatars.thetadata import ThetaDataRS
from thetadatars.options.batch import RateLimitPolicy, RetryPolicy, TimeoutPolicy

theta = ThetaDataRS()

quotes = theta.get_snapshot_quote_batch(
    ["AAPL", "MSFT", "NVDA"],
    expiration="2026-04-24",
    right="both",
    strike_range=20,
    cache_policy="prefer_cache",
    concurrency=3,
    retry_policy=RetryPolicy(max_attempts=2, backoff_seconds=0.5),
    timeout_policy=TimeoutPolicy(per_ticker_seconds=30),
    rate_limit_policy=RateLimitPolicy(min_interval_seconds=0.25),
)

print(quotes.data)
print(quotes.stats)
print(quotes.failures)

The batch helpers delegate to the normal endpoint functions for each ticker, so cache behavior is identical to direct calls:

  • prefer_cache: use fresh matching cache data per ticker, otherwise fetch and store.
  • cache_only: return per-ticker CacheMissError failures without fetching missing data.
  • refresh: fetch each ticker and update DuckDB.
  • no_cache: fetch each ticker without reading or writing DuckDB.

Batch results do not fail the whole request because one ticker fails. They return:

  • data: one combined Polars DataFrame for successful tickers.
  • successes: per-ticker row counts and timing.
  • failures: structured errors with ticker, endpoint, params, retryability, and attempts.
  • stats: total, succeeded, failed, and total duration.

Warm cache helpers force cache_policy="refresh" and are useful before running screeners or UI workflows:

warmup = theta.warm_snapshot_greeks_first_order_cache(
    ["AAPL", "MSFT"],
    expiration="2026-04-24",
    strike_range=20,
)

print(warmup.stats)

If you pass a shared DuckDB conn, batch helpers run serially to avoid using the same connection from multiple worker threads. Leave conn=None when you want concurrent per-ticker endpoint calls.

Currently supported raw batch helpers are:

  • get_snapshot_quote_batch
  • get_snapshot_greeks_first_order_batch
  • warm_snapshot_quote_cache
  • warm_snapshot_greeks_first_order_cache

Historical Option Example

from thetadatars.thetadata import ThetaDataRS

theta = ThetaDataRS()

eod = theta.get_option_history_eod(
    ticker="AAPL",
    start_date="2026-04-01",
    end_date="2026-04-24",
    expiration="2026-04-24",
    strike="*",
    right="both",
)

print(eod)

Credit Spread Screening

Credit spread screeners can be called through the legacy DataFrame API or through a request object that returns data, stats, and warnings.

from thetadatars.options.screeners.credit_spreads import (
    CreditSpreadRequest,
    warm_credit_spread_cache,
    screen_credit_spreads,
    screen_credit_spread_watchlist,
    TimeoutPolicy,
    RateLimitPolicy,
    CircuitBreakerPolicy,
)

request = CreditSpreadRequest(
    ticker="AAPL",
    expiration="*",
    right="put",
    max_dte=45,
    strike_range=20,
    top_n=25,
    cache_policy="prefer_cache",
    max_candidates_per_expiration=500,
    max_candidates_total=2_000,
)

result = screen_credit_spreads(request)

print(result.data)
print(result.stats)
print(result.warnings)

Typed requests guard expensive broad scans. expiration="*" requires max_dte unless allow_full_chain=True. Greek source can be selected with greeks_source="auto", "thetadata", "local", or "none".

You can inspect the expected cache/upstream behavior before running a credit-spread screen:

from thetadatars.options.screeners.credit_spreads import plan_credit_spreads

plan = plan_credit_spreads(request)

print(plan.expected_endpoint)
print(plan.cache_hits, plan.cache_misses, plan.upstream_calls)
print(plan.cost, plan.local_computation)

ScreenerStats includes cache fields such as cache_hits, cache_misses, upstream_calls, and cache_policy so app code can explain whether a result came from cache or required an upstream request.

Warm cache data for a watchlist without building spread candidates:

warmup = warm_credit_spread_cache(
    ["AAPL", "MSFT"],
    request,
    concurrency=2,
    rate_limit_policy=RateLimitPolicy(min_interval_seconds=0.25),
)

print(warmup.stats)
print(warmup.failures)

Screen watchlists with bounded runtime and failure controls:

batch = screen_credit_spread_watchlist(
    ["AAPL", "MSFT"],
    request,
    concurrency=2,
    timeout_policy=TimeoutPolicy(per_ticker_seconds=30),
    rate_limit_policy=RateLimitPolicy(min_interval_seconds=0.25),
    circuit_breaker_policy=CircuitBreakerPolicy(max_failures=3),
)

print(batch.data)
print(batch.failures)
print(batch.stats)

Candidate caps limit strategy-builder work after normal filters have selected otherwise eligible spreads. When caps prune candidates, the result includes a candidate_limit warning and ScreenerStats.pruned_candidate_rows.

Multi-Strategy Screening

All bundled strategy screeners now have typed request/result APIs:

  • call
  • put
  • covered_call
  • protective_put
  • cash_secured_put
  • credit_spread
  • debit_spread
  • calendar_spread
  • diagonal_spread
  • straddle
  • strangle
  • iron_condor
  • iron_butterfly

Single-leg and stock-linked strategies use the same request/result flow as spreads:

from thetadatars.options.screeners.cash_secured_put import (
    CashSecuredPutRequest,
    screen_cash_secured_puts,
)

request = CashSecuredPutRequest(
    ticker="AAPL",
    expiration="*",
    max_dte=45,
    strike_range=20,
    min_delta=0.10,
    max_delta=0.35,
    top_n=25,
    greeks_source="none",
)

result = screen_cash_secured_puts(request)

print(result.data)
print(result.stats)

The generic strategy dispatcher lets backend applications use one watchlist API for first-class typed strategies:

from thetadatars.options.screeners.cash_secured_put import CashSecuredPutRequest
from thetadatars.options.screeners.strategies import (
    get_available_strategies,
    plan_screener,
    screen_watchlist,
    warm_cache,
)

request = CashSecuredPutRequest(expiration="*", max_dte=45, strike_range=20)

print(get_available_strategies())
print(plan_screener(request))

warmup = warm_cache(["AAPL", "MSFT"], strategy="cash_secured_put", request=request)
batch = screen_watchlist(["AAPL", "MSFT"], strategy="cash_secured_put", request=request)

The same dispatcher can run multi-leg strategies:

from thetadatars.options.screeners.iron_condor import IronCondorRequest
from thetadatars.options.screeners.strategies import screen_watchlist

request = IronCondorRequest(
    expiration="*",
    max_dte=45,
    strike_range=30,
    min_credit=0.50,
    max_candidates_per_expiration=500,
    max_candidates_total=2_000,
)

batch = screen_watchlist(["AAPL", "MSFT"], strategy="iron_condor", request=request)
print(batch.data)
print(batch.failures)

Legacy get_best_* and find_best_* DataFrame helpers remain available for compatibility. Typed APIs add planning, cache statistics, warm-cache helpers, watchlist batching, structured failures, and candidate pruning warnings.

Streaming Example

Streaming helpers return async iterators. ThetaDataRS automatically applies its stream_url, which defaults to ws://127.0.0.1:25520/v1/events.

import asyncio

from thetadatars.thetadata import ThetaDataRS


async def main():
    theta = ThetaDataRS()

    async for message in theta.stream_option_trades(
        "AAPL",
        "2026-04-24",
        200,
        "C",
        max_messages=5,
        timeout=10,
    ):
        print(message)


asyncio.run(main())

Direct Function Usage

You can also import endpoint functions directly if you want explicit control over the client.

from thetadatars.client import create_client
from thetadatars.options.list.contracts import get_options_contract_list

client = create_client()

contracts = get_options_contract_list(
    "AAPL",
    "2026-04-24",
    client=client,
)

Local Data

Cached endpoint data is stored in DuckDB through thetadatars.data.db. The default database path is controlled by THETADATARS_DB, the user config file, or the platform config directory.

Do not commit local database files, .env files, credentials, or generated market data.

Development

uv sync
uv run --with pytest pytest
uv build
  • uv sync installs dependencies.
  • uv run --with pytest pytest runs the offline test suite.
  • uv run python test.py runs the live smoke-test script and may call ThetaData services.
  • uv build creates package distributions in dist/.

Project details


Download files

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

Source Distribution

thetadatars-0.3.0.tar.gz (129.7 kB view details)

Uploaded Source

Built Distribution

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

thetadatars-0.3.0-py3-none-any.whl (165.9 kB view details)

Uploaded Python 3

File details

Details for the file thetadatars-0.3.0.tar.gz.

File metadata

  • Download URL: thetadatars-0.3.0.tar.gz
  • Upload date:
  • Size: 129.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for thetadatars-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7124a348baa80b42e8429e84c2a85cfa3f6d8786ec2e3fcf9f9389ac47c82a03
MD5 244835a93bd89ed26ccf2fa3b057802e
BLAKE2b-256 fb1786ba4af3b51a58bfcd1e0e20a385afd850e5d754db55f563e83c6a79484a

See more details on using hashes here.

File details

Details for the file thetadatars-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: thetadatars-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 165.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for thetadatars-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4f499ea4f9870ea5cdfcf808012142178056d826c41d60289e85a94b12ba91d8
MD5 8228425964cd1a2be520c490d57616ab
BLAKE2b-256 a32d3f9ef6f9f93de66b2e8fd3e8258ef0e86310ba2f1166b3b6d6368487419c

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