feat(phase-e-4-a-T16): vol-regime detection + cost-aware training

Adds two coupled interventions on the regime fragility exposed by
walk-forward CV (mean Sharpe +27 ± 56 at half-tick across 3 folds —
std-dev ≈ mean means the strategy is regime-dependent).

(1) Vol-EMA regime detector (new ISV slots 549/550)
- New alpha_regime_vol_update.cu kernel: per inference step, reads B
  parallel-env mid prices, computes cross-env mean squared log-return,
  and maintains ISV[549]=REGIME_VOL_EMA (Wiener-α with 0.4 floor +
  Pearl A bootstrap) and ISV[550]=REGIME_VOL_REF (slow tracker β=0.005,
  ≈200-step horizon).
- Block-tree-reduce (no atomicAdd), guards against zero/non-finite mids.

(2) Pre-emptive Kelly attenuation (modified stacker controller)
- stacker_threshold_controller.cu takes 3 new args: regime_vol_ema_idx,
  regime_vol_ref_idx, regime_scale_floor.
- Multiplies its reactive Sharpe-error Kelly output by
    regime_scale = clamp(vol_ref / vol_ema, 0.25, 1.0)
- Disabled when indices = -1 (backward-compatible smoke + kernel test).

(3) Cost-aware training (--train-cost-hi)
- alpha_compose_backtest --train-cost-hi: when > --train-cost, each
  training epoch samples cost ~ U[lo, hi] so the Q-network learns
  cost-conservative behaviour across the realistic ES range.

(4) Wiring
- alpha_compose_backtest --regime-scale enables both per-step regime
  kernel firing during eval AND the regime hookup in the per-episode
  controller call. Mapped-pinned mids buffers, all compute device-side.
- ExecutionEnv exposes current_mid() so the host gather reads the
  active snapshot mid per env without leaking the private cursor field.

Smoke + test sites pass -1/-1 for regime indices (backward compat).
Doc: docs/isv-slots.md ledger for slots 549/550.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-16 00:28:01 +02:00
parent 3aef276255
commit d090685ca9
10 changed files with 482 additions and 4 deletions

40
scripts/build_2q_fxcache.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Build a Q1+Q2 2024 combined fxcache for cross-quarter walk-forward CV.
#
# Requires:
# test_data/futures-baseline/ES.FUT/ES.FUT_2024-Q1.dbn.zst (present)
# test_data/futures-baseline/ES.FUT/ES.FUT_2024-Q2.dbn.zst (present)
# test_data/futures-baseline-mbp10/ES.FUT/ES.FUT_2024-Q1.dbn.zst (present)
# test_data/futures-baseline-mbp10/ES.FUT/ES.FUT_2024-Q2.dbn.zst (download in progress)
# test_data/futures-baseline-trades/ES.FUT/ES.FUT_2024-Q1.dbn.zst (present)
# test_data/futures-baseline-trades/ES.FUT/ES.FUT_2024-Q2.dbn.zst (present)
#
# Produces a single fxcache + norm_stats in test_data/feature-cache/.
# Cache key is SHA256 over all matching DBN files in the input dirs.
set -euo pipefail
# Stage Q1+Q2 only — symlink them into a temporary dir so the builder
# doesn't pick up other quarters that may land later.
STAGE="${STAGE:-/tmp/fxbuild_2q_2024}"
rm -rf "$STAGE"
mkdir -p "$STAGE"/{ohlcv,mbp10,trades}
for q in Q1 Q2; do
ln -s "/home/jgrusewski/Work/foxhunt/test_data/futures-baseline/ES.FUT/ES.FUT_2024-${q}.dbn.zst" "$STAGE/ohlcv/"
ln -s "/home/jgrusewski/Work/foxhunt/test_data/futures-baseline-mbp10/ES.FUT/ES.FUT_2024-${q}.dbn.zst" "$STAGE/mbp10/"
ln -s "/home/jgrusewski/Work/foxhunt/test_data/futures-baseline-trades/ES.FUT/ES.FUT_2024-${q}.dbn.zst" "$STAGE/trades/"
done
mkdir -p test_data/feature-cache
SQLX_OFFLINE=true cargo run -p ml --release --example precompute_features -- \
--data-dir "$STAGE/ohlcv" \
--mbp10-data-dir "$STAGE/mbp10" \
--trades-data-dir "$STAGE/trades" \
--output-dir test_data/feature-cache \
--symbol ES.FUT \
--yes
echo
echo "Built fxcaches in test_data/feature-cache/:"
ls -lh test_data/feature-cache/ | grep "$(date +%Y-%m-%d)" || ls -lh test_data/feature-cache/ | tail