
hplin/tastytrade-research-mcp
Developer toolsResearch-focused MCP server for tastytrade historical market data and backtesting, including Backtester API integration and historical candles for XSP/SPX and SPY options regression testing.
Overview
Research-only Model Context Protocol (MCP) server for tastytrade historical options research, bounded live SPX/SPXW option evidence, package-price evidence, and strategy regression testing. The project is intentionally separate from the official tastytrade/tastytrade-mcp: - : general live market data, account workflows, and broker dry-run validation - : historical candles, Backtester access, bounded direct SPX/SPXW Gamma+OI snapshots, package-pricing research, and post-session fill verification - : brokerage order placement, replacement, or cancellation These tools dynamically resolve an explicitly selected Traditional IRA, mask account identifiers, and expose only broker GET requests. They do not use the recent/live order snapshot. See broker history and OO audit for window semantics, privacy, completeness, quote limitations, and a synthetic FinPilot input. Engineering fixtures remain explicitly synthetic, not live acceptance evidence.
README
tastytrade-research-mcp
Research-only Model Context Protocol (MCP) server for tastytrade historical options research, bounded live SPX/SPXW option evidence, package-price evidence, and strategy regression testing.
The project is intentionally separate from the official tastytrade/tastytrade-mcp:
- official tastytrade MCP: general live market data, account workflows, and broker dry-run validation
- this project: historical candles, Backtester access, bounded direct SPX/SPXW Gamma+OI snapshots, package-pricing research, and post-session fill verification
- never exposed here: brokerage order placement, replacement, or cancellation
MCP tools
Provider-native Backtester tools
| Tool | Upstream endpoint | Purpose |
|---|---|---|
tastytrade_get_backtest_available_dates |
GET /available-dates |
Discover symbols and historical coverage |
tastytrade_list_backtests |
GET /backtests |
List submitted research jobs |
tastytrade_create_backtest |
POST /backtests |
Submit a provider-native historical strategy |
tastytrade_get_backtest |
GET /backtests/{id} |
Poll status and retrieve results |
tastytrade_get_backtest_logs |
GET /backtests/{id}/logs |
Retrieve trials and execution logs |
tastytrade_cancel_backtest |
POST /backtests/{id}/cancel |
Cancel only a Backtester job |
tastytrade_simulate_trade |
POST /simulate-trade |
Simulate one exact historical trade |
Read-only broker history and OO audit
| Tool | Purpose |
|---|---|
tastytrade_get_historical_orders |
Paginated historical SPX/SPXW orders, all statuses, available lifecycle timestamps and explicit coverage |
tastytrade_get_historical_transactions |
Exact-decimal per-leg historical executions, transaction cash flows, fees and UTC/ET timestamps |
tastytrade_audit_oo_broker_orders |
OO-to-broker ID/leg correlation, attempts, independent call/put cash flows, evidence matrix and Traditional Chinese summary |
These tools dynamically resolve an explicitly selected Traditional IRA, mask account identifiers, and expose only broker GET requests. They do not use the recent/live order snapshot. See broker history and OO audit for window semantics, privacy, completeness, quote limitations, and a synthetic FinPilot input. Engineering fixtures remain explicitly synthetic, not live acceptance evidence. Authorized October 12–13 ET broker queries enumerated 59 orders and 76 transactions with complete provider-result pagination, not exhaustive lifecycle-event history. User-provided original OO Activity Log text for both days is now available privately and correlated through source-bound normalization. Its clocks have whole-second precision; ET is attributed to the user context, not embedded offsets. One October 12 exhaustion episode is identified; a second reported episode retains a definite identification-coverage gap, not an invented failure or proof of absence. Issue #102 remains open pending independent acceptance of the actual evidence. Fees and cash flows are reconciled when available; final fee, settlement and campaign incompleteness remains an explicit evidence limitation, with definitive claims suppressed, not a new requirement to acquire unavailable final data.
Research and regression tools
| Tool | Purpose |
|---|---|
tastytrade_get_live_option_snapshot |
Auto-chunk and merge exact SPX/SPXW DXLink Quote, Greeks, and Summary.openInterest cohorts; preserve native/local timing, evaluate optional 2/3/4-leg exact packages with caller quality policy for FinPilot evidence handoff, and calculate a research-only unsigned Gamma concentration proxy |
tastytrade_compute_heuristic_signed_gex |
Apply an explicit versioned research signing hypothesis to one unified snapshot, with separate current-Gamma signed GEX and optional bounded spot-repriced heuristic gamma flip |
tastytrade_price_option_package |
Price verticals, iron condors, and double diagonals with explicit native/synthetic provenance |
tastytrade_discover_historical_spx_candidates |
Reconstruct timestamp-safe historical SPXW candidates under a versioned resolution profile, with exact-timestamp Backtester fallback |
tastytrade_discover_historical_spx_candidates_range |
Run the same candidate discovery over an explicit trading calendar with bounded concurrency, deadlines, immutable-cache diagnostics, and resumable partial progress |
tastytrade_get_historical_spx_candidate_universe |
Return a bounded multi-strike, multi-expiration SPXW universe and optional versioned RESEARCH_ONLY DD selected-leg/matched-delta IV handoff |
tastytrade_get_historical_option_package_at_checkpoint |
Reconstruct an exact-leg SPX package reference at an RFC3339 or IANA-local checkpoint with explicit age and skew controls |
tastytrade_get_historical_option_package_path |
Return profile-aligned completed-candle package points and explicit gaps without interpolation |
tastytrade_get_historical_option_package_horizons |
Reconstruct a frozen exact-leg candidate inventory at caller-supplied ENTRY/+3/+5 trading sessions, with an opt-in research-only model fallback for candle-missing legs |
tastytrade_normalize_historical_execution_evidence |
Normalize immutable exact-leg quote snapshots/windows into signed, provider-neutral simulated-execution inputs without claiming a fill |
tastytrade_normalize_historical_strategy_evidence |
Normalize V2 frozen strategy identities, exact inventory, historical quote/reference evidence and optional actual entry facts, including staged SIC, without inferring fills or certifying maximum loss |
tastytrade_build_historical_event_path |
Replay caller-frozen exact-leg observations and staged entry/partial close/roll/settlement events with timestamp-safe risk, shared execution/fee models, bounded resume and full metric denominators |
tastytrade_simulate_historical_execution |
Apply one caller-frozen, hashed execution profile to immutable exact-leg evidence and return a separate simulated fill/P&L result |
tastytrade_build_historical_replay_report |
Aggregate frozen baseline/research decisions and immutable execution simulations into deterministic +3/+5-day metrics without changing grading or paper state |
tastytrade_prepare_spx_spread |
Deterministically normalize SPX legs without calling an upstream service |
tastytrade_simulate_spx_spread |
Run exact-leg SPX historical simulation and normalize its result |
tastytrade_create_spx_spread_backtest |
Submit supported SPX structures through relative Backtester selectors |
tastytrade_verify_historical_fill |
Check a frozen paper limit against a caller-supplied historical package path or a forward Backtester path |
tastytrade_get_historical_candles |
Retrieve DXLink LAST/BID/ASK OHLC aggregates without resampling; valuation-only, not executable quotes |
Use MCP tools/list for the complete JSON input schemas.
The direct live snapshot, bounded DXLink batch provenance, event/cohort
freshness contract, OI-based unsigned Gamma concentration methodology,
semantic limits, regression handoff, and opt-in live gate are documented in
docs/live-option-snapshot.md.
The separate Level 3 signing hypothesis, model/result identities,
current-Gamma versus spot-repriced evidence, bounded heuristic gamma-flip
search, and research-only live gate are documented in
docs/heuristic-signed-gex.md.
The shared profile contract and the optional seven-date live capability gate
are documented in
docs/resolution-profiles.md.
The separate Double Diagonal IV measurement contract, legacy-preserving
migration, and reproducible grading/regression handoff are documented in
docs/dd-iv-measurements.md.
Private immutable source-cache configuration, exact offline replay, and
migration guidance are documented in
docs/evidence-cache.md.
The external historical-source capability review, explicit no-spend decision,
and approval gates are documented in
docs/historical-provider-decision.md.
The pinned offline March–August 2026 exact-leg inventory, blocked coverage
matrix, and reproducible report command are documented in
docs/historical-coverage-audit.md.
The quote-backed exact-leg handoff, signed inventory convention, and
valuation/simulation/broker separation are documented in
docs/historical-execution-evidence.md.
The additive V2 family shapes, frozen selection/reference costs, actual entry
facts, and independently timestamped staged SIC exposure are documented in
docs/historical-strategy-evidence.md.
The additive variable-checkpoint event-path contract, caller-frozen exit
instructions, lifecycle ledger, pagination and reproducible real-evidence
NOT_ASSESSABLE audit are documented in
docs/historical-event-path.md.
Caller-frozen execution profiles, fill statuses, exact P&L arithmetic, and
fee semantics are documented in
docs/historical-execution-model.md.
The replay acceptance matrix, denominator rules, one-week smoke manifest, and
runner guidance are documented in
docs/historical-replay.md.
Private historical evidence cache
The historical candle, SPX candidate/universe, and exact-package tools accept
an opt-in evidence_cache policy. The server exposes 29 tools, including the
local-only historical quote normalizer, deterministic execution-model
adapter, and historical replay report builder.
READ_WRITE stores sanitized source observations and normalized results as
separate content-addressed objects; REFRESH creates a new immutable revision
and diff; CACHE_ONLY replays only caller-supplied exact manifest IDs and
never contacts the provider.
The filesystem backend is disabled until
TASTYTRADE_EVIDENCE_CACHE_DIR points to a private volume. It uses atomic
writes, read-back checksum verification, bounded provider concurrency,
request deduplication, short-lived retryable-failure indexes, and a hard disk
quota without evicting immutable evidence. Cache identity includes exact
symbols, range/as-of, provider/dataset/license scope, aggregation/session/
alignment/price type, the full resolution profile, resource policy, and
normalization/model/source revisions. Retrieval time is frozen in each
immutable manifest and never replaces bar availability time.
Run npm run report:evidence-cache for a deterministic synthetic cache
hit/miss, byte-count, normalized-hash, and provider-call-reduction report.
Approval-gated bounded history
The repository contains a provider-neutral bounded-history contract for a future approved source. It is intentionally dormant: no vendor transport, credential, paid fallback, environment configuration, or MCP tool is enabled. Construction requires an exact approval scope, confirmed field and expired-symbol entitlements, a provider-specific native-field allowlist, a fixed HTTPS host, separate credentials, and explicit cache storage/reuse permission. Synthetic tests validate the contract but do not establish external SPX/SPXW coverage.
Execution evidence contract
Every normalized research result uses execution-evidence contract 1.0.0.
The contract distinguishes:
NATIVE_PACKAGESYNTHETIC_NATURALSYNTHETIC_MID_REFERENCEHISTORICAL_OPTION_PACKAGE_REFERENCEHISTORICAL_PATHBACKTESTER_SIMULATIONBROKER_DRY_RUN
The machine-readable schema is
docs/execution-evidence.schema.json.
Migration guidance for existing paper-simulation logs is in
docs/execution-evidence-migration.md.
BROKER_DRY_RUN validates a caller-supplied order; it never implies
fillability. Synthetic midpoint evidence is valuation-only.
Package pricing
Package arithmetic uses an in-repository exact decimal implementation rather than binary floating-point arithmetic.
NATIVE_PACKAGEis selected only when the caller supplies a valid, non-crossed upstream package market.SYNTHETIC_NATURALbuys each leg at its ask and sells each leg at its bid.SYNTHETIC_MID_REFERENCEuses leg midpoints and always reportsguaranteed_executable: false.- Native package bid/ask fields are never populated with synthetic arithmetic.
- Every leg retains its exact symbol, action, quantity, expiration, timestamp, and provider source.
as_of, freshness, and temporal alignment are computed from the decision-critical observations.- Missing, crossed, stale, future-dated, and materially misaligned quotes are returned as explicit warnings. Unusable evidence is never success-shaped.
Supported families are DEBIT_VERTICAL, CREDIT_VERTICAL, IRON_CONDOR, and
multi-expiration DOUBLE_DIAGONAL.
SPX spread adapter
The high-level adapter preserves each exact provider/OCC option symbol in
/simulate-trade requests and returns stable 1.0.0 result envelopes. Each
result repeats the immutable normalized legs and carries a deterministic
SHA-256 request_id, so persisted evidence remains attributable even though
the provider response itself contains only prices and timestamps.
Aggregate /backtests requests use relative selectors (delta,
percentageOTM, currentPriceOffset, or premium) and therefore cannot
preserve an exact historical strike or expiration. The adapter exposes that
limitation in its capability flags.
Double diagonals remain exact-simulation only because the aggregate
Backtester cannot faithfully preserve their multi-expiration identity. 0DTE
legs are rejected unless the caller explicitly sets allow_0dte: true.
This MCP supplies evidence only. It does not assign grades or make production entry decisions.
Historical SPX candidate discovery
tastytrade_discover_historical_spx_candidates is the
REGRESSION_RESEARCH bridge between timestamp-safe selector evidence and
exact contract identity.
tastytrade_discover_historical_spx_candidates_range applies that same
single-checkpoint contract to an explicit caller-supplied trading calendar.
It preserves chronological output order while using a bounded worker pool,
isolates checkpoint failures, retries only transient provider failures, and
returns an opaque continuation cursor for unresolved or deferred sessions.
Continuation scheduling completes a first pass over never-attempted sessions
before retrying failures. Retry-eligible checkpoints run before checkpoints
whose provider cooldown is still active, and unresolved retries rotate to the
queue tail so rate-limited dates cannot block later checkpoints.
All Backtester endpoints in one server process share a request-start gate.
It spaces starts by at least 100 ms by default, honors Retry-After before
X-RateLimit-Reset, and falls back to bounded jittered 10/30/90-second
cooldowns. Override only the pacing interval with
TASTYTRADE_BACKTESTER_MIN_REQUEST_INTERVAL_MS; continuation retry timestamps
remain provider- or scheduler-derived.
Per-checkpoint stage timings identify cache lookup, provider bootstrap,
contract-universe construction, candle reconstruction, selector evaluation,
and the stage interrupted by a deadline. Operational settings and diagnostic
callbacks do not alter the logical batch request ID or candle-cache
fingerprints.
The official option-chain and REST quote endpoints do not document an
historical as_of parameter. Backtester logs currently expose exact selected
strike, expiration, side, and a provider-internal symbol, but those log fields
are undocumented. The documented Backtester EntryConditions also has no
time-of-day field, and a live request containing undocumented
entryTime: "14:30:00Z" was silently ignored: the SPX trial still opened at
19:45:00Z.
For DELTA and PERCENTAGE_OTM, the adapter reconstructs a bounded SPXW
universe from generated OCC/streamer symbols and completed DXLink candles.
Candidate discovery uses the provider-supported maximum of 100 option symbols
per candle batch; the standalone bounded-universe tool retains its existing
20-symbol batch policy.
Omitting resolution_profile preserves the 5-minute cohort; strict native-hour RTH
research requires explicit HOURLY_VALUATION_RESEARCH. When DXLink labels SPXW
hour bars on its provider clock instead of the 09:30 ET session anchor, callers
must opt into the separate HOURLY_PROVIDER_ALIGNED_RESEARCH cohort; those bars
are never relabeled as session-aligned. It enforces
available_at <= as_of, profile age/skew limits, aligned call/put parity for
the forward, and contract-candle IV for Black-76-style delta. Selection is
deterministic by selector error, observation age, DTE distance, and strike.
Generated contract identity becomes eligible only when DXLink returns historical evidence for that exact symbol. The result preserves checkpoint selection time, observation age, price, contract IV, reconstructed delta, volume/OI when present, OCC identity, and field-level provenance.
Exact-timestamp Backtester selection remains the fallback for unsupported
selectors or insufficient reconstruction evidence. It never sends or trusts
entryTime, and it accepts only a trial and opening order exactly at
as_of. Stale and future trials remain fail-closed.
The capability returns HISTORICAL_SELECTOR_CANDIDATE_SET, never a
full-chain snapshot. Historical bid/ask, ATM IV surface, skew, and term
structure remain unavailable. Exact symbols are /simulate-trade compatible,
but the capability separately reports whether an exact simulation snapshot
exists at the arbitrary checkpoint.
The live 2026-08-25 07:30 PT smoke test returns four timestamp-safe CALL/PUT Delta-20 and 1%-OTM candidates without creating Backtester jobs.
The full provider findings, contract, anti-lookahead rules, and limitations
are documented in
docs/historical-spx-candidates.md.
Historical SPX candidate universe
tastytrade_get_historical_spx_candidate_universe expands checkpoint-safe
evidence from selector winners into a caller-bounded strike grid. By default
it covers the nearest min/mid/max DTE SPXW expirations, both option sides, and
a 25-point strike step.
Generated OCC identity is returned only after DXLink supplies a completed historical candle for that exact symbol. Contracts without timestamp-safe evidence are omitted and counted as coverage gaps. The endpoint preserves price, reconstructed delta, contract IV, OI, volume, observation age, and field-level provenance when available; it never selects the final spread. Delta reconstruction prefers a timestamp-aligned put/call parity forward. When parity is unavailable but historical IV exists, the universe endpoint uses the completed checkpoint SPX price as an explicitly warned zero-carry forward approximation. Missing IV remains null, and this fallback does not change the stricter selector-discovery path.
The default freshness maximum is 60 minutes. A caller may explicitly extend
it to 24 hours; older pre-checkpoint observations are then retained only as
STALE/LOW evidence. Coverage gaps, provider batch errors, and per-field
availability counts remain machine-readable, and aggregate field capability
flags are true only when every returned contract supports the field.
Both historical SPX tools accept either RFC3339 as_of or an unambiguous
IANA local_checkpoint. They preserve bar_start, bar_end,
available_at, and retrieved_at, and return the normalized resolution
profile with deterministic requested/effective cohort IDs. An opaque
versioned candidate_construction_profile is returned unchanged; this MCP
does not duplicate grading, DD-bucket, or final-leg-selection rules.
An optional dd_iv_measurement request freezes an exact four-leg Double
Diagonal candidate and returns distinct SELECTED_LEG_IV_DIFFERENCE and
MATCHED_DELTA / MATCHED_FORWARD_MONEYNESS cohorts. The handoff is always
RESEARCH_ONLY, preserves provider/derived models, timestamp lineage, and
optional immutable evidence-manifest IDs, and explicitly does not replace
legacy production term_structure.
See
docs/historical-spx-universe.md
for the input contract, reconstruction rules, and live checkpoint findings.
Historical exact-leg package reconstruction
tastytrade_get_historical_option_package_at_checkpoint selects only
completed option candles available by the requested checkpoint and preserves
each leg’s exact OCC identity, derived streamer symbol, historical close, IV,
bar-start timestamp, availability timestamp, age, and provenance. Explicit
age and temporal-skew limits fail closed. Candle-derived values are always
VALUATION_ONLY; historical bid/ask is not fabricated.
The checkpoint and path results include deterministic request IDs plus requested/native/effective aggregation, session, alignment, age/skew, fallback, and cohort metadata. The default checkpoint behavior remains the existing 5-minute compatibility path. Native-hour New York RTH valuation is explicit opt-in and does not silently fall into the 5-minute cohort. Checkpoint legs also return a structured reconstruction status and failure reason. Their provenance carries the requested lifecycle, resolution profile, provider failure reasons, source revision, and exact cache manifest/content IDs when available.
tastytrade_get_historical_option_package_horizons accepts a bounded frozen
candidate inventory and a caller-supplied ordered trading-session calendar.
It resolves ENTRY, OUTCOME_3_TRADING_DAYS, and
OUTCOME_5_TRADING_DAYS by session index rather than assuming weekdays are
trading days. Every horizon reuses the same OCC symbols, actions, quantities,
roles, strategy family, and resolution profile. The result keeps candle
references labeled VALUATION_ONLY / CANDLE_REFERENCE and reports complete
package counts, missing legs by role, missing-reason counts, and coverage by
strategy, expiration, entry DTE, and resolution profile. Exact 21/35-DTE
Double Diagonals are supported without changing grading or leg selection.
An optional valuation_fallback can supply checkpoint-frozen SPX levels,
per-exact-leg IV or surface inputs, rates, dividends, immutable source IDs,
and an IV-shift uncertainty assumption. The strict candle status, package,
legs, and failure taxonomy remain unchanged. A separate valuation object
classifies each result as EXACT_PACKAGE_REFERENCE,
MIXED_OBSERVED_MODELED, or MODEL_SURFACE; observed leg values are copied
unchanged and only unavailable exact legs are modeled. Cache/provider errors,
stale observations, and alignment failures remain non-modelable.
Any package containing a modeled leg is MODEL_REFERENCE /
VALUATION_ONLY, carries guaranteed_executable: false, and is explicitly
not bid/ask, NBBO, midpoint, touch, or fill evidence. Its
execution_evidence_input can be passed to
tastytrade_normalize_historical_execution_evidence and then evaluated only
through a separately frozen REFERENCE_COST execution profile. Coverage
keeps strict package counts separate from valued exact, mixed, and fully
modeled cohorts.
tastytrade_get_historical_option_package_path emits a point only when every
leg has an exact timestamp-aligned completed bar. It never interpolates or
forward-fills missing legs. If a bounded old 1-minute DXLink replay exhausts a
configured local resource budget or the provider returns a clipped snapshot,
the result records the failed attempt and explicitly selects the finest
retrievable coarser resolution. A local budget result is not described as a
provider limit.
The returned fill_verification_path can be passed directly to
tastytrade_verify_historical_fill. Full rules and the 2026-08-27 07:30 PT
acceptance finding are documented in
docs/historical-option-package.md.
Historical fill verification
tastytrade_verify_historical_fill:
- evaluates only
[submitted_at, valid_until]; - accepts either caller-supplied historical package points or the existing exact-leg Backtester mode;
- supports both
ENTRYandEXIT; - preserves
paper_order_id,checkpoint_id, andposition_idreferences; - returns legacy
statusvaluesTOUCHED,NOT_TOUCHED, orNOT_VERIFIABLE, plusassessment_status: NOT_ASSESSABLEfor an insufficient path; - distinguishes
LIMIT_TOUCHfromCONSERVATIVE_CROSS; - reports an exact observed touch timestamp when defensible, otherwise a bounded interval for a sparse path;
- records disagreement with a live paper assumption without mutating the original paper event.
All verification is marked POST_SESSION_REGRESSION and includes
HISTORICAL_EVIDENCE_ONLY_DO_NOT_REWRITE_LIVE_EVENT.
DXLink historical candles
tastytrade_get_historical_candles obtains an API quote token from
GET /api-quote-tokens, connects only to a wss:// host under
dxfeed.com, completes the DXLink handshake, and waits for the indexed-event
snapshot boundary.
Optional price_type: LAST | BID | ASK selects separate provider-native
price aggregates. LAST remains the default; BID/ASK are not synchronized
NBBO and have no quote sizes or executable-package guarantee. Each price
type has a distinct cache identity. Recent SPXW BID/ASK aggregates were
retrieved, but July 2 minute history remains unavailable; dxFeed documents
approximately 14 calendar days of BID/ASK candle retention. See
docs/historical-candles.md for the measured
coverage, request example, and completed-bar timing requirements.
The normalized output includes:
- deterministic request and cohort identity;
dxlink_authrecovery provenance and a sanitizedprovider_errorwhen authentication required recovery;- requested and actual UTC ranges;
source_time,bar_start,bar_end,available_at, andretrieved_atper bar;- explicit instrument type and interval;
ALL, USREGULAR, or caller-definedCUSTOMsession filtering with an IANA timezone;- OHLC, volume, VWAP, bid/ask volume, implied volatility, and open interest when supplied;
- missing-bar, empty-result, and snapshot-truncation warnings;
resampled: false.
REGULAR requests use provider-native a=s,tho=true candle attributes, so
bars are aligned to and built only from the instrument’s regular trading
session. CUSTOM windows filter complete provider bars by their source
timestamp; they are never reaggregated and include an explicit warning about
that limitation.
One-unit periods use DXLink’s native normalized spelling (m, h, d, or
w). In particular, public interval 1h subscribes to native HOUR {=h};
60m remains {=60m} and is not treated as equivalent. Request/response
matching accepts only provider-defined canonical aliases such as omitted
defaults and attribute ordering differences.
The server sends fromTime as epoch milliseconds, matching the current
production DXLink service. The published AsyncAPI description currently says
seconds, but seconds cause the service to replay the full available history.
Production currently appears to ignore toTime. The client reports a
continuous-calendar replay estimate for planning, but that estimate is
explicitly advisory: it does not account for sessions, closures, sparse
options, or expiration, and never blocks a request before connecting.
Streaming state is indexed and deduplicated per symbol, retains only rows in the requested range and session, serializes message processing, and does not infer completion from timestamp order. Completion still requires DXLink snapshot protocol evidence. Each result includes:
status,snapshot_complete,snapshot_truncated, andprovider_snapshot_complete;- machine-readable
failure_reasons; - per-symbol and aggregate request counters for received events, valid events, unique observations, retained rows/bytes, and returned rows;
- configured limits and the advisory calendar-slot estimates.
snapshot_complete means a provider END marker was observed without local or
provider truncation. It does not assert that every interval traded;
status and failure_reasons separately report missing or uncovered evidence.
The independent client controls are:
| Input | Scope | Default | Maximum |
|---|---|---|---|
max_output_candles |
retained/returned rows per symbol | 10,000 | 250,000 |
max_received_events |
aggregate Candle protocol rows per request | 10,000 | 1,000,000 |
max_buffer_bytes |
aggregate queued wire data plus accounted retained state | 16 MiB | 128 MiB |
deadline_ms |
complete DXLink snapshot lifecycle | 15,000 ms | 60,000 ms |
Received-event counts include marker, remove, invalid, duplicate, and unmatched Candle rows. Unique observations count distinct in-window indexed events admitted to bounded state; retained rows reflect removals, and returned rows reflect the final normalized output. Retained-byte accounting uses serialized field sizes plus conservative per-object/index overhead rather than claiming exact V8 heap usage.
max_candles remains as a deprecated compatibility shorthand, capped at
20,000, that applies the same value to per-symbol output and aggregate receive
budgets. It cannot be combined with either explicit field. timeout_ms is a
deprecated alias for deadline_ms and cannot be combined with it.
LOCAL_RECEIVE_BUDGET_EXCEEDED, LOCAL_BUFFER_BUDGET_EXCEEDED, and
LOCAL_OUTPUT_BUDGET_EXCEEDED are client facts. PROVIDER_SNAPSHOT_SNIPPED
is provider protocol evidence. REQUESTED_WINDOW_NOT_COVERED,
SNAPSHOT_TIMEOUT, and MISSING_CONTRACT_EVIDENCE describe observed
availability; none is automatically classified as an entitlement failure.
Completed symbols remain usable when another batch symbol fails locally or is
snipped.
Canonical identity, snapshot transaction rules, the sanitized timeout root
cause, and live native-hour findings are documented in
docs/historical-candles.md.
Rate limits and retries
- API quote tokens use the provider’s
expires-attimestamp with a one-minute safety margin. The previous 23-hour bound remains only as a fallback when the provider omits expiry metadata. - Historical candles and live option snapshots share one quote-token lifecycle
in the production service. A post-
AUTHUNAUTHORIZEDinvalidates both the cached quote token and cached OAuth access token, reacquires credentials, rebuilds the WebSocket connection, and retries exactly once. - Successful recovery returns
dxlink_auth.status = REFRESHEDwithretry_count = 1. A second rejection returns explicitAUTH_FAILEDdiagnostics; a timeout beforeAUTH_STATE/AUTHORIZEDisNOT_CONFIRMED. Tokens are never included. - The quote-token REST request retries network errors,
429, and5xxup to three attempts with capped exponential backoff and jitter. - A candle snapshot has a configurable
deadline_ms(maximum 60 seconds). - Non-authentication WebSocket failures are returned explicitly and are not silently retried or merged.
- Received events, returned rows, and memory are bounded independently as documented above.
- DXLink permits at most 5 concurrent sessions and 100 Candle subscriptions per session. Callers should batch work rather than fan out unbounded calls.
Security model
OAuth client credentials and refresh tokens are sent only to
api.tastyworks.com. The short-lived OAuth token is sent to the fixed
Backtester host and to the tastytrade quote-token endpoint. The resulting
quote token is sent only over wss:// to a host under dxfeed.com.
Do not commit credentials. If using Node’s --env-file, unset inherited
TASTYTRADE_* variables first because inherited values override the file.
All API timestamps must be RFC3339 values containing Z or an explicit UTC
offset; timezone-less timestamps are rejected.
The remote HTTP entrypoint supports two authentication modes:
api-keyfor local development and emergency rollback, using a constant-time comparison againstMCP_API_KEY;oauthfor production, validating Entra JWT signature, issuer, audience, expiration, andmcp.readscope through cached JWKS.
OAuth mode publishes RFC 9728 protected-resource metadata at both supported
well-known paths and includes that URL and the required scope in every 401
challenge. MCP is accepted only at POST /mcp, request bodies are limited to
1 MiB, and GET /healthz remains unauthenticated with health metadata only.
Requirements and setup
- Node.js 22+
- tastytrade OAuth API grant:
TASTYTRADE_CLIENT_IDTASTYTRADE_CLIENT_SECRETTASTYTRADE_REFRESH_TOKEN
- A fully onboarded tastytrade customer for DXLink quote tokens
npm ci
npm run build
export TASTYTRADE_CLIENT_ID="..."
export TASTYTRADE_CLIENT_SECRET="..."
export TASTYTRADE_REFRESH_TOKEN="..."
npm start
For remote Streamable HTTP:
export MCP_API_KEY="$(openssl rand -hex 32)"
export MCP_HTTP_HOST=127.0.0.1
export MCP_HTTP_PORT=8000
npm run start:http
Connect to http://127.0.0.1:8000/mcp with
Authorization: Bearer . Azure Container Apps deployment and
Key Vault guidance are documented in
docs/azure-deployment.md.
Production OAuth configuration additionally requires MCP_PUBLIC_URL,
OAUTH_ISSUER, OAUTH_JWKS_URL, OAUTH_AUDIENCE,
OAUTH_REQUIRED_SCOPE, and OAUTH_TOKEN_SCOPE. ChatGPT discovers the Entra
authorization server from
/.well-known/oauth-protected-resource, then sends its access token in the
standard Authorization header.
For an MCP client:
{
"mcpServers": {
"tastytrade-research": {
"command": "node",
"args": ["/absolute/path/to/tastytrade-research-mcp/dist/index.js"],
"env": {
"TASTYTRADE_CLIENT_ID": "...",
"TASTYTRADE_CLIENT_SECRET": "...",
"TASTYTRADE_REFRESH_TOKEN": "..."
}
}
}
}
Development
npm ci
npm run typecheck
npm test
The regression suite covers clean and degraded package markets, all supported spread families, regular and custom candle sessions, both fill models, ambiguous paths, Backtester normalization, and MCP tool dispatch.
Upstream documentation
- Backtesting guide: https://developer.tastytrade.com/docs/guides/backtesting/
- Backtesting API: https://developer.tastytrade.com/reference/backtesting/
- Streaming guide: https://developer.tastytrade.com/docs/guides/stream-market-data/
- Streaming concepts: https://developer.tastytrade.com/docs/concepts/streaming/
- Rate limits: https://developer.tastytrade.com/docs/guides/rate-limits-and-backoff/
Disclaimer
This is an independent research project and is not an official tastytrade product. Historical simulations can differ materially from live execution because of fills, liquidity, spreads, slippage, data availability, and model assumptions.
MIT License.
Install
This server does not publish a one-line install command.
Open the repository installation guideConfiguration
{
"mcpServers": {
"tastytrade-research": {
"command": "node",
"args": ["/absolute/path/to/tastytrade-research-mcp/dist/index.js"],
"env": {
"TASTYTRADE_CLIENT_ID": "...",
"TASTYTRADE_CLIENT_SECRET": "...",
"TASTYTRADE_REFRESH_TOKEN": "..."
}
}
}
}