Skip to content

fix: align endpoint docs with upstream operations + interval normaliser#527

Merged
userFRM merged 7 commits into
mainfrom
fix/docs-upstream-parity
May 18, 2026
Merged

fix: align endpoint docs with upstream operations + interval normaliser#527
userFRM merged 7 commits into
mainfrom
fix/docs-upstream-parity

Conversation

@userFRM

@userFRM userFRM commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Audit the 60 hand-maintained docs-site/docs/historical/**/*.md doc pages against the upstream operation specs at docs.thetadata.us/operations/*.html, fix every doc-parity issue surfaced by the audit, and harden the SDK's interval handling so the runtime behaviour matches the doc.

Triggered by a user report: tdx.option_history_quote("QQQ", "*", "20260507").strike("*").right("both").interval("0") hung indefinitely. Two root causes were uncovered:

  1. Doc bug. docs-site/docs/historical/option/history/quote.md claimed interval="0" returned every quote tick-by-tick. That value is not in the upstream enum (tick, 10ms, 100ms, ..., 1h). The SDK silently mapped "0" to "100ms", so the user's intent (every quote) was downgraded to a 100ms sample without warning.
  2. Wildcard-volume hang. With expiration="*" on a liquid symbol the upstream MDDS server can take a long time to start emitting data. The TLS handshake completes, the gRPC stream opens, but no DATA frames arrive in the SDK's observation window. Root cause analysis in /tmp/wildcard-hang-rootcause.md: server-side query compilation latency (hypothesis H4). Out of scope for a hot fix in v10.0.0 — PR feat(grpc): in-house gRPC transport replacing tonic #524's in-house gRPC transport with per-call deadlines is the recommended landing place; the doc + validator changes here close the surface the user can hit immediately.

Doc changes (47 markdown files)

Universal alignment

Every option doc that documents strike / right now marks them optional with the upstream defaults (* / both). The SDK builder was already building the wire-side wildcards via wire_strike_opt / wire_right_opt; only the badge was wrong. start_time / end_time are documented as HH:MM:SS.SSS ET wall-clock with the upstream defaults (09:30:00 / 16:00:00), not as "milliseconds from midnight". expiration now documents the upstream wildcard (*) and the alternate YYYY-MM-DD form. start_date / end_date are added as builder optionals on every endpoint upstream documents them on. The one-month / must-specify-expiration multi-day ceiling is called out where upstream documents it.

Interval enum

Every page with an interval parameter now lists the full upstream enum (tick, 10ms, 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h) and marks interval optional with the upstream 1s default. The historical "Use \"0\" to get every quote change" claim is removed across the board.

Greeks inputs

The history/greeks and history/trade_greeks pages now document annual_dividend, rate_type (with the full Treasury enum and sofr default), rate_value, version (latest / 1), and underlyer_use_nbbo / use_market_value with their upstream defaults. The snapshot/greeks pages add stock_price, min_time, and the same overrides.

Misc

  • option_list_dates / option_list_contracts: request_type now points to the upstream lower-case trade / quote enum (SDK keeps the historical upper-case casing for back-compat).
  • option_list_strikes: upstream surface only has symbol + expiration; the SDK doc already matched — no change.
  • stock_history_*: venue documented with the nqb / utp_cta enum and nqb default.
  • index_history_*, index_snapshot_*, index_at_time_*: same alignment sweep applied.

Code changes

crates/thetadatadx/src/mdds/endpoints.rs::normalize_interval:

  • "0" now snaps to "tick" (every-event) instead of "100ms". The historical mapping contradicted the previously-documented every-event semantics.
  • The 0..=100 ms snap range now exposes the 10ms upstream preset (1..=1010ms, 11..=100100ms) instead of skipping it.
  • Docstring documents the upstream enum and the rationale.

crates/thetadatadx/src/mdds/validate.rs::validate_interval:

  • Strict enum check against the upstream presets; rejects garbage like "twosec" or "1minute" up front with a typed InvalidParams error that lists the accepted values.
  • Decimal millisecond shorthand still accepted (the runtime normaliser snaps it to a preset).

Unit tests cover both the preset and ms-shorthand paths.

Wildcard-hang investigation

Hypothesis H4 (server-side query compilation latency for full-chain wildcard requests) remains the working theory. Methodology and full hypothesis chain documented in /tmp/wildcard-hang-rootcause.md (kept out of the repo because it references the in-flight PR #524 transport work). The doc + validator changes here close the user-visible surface: callers no longer get the silent "0" → 100ms downgrade and the doc no longer promises behaviour the wire doesn't expose.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace (411 lib tests + doc-tests pass; 6 new tests added)
  • python3 scripts/check_docs_consistency.py (api-reference, openapi, tier badges all ok)
  • cd docs-site && npm run build (15.69s, clean)

Files changed

crates/thetadatadx/src/mdds/{endpoints.rs, validate.rs} (code, +tests) and 45 markdown files under docs-site/docs/historical/.

userFRM added 7 commits May 18, 2026 17:45
Mirror the upstream operation spec at docs.thetadata.us/operations/
option_history_quote.html. The previous doc claimed `interval="0"`
returned every quote change tick-by-tick; that value is not in the
upstream enum and the SDK silently normalises it to `100ms`. Replace
with the documented presets (tick / 10ms / .../ 1h) and default 1s.

Mark `strike` and `right` optional (default `*` and `both`); they are
optional at the wire level and the SDK builder treats them the same
way. Correct `start_time` / `end_time` to HH:MM:SS.SSS wall-clock with
the upstream defaults (09:30:00 / 16:00:00). Add `start_date` /
`end_date` for multi-day requests and document the one-month / must-
specify-expiration constraints called out by the upstream spec.

Refs ThetaData v3 spec for /v3/option/history/quote.
Mirror the upstream operation specs at docs.thetadata.us/operations/
for the option_history_{trade, ohlc, eod, open_interest, trade_quote}
endpoints.

Common fixes across the family:
- strike and right are optional with default `*` and `both` (the SDK
  was already constructing the wire wildcards through wire_strike_opt
  / wire_right_opt; only the doc badge was wrong).
- start_time / end_time take HH:MM:SS.SSS wall-clock with defaults
  09:30:00 / 16:00:00, not 'milliseconds from midnight'.
- expiration accepts YYYY-MM-DD as well as YYYYMMDD, plus `*` for
  all expirations.
- Add start_date / end_date as builder optionals; document the
  single-`date` override semantics from upstream.
- Call out the upstream one-month / must-specify-expiration ceiling
  on multi-day requests.

ohlc additionally pulls the new `tick` and `10ms` presets into the
interval enum and marks interval optional with default `1s`.
trade_quote additionally documents the `exclusive` flag with its
upstream `true` default.
Apply the same parity sweep to the 11 greeks endpoints:
option_history_greeks_{all, eod, implied_volatility, first_order,
second_order, third_order} and
option_history_trade_greeks_{all, implied_volatility, first_order,
second_order, third_order}.

For each:
- strike / right marked optional with upstream defaults (`*` / `both`)
- start_time / end_time documented as HH:MM:SS.SSS with the upstream
  09:30:00 / 16:00:00 defaults
- expiration accepts YYYY-MM-DD and YYYYMMDD plus `*`
- interval (where applicable) marked optional, default `1s`, enum
  extended to include `tick` and `10ms`
- Greeks inputs (annual_dividend, rate_type with full Treasury enum,
  rate_value, version) documented with their upstream defaults and
  allowed values
- start_date / end_date listed as builder optionals on the intraday
  greeks endpoints; greeks_eod keeps start_date / end_date required
  per upstream
- underlyer_use_nbbo (greeks_eod only) documents its `false` default

Multi-day one-month / must-specify-expiration ceiling called out where
upstream documents it.
Same parity sweep as the option/history family — strike and right are
optional with default `*` and `both`; min_time on snapshot endpoints
is HH:MM:SS.SSS, not milliseconds; expiration accepts YYYY-MM-DD and
wildcard `*`; Greeks-input defaults documented.

option_list_dates and option_list_contracts: request_type lower-case
trade/quote is the upstream enum; SDK still accepts the historical
TRADE/QUOTE casing.

option_list_strikes already mirrored upstream exactly — no change
needed.
Apply the same parity sweep to the stock and index history /
snapshot / at_time pages. interval is optional with a `1s` default
and the upstream enum extended to `tick` and `10ms`. start_time /
end_time are documented as HH:MM:SS.SSS ET wall-clock with the
upstream 09:30:00 / 16:00:00 defaults instead of 'milliseconds from
midnight'. The `Use "0"` claim is removed across the board — the
SDK does not have a tick-by-tick sentinel that maps to a real
upstream value, and the doc was implying behaviour the wire does
not expose.

venue (stock-only) is documented with the upstream `nqb` / `utp_cta`
enum and `nqb` default. min_time on stock snapshots takes the same
HH:MM:SS.SSS form.

stock_history_trade_quote: exclusive flag documented with the `true`
upstream default.
normalize_interval now maps `"0"` to `"tick"` instead of `"100ms"`.
The historical mapping contradicted the previously-documented
every-event semantics — the upstream enum spells that as `tick`, and
the SDK silently downgraded user requests to a 100ms sample without
warning. Mapping `0` to `tick` makes the behaviour match the doc
without breaking callers that picked an interval explicitly. The
small-ms snap range now also exposes `10ms`, which is the upstream
preset between `tick` and `100ms` that the SDK previously skipped.

validate_interval (CLI / MCP entry point) now enforces the upstream
enum verbatim plus the SDK's millisecond shorthand. Garbage strings
like `"twosec"` are rejected up front with a typed error listing the
accepted values, instead of being passed through to the gRPC layer
where they used to surface as opaque server-side rejections.

Mirrors the upstream operation specs at
`docs.thetadata.us/operations/option_history_quote.html` (and the
other 14 endpoints that carry an `interval` parameter).
…ntinel

The earlier rewrite left a stale copy of the pre-alignment param
list at the bottom of greeks_eod.md, which broke the VitePress
build. Remove the duplicate and rewrite the option_history_eod
multi-strike note to use the upstream `strike="*"` wildcard
vocabulary instead of the SDK-only `"0"` sentinel that contradicted
the rest of the alignment sweep.
@userFRM userFRM force-pushed the fix/docs-upstream-parity branch from 21f9bc1 to f8df8c2 Compare May 18, 2026 15:46
@userFRM userFRM merged commit 5fedaf6 into main May 18, 2026
33 checks passed
@userFRM userFRM deleted the fix/docs-upstream-parity branch May 18, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant