Skip to main content

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/.

Metadata

Release files for thetadatars 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for thetadatars 0.5.0
File Size Uploaded
thetadatars-0.5.0.tar.gz 130.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for thetadatars 0.5.0
File Interpreter ABI Platform
thetadatars-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 297.7 kB

Release files / thetadatars-0.5.0.tar.gz

Download URL thetadatars-0.5.0.tar.gz
Size 130.7 kB
Tags Source
SHA-256 checksum
How to use checksums
98e07536865112b0a6ef6469bcfa144a9255a0e36f5f8289cf6c731424a9cbbf
BLAKE2b-256 checksum
How to use checksums
d23d497562f57e3d3909cc7a63676306765efe991c01246d70d6ed85c38cb5ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / thetadatars-0.5.0-py3-none-any.whl

Download URL thetadatars-0.5.0-py3-none-any.whl
Size 166.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aa6f669713d21210a45d0c07bb7714fb85fd675929c601911bc8ada1fbf628f9
BLAKE2b-256 checksum
How to use checksums
bfc5eef22755d85ce809470ab267aff85bcc3346140a67640862f5cf0e6fc5d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page