Skip to content

Commit 70eb69a

Browse files
userFRMclaude
andauthored
refactor(mdds): forward list order and interval verbatim, matching the terminal (#1202)
* refactor(mdds): forward list order and interval verbatim, matching the terminal The terminal's REST layer is a pure pass-through: it streams the server's DataTable in wire order and forwards the interval string unchanged. Two SDK behaviors diverged from that and are removed. List endpoints no longer re-sort their values. `sorted_list_values` imposed a numeric-aware ascending order on every `list_*` return (roots, expirations, strikes, dates); the wire order is the server's order and is now preserved. The interval argument is no longer snapped to the nearest preset. `normalize_interval` silently mapped an arbitrary millisecond string (`"250"`, `"60000"`) to a documented bucket; the server accepts only the closed preset enum and the terminal forwards the string as-is. The codegen no longer wraps interval args, and `validate_interval` now rejects anything outside the preset set client-side instead of accepting raw milliseconds. Raw-ms shorthand is a breaking change to the `interval` parameter. Interval parameter docs are regenerated to drop the millisecond-string mention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: drop stale millisecond-interval mentions from spec and examples The interval parameter no longer accepts raw-millisecond strings, but the OpenAPI spec, the symbology article, the codegen fallback description, an example fixture, and the local server's doc comment still advertised milliseconds ("60000") and used it in every history-endpoint code sample — snippets that now fail client-side validation. Rewrite the descriptions to the closed preset enum and switch the example interval to "1m" (the preset for the same one-minute bar). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: preview <noreply@anthropic.com>
1 parent f3de3ac commit 70eb69a

29 files changed

Lines changed: 116 additions & 256 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121

2222
- **Config-file migration.** A `config.toml` that still sets `flush_mode`, `streaming.host_selection`, `streaming.host_shuffle_seed`, or a `reconnect_jitter` of `"equal"` / `"decorrelated"` now fails to load with a typed error rather than being silently ignored — remove those keys / values when upgrading.
2323

24+
- **`QuoteTick.midpoint` derived column.** Removed from every binding (Python `QuoteTick.midpoint`, TypeScript `QuoteTick.midpoint`, C `ThetaDataDxQuoteTick.midpoint`, and the Arrow / Polars `midpoint` column). The quote wire carries `bid` and `ask`; `midpoint` was an SDK-computed `(bid + ask) / 2` column the server never sends and the terminal never exposes — compute it from `bid` / `ask` at the call site if you need it. The `IvTick.midpoint` field is a real wire column and is unaffected. This is a breaking change to the quote tick schema.
25+
26+
- **List endpoints now preserve wire order.** `list_*` results (roots / symbols, expirations, strikes, dates) come back in the server's row order rather than re-sorted ascending by the SDK. The terminal streams these lists in wire order untouched; the SDK's numeric-aware ascending sort had no terminal counterpart. Sort client-side if you need a specific order. This is a behavioral change to the `list_*` returns.
27+
28+
- **`interval` is forwarded verbatim.** The `interval` argument is no longer snapped to the nearest preset: a raw-millisecond value such as `"250"` or `"60000"` is now rejected client-side rather than silently mapped to `"500ms"` / `"1m"`. The server accepts only the closed preset enum (`tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`); the SDK now forwards the string as-is and validates against that set. Pass an explicit preset. This is a breaking change to the `interval` parameter.
29+
2430
## [0.1.1] - 2026-07-07
2531

2632
### Added

docs-site/docs/articles/symbology.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Strikes are dollars across every surface, including the bundled server's [WebSoc
2929
- Dates are `YYYYMMDD` strings (`"20250303"`). The HTTP server also accepts ISO `YYYY-MM-DD`.
3030
- Date ranges are inclusive on both ends.
3131
- Time-of-day inputs (`start_time`, `end_time`, `time_of_day`) are Eastern Time wall-clock `HH:MM:SS` (at-time endpoints take milliseconds: `HH:MM:SS.SSS`).
32-
- `interval` accepts a preset (`1s`, `1m`, `1h`, …) or a millisecond count as a string (`"60000"`).
32+
- `interval` is one of the presets: `tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`.
3333

3434
## Timestamps out
3535

docs-site/docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121

2222
- **Config-file migration.** A `config.toml` that still sets `flush_mode`, `streaming.host_selection`, `streaming.host_shuffle_seed`, or a `reconnect_jitter` of `"equal"` / `"decorrelated"` now fails to load with a typed error rather than being silently ignored — remove those keys / values when upgrading.
2323

24+
- **`QuoteTick.midpoint` derived column.** Removed from every binding (Python `QuoteTick.midpoint`, TypeScript `QuoteTick.midpoint`, C `ThetaDataDxQuoteTick.midpoint`, and the Arrow / Polars `midpoint` column). The quote wire carries `bid` and `ask`; `midpoint` was an SDK-computed `(bid + ask) / 2` column the server never sends and the terminal never exposes — compute it from `bid` / `ask` at the call site if you need it. The `IvTick.midpoint` field is a real wire column and is unaffected. This is a breaking change to the quote tick schema.
25+
26+
- **List endpoints now preserve wire order.** `list_*` results (roots / symbols, expirations, strikes, dates) come back in the server's row order rather than re-sorted ascending by the SDK. The terminal streams these lists in wire order untouched; the SDK's numeric-aware ascending sort had no terminal counterpart. Sort client-side if you need a specific order. This is a behavioral change to the `list_*` returns.
27+
28+
- **`interval` is forwarded verbatim.** The `interval` argument is no longer snapped to the nearest preset: a raw-millisecond value such as `"250"` or `"60000"` is now rejected client-side rather than silently mapped to `"500ms"` / `"1m"`. The server accepts only the closed preset enum (`tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`); the SDK now forwards the string as-is and validates against that set. Pass an explicit preset. This is a breaking change to the `interval` parameter.
29+
2430
## [0.1.1] - 2026-07-07
2531

2632
### Added

docs-site/docs/public/thetadatadx.yaml

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ info:
1111
bindings backed by the same Rust core.
1212
1313
All dates use YYYYMMDD format. Times use milliseconds since midnight ET.
14-
Intervals accept milliseconds ("60000") or shorthand ("1m"). Valid presets: 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.
14+
Intervals are one of the presets: tick, 10ms, 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.
1515
All prices are f64 (double) -- decoded during parsing.
1616
version: 0.1.1
1717
contact:
@@ -152,7 +152,7 @@ x-anchors:
152152
required: true
153153
schema:
154154
type: string
155-
description: 'Accepts milliseconds ("60000") or shorthand ("1m"). Valid presets: 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.'
155+
description: 'One of the interval presets: tick, 10ms, 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.'
156156

157157
time_of_day-param: &time_of_day-param
158158
name: time_of_day
@@ -871,7 +871,7 @@ paths:
871871
summary: Stock intraday OHLC bars
872872
description: |
873873
Intraday OHLC bars. Supply `date` for a single day, or `start_date`/`end_date` for a date range on the same route.
874-
Interval accepts milliseconds ("60000") or shorthand ("1m"). Valid presets: 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.
874+
Interval is one of the presets: tick, 10ms, 100ms, 500ms, 1s, 5s, 10s, 15s, 30s, 1m, 5m, 10m, 15m, 30m, 1h.
875875
gRPC: GetStockHistoryOhlc
876876
tags: [Stock / History]
877877
parameters:
@@ -889,15 +889,15 @@ paths:
889889
- lang: rust
890890
label: Rust
891891
source: |
892-
let bars = client.market_data().stock_history_ohlc("AAPL").date("20240315").interval("60000").await?;
892+
let bars = client.market_data().stock_history_ohlc("AAPL").date("20240315").interval("1m").await?;
893893
- lang: python
894894
label: Python
895895
source: |
896-
bars = client.market_data.stock_history_ohlc("AAPL", date="20240315", interval="60000")
896+
bars = client.market_data.stock_history_ohlc("AAPL", date="20240315", interval="1m")
897897
- lang: cpp
898898
label: C++
899899
source: |
900-
auto bars = client.market_data().stock_history_ohlc("AAPL", thetadatadx::EndpointRequestOptions{}.with_date("20240315").with_interval("60000"));
900+
auto bars = client.market_data().stock_history_ohlc("AAPL", thetadatadx::EndpointRequestOptions{}.with_date("20240315").with_interval("1m"));
901901
902902
/v3/stock/history/trade:
903903
x-min-subscription: standard
@@ -975,15 +975,15 @@ paths:
975975
- lang: rust
976976
label: Rust
977977
source: |
978-
let quotes = client.market_data().stock_history_quote("AAPL").date("20240315").interval("60000").await?;
978+
let quotes = client.market_data().stock_history_quote("AAPL").date("20240315").interval("1m").await?;
979979
- lang: python
980980
label: Python
981981
source: |
982-
quotes = client.market_data.stock_history_quote("AAPL", date="20240315", interval="60000")
982+
quotes = client.market_data.stock_history_quote("AAPL", date="20240315", interval="1m")
983983
- lang: cpp
984984
label: C++
985985
source: |
986-
auto quotes = client.market_data().stock_history_quote("AAPL", thetadatadx::EndpointRequestOptions{}.with_date("20240315").with_interval("60000"));
986+
auto quotes = client.market_data().stock_history_quote("AAPL", thetadatadx::EndpointRequestOptions{}.with_date("20240315").with_interval("1m"));
987987
/v3/stock/history/trade_quote:
988988
x-min-subscription: standard
989989
get:
@@ -1772,15 +1772,15 @@ paths:
17721772
- lang: rust
17731773
label: Rust
17741774
source: |
1775-
let bars = client.market_data().option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "60000").await?;
1775+
let bars = client.market_data().option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "1m").await?;
17761776
- lang: python
17771777
label: Python
17781778
source: |
1779-
bars = client.market_data.option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "60000")
1779+
bars = client.market_data.option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "1m")
17801780
- lang: cpp
17811781
label: C++
17821782
source: |
1783-
auto bars = client.market_data().option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "60000");
1783+
auto bars = client.market_data().option_history_ohlc("SPY", "20240419", "500", "C", "20240315", "1m");
17841784
17851785
/v3/option/history/trade:
17861786
x-min-subscription: standard
@@ -1866,15 +1866,15 @@ paths:
18661866
- lang: rust
18671867
label: Rust
18681868
source: |
1869-
let quotes = client.market_data().option_history_quote("SPY", "20240419", "500", "C", "20240315", "60000").await?;
1869+
let quotes = client.market_data().option_history_quote("SPY", "20240419", "500", "C", "20240315", "1m").await?;
18701870
- lang: python
18711871
label: Python
18721872
source: |
1873-
quotes = client.market_data.option_history_quote("SPY", "20240419", "500", "C", "20240315", "60000")
1873+
quotes = client.market_data.option_history_quote("SPY", "20240419", "500", "C", "20240315", "1m")
18741874
- lang: cpp
18751875
label: C++
18761876
source: |
1877-
auto quotes = client.market_data().option_history_quote("SPY", "20240419", "500", "C", "20240315", "60000");
1877+
auto quotes = client.market_data().option_history_quote("SPY", "20240419", "500", "C", "20240315", "1m");
18781878
/v3/option/history/trade_quote:
18791879
x-min-subscription: standard
18801880
get:
@@ -2052,15 +2052,15 @@ paths:
20522052
- lang: rust
20532053
label: Rust
20542054
source: |
2055-
let greeks = client.market_data().option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "60000").await?;
2055+
let greeks = client.market_data().option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "1m").await?;
20562056
- lang: python
20572057
label: Python
20582058
source: |
2059-
greeks = client.market_data.option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "60000")
2059+
greeks = client.market_data.option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "1m")
20602060
- lang: cpp
20612061
label: C++
20622062
source: |
2063-
auto greeks = client.market_data().option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "60000");
2063+
auto greeks = client.market_data().option_history_greeks_all("SPY", "20240419", "500", "C", "20240315", "1m");
20642064
20652065
/v3/option/history/greeks/first_order:
20662066
x-min-subscription: standard
@@ -2103,15 +2103,15 @@ paths:
21032103
- lang: rust
21042104
label: Rust
21052105
source: |
2106-
let g1 = client.market_data().option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "60000").await?;
2106+
let g1 = client.market_data().option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "1m").await?;
21072107
- lang: python
21082108
label: Python
21092109
source: |
2110-
g1 = client.market_data.option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "60000")
2110+
g1 = client.market_data.option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "1m")
21112111
- lang: cpp
21122112
label: C++
21132113
source: |
2114-
auto g1 = client.market_data().option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "60000");
2114+
auto g1 = client.market_data().option_history_greeks_first_order("SPY", "20240419", "500", "C", "20240315", "1m");
21152115
21162116
/v3/option/history/greeks/second_order:
21172117
x-min-subscription: professional
@@ -2154,15 +2154,15 @@ paths:
21542154
- lang: rust
21552155
label: Rust
21562156
source: |
2157-
let g2 = client.market_data().option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "60000").await?;
2157+
let g2 = client.market_data().option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "1m").await?;
21582158
- lang: python
21592159
label: Python
21602160
source: |
2161-
g2 = client.market_data.option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "60000")
2161+
g2 = client.market_data.option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "1m")
21622162
- lang: cpp
21632163
label: C++
21642164
source: |
2165-
auto g2 = client.market_data().option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "60000");
2165+
auto g2 = client.market_data().option_history_greeks_second_order("SPY", "20240419", "500", "C", "20240315", "1m");
21662166
21672167
/v3/option/history/greeks/third_order:
21682168
x-min-subscription: professional
@@ -2205,15 +2205,15 @@ paths:
22052205
- lang: rust
22062206
label: Rust
22072207
source: |
2208-
let g3 = client.market_data().option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "60000").await?;
2208+
let g3 = client.market_data().option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "1m").await?;
22092209
- lang: python
22102210
label: Python
22112211
source: |
2212-
g3 = client.market_data.option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "60000")
2212+
g3 = client.market_data.option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "1m")
22132213
- lang: cpp
22142214
label: C++
22152215
source: |
2216-
auto g3 = client.market_data().option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "60000");
2216+
auto g3 = client.market_data().option_history_greeks_third_order("SPY", "20240419", "500", "C", "20240315", "1m");
22172217
22182218
/v3/option/history/greeks/implied_volatility:
22192219
x-min-subscription: standard
@@ -2256,15 +2256,15 @@ paths:
22562256
- lang: rust
22572257
label: Rust
22582258
source: |
2259-
let iv = client.market_data().option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "60000").await?;
2259+
let iv = client.market_data().option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "1m").await?;
22602260
- lang: python
22612261
label: Python
22622262
source: |
2263-
iv = client.market_data.option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "60000")
2263+
iv = client.market_data.option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "1m")
22642264
- lang: cpp
22652265
label: C++
22662266
source: |
2267-
auto iv = client.market_data().option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "60000");
2267+
auto iv = client.market_data().option_history_greeks_implied_volatility("SPY", "20240419", "500", "C", "20240315", "1m");
22682268
22692269
# =========================================================================
22702270
# OPTION / HISTORY TRADE GREEKS
@@ -2799,15 +2799,15 @@ paths:
27992799
- lang: rust
28002800
label: Rust
28012801
source: |
2802-
let bars = client.market_data().index_history_ohlc("SPX", "20240101", "20240301", "60000").await?;
2802+
let bars = client.market_data().index_history_ohlc("SPX", "20240101", "20240301", "1m").await?;
28032803
- lang: python
28042804
label: Python
28052805
source: |
2806-
bars = client.market_data.index_history_ohlc("SPX", "20240101", "20240301", "60000")
2806+
bars = client.market_data.index_history_ohlc("SPX", "20240101", "20240301", "1m")
28072807
- lang: cpp
28082808
label: C++
28092809
source: |
2810-
auto bars = client.market_data().index_history_ohlc("SPX", "20240101", "20240301", "60000");
2810+
auto bars = client.market_data().index_history_ohlc("SPX", "20240101", "20240301", "1m");
28112811
28122812
/v3/index/history/price:
28132813
x-min-subscription: value
@@ -2842,15 +2842,15 @@ paths:
28422842
- lang: rust
28432843
label: Rust
28442844
source: |
2845-
let prices = client.market_data().index_history_price("SPX", "20240315", "60000").await?;
2845+
let prices = client.market_data().index_history_price("SPX", "20240315", "1m").await?;
28462846
- lang: python
28472847
label: Python
28482848
source: |
2849-
prices = client.market_data.index_history_price("SPX", "20240315", "60000")
2849+
prices = client.market_data.index_history_price("SPX", "20240315", "1m")
28502850
- lang: cpp
28512851
label: C++
28522852
source: |
2853-
auto prices = client.market_data().index_history_price("SPX", "20240315", "60000");
2853+
auto prices = client.market_data().index_history_price("SPX", "20240315", "1m");
28542854
28552855
# =========================================================================
28562856
# INDEX / AT-TIME

