Time spine: int-nanosecond UTC hot path, ET (America/New_York) boundaries.
The engine's hot path stores time as int nanoseconds since the UTC epoch.
The project's canonical timezone is ET — every session boundary below is
defined in ET and converted tz-aware (Topstep publishes rules in CT, but
ET = CT + 1h always; the two share the identical US DST schedule).
Topstep session boundaries (ET):
- Globex open: 18:00 previous calendar day (Sun 18:00 for Monday)
- auto-flatten start: 16:08
- flatten deadline: 16:10 (positions & working orders force-closed)
- Globex close: 17:00 (EOD balance snapshot for the MLL ratchet)
- day reset: 18:00 (DLL reset; next trading day begins)
A bar timestamped at/after 18:00 ET belongs to the next trading day.
ET
module-attribute
ET = ZoneInfo('America/New_York')
NS_PER_SEC
module-attribute
NS_PER_MIN
module-attribute
TOPSTEP_SESSION
module-attribute
SessionTimes
Bases: Struct
The ET wall-clock boundaries of a Topstep trading day (all datetime.time).
open_prev_day
class-attribute
instance-attribute
open_prev_day: time = time(18, 0)
auto_flatten
class-attribute
instance-attribute
auto_flatten: time = time(16, 8)
flatten
class-attribute
instance-attribute
flatten: time = time(16, 10)
close
class-attribute
instance-attribute
close: time = time(17, 0)
reset
class-attribute
instance-attribute
reset: time = time(18, 0)
open_ns
open_ns(day: date) -> int
Session open (18:00 ET on the previous calendar day).
Source code in src/topstep_backtest/core/time.py
| def open_ns(self, day: date) -> int:
"""Session open (18:00 ET on the previous calendar day)."""
return et_time_of(day - timedelta(days=1), self.open_prev_day)
|
auto_flatten_ns
auto_flatten_ns(day: date) -> int
Source code in src/topstep_backtest/core/time.py
| def auto_flatten_ns(self, day: date) -> int:
return et_time_of(day, self.auto_flatten)
|
flatten_ns
flatten_ns(day: date) -> int
Source code in src/topstep_backtest/core/time.py
| def flatten_ns(self, day: date) -> int:
return et_time_of(day, self.flatten)
|
close_ns
close_ns(day: date) -> int
Source code in src/topstep_backtest/core/time.py
| def close_ns(self, day: date) -> int:
return et_time_of(day, self.close)
|
reset_ns
reset_ns(day: date) -> int
Source code in src/topstep_backtest/core/time.py
| def reset_ns(self, day: date) -> int:
return et_time_of(day, self.reset)
|
in_no_trade_window
in_no_trade_window(ns: int) -> bool
True inside the daily no-trade window [flatten, reset) = [16:10, 18:00) ET.
Source code in src/topstep_backtest/core/time.py
| def in_no_trade_window(self, ns: int) -> bool:
"""True inside the daily no-trade window [flatten, reset) = [16:10, 18:00) ET."""
t = ns_to_et(ns).time()
return self.flatten <= t < self.reset
|
dt_to_ns
dt_to_ns(dt: datetime) -> int
tz-aware datetime -> int UTC nanoseconds. Naive datetimes are rejected.
Computed with exact integer arithmetic (datetime carries microseconds);
a float round-trip would lose sub-microsecond exactness at 2026 epochs.
Source code in src/topstep_backtest/core/time.py
| def dt_to_ns(dt: datetime) -> int:
"""tz-aware datetime -> int UTC nanoseconds. Naive datetimes are rejected.
Computed with exact integer arithmetic (datetime carries microseconds);
a float round-trip would lose sub-microsecond exactness at 2026 epochs.
"""
if dt.tzinfo is None:
raise ValueError(f"naive datetime {dt!r}: all engine datetimes must be tz-aware")
delta = dt.astimezone(UTC) - _EPOCH
return ((delta.days * 86_400 + delta.seconds) * NS_PER_SEC) + delta.microseconds * 1_000
|
ns_to_dt
ns_to_dt(ns: int) -> datetime
int UTC nanoseconds -> tz-aware UTC datetime.
Exact integer arithmetic, truncating sub-microsecond remainders toward the
past (datetime resolution is 1 us). Truncation — never float rounding — so
an instant strictly before a boundary (e.g. 17:59:59.999999999 ET) can
never classify as at/after it (the 18:00 trading-day boundary hinges on this).
Source code in src/topstep_backtest/core/time.py
| def ns_to_dt(ns: int) -> datetime:
"""int UTC nanoseconds -> tz-aware UTC datetime.
Exact integer arithmetic, truncating sub-microsecond remainders toward the
past (datetime resolution is 1 us). Truncation — never float rounding — so
an instant strictly before a boundary (e.g. 17:59:59.999999999 ET) can
never classify as at/after it (the 18:00 trading-day boundary hinges on this).
"""
seconds, rem = divmod(ns, NS_PER_SEC)
return datetime.fromtimestamp(seconds, tz=UTC) + timedelta(microseconds=rem // 1_000)
|
ns_to_et
ns_to_et(ns: int) -> datetime
int UTC nanoseconds -> tz-aware ET datetime.
Source code in src/topstep_backtest/core/time.py
| def ns_to_et(ns: int) -> datetime:
"""int UTC nanoseconds -> tz-aware ET datetime."""
return ns_to_dt(ns).astimezone(ET)
|
et_time_of
et_time_of(d: date, t: time) -> int
An ET wall-clock instant on calendar date d -> int UTC nanoseconds.
Source code in src/topstep_backtest/core/time.py
| def et_time_of(d: date, t: time) -> int:
"""An ET wall-clock instant on calendar date ``d`` -> int UTC nanoseconds."""
return dt_to_ns(datetime.combine(d, t, tzinfo=ET))
|
trading_day_of
trading_day_of(ns: int) -> date
The Topstep trading day containing UTC-ns instant ns.
The day boundary is 18:00 ET: at/after 18:00 the instant belongs to the
next calendar day's session (Sunday 18:00+ belongs to Monday).
Source code in src/topstep_backtest/core/time.py
| def trading_day_of(ns: int) -> date:
"""The Topstep trading day containing UTC-ns instant ``ns``.
The day boundary is 18:00 ET: at/after 18:00 the instant belongs to the
next calendar day's session (Sunday 18:00+ belongs to Monday).
"""
local = ns_to_et(ns)
if local.time() >= time(18, 0):
return local.date() + timedelta(days=1)
return local.date()
|