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 raiseCacheMissErrorif 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-tickerCacheMissErrorfailures 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_batchget_snapshot_greeks_first_order_batchwarm_snapshot_quote_cachewarm_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:
callputcovered_callprotective_putcash_secured_putcredit_spreaddebit_spreadcalendar_spreaddiagonal_spreadstraddlestrangleiron_condoriron_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 syncinstalls dependencies.uv run --with pytest pytestruns the offline test suite.uv run python test.pyruns the live smoke-test script and may call ThetaData services.uv buildcreates package distributions indist/.
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)
| File | Size | Uploaded | |
|---|---|---|---|
| thetadatars-0.5.0.tar.gz | 130.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|