Two complementary additions to validate the minute-horizon alpha
hypothesis at IBKR-realistic costs:
1. `alpha_baseline --decision-stride N`: emits a new action every N
steps; between decisions force action=0 (wait) so an open position
is held rather than re-decided per bar. Cuts per-bar trade counts
~stride× and removes the coin-flip overtrading. Local 2Q sweep
showed stride=200 + scaled training (8K episodes × 25 envs × H=1200)
flipped Sharpe at ¼-tick from -4.29 (per-bar, 3-fold mean) to +1.78,
with std collapsing from ±8.8 to ±1.15. Break-even cost moved from
<¼-tick to ~1-tick — for the first time positive at IBKR-realistic
passive-execution frictions.
2. `alpha_train_stacker --max-rows N`: optional cap on bars consumed
from the fxcache. Used during local 2Q smoke (--max-rows 4M against
the 17.8M-row 9Q fxcache) to fit Mamba2 training on a 4 GB consumer
GPU; on the cluster (--no-cap) it sees all 9Q.
3. New Argo workflow `alpha-cv`: standalone template that compiles
alpha_train_stacker + alpha_baseline + alpha_fill_coeffs.json,
trains the stacker on the 9Q fxcache, then runs 9 sequential
walk-forward folds of alpha_baseline on disjoint 1.9M-bar windows
(one per quarter). Launcher script `scripts/argo-alpha-cv.sh`
mirrors argo-train.sh conventions.
The local 2Q test that motivated this commit is summarised inline in
the alpha-cv template comments; the verdict was "framing was the bug —
once decision cadence matches the multi-minute alpha horizon, the
strategy is positive at IBKR commission".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cleanup of compiler warnings flagged by both local cargo check and the
cluster ensure-binary log. Per `feedback_no_hiding`, every site is
either deleted or wired up — no #[allow] suppressions.
Lib (5 sites):
- gpu_backtest_evaluator.rs:34 — drop unused DevicePtrMut.
- gpu_dqn_trainer.rs:49 — drop unused DevicePtrMut (8 device_ptr_mut
calls don't need the trait import in current cudarc). Line 19852:
drop unnecessary parens around `b * sh2`.
- training_loop.rs:20 — drop unused DevicePtrMut; unbrace single-
symbol use at 5766.
- state_reset_registry.rs:4 — delete the 10-symbol use-block of slot
constants. Names appear in description strings (documentation only),
symbols are never referenced.
Examples (3 sites):
- alpha_dqn_h600_smoke.rs:181, 186 — drop COL_RAW_CLOSE, FEAT_DIM,
FillCoeffs, FillModel imports.
- alpha_baseline.rs:79 — delete unused MappedI32::read. Batched path
uses read_all for N-element action readback; the single-element
method was leftover from the pre-batched legacy path.
Lib + examples now have zero removable warnings. The remaining
unsafe_block lints (each cudarc kernel launch needs unsafe) are
structural and not actionable under the project's -W unsafe-code
policy.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the TrainingPersist loop for the regime defense per the
KELLY_F_SMOOTH precedent (pearl_kelly_cap_signal_driven_floors).
Three layers:
(1) Per-step: alpha_regime_vol_update_kernel tracks min(vol_obs > 1e-12)
in new slot 553 (REGIME_VOL_OBS_MIN_INDEX). Filtered against artifact-
zero observations from stationary snapshots where curr == prev.
(2) Per-cell: stacker_threshold_controller_update gains a fifth branch
that reads vol_ref (slot 550) at cell-end, computes
`target = floor_target_ratio × vol_ref`, slow-EMAs the floor anchor
(slot 552) toward the target with rate `floor_update_rate` (0.1 per
cell). Subfloor 1e-12 inside the kernel guards against the anchor
collapsing to zero.
(3) Per-invocation: alpha_baseline reads
`config/ml/alpha_baseline_state.json` at startup and seeds slot 552
from the `regime_vol_ref_floor` field. At end of main(), the learned
floor is written back via tmp+rename atomic write so concurrent
walk-forward invocations see a consistent file. Matches the
cross-fold-persistent shape of KELLY_F_SMOOTH.
Block extended to 15 slots (539..=553). Smoke + kernel unit test
pass -1/-1 for the new floor-controller indices (backward compat).
Walk-forward CV verdict (Q1 fxcache, 3 sequential folds):
iteration fold-A fold-B fold-C mean ± SD
pre-defense (no regime) +91.52 -21.44 +46.74 +38.94 ± 56.88
hardcoded 1e-9 floor -19.77 +65.04 +6.45 +17.24 ± 43.42
learned floor (0.5 × cell_min) +74.78 -12.72 +8.80 +23.62 ± 45.59
learned floor (0.1 × vol_ref) -19.53 -26.51 +15.46 -10.20 ± 22.49
Controller infrastructure is structurally correct (loop closes, floor
persists across invocations, kernel + disk + ISV all roundtrip). The
TUNING is data-dependent — single-quarter CV doesn't have enough
regime diversity to anchor the floor against. Multi-quarter fxcache
validation is the next step (built cluster-side on the 9-quarter
2024-Q1..2026-Q1 ES futures dataset, downloaded as a single artifact
for local CV).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two coupled fixes to the vol-EMA regime detector exposed by walk-forward
CV after all features were promoted to always-on:
(1) Bootstrap window for vol_ref (slot 551 = REGIME_VOL_REF_SAMPLES)
- Replace the Pearl-A "first observation replaces directly" bootstrap
with a running mean over the first N=100 vol_ema observations, then
switch to β-tracking. For IID observations the running-mean estimator
has variance σ²/N — a 100-sample mean is 10× less noisy than the
single-shot replace.
(2) Permanent floor on vol_ref (slot 552 = REGIME_VOL_REF_FLOOR)
- The bootstrap alone exposed the asymmetric deadband-deadlock: if
vol_ref converged to a tiny value during a calm initial stretch,
vol_ref / vol_ema fired the moment any realistic vol resumed and
Kelly stayed trapped at regime_scale_floor=0.25 forever. Floor
lives in ISV slot (TrainingPersist) with a hardcoded 1e-12 sub-floor
inside the kernel as numerical-underflow guard.
- Host seeds slot 552 with 1e-9. Future controller kernel will refine
this from observed cell-level vol minima with cross-fold persistence.
Walk-forward CV on Q1 fxcache (3 folds, window=700K, train_frac=0.6):
cost fold-A fold-B fold-C mean ± SD
0.00 -19.77 +65.04 (100%) +6.45 +17.24 ± 43.42
Fold B turnaround is the headline: -47.64 (bootstrap-only) → +65.04
(bootstrap + floor) confirms the floor is the load-bearing fix.
Cross-fold std-dev compressed 24% at cost=0; mean dropped from +38.94
(pre-defense) to +17.24 (with defense). Classic mean/variance trade.
Block extended to 14 slots (539..=552). Kernel sig: vol_ref_floor
moved from f32 scalar to vol_ref_floor_index i32, so the anchor is
named/addressable in ISV.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rename binary alpha_compose_backtest → alpha_baseline and remove the
boolean flags whose features are now mandatory:
--c51 (always C51 distributional Q)
--temporal (always Mamba2 temporal encoder)
--isv-continual (controller always fires per eval episode)
--regime-scale (vol-EMA regime defense always on)
--pruned-actions (FALSIFIED 2026-05-15 per pearl_action_pruning_falsified)
Every dependent code path was stripped, not just gated:
- Linear-Q kernels (lq_fwd, lq_grad, munch_kernel) and their cubin loads
are gone — C51 is the only Q-network.
- Single-env push_kernel / h_store_kernel loads removed; the backtest
has been batched-parallel-env since T14 and only the _batched
variants are called here. (The smoke binary still uses single-env
variants because one env per episode is its job.)
- Dead transition buffers removed: states_dev, next_states_dev,
actions_dev, rewards_dev, dones_dev, q_current_dev, q_next_dev,
target_dev, single_state_dev, single_q_dev, probs_current_dev,
probs_next_dev, m_dev, single_probs_dev, single-env state_pinned,
action_pinned, window_tensor, h_enriched_buf_dev.
- Dead constants and helpers: PRUNED_ACTIONS, N_WEIGHTS, N_BIASES,
epsilon_greedy, epsilon_greedy_gated.
End-to-end verification on the existing Q1 fxcache (rebuild was OOM
locally; full multi-quarter validation is the next phase):
cost=0.0000 best τ=0.250 Sharpe_ann=+36.83 win=0.984 trades/ep=83.3
cost=0.0625 best τ=0.250 Sharpe_ann=+38.53 win=0.996 trades/ep=83.2
cost=0.1250 best τ=0.250 Sharpe_ann=+38.37 win=0.994 trades/ep=83.3
cost=0.2500 best τ=0.250 Sharpe_ann=+34.24 win=0.990 trades/ep=85.6
cost=0.5000 best τ=0.250 Sharpe_ann=+31.83 win=0.946 trades/ep=84.8
Numbers track the prior T16-flag config (within stochastic noise),
confirming the conditional-stripping was a pure simplification — no
behavioral change, just a smaller, honester binary.
Also updated:
- scripts/alpha_pipeline.sh — A/B conditions collapse to fixed-cost
vs cost-randomized training (the only opt-in left).
- scripts/walk_forward_cv.sh — drop legacy flags, pass --window-k only.
- crates/ml/src/env/loaders.rs — module doc-comment updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>