Commit Graph

5 Commits

Author SHA1 Message Date
jgrusewski
771faac723 test(ml-backtesting): Ring 2 invariant fuzz at N ∈ {1, 16, 256} (C10)
Property-based fuzz tests with random-walk MBP-10 sequences applied to
the LOB simulator at three backtest counts; assert per-snapshot
invariants that must hold regardless of input or block scheduling:

  fuzz_n1_book_only (200 events, no orders)
    Pure book-update kernel — verifies mid-drift random walks preserve
    book monotonicity and never produce a crossed book.

  fuzz_n16_with_orders (200 events, market orders every 8th event)
    16 backtests in parallel, each submitting random buy/sell market
    orders 1-3 lots at every 8th event. Asserts book invariants on
    each backtest's state PLUS:
      - position_lots stays within ±30 (plausible given fixture book depth)
      - realized_pnl + vwap_entry finite (no NaN/Inf leaks)

  fuzz_n256_with_orders (100 events, market orders)
    Production-scale parallelism. Same invariants as N=16. Each block
    has its own per-backtest Pos + OpenTradeState + TradeLog, exercising
    the per-block isolation discipline established in C5-C7.

All 3 pass on RTX 3050. Spec §8 Ring 2 confidence gate hit.

Adds rand + rand_chacha to ml-backtesting dev-dependencies.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 09:00:49 +02:00
jgrusewski
63ed6d0217 feat(ml-backtesting): artifacts + sweep aggregate + fxt-backtest CLI (C9)
artifacts.rs:
  - Summary struct (total_pnl_usd, sharpe_ann, sortino_ann,
    max_drawdown_usd, calmar, n_trades, win_rate, avg_win/avg_loss,
    profit_factor, total_fees_usd, exposure_pct,
    kelly_cap_history_sample).
  - compute_summary(records, pnl_curve_usd) — non-overlapping
    annualisation × √825 per pearl_phase1d4_backtest_cost_edge_frontier
    (K=6000 holding × 250 trading days ≈ 825 trades/year).
    Sharpe + Sortino + max drawdown + Calmar.
  - write_summary (JSON pretty-printed), write_trades_csv (with USD
    conversion from fp ×100), write_pnl_curve_bin (bytemuck-cast f32
    slice). 5 unit tests with tempdir.

aggregate.rs:
  - aggregate_sweep_dir walks <root>/<cell>/summary.json, builds an
    arrow RecordBatch (cell name + 9 stats columns), writes
    SNAPPY-compressed aggregate.parquet.
  - pareto_frontier: cells are kept unless another cell weakly
    dominates on all three of (sharpe_ann maxed, max_drawdown_usd
    minimised, total_fees_usd minimised) AND strictly improves on one.
    Written to pareto_frontier.json (Vec<cell-name>).
  - 2 unit tests (3-cell mutual-non-dominance; B-dominates-A).

harness.rs:
  - run() now samples Pos.realized_pnl × $50/index-pt per event into
    self.pnl_curves[b], so the per-cell P&L curve is ready for
    write_artifacts() without an extra sim pass.
  - write_artifacts(out_dir) — per-cell <out>/cell_NNNN/{summary.json,
    trades.csv, pnl_curve.bin}.

