Skip to content

Commit ca7df36

Browse files
userFRMclaude
andauthored
refactor(calendar): drop the derived CalendarDay.is_open column (#1203)
The calendar wire carries a single day-type column. The SDK fanned it into two public fields: `status` (the vendor vocabulary) and `is_open`, a boolean derived as `status in {open, early_close}`. The terminal exposes no such boolean — its calendar model carries only date, type, open, and close — so `is_open` was an SDK-only derivation in the same class as the removed QuoteTick.midpoint column. Remove the `is_open` field from `CalendarDay` across every surface: the schema, the generated Rust struct and layout asserts, the Arrow/Polars frame builders, the Python and TypeScript classes, and the C ABI struct (whose tail padding grows to keep the 64-byte aligned layout). The calendar parser now maps the wire day-type to `status` alone. Read `status` and treat `open` / `early_close` as trading days. `CalendarStatus::is_open()` stays — it is a plain enum predicate the local server uses for display, not a wire-derived column. Dropping the last `bool` schema column also removes the now-dead `row_bool` cell decoder and the `BooleanArray` imports from the generated frame builders. Co-authored-by: preview <noreply@anthropic.com>
1 parent 70eb69a commit ca7df36

36 files changed

Lines changed: 68 additions & 193 deletions

File tree

CHANGELOG.md

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

2828
- **`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.
2929

30+
- **`CalendarDay.is_open` derived boolean.** Removed from every binding (Python `CalendarDay.is_open`, TypeScript `CalendarDay.isOpen`, C `ThetaDataDxCalendarDay.is_open`, and the Arrow / Polars `is_open` column). The calendar wire carries a single day-type column; `is_open` was an SDK-derived boolean (`status in {open, early_close}`) that the terminal never exposes. Read `status` instead — it carries the full `open` / `early_close` / `full_close` / `weekend` vocabulary — and treat `open` / `early_close` as trading days. This is a breaking change to the calendar tick schema.
31+
3032
## [0.1.1] - 2026-07-07
3133

3234
### Added

docs-site/docs/changelog.md

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

2828
- **`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.
2929

30+
- **`CalendarDay.is_open` derived boolean.** Removed from every binding (Python `CalendarDay.is_open`, TypeScript `CalendarDay.isOpen`, C `ThetaDataDxCalendarDay.is_open`, and the Arrow / Polars `is_open` column). The calendar wire carries a single day-type column; `is_open` was an SDK-derived boolean (`status in {open, early_close}`) that the terminal never exposes. Read `status` instead — it carries the full `open` / `early_close` / `full_close` / `weekend` vocabulary — and treat `open` / `early_close` as trading days. This is a breaking change to the calendar tick schema.
31+
3032
## [0.1.1] - 2026-07-07
3133

3234
### Added

docs-site/docs/reference/calendar/on-date.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const cfg = {
1212
method: { rust: "calendar_on_date", python: "calendar_on_date", ts: "calendarOnDate", cpp: "calendar_on_date" },
1313
required: [{ key: "date", type: "date", default: "20250303" }],
1414
optional: [],
15-
print: ["date", "is_open", "status"],
15+
print: ["date", "open_time", "close_time", "status"],
1616
returns: "CalendarDay",
1717
}
1818
</script>
@@ -44,7 +44,6 @@ Rows of `CalendarDay`:
4444
| Field | Type | Description |
4545
|---|---|---|
4646
| `date` | i32 | Calendar date as a YYYYMMDD integer. 0 when the server omits the column (single-day endpoints). |
47-
| `is_open` | bool | Whether the market trades at all on this date (true for open and early-close days). |
4847
| `open_time` | i32 | Market open time, milliseconds since midnight ET. |
4948
| `close_time` | i32 | Market close time, milliseconds since midnight ET. |
5049
| `status` | string | Vendor day classification: open, early_close, full_close, or weekend. |

docs-site/docs/reference/calendar/open-today.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const cfg = {
1212
method: { rust: "calendar_open_today", python: "calendar_open_today", ts: "calendarOpenToday", cpp: "calendar_open_today" },
1313
required: [],
1414
optional: [],
15-
print: ["date", "is_open", "status"],
15+
print: ["date", "open_time", "close_time", "status"],
1616
returns: "CalendarDay",
1717
}
1818
</script>
@@ -42,7 +42,6 @@ Rows of `CalendarDay`:
4242
| Field | Type | Description |
4343
|---|---|---|
4444
| `date` | i32 | Calendar date as a YYYYMMDD integer. 0 when the server omits the column (single-day endpoints). |
45-
| `is_open` | bool | Whether the market trades at all on this date (true for open and early-close days). |
4645
| `open_time` | i32 | Market open time, milliseconds since midnight ET. |
4746
| `close_time` | i32 | Market close time, milliseconds since midnight ET. |
4847
| `status` | string | Vendor day classification: open, early_close, full_close, or weekend. |

docs-site/docs/reference/calendar/year.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const cfg = {
1212
method: { rust: "calendar_year", python: "calendar_year", ts: "calendarYear", cpp: "calendar_year" },
1313
required: [{ key: "year", type: "string", default: "2025" }],
1414
optional: [],
15-
print: ["date", "is_open", "status"],
15+
print: ["date", "open_time", "close_time", "status"],
1616
returns: "CalendarDay",
1717
}
1818
</script>
@@ -44,7 +44,6 @@ Rows of `CalendarDay`:
4444
| Field | Type | Description |
4545
|---|---|---|
4646
| `date` | i32 | Calendar date as a YYYYMMDD integer. 0 when the server omits the column (single-day endpoints). |
47-
| `is_open` | bool | Whether the market trades at all on this date (true for open and early-close days). |
4847
| `open_time` | i32 | Market open time, milliseconds since midnight ET. |
4948
| `close_time` | i32 | Market close time, milliseconds since midnight ET. |
5049
| `status` | string | Vendor day classification: open, early_close, full_close, or weekend. |

parity.toml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,12 +2476,6 @@ name = "last_trade_ms_of_day"
24762476
python = "i32"
24772477
typescript = "i32"
24782478

2479-
[[value_field]]
2480-
class = "CalendarDay"
2481-
name = "is_open"
2482-
python = "bool"
2483-
typescript = "bool"
2484-
24852479
[[value_field]]
24862480
class = "CalendarDay"
24872481
name = "status"

thetadatadx-cpp/include/thetadatadx.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,12 @@ typedef struct ThetaDataDxClient ThetaDataDxClient;
6565
* session open/close times and a THETADATADX_CALENDAR_STATUS_* day-type code. */
6666
THETADATADX_ALIGN64_BEGIN typedef struct {
6767
int32_t date;
68-
/* C99 bool (1 byte): whether the market trades at all on this date
69-
* (true for open and early-close days). 3 bytes padding follow. */
70-
bool is_open;
7168
int32_t open_time;
7269
int32_t close_time;
7370
/* One of the THETADATADX_CALENDAR_STATUS_* codes; string form via
7471
* thetadatadx_calendar_status_name(). */
7572
int32_t status;
76-
uint8_t _tail_padding[44];
73+
uint8_t _tail_padding[48];
7774
} ThetaDataDxCalendarDay THETADATADX_ALIGN64_END;
7875

7976
/* End-of-day OHLC + closing-quote tick (*_history_eod) -- one row per

thetadatadx-cpp/include/tick_layout_asserts.hpp.inc

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ static_assert(sizeof(CalendarDay) == 64 && alignof(CalendarDay) == 64,
44
"ThetaDataDxCalendarDay layout drifted from Rust");
55
static_assert(offsetof(CalendarDay, date) == 0,
66
"ThetaDataDxCalendarDay.date offset drifted from Rust");
7-
static_assert(offsetof(CalendarDay, is_open) == 4,
8-
"ThetaDataDxCalendarDay.is_open offset drifted from Rust");
9-
static_assert(offsetof(CalendarDay, open_time) == 8,
7+
static_assert(offsetof(CalendarDay, open_time) == 4,
108
"ThetaDataDxCalendarDay.open_time offset drifted from Rust");
11-
static_assert(offsetof(CalendarDay, close_time) == 12,
9+
static_assert(offsetof(CalendarDay, close_time) == 8,
1210
"ThetaDataDxCalendarDay.close_time offset drifted from Rust");
13-
static_assert(offsetof(CalendarDay, status) == 16,
11+
static_assert(offsetof(CalendarDay, status) == 12,
1412
"ThetaDataDxCalendarDay.status offset drifted from Rust");
1513
static_assert(sizeof(EodTick) == 128 && alignof(EodTick) == 64,
1614
"ThetaDataDxEodTick layout drifted from Rust");

thetadatadx-ffi/src/types.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,7 @@ macro_rules! tick_array_to_arrow_ipc_projected {
875875
/// `rows` must point to `len` initialised `$tick` values valid for the
876876
/// call; `presence` must be a valid [`ThetaDataDxColumnPresence`] (its
877877
/// name pointers valid for the call), typically from
878-
/// `thetadatadx_<tick>_present_columns`. For calendar rows, `is_open`
879-
/// must contain a valid C `bool` value.
878+
/// `thetadatadx_<tick>_present_columns`.
880879
#[no_mangle]
881880
pub unsafe extern "C" fn $fn_name(
882881
rows: *const $tick,

thetadatadx-py/src/_generated/tick_arrow.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ pub(crate) fn arrow_schema_for_qualname(qualname: &str) -> Option<Arc<Schema>> {
1818
match qualname {
1919
"CalendarDay" => Some(Arc::new(Schema::new(vec![
2020
Field::new("date", DataType::Int32, false),
21-
Field::new("is_open", DataType::Boolean, false),
2221
Field::new("open_time", DataType::Int32, false),
2322
Field::new("close_time", DataType::Int32, false),
2423
Field::new("status", DataType::Utf8, false),
@@ -501,7 +500,7 @@ pub(crate) fn record_batch_to_pyarrow_table(py: Python<'_>, batch: RecordBatch)
501500
pub(crate) mod slice_arrow {
502501
use super::*;
503502
use super::tick;
504-
use arrow::array::{ArrayRef, BooleanArray, Float64Array, Int32Array, Int64Array, StringArray};
503+
use arrow::array::{ArrayRef, Float64Array, Int32Array, Int64Array, StringArray};
505504
use arrow::array::RecordBatchOptions;
506505
use arrow::record_batch::RecordBatch;
507506
use arrow::datatypes::{DataType, Field, Schema};
@@ -510,18 +509,15 @@ pub(crate) mod slice_arrow {
510509
fn read_arrow_batch_from_calendar_day_slice_projected(ticks: &[tick::CalendarDay], present: &thetadatadx::columns::ColumnPresence) -> PyResult<RecordBatch> {
511510
let n = ticks.len();
512511
let has_date = present.contains("date");
513-
let has_is_open = present.contains("is_open");
514512
let has_open_time = present.contains("open_time");
515513
let has_close_time = present.contains("close_time");
516514
let has_status = present.contains("status");
517515
let mut col_date: Vec<i32> = Vec::with_capacity(if has_date { n } else { 0 });
518-
let mut col_is_open: Vec<bool> = Vec::with_capacity(if has_is_open { n } else { 0 });
519516
let mut col_open_time: Vec<i32> = Vec::with_capacity(if has_open_time { n } else { 0 });
520517
let mut col_close_time: Vec<i32> = Vec::with_capacity(if has_close_time { n } else { 0 });
521518
let mut col_status: Vec<String> = Vec::with_capacity(if has_status { n } else { 0 });
522519
for t in ticks {
523520
if has_date { col_date.push(t.date); }
524-
if has_is_open { col_is_open.push(t.is_open); }
525521
if has_open_time { col_open_time.push(t.open_time); }
526522
if has_close_time { col_close_time.push(t.close_time); }
527523
if has_status { col_status.push(t.status.as_str().to_string()); }
@@ -539,10 +535,6 @@ pub(crate) mod slice_arrow {
539535
fields.push(Field::new("date", DataType::Int32, false));
540536
columns.push(Arc::new(Int32Array::from(col_date)) as ArrayRef);
541537
}
542-
if has_is_open {
543-
fields.push(Field::new("is_open", DataType::Boolean, false));
544-
columns.push(Arc::new(BooleanArray::from(col_is_open)) as ArrayRef);
545-
}
546538
if has_open_time {
547539
fields.push(Field::new("open_time", DataType::Int32, false));
548540
columns.push(Arc::new(Int32Array::from(col_open_time)) as ArrayRef);

0 commit comments

Comments
 (0)