Data card — WP41 CHRONOS-X
Source
FinData OS (https://findata.spboucher.ai/api/v1), a private lakehouse of FirstRate Data bars. Datasets used:
market_bars.stock.1m.raw (6.70 G rows, 2000→2026-10-30) and market_bars.etf.1m.raw (0.91 G rows). Stored bars are
raw (unadjusted); corporate actions come from the instrument master (/instruments/{symbol}). No fundamentals, news
or quotes are used. Finnhub: not used (optional vendor, not verified).
Connector contract (src/chronos_x/data/client.py, exports.py)
- Discovery:
GET /catalog/datasets,/catalog/schema/bars,/openapi.json(audited indocs/PHASE_0_REPORT.md). - Small reads:
GET /market/bars(1m ≤ 31 days, ≤ 10 000 rows/page, cursor, Arrow IPC) for tests and audits. - Bulk:
POST /exports(Parquet, dataset version pinned) per instrument for the whole sample window, polled, streamed to disk, schema-validated, sha256-hashed; manifest line with dataset version, request id, revision ids, time. - Preserved per call:
X-Request-ID,X-Dataset-Id,X-Dataset-Version(JSONL provenance logs). - Rate: client token bucket (5–8 req/s), retries with jittered backoff on 429/5xx, disk quota guard (≥ 40 GB free).
Conventions (verified on real rows, P0)
| Convention | Value |
|---|---|
| Timestamp | timestamp_utc = bar OPEN (UTC). Bar at 09:30 ET is complete at 09:31 ET |
| Regular session | 09:30–16:00 ET → 391 bars (slots 0..390); slot 390 = closing print, excluded from context and targets |
| Early close | 13:00 ET → 211 bars (slot 210 = closing print) |
| Extended hours | 04:00–20:00 ET present at the source; excluded (separate experiment, not run in v1.0) |
| Missing minutes | absent at the source (zero volume); kept missing, masked; never forward-filled |
| DST | handled via exchange_calendars XNYS opens/closes in UTC |
| Integrity | rows violating OHLC inequalities, non-positive prices or negative volume are dropped and counted (0 in the pilot) |
| Duplicates | highest revision_id kept per (instrument, timestamp) (0 duplicates in the pilot) |
Corporate-action policy (adjustments.py)
Splits applied explicitly per session from the instrument master (equals the server's adjustment=split; live test).
Cash dividends are not applied: intraday log returns within a session are invariant to the dividend factor and any
dividend adjustment is window dependent. The overnight gap feature across an ex-dividend date is left raw. Mergers and
spin-offs are not available: a delisted series simply ends.
Dense grid (features.py)
Per instrument: base[S, 391, 5] (log open/high/low/close, log1p volume, float32), mask[S, 391], in_session,
seas[S, 391] (past-20-session mean log volume per slot, past only), b5[S, 78, 5] + m5 (5-minute buckets of slots
5j..5j+4), b30[S, 13, 5] + m30 (30-minute buckets), meta.json (session dates, slots, early closes, split factor,
overnight gaps, QA counts). ≈ 16 MB per instrument-decade.
Origins and targets (windows.py)
Origin (s, m): completed bars 0..m of session s. Eligible iff m ≥ 30, m + 60 ≤ last target slot (389 / 209), bar m
present, ≥ 80 % of the next 60 slots present, s ≥ 1. Training: origins_per_session random origins per session; evaluation:
stride 10 minutes (36 origins per normal session). Targets y[h] = lc[m+h] − lc[prev present ≤ m+h−1], mask = present(m+h),
h = 1..60; cumulative H-minute return = Σ_{h≤H} y[h]·mask (exact log return of the last observed bar ≤ m+H);
realised variance = Σ y²·mask; tail event = |cum| > 2.5·σ_t·√H.
Features (strictly causal)
Per bar (15): scaled return, range, upper/lower wick, body, close location, log volume, relative volume vs seasonal
profile, missing flag, session-boundary flag, log temporal gap, minute fraction, time to close, weekday, early-close flag.
Scaling: σ_t = RMS of present same-session 1-minute returns in the 390-bar window (floor 1e-5), computed at origin only.
Global (9): log σ_t, scaled overnight gap, minute fraction, time to close, weekday, early close, log RV30/σ², log RVday/σ²,
mean relative volume over 30 minutes. Branch B/C features use the same definitions on completed buckets with √5, √30 scaling.
Leakage gate: tests/leakage/ (inputs invariant to any future bar; deliberate-future fixture rejected).
Universe (data/manifests/universe_main.json, sha256 inside; v1 kept as universe_main_v1_precoverage.json)
- Selection date 2017-12-29; information set: instrument master (first/last date, delisted status) + daily bars 2017-12-01..29 (ADV, daily volatility). Rules: first_date ≤ 2017-06-29; last_date ≥ 2018-07-02 (6-month survival requirement, documented bias); ADV ≥ 5 M USD; ≥ 15 daily bars in the ADV window.
- Coverage gate (P1 audit, before any model tuning): regular-hours 1-minute coverage over 2018–2025 ≥ 80 %
(
results/metrics/coverage_audit_candidates.csv, 2 147 candidates audited; Spearman(ADV, coverage) = 0.76). The v1 draw had a median coverage of 82 % and a minimum of 6 %; the gate leaves 1 152 candidates (947 stocks, 205 ETFs, 5 delisted). - Allocation: proportional over asset class (75 % stocks / 25 % ETFs) × ADV tercile × volatility tercile; inside each stratum round-robin over SIC division (stocks, from SEC submissions; 25 "unknown") or ETF name category.
- 300 instruments; 60 (20 %) held out entirely (zero-shot); 1 delisted instrument in the sample. Pilot = 20 pooled instruments, feasibility = 50.
- Survivorship: only 5 delisted candidates passed the gate because FinData's delisted 1-minute archives are only partly ingested; the sample is therefore close to a survivor sample for 2018–2025 (limitation, reported in the paper).
Splits
Chronological by session: train ≤ 2022-12-31, validation 2023, development test 2024, final untouched test 2025
(run once, configs/final.yaml). A one-session embargo at the start of each evaluation split removes overlap with the
last training targets. Held-out instruments are evaluated only with models trained on pooled instruments.
License
FirstRate Data private use; see docs/LICENSE.md. No bars, grids or per-origin returns are committed or released.