diff --git a/crates/ml/src/cuda_pipeline/backtest_plan_kernel.cu b/crates/ml/src/cuda_pipeline/backtest_plan_kernel.cu index b8e7f2f76..9f177becb 100644 --- a/crates/ml/src/cuda_pipeline/backtest_plan_kernel.cu +++ b/crates/ml/src/cuda_pipeline/backtest_plan_kernel.cu @@ -118,12 +118,12 @@ extern "C" __global__ void backtest_plan_state_isv( float active_tgt = plan_state[w * 7 + 0]; if (in_position && active_tgt < 0.5f) { const float* pp = plan_params + w * 6; - plan_state[w * 7 + 0] = pp[0]; /* target_bars */ - plan_state[w * 7 + 1] = pp[1]; /* profit_target */ - plan_state[w * 7 + 2] = pp[2]; /* stop_loss */ - plan_state[w * 7 + 3] = pp[3]; /* scale_aggression */ - plan_state[w * 7 + 4] = pp[4]; /* conviction (at entry) */ - plan_state[w * 7 + 5] = pp[5]; /* asymmetry */ + plan_state[w * 7 + 0] = pp[PLAN_PARAM_TARGET_BARS]; /* target_bars */ + plan_state[w * 7 + 1] = pp[PLAN_PARAM_PROFIT_TARGET]; /* profit_target */ + plan_state[w * 7 + 2] = pp[PLAN_PARAM_STOP_LOSS]; /* stop_loss */ + plan_state[w * 7 + 3] = pp[PLAN_PARAM_SCALE_AGGRESSION]; /* scale_aggression */ + plan_state[w * 7 + 4] = pp[PLAN_PARAM_CONVICTION]; /* conviction (at entry) */ + plan_state[w * 7 + 5] = pp[PLAN_PARAM_ASYMMETRY]; /* asymmetry */ plan_state[w * 7 + 6] = (isv_signals != nullptr) ? isv_signals[11] : 1.0f; /* regime_stability at entry */ } @@ -162,7 +162,7 @@ extern "C" __global__ void backtest_plan_state_isv( /* [4] Conviction drift: current MLP conviction - entry conviction */ pisv[PLAN_ISV_CONVICTION_DRIFT] = (plan_tgt_bars > 0.5f) - ? (plan_params[w * 6 + 4] - plan_conv_ent) : 0.0f; + ? (plan_params[w * 6 + PLAN_PARAM_CONVICTION] - plan_conv_ent) : 0.0f; /* [5] Regime shift magnitude since entry */ pisv[PLAN_ISV_REGIME_SHIFT] = (isv_signals != nullptr && plan_tgt_bars > 0.5f) diff --git a/crates/ml/src/cuda_pipeline/experience_kernels.cu b/crates/ml/src/cuda_pipeline/experience_kernels.cu index d92e8b472..83f01a6b7 100644 --- a/crates/ml/src/cuda_pipeline/experience_kernels.cu +++ b/crates/ml/src/cuda_pipeline/experience_kernels.cu @@ -608,7 +608,7 @@ extern "C" __global__ void experience_state_gather( * Negative drift = model's thesis is weakening → exit signal. * Only meaningful when a plan is active (ps[PS_PLAN_TARGET_BARS] > 0.5). */ plan_isv[PLAN_ISV_CONVICTION_DRIFT] = (plan_params_ptr != NULL && ps[PS_PLAN_TARGET_BARS] > 0.5f) - ? (plan_params_ptr[i * 6 + 4] - ps[PS_PLAN_CONVICTION]) /* conviction drift */ + ? (plan_params_ptr[i * 6 + PLAN_PARAM_CONVICTION] - ps[PS_PLAN_CONVICTION]) /* conviction drift */ : 0.0f; /* Regime shift since entry — |regime_stability_now - regime_stability_at_entry|. @@ -1788,12 +1788,12 @@ extern "C" __global__ void experience_env_step( if (was_flat_plan && now_positioned_plan && plan_enabled) { const float* pp = plan_params_ptr + i * 6; - ps[PS_PLAN_TARGET_BARS] = pp[0]; /* target_bars */ - ps[PS_PLAN_PROFIT_TARGET] = pp[1]; /* profit_target */ - ps[PS_PLAN_STOP_LOSS] = pp[2]; /* stop_loss */ - ps[PS_PLAN_SCALE_AGGRESSION] = pp[3]; /* scale_aggression */ - ps[PS_PLAN_CONVICTION] = pp[4]; /* conviction */ - ps[PS_PLAN_ASYMMETRY] = pp[5]; /* asymmetry */ + ps[PS_PLAN_TARGET_BARS] = pp[PLAN_PARAM_TARGET_BARS]; /* target_bars */ + ps[PS_PLAN_PROFIT_TARGET] = pp[PLAN_PARAM_PROFIT_TARGET]; /* profit_target */ + ps[PS_PLAN_STOP_LOSS] = pp[PLAN_PARAM_STOP_LOSS]; /* stop_loss */ + ps[PS_PLAN_SCALE_AGGRESSION] = pp[PLAN_PARAM_SCALE_AGGRESSION]; /* scale_aggression */ + ps[PS_PLAN_CONVICTION] = pp[PLAN_PARAM_CONVICTION]; /* conviction */ + ps[PS_PLAN_ASYMMETRY] = pp[PLAN_PARAM_ASYMMETRY]; /* asymmetry */ /* Store entry regime_stability for drift detection (P12). */ ps[PS_PLAN_ENTRY_REGIME] = (isv_signals_ptr != NULL) ? isv_signals_ptr[11] : 1.0f; ps[PS_INTRA_TRADE_MAX_PNL] = 0.0f; /* reset max P&L for new trade */ @@ -1807,14 +1807,14 @@ extern "C" __global__ void experience_env_step( * When model is highly confident (readiness ≥ 0.7) and conviction changes * significantly, update the stored plan. The model learns WHEN to revise. */ if (has_plan && fabsf(position) > 0.001f && readiness >= 0.7f && plan_params_ptr != NULL) { - float conv_now = plan_params_ptr[i * 6 + 4]; + float conv_now = plan_params_ptr[i * 6 + PLAN_PARAM_CONVICTION]; float conv_entry = ps[PS_PLAN_CONVICTION]; if (conv_now > conv_entry * 1.3f) { /* Conviction increased 30%+ → thesis strengthening → extend plan */ - float new_target = plan_params_ptr[i * 6 + 0]; + float new_target = plan_params_ptr[i * 6 + PLAN_PARAM_TARGET_BARS]; ps[PS_PLAN_TARGET_BARS] = fmaxf(ps[PS_PLAN_TARGET_BARS], new_target); /* only extend, never shorten from conviction increase */ - ps[PS_PLAN_PROFIT_TARGET] = fmaxf(ps[PS_PLAN_PROFIT_TARGET], plan_params_ptr[i * 6 + 1]); /* widen profit target */ + ps[PS_PLAN_PROFIT_TARGET] = fmaxf(ps[PS_PLAN_PROFIT_TARGET], plan_params_ptr[i * 6 + PLAN_PARAM_PROFIT_TARGET]); /* widen profit target */ } else if (conv_now < conv_entry * 0.4f) { /* Conviction dropped 60%+ → thesis collapsing → shorten plan to exit soon */ ps[PS_PLAN_TARGET_BARS] = fminf(ps[PS_PLAN_TARGET_BARS], hold_time + 3.0f); /* exit within 3 bars */ @@ -2397,7 +2397,7 @@ extern "C" __global__ void experience_env_step( if (plan_params_ptr != NULL && fabsf(reward) > 1e-8f) { float readiness_val = (readiness_ptr != NULL) ? readiness_ptr[0] : 0.0f; float conviction = (readiness_val >= 0.5f) - ? plan_params_ptr[i * 6 + 4] /* [0, 1] from plan head */ + ? plan_params_ptr[i * 6 + PLAN_PARAM_CONVICTION] /* [0, 1] from plan head */ : 1.0f; /* identity until plan matures */ reward *= conviction; } diff --git a/crates/ml/src/cuda_pipeline/state_layout.cuh b/crates/ml/src/cuda_pipeline/state_layout.cuh index 13b0e5eee..ebeb3fb3f 100644 --- a/crates/ml/src/cuda_pipeline/state_layout.cuh +++ b/crates/ml/src/cuda_pipeline/state_layout.cuh @@ -81,6 +81,19 @@ #define PLAN_ISV_REGIME_SHIFT 5 // |isv[11] − entry_regime_stability| #define PLAN_ISV_DIM 6 +// ──────────────────────────────────────────────────────────────────────────── +// Plan MLP output slot indices (plan_params[0..PLAN_PARAM_DIM=6)). +// Invariant 8: every slot has a named constant. +// Used for pp[N] and plan_params_ptr[i * 6 + N] access patterns. +// ──────────────────────────────────────────────────────────────────────────── +#define PLAN_PARAM_TARGET_BARS 0 // max hold bars +#define PLAN_PARAM_PROFIT_TARGET 1 // raw profit threshold % +#define PLAN_PARAM_STOP_LOSS 2 // raw stop loss threshold % +#define PLAN_PARAM_SCALE_AGGRESSION 3 // position ramp speed +#define PLAN_PARAM_CONVICTION 4 // position size fraction [0, 1] +#define PLAN_PARAM_ASYMMETRY 5 // profit/stop ratio +#define PLAN_PARAM_DIM 6 + // ── Compile-time checks ── static_assert(SL_PADDING_START + SL_PADDING_DIM == SL_STATE_DIM, "State layout dimensions must sum to SL_STATE_DIM"); diff --git a/docs/dqn-named-dims.md b/docs/dqn-named-dims.md index d0e845c95..808837d94 100644 --- a/docs/dqn-named-dims.md +++ b/docs/dqn-named-dims.md @@ -119,4 +119,5 @@ From the trade_plan MLP output. ## Commit history - Task 4A (ps[0..PS_STRIDE) constants): commit 144c85b85 -- Task 4B (plan_isv[0..6) constants): commit +- Task 4B (plan_isv[0..6) constants): commit 741cb48d5 +- Task 4C (plan_params[0..6) constants): commit