bin/fxt-backtest:
  - clap-derive CLI with two subcommands:
      run --data <dir> [--predecoded-dir <dir>] [--policy-grid <yaml>]
          [--n-parallel N] [--decision-stride S] [--latency-ns N]
          [--target-annual-vol-units F] [--annualisation-factor F]
          [--max-lots N] [--max-events N] [--seed N] --out <dir>
      aggregate <sweep_dir>
  - Constructs MlDevice::cuda(0) + CfcTrunk::new_random for the trunk
    (v1 — ml-alpha has no checkpoint format yet; --seed gates init).
  - Parses --policy-grid YAML if given but doesn't yet plumb to the
    LobSimCuda decision kernel (the v1 kernel hardcodes the
    Strategy::default_for path; bytecode VM is C7's deferred follow-up).
    Parse step kept end-to-end so the YAML format is validated now.
  - --latency-ns parsed but not consumed — reserved for follow-up
    resting-order in-flight promotion (deferred from C5).

Adds parquet + arrow + arrow-array + arrow-schema + serde_yaml to
ml-backtesting deps; bin/fxt-backtest added to workspace members.

All 33 lib tests + 6 GPU fixture tests green. CLI --help renders both
subcommands correctly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 08:59:41 +02:00
jgrusewski
33636d250b feat(ml-backtesting): build.rs + book_update CUDA kernel + sim skeleton
build.rs compiles every .cu under crates/ml-backtesting/cuda/ to
\$OUT_DIR/<name>.cubin via nvcc (no nvrtc per feedback_no_nvrtc.md);
pairs every env::var with rerun-if-env-changed per
pearl_build_rs_rerun_if_env_changed.md. Default arch sm_86 (RTX 3050
local); production sets FOXHUNT_CUDA_ARCH=sm_89 (L40S) or sm_90 (H100).

First kernel: book_update_apply_snapshot — block-per-backtest replace
of the 10-level book from a broadcast MBP-10 snapshot. Single-writer
per level (thread tid = level idx), no atomics per feedback_no_atomicadd.md.

lob_state.cuh defines the canonical per-block shared-mem layout that
all subsequent kernels share (Book struct in this commit; Orders, Pos,
IsvKellyState[5] added in C5-C7).

LobSimCuda host struct (in src/sim.rs) constructed from an &MlDevice;
owns per-backtest device book state + mapped-pinned input snapshots.
apply_snapshot launches the kernel and synchronises; read_books drains
device state for tests.

Three Ring 1 fixture tests (book_update_replace, _two_step,
_n_backtests=4) — N≤4 bit-exact GPU oracle assertions loaded from JSON.
All three pass on RTX 3050 locally. Verifies the build-script → cubin →
cudarc.load_cubin → kernel launch → DtoH read pipeline end-to-end.

cudarc added as direct path dep (../../vendor/cudarc) since it's not in
[workspace.dependencies] — same vendored fork ml-alpha uses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 08:26:53 +02:00
jgrusewski
0b8dfa946f feat(ml-backtesting): host-side order types + SlotTag
OrderIntent (ergonomic host-side enum for policy YAML config + audit
reconstruction), OrderEvent (24-byte repr(C) Pod for the device-side
audit ring), TradeRecord (40-byte repr(C) Pod for the device-side
trade-log buffer), SlotTag (u32-packed {SlotKind, index, generation}
for cancel/modify against slot reuse), plus Side / LimitKind /
SlotKind / LimitParams.

Size pinning tests (24/40 bytes) ensure any future field-add forces
the kernel-side struct to migrate in lockstep. Serde roundtrips cover
OCO + Cancel variants. Renamed `.gen()` accessor to `.generation()`
to dodge the Rust 2024 reserved-keyword clash.

OCO is two limit legs linked by oco_pair byte on-device (not recursive
in OrderIntent) per spec §3 — keeps device-side layout flat for the
matching kernel coming in C5.

Adds ml-alpha + bytemuck + serde_json + tracing + thiserror to deps.
cudarc + parquet + arrow added later when CUDA + sweep aggregator
land (C4 / C9). No CUDA dependency in this commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 08:11:50 +02:00
jgrusewski
b7597a7543 refactor(ml): extract universe, backtesting, asset-selection into sub-crates
- ml-universe (1.7K lines): correlation, liquidity, momentum, volatility
  modules. Only depends on ml-core (MLError, PRECISION_FACTOR).
  6 tests passing.

- ml-backtesting (1.1K lines): action_loader, barrier_backtest, report
  modules. Only depends on ml-core (MLError). Discovered and included
  previously undeclared report.rs module. 10 tests passing.

- ml-asset-selection (1.2K lines): scorer, selector modules with
  AssetClass, AssetUniverse, PredictabilityScorer, ActiveSetSelector.
  Only depends on ml-core (MLError). 33 tests passing.

All three replaced with thin facade re-exports in ml — existing
`use ml::universe::*` / `use ml::backtesting::*` /
`use ml::asset_selection::*` paths continue to work.

Total: 15 sub-crates extracted from ml monolith.
Workspace: 0 errors, ml tests 902 passed + 49 in sub-crates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:17:22 +01:00