Skip to content

Commit 5fedaf6

Browse files
authored
fix: align endpoint docs with upstream operations + interval normaliser (#527)
* docs(option/history): align option_history_quote with upstream 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. * docs(option/history): align trade/ohlc/eod/open_interest/trade_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. * docs(option/history): align greeks family with upstream 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. * docs(option/{at_time,snapshot,list}): align param surface with upstream 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. * docs(stock,index): align interval / time / venue surface with upstream 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. * fix(mdds): align interval normalisation and validator with upstream 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). * docs(option/history): drop duplicate greeks_eod param block + zero-sentinel 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.
1 parent a253648 commit 5fedaf6

47 files changed

Lines changed: 657 additions & 409 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/thetadatadx/src/mdds/endpoints.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,35 @@ impl From<&[String]> for SymbolInput {
9999
/// Convert an interval to the format the MDDS gRPC server accepts.
100100
///
101101
/// Users can pass either:
102-
/// - Milliseconds as a string: `"60000"`, `"300000"`, `"900000"`
103-
/// - Shorthand directly: `"1m"`, `"5m"`, `"1h"`
102+
/// - Shorthand directly: `"tick"`, `"10ms"`, `"100ms"`, `"500ms"`,
103+
/// `"1s"`, `"5s"`, `"10s"`, `"15s"`, `"30s"`, `"1m"`, `"5m"`,
104+
/// `"10m"`, `"15m"`, `"30m"`, `"1h"`.
105+
/// - Milliseconds as a string (e.g. `"60000"` or `"300000"`). Values
106+
/// are snapped to the nearest documented preset.
104107
///
105-
/// The server accepts these specific presets:
106-
/// `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`,
107-
/// `15m`, `30m`, `1h`.
108+
/// The full upstream enum is reproduced verbatim in
109+
/// `docs.thetadata.us/operations/option_history_quote.html` (and every
110+
/// other endpoint with an `interval` parameter).
108111
///
109-
/// If milliseconds are passed, they're converted to the nearest matching
110-
/// preset. If already a valid shorthand (contains 's', 'm', or 'h'), the
111-
/// value is passed through as-is.
112+
/// The historical SDK accepted `"0"` and silently mapped it to
113+
/// `"100ms"`. That contradicted the previously-documented behaviour
114+
/// (`"0"` was advertised as "every quote change"), so `"0"` now snaps
115+
/// to `"tick"` — which is the upstream every-event vocabulary. Calls
116+
/// that depended on the silent-100ms behaviour should switch to an
117+
/// explicit preset.
112118
fn normalize_interval(interval: &str) -> String {
113119
if interval.ends_with('s') || interval.ends_with('m') || interval.ends_with('h') {
114120
return interval.to_string();
115121
}
122+
if interval == "tick" {
123+
return "tick".to_string();
124+
}
116125

117126
match interval.parse::<u64>() {
118127
Ok(ms) => match ms {
119-
0..=100 => "100ms".to_string(),
128+
0 => "tick".to_string(),
129+
1..=10 => "10ms".to_string(),
130+
11..=100 => "100ms".to_string(),
120131
101..=500 => "500ms".to_string(),
121132
501..=1000 => "1s".to_string(),
122133
1_001..=5_000 => "5s".to_string(),
@@ -269,6 +280,8 @@ mod tests {
269280

270281
#[test]
271282
fn normalize_interval_passes_shorthand_through() {
283+
assert_eq!(normalize_interval("tick"), "tick");
284+
assert_eq!(normalize_interval("10ms"), "10ms");
272285
assert_eq!(normalize_interval("1m"), "1m");
273286
assert_eq!(normalize_interval("5m"), "5m");
274287
assert_eq!(normalize_interval("1h"), "1h");
@@ -281,4 +294,22 @@ mod tests {
281294
assert_eq!(normalize_interval("900000"), "15m");
282295
assert_eq!(normalize_interval("3600000"), "1h");
283296
}
297+
298+
#[test]
299+
fn normalize_interval_snaps_zero_to_tick() {
300+
// The historical mapping was `0 -> 100ms`, which contradicted
301+
// the previously-documented every-event semantics. The upstream
302+
// `tick` keyword is the every-event vocabulary, so the zero
303+
// sentinel snaps to it instead.
304+
assert_eq!(normalize_interval("0"), "tick");
305+
}
306+
307+
#[test]
308+
fn normalize_interval_snaps_small_milliseconds_to_documented_presets() {
309+
// 10ms-and-under -> 10ms; 11-100ms -> 100ms. The earlier
310+
// 0..=100 -> 100ms mapping skipped the 10ms preset entirely.
311+
assert_eq!(normalize_interval("10"), "10ms");
312+
assert_eq!(normalize_interval("11"), "100ms");
313+
assert_eq!(normalize_interval("100"), "100ms");
314+
}
284315
}

crates/thetadatadx/src/mdds/validate.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,40 @@ pub(crate) fn validate_symbol(value: &str, param_name: &str) -> Result<(), Endpo
8888
Ok(())
8989
}
9090

91+
/// The exact set of `interval` strings the v3 ThetaData server accepts.
92+
///
93+
/// Mirrors the upstream enum at
94+
/// `https://docs.thetadata.us/operations/option_history_quote.html`.
95+
/// The SDK additionally accepts decimal millisecond shorthand
96+
/// (`"60000"`, `"300000"`, ...) and snaps it to the nearest preset via
97+
/// [`crate::mdds::endpoints::normalize_interval`]; this validator
98+
/// recognises both shapes so the CLI / MCP layer rejects garbage
99+
/// before the gRPC dispatch.
100+
const VALID_INTERVAL_PRESETS: &[&str] = &[
101+
"tick", "10ms", "100ms", "500ms", "1s", "5s", "10s", "15s", "30s", "1m", "5m", "10m", "15m",
102+
"30m", "1h",
103+
];
104+
91105
pub(crate) fn validate_interval(value: &str, param_name: &str) -> Result<(), EndpointError> {
92-
if value.is_empty() || !value.bytes().all(|b| b.is_ascii_alphanumeric()) {
106+
if value.is_empty() {
93107
return Err(EndpointError::InvalidParams(format!(
94-
"'{param_name}' must be a non-empty alphanumeric string (e.g. '60000' or '1m'), got: '{value}'"
108+
"'{param_name}' must be a non-empty string from the upstream enum ({}) or a millisecond value (e.g. '60000'), got empty string",
109+
VALID_INTERVAL_PRESETS.join(", "),
95110
)));
96111
}
97-
Ok(())
112+
if VALID_INTERVAL_PRESETS.contains(&value) {
113+
return Ok(());
114+
}
115+
if value.bytes().all(|b| b.is_ascii_digit()) {
116+
// Millisecond shorthand: `normalize_interval` will snap to the
117+
// nearest documented preset. Any positive integer is accepted
118+
// here; the snap range covers `0` (-> "tick") through `1h`.
119+
return Ok(());
120+
}
121+
Err(EndpointError::InvalidParams(format!(
122+
"'{param_name}' must be one of the upstream presets ({}) or a millisecond value (e.g. '60000'), got: '{value}'",
123+
VALID_INTERVAL_PRESETS.join(", "),
124+
)))
98125
}
99126

100127
pub(crate) fn validate_right(value: &str, param_name: &str) -> Result<(), EndpointError> {
@@ -241,4 +268,21 @@ mod tests {
241268
assert!(validate_strike(bad, "strike").is_err(), "{bad}");
242269
}
243270
}
271+
272+
#[test]
273+
fn interval_accepts_upstream_enum_and_ms_shorthand() {
274+
for good in [
275+
"tick", "10ms", "100ms", "500ms", "1s", "5s", "10s", "15s", "30s", "1m", "5m", "10m",
276+
"15m", "30m", "1h", "0", "60000", "300000",
277+
] {
278+
assert!(validate_interval(good, "interval").is_ok(), "{good}");
279+
}
280+
}
281+
282+
#[test]
283+
fn interval_rejects_garbage() {
284+
for bad in ["", "twosec", "2sec", "1minute", "-1", "1.5s", "1 s", "*"] {
285+
assert!(validate_interval(bad, "interval").is_err(), "{bad}");
286+
}
287+
}
244288
}

docs-site/docs/historical/index-data/history/ohlc.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ for (const auto& t : data) {
5656
<div class="param-desc">End date in <code>YYYYMMDD</code> format</div>
5757
</div>
5858
<div class="param">
59-
<div class="param-header"><code>interval</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
60-
<div class="param-desc">Accepts milliseconds (<code>"60000"</code>) or shorthand (<code>"1m"</code>). Valid presets: <code>100ms</code>, <code>500ms</code>, <code>1s</code>, <code>5s</code>, <code>10s</code>, <code>15s</code>, <code>30s</code>, <code>1m</code>, <code>5m</code>, <code>10m</code>, <code>15m</code>, <code>30m</code>, <code>1h</code>.</div>
59+
<div class="param-header"><code>interval</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
60+
<div class="param-desc">Sampling interval. Allowed values: <code>tick</code>, <code>10ms</code>, <code>100ms</code>, <code>500ms</code>, <code>1s</code>, <code>5s</code>, <code>10s</code>, <code>15s</code>, <code>30s</code>, <code>1m</code>, <code>5m</code>, <code>10m</code>, <code>15m</code>, <code>30m</code>, <code>1h</code>. Millisecond strings (e.g. <code>"60000"</code>) are accepted and snapped to the nearest preset. Default: <code>"1s"</code>. Sub-minute intervals are available only for single-day requests.</div>
6161
</div>
6262
<div class="param">
6363
<div class="param-header"><code>start_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
64-
<div class="param-desc">Start time of day as milliseconds from midnight</div>
64+
<div class="param-desc">Start time (inclusive) in <code>HH:MM:SS.SSS</code> ET wall-clock format. Default: <code>"09:30:00"</code>.</div>
6565
</div>
6666
<div class="param">
6767
<div class="param-header"><code>end_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
68-
<div class="param-desc">End time of day as milliseconds from midnight</div>
68+
<div class="param-desc">End time (inclusive) in <code>HH:MM:SS.SSS</code> ET wall-clock format. Default: <code>"16:00:00"</code>.</div>
6969
</div>
7070
</div>
7171

docs-site/docs/historical/index-data/history/price.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ for (const auto& t : data) {
4949
<div class="param-desc">Date in <code>YYYYMMDD</code> format</div>
5050
</div>
5151
<div class="param">
52-
<div class="param-header"><code>interval</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
53-
<div class="param-desc">Accepts milliseconds (<code>"60000"</code>) or shorthand (<code>"1m"</code>). Valid presets: <code>100ms</code>, <code>500ms</code>, <code>1s</code>, <code>5s</code>, <code>10s</code>, <code>15s</code>, <code>30s</code>, <code>1m</code>, <code>5m</code>, <code>10m</code>, <code>15m</code>, <code>30m</code>, <code>1h</code>.</div>
52+
<div class="param-header"><code>interval</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
53+
<div class="param-desc">Sampling interval. Allowed values: <code>tick</code>, <code>10ms</code>, <code>100ms</code>, <code>500ms</code>, <code>1s</code>, <code>5s</code>, <code>10s</code>, <code>15s</code>, <code>30s</code>, <code>1m</code>, <code>5m</code>, <code>10m</code>, <code>15m</code>, <code>30m</code>, <code>1h</code>. Millisecond strings (e.g. <code>"60000"</code>) are accepted and snapped to the nearest preset. Default: <code>"1s"</code>. Sub-minute intervals are available only for single-day requests.</div>
5454
</div>
5555
<div class="param">
5656
<div class="param-header"><code>start_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
57-
<div class="param-desc">Start time of day as milliseconds from midnight</div>
57+
<div class="param-desc">Start time (inclusive) in <code>HH:MM:SS.SSS</code> ET wall-clock format. Default: <code>"09:30:00"</code>.</div>
5858
</div>
5959
<div class="param">
6060
<div class="param-header"><code>end_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
61-
<div class="param-desc">End time of day as milliseconds from midnight</div>
61+
<div class="param-desc">End time (inclusive) in <code>HH:MM:SS.SSS</code> ET wall-clock format. Default: <code>"16:00:00"</code>.</div>
6262
</div>
6363
</div>
6464

docs-site/docs/historical/index-data/snapshot/market-value.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ for (const auto& t : data) {
4949
</div>
5050
<div class="param">
5151
<div class="param-header"><code>min_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
52-
<div class="param-desc">Minimum time of day as milliseconds from midnight</div>
52+
<div class="param-desc">Filters snapshots to timestamps at or after this ET wall-clock time. Format <code>HH:MM:SS.SSS</code>; legacy millisecond strings are also accepted.</div>
5353
</div>
5454
</div>
5555

docs-site/docs/historical/index-data/snapshot/ohlc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ for (const auto& t : data) {
4949
</div>
5050
<div class="param">
5151
<div class="param-header"><code>min_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
52-
<div class="param-desc">Minimum time of day as milliseconds from midnight</div>
52+
<div class="param-desc">Filters snapshots to timestamps at or after this ET wall-clock time. Format <code>HH:MM:SS.SSS</code>; legacy millisecond strings are also accepted.</div>
5353
</div>
5454
</div>
5555

docs-site/docs/historical/index-data/snapshot/price.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ for (const auto& t : data) {
4646
</div>
4747
<div class="param">
4848
<div class="param-header"><code>min_time</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
49-
<div class="param-desc">Minimum time of day as milliseconds from midnight</div>
49+
<div class="param-desc">Filters snapshots to timestamps at or after this ET wall-clock time. Format <code>HH:MM:SS.SSS</code>; legacy millisecond strings are also accepted.</div>
5050
</div>
5151
</div>
5252

docs-site/docs/historical/option/at-time/quote.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ for (const auto& t : data) {
4949
</div>
5050
<div class="param">
5151
<div class="param-header"><code>expiration</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
52-
<div class="param-desc">Expiration date in <code>YYYYMMDD</code> format</div>
52+
<div class="param-desc">Expiration date in <code>YYYYMMDD</code> or <code>YYYY-MM-DD</code> format, or <code>"*"</code> for all expirations</div>
5353
</div>
5454
<div class="param">
55-
<div class="param-header"><code>strike</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
56-
<div class="param-desc">Strike price in dollars as a string</div>
55+
<div class="param-header"><code>strike</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
56+
<div class="param-desc">Strike price in dollars (e.g. <code>"550"</code> or <code>"17.5"</code>), or <code>"*"</code> for all strikes. Default: <code>"*"</code>.</div>
5757
</div>
5858
<div class="param">
59-
<div class="param-header"><code>right</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
60-
<div class="param-desc"><code>"C"</code> for call, <code>"P"</code> for put</div>
59+
<div class="param-header"><code>right</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
60+
<div class="param-desc">Option side: <code>"call"</code>, <code>"put"</code>, or <code>"both"</code>. SDK also accepts <code>"C"</code>/<code>"P"</code>. Default: <code>"both"</code>.</div>
6161
</div>
6262
<div class="param">
6363
<div class="param-header"><code>start_date</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
@@ -73,11 +73,11 @@ for (const auto& t : data) {
7373
</div>
7474
<div class="param">
7575
<div class="param-header"><code>max_dte</code><span class="param-type">int</span><span class="param-badge optional">optional</span></div>
76-
<div class="param-desc">Maximum days to expiration</div>
76+
<div class="param-desc">Maximum days to expiration. Filters contracts returned when <code>expiration="*"</code>.</div>
7777
</div>
7878
<div class="param">
7979
<div class="param-header"><code>strike_range</code><span class="param-type">int</span><span class="param-badge optional">optional</span></div>
80-
<div class="param-desc">Strike range filter</div>
80+
<div class="param-desc">Returns <code>n</code> strikes above and below spot price plus one ATM strike (up to <code>2n + 1</code> strikes).</div>
8181
</div>
8282
</div>
8383

docs-site/docs/historical/option/at-time/trade.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ for (const auto& t : data) {
4747
</div>
4848
<div class="param">
4949
<div class="param-header"><code>expiration</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
50-
<div class="param-desc">Expiration date in <code>YYYYMMDD</code> format</div>
50+
<div class="param-desc">Expiration date in <code>YYYYMMDD</code> or <code>YYYY-MM-DD</code> format, or <code>"*"</code> for all expirations</div>
5151
</div>
5252
<div class="param">
53-
<div class="param-header"><code>strike</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
54-
<div class="param-desc">Strike price in dollars as a string</div>
53+
<div class="param-header"><code>strike</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
54+
<div class="param-desc">Strike price in dollars (e.g. <code>"550"</code> or <code>"17.5"</code>), or <code>"*"</code> for all strikes. Default: <code>"*"</code>.</div>
5555
</div>
5656
<div class="param">
57-
<div class="param-header"><code>right</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
58-
<div class="param-desc"><code>"C"</code> for call, <code>"P"</code> for put</div>
57+
<div class="param-header"><code>right</code><span class="param-type">string</span><span class="param-badge optional">optional</span></div>
58+
<div class="param-desc">Option side: <code>"call"</code>, <code>"put"</code>, or <code>"both"</code>. SDK also accepts <code>"C"</code>/<code>"P"</code>. Default: <code>"both"</code>.</div>
5959
</div>
6060
<div class="param">
6161
<div class="param-header"><code>start_date</code><span class="param-type">string</span><span class="param-badge required">required</span></div>
@@ -71,11 +71,11 @@ for (const auto& t : data) {
7171
</div>
7272
<div class="param">
7373
<div class="param-header"><code>max_dte</code><span class="param-type">int</span><span class="param-badge optional">optional</span></div>
74-
<div class="param-desc">Maximum days to expiration</div>
74+
<div class="param-desc">Maximum days to expiration. Filters contracts returned when <code>expiration="*"</code>.</div>
7575
</div>
7676
<div class="param">
7777
<div class="param-header"><code>strike_range</code><span class="param-type">int</span><span class="param-badge optional">optional</span></div>
78-
<div class="param-desc">Strike range filter</div>
78+
<div class="param-desc">Returns <code>n</code> strikes above and below spot price plus one ATM strike (up to <code>2n + 1</code> strikes).</div>
7979
</div>
8080
</div>
8181

0 commit comments

Comments
 (0)