// MarketCalendar — XNYS (NYSE/Nasdaq) trading days, early closes, regular-session bounds and America/New_York DST. // Rules are computed (no external data): NYSE holiday rules since 2000, special closures listed explicitly, // US DST rules (2007+: 2nd Sunday March → 1st Sunday November; 1987–2006: 1st Sunday April → last Sunday October). // Validation: tests/reference/calendar_vs_exchange_calendars.cpp compares against the Python exchange_calendars fixture. #pragma once #include #include #include #include "raid/core/types.hpp" namespace raid { struct SessionBounds { Date session; Timestamp open_utc; // 09:30 ET Timestamp close_utc; // 16:00 ET (13:00 on early closes) — the close bar OPENS at this instant (closing print) Timestamp ext_open_utc; // 04:00 ET Timestamp ext_close_utc; // 20:00 ET (bars open until 19:59). On early-close days the vendor stops at 17:00. bool early_close{false}; i64 utc_offset_seconds{0}; // ET offset that day at the open (−18000 EST / −14400 EDT) }; class MarketCalendar { public: enum class Market { XNYS }; explicit MarketCalendar(Market m = Market::XNYS) : market_(m) {} Market market() const { return market_; } bool is_trading_day(Date d) const; bool is_early_close(Date d) const; std::optional holiday_name(Date d) const; /// Session bounds for a trading day (throws if not a trading day). SessionBounds session(Date d) const; /// Regular-hours bars expected for one session at the given interval (1m: 391 normal, 211 early close). usize expected_regular_bars(Date d, Interval iv) const; /// Trading sessions in [start, end] inclusive. std::vector sessions(Date start, Date end) const; /// Next / previous trading day strictly after / before d. Date next_session(Date d) const; Date previous_session(Date d) const; /// America/New_York offset (seconds east of UTC) at a UTC instant. static i64 new_york_offset(Timestamp utc); /// Local New York civil date and minute-of-day of a UTC instant. struct Local { Date date; int minute_of_day; i64 offset_seconds; }; static Local to_new_york(Timestamp utc); /// UTC instant of a New York wall-clock time (assumes the time exists; gap hour maps to post-transition). static Timestamp from_new_york(Date d, int minute_of_day); /// Session date of a bar-open timestamp under the US equity convention (04:00–20:00 ET same civil day). static Date session_of(Timestamp bar_open_utc) { return to_new_york(bar_open_utc).date; } private: Market market_; }; /// Convenience: trading-day distance between two sessions (number of sessions strictly between + 1). usize trading_days_between(const MarketCalendar& cal, Date a, Date b); } // namespace raid