← all research

Status: DONE (2026-08-15) · Suite: 260 passed (9 new tests in tests/test_alpaca_dataset.py)

What was built

FileRole
data/alpaca/labels.pyExecutable labels for the long/flat bar system
data/alpaca/dataset.pyDataset assembly, persistence, walk-forward split tooling

Labels (labels.py)

The bar-close signal must be executable: a signal read at bar t close enters at the next open and exits h bars later at the open — the only honest leg (this matches the repo's PANEL open-entry book).

  • Primary label: y[t] = open[t+1+h] / open[t+1] - 1 (executable, long/flat).
  • Research label: y_cc[t] = close[t+h] / close[t] - 1 — not the executable leg; kept only for IC diagnostics side-by-side with y.
  • h must be ≥ 1 (raising otherwise); NaN only at tail rows that lack the future t+1+h bar — never imputed.
  • add_labels() merges labels onto a Phase-2 feature frame on the bar-close timestamp grid; if feature/bars lengths diverge (dropped/duplicate bars) it reindexes labels onto the feature timestamps instead of assuming alignment.

Dataset assembly (dataset.py)

  • assemble_dataset(symbol, timeframe, h) — features + labels, then:
    1. drop tail rows with NaN y (never impute);
    2. optional warmup drop (rolling-feature warmup rows);
    3. enforce fixed schema [timestamp, symbol, y, y_cc] + FEATURE_SCHEMA[2:].
  • Persistence: data/processed/alpaca/datasets/{SYM}/{TF}/h{h}.parquet with a strict pyarrow schema (dataset_schema(), ns timestamps), safe reload.
  • build_all_datasets(...) bulk-builds (symbol × timeframe × horizon).

Walk-forward splits (leakage-free by construction)

  • wf_splits(ts, n_splits, gap_bars) — reference-based, expandable splits over the sorted timestamp axis: split k tests interval k with train = all rows before it minus an embargo of gap_bars rows. Earlier splits carry less history (the honest walk-forward property); no randomized/CV splitting.
  • leakage_check(train_ts, test_ts, gap_bars) — asserts the actual timestamps of train/test respect the embargo before training starts (ground-truth check, converts to ns for exact timedelta math).

Tests (tests/test_alpaca_dataset.py, isolated dirs → tmp)

  • forward_open_return exact math (and forward_close_return);
  • add_labels alignment, incl. reindex path when bars are dropped;
  • tail rows without future label dropped; warmup trim;
  • dataset save/load round-trip with strict schema;
  • wf_splits invariants + embargo respected (gap_bars honored in timestamps);
  • leakage_check raises on overlap.

Integration notes

  • Datasets are inputs to Phase 4 (per-symbol walk-forward trainer): config defaults gap_bars = config.ml.embargo_bars (50 for the PANEL-style monthly cadence; crypto runs use same default), horizons (1, 5, 12, 24).
  • Real datasets get built after the historical backfill completes (Phase 1/2 raw/feature stores are currently being repopulated 2021→now).