docs-site/docs/reference/index/history/ohlc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Fetch intraday OHLC bars for an index.
4141
| `symbol` | string | yes || Ticker symbol (e.g. AAPL) |
4242
| `start_date` | date | yes || Start date YYYYMMDD |
4343
| `end_date` | date | yes || End date YYYYMMDD |
44-
| `interval` | string | no | `1s` | Interval preset or millisecond string. Defaults to `1s` when omitted — matching the upstream ThetaData Python library. Accepted values: `tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`. |
44+
| `interval` | string | no | `1s` | Interval preset. Defaults to `1s` when omitted — matching the upstream ThetaData Python library. Accepted values: `tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`. |
4545
| `start_time` | string | no | `09:30:00` | Start time filter |
4646
| `end_time` | string | no | `16:00:00` | End time filter |
4747
| `timeout_ms` | int | no || Per-request deadline in milliseconds. 0 means no deadline. |

docs-site/docs/reference/index/history/price.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Fetch intraday price history for an index.
4141
|---|---|---|---|---|
4242
| `symbol` | string | yes || Ticker symbol (e.g. AAPL) |
4343
| `date` | date | no || Single date YYYYMMDD. Supply this for a single-day pull, or supply `start_date`/`end_date` for a range. When present, `date` takes precedence over the range. |
44-
| `interval` | string | no | `1s` | Interval preset or millisecond string. Defaults to `1s` when omitted — matching the upstream ThetaData Python library. Accepted values: `tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`. |
44+
| `interval` | string | no | `1s` | Interval preset. Defaults to `1s` when omitted — matching the upstream ThetaData Python library. Accepted values: `tick`, `10ms`, `100ms`, `500ms`, `1s`, `5s`, `10s`, `15s`, `30s`, `1m`, `5m`, `10m`, `15m`, `30m`, `1h`. |
4545
| `start_time` | string | no | `09:30:00` | Start time filter |
4646
| `end_time` | string | no | `16:00:00` | End time filter |
4747
| `start_date` | date | no || Start date YYYYMMDD |

0 commit comments

Comments
 (0)