trader.replay.sample module¶
The pre-registered row set: which (symbol, date) pairs get replayed.
Fixed in advance by issue #7 note_3689814772, and deliberately not parameterised beyond what that note names. A sampler with knobs is a sampler that can be turned until the result flatters the model, which is the selection-on-outcome error this project already made once with oversold: 45.
- trader.replay.sample.DECISION_HOUR_UTC = 14¶
00 UTC. Inside regular trading hours (13:30-20:00 UTC) and when the app actually traded — the live GOOG entry filled at 2026-08-14 14:00:09Z, and 5 of 9 live decision dates opened between 14:21 and 15:15 UTC. Fixing the time of day removes an otherwise free parameter.
- Type:
14
- class trader.replay.sample.ReplayRow(symbol, decision_at, split, entry_day)[source]¶
Bases:
objectOne decision to replay.
- Parameters:
symbol (str)
decision_at (datetime)
split (Literal['train', 'holdout'])
entry_day (date)
- symbol: str¶
- decision_at: datetime¶
- split: Literal['train', 'holdout']¶
- entry_day: date¶
- class trader.replay.sample.SampleShortfall(symbol, split, wanted, got)[source]¶
Bases:
objectA symbol that could not fill its quota in a split.
- Parameters:
symbol (str)
split (str)
wanted (int)
got (int)
- symbol: str¶
- split: str¶
- wanted: int¶
- got: int¶
- property missing: int¶
- trader.replay.sample.build_sample(archive, symbols, grid, *, train, holdout, quotas=(40, 20), window_hours=72.0)[source]¶
The pre-registered sample: 40 train + 20 holdout dates per symbol.
Rows come back grouped by symbol then chronological within each symbol’s split. Shortfalls are returned rather than raised — a thin symbol with 6 eligible holdout dates is a finding to report, not a failure — and are never backfilled from the other split, which would leak holdout dates into train.
- Parameters:
archive (_ArchiveRepo)
symbols (Sequence[str])
grid (Sequence[date])
train (tuple[date, date])
holdout (tuple[date, date])
quotas (tuple[int, int])
window_hours (float)
- Return type:
tuple[list[ReplayRow], list[SampleShortfall]]
- trader.replay.sample.decision_instant(day)[source]¶
day at the pre-registered decision hour, tz-aware UTC.
- Parameters:
day (date)
- Return type:
datetime
- trader.replay.sample.eligible_dates(archive, symbol, days, window_hours)[source]¶
The trading days on which symbol had at least one item in the window.
Deliberately the same gate as production’s max_news_age_hours: with no fresh news and no position the live strategy does not call the model at all, so an ineligible date is one on which no live decision would have existed. The replay set is therefore the set of decisions the strategy would actually have made, not an arbitrary calendar.
- Parameters:
archive (_ArchiveRepo)
symbol (str)
days (Sequence[date])
window_hours (float)
- Return type:
list[date]
- trader.replay.sample.stride_sample(dates, quota)[source]¶
quota dates, uniformly spaced through dates, both ends kept.
A stride, not a random draw and not “the most newsworthy dates”: it is reproducible, it cannot be reshuffled until it works, and it keeps the panel balanced across the ~30x coverage difference between the dense and thin names. Taking the first quota instead would bias every symbol to the start of its window.
- Parameters:
dates (Sequence[date])
quota (int)
- Return type:
list[date]