Files
foxhunt/crates
jgrusewski 1ffdf38ddc phase3(env-unification): val step_returns measure pure P&L (no shaping)
Root cause of the long-running "catastrophic train/val Sharpe gap":
backtest_env_kernel was subtracting behavioral shaping (inventory penalty,
churn penalty, opportunity cost) from step_returns BEFORE the metrics layer
computed Sharpe / Sortino / WinRate. Validation was reporting "P&L minus
shaping" as if it were realized P&L.

Both single-step and batched variants of backtest_env_step had the bug.

The shaping terms exist for a reason — they steer the training policy toward
risk-aware behavior. They belong in TRAINING reward, where they shape the
gradient. They do NOT belong in VALIDATION step_returns, which is the
measurement we use to judge whether the model would be profitable in
production. Production deployment doesn't pay an inventory penalty for
holding a position — it pays the actual market P&L of holding it.

Equivalent semantically to running experience_env_step with shaping_scale = 0
(the Phase 3 control scalar landed in commit 3f6eb006c).

Smoke-test verification (TD-propagation, RTX 3050 Ti, 20 epochs):

  metric                          before      after
  val_Sharpe range                -17 to -33   -1.24 to +2.34
  epochs val_Sharpe > 0           0 / 20       10 / 20
  Best (training) Sharpe          ~15-19       +21.04
  train Sharpe trajectory         unchanged    unchanged

The ~30-point Sharpe gap that motivated the entire env-unification effort
was ~80% measurement bug and ~20% legitimate train/val differences. The
remaining gap (val WinRate still anomalously low, 1.5–4.7% vs training
15–23%) suggests one more accounting issue in the val win-rate counter
but is non-blocking — Sharpe is now an honest production-equivalent
measurement.

Kernel signature kept stable (holding_cost_rate, churn_threshold,
churn_penalty_scale, opp_cost_scale args still present, suppressed via
(void) casts) so the Rust launch site does not need to change. Clean
deletion of those args is a follow-up after validation that no other
caller depends on the ABI.

Files touched:
  crates/ml/src/cuda_pipeline/backtest_env_kernel.cu  (-48 / +35)

Verified: SQLX_OFFLINE=true cargo check -p ml --lib --tests passes.
TD-propagation smoke test runs cleanly end-to-end (33s).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:13:24 +02:00
..