fix(sp11): plan_isv symmetric clamp — 4 sites mirror reward-cap bug

Implementer of 35db31089 (symmetric reward cap) flagged 4 additional
asymmetric-clamp sites in policy-input features (plan_isv slots),
mirroring the same bug class but on the feature side rather than
reward side:

  experience_kernels.cu:850   plan_isv[PNL_VS_TARGET] capped above only
  experience_kernels.cu:853   plan_isv[PNL_VS_STOP]   capped above only
  backtest_plan_kernel.cu:164 — mirror of 850
  backtest_plan_kernel.cu:168 — mirror of 853

unrealized P&L can be negative -> fminf(x, 2.0) leaves an unbounded
lower tail that produces feature jitter at the policy input, hurting
the network's state representation under losing-trade conditions.
Fix: symmetric clamp via fmaxf(-2.0, fminf(x, 2.0)).

Per pearl_symmetric_clamp_audit (added in close-out commit) and
pearl_bounded_modifier_outputs_require_structural_activation: any
spec-bounded scalar requires bilateral enforcement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-04 15:07:08 +02:00
parent 35db310893
commit 348f6078b8
3 changed files with 39 additions and 6 deletions

View File

@@ -160,13 +160,15 @@ extern "C" __global__ void backtest_plan_state_isv(
pisv[PLAN_ISV_PROGRESS] = (plan_tgt_bars > plan_thr)
? fminf(hold_time / plan_tgt_bars, 2.0f) : 0.0f;
/* [1] PnL vs profit target [bounded 2.0] */
/* [1] PnL vs profit target [symmetric bound -2.0..2.0]
* unrealized can be negative → fminf alone leaves unbounded lower tail.
* Mirror of SP11 symmetric reward cap (35db31089). */
pisv[PLAN_ISV_PNL_VS_TARGET] = (plan_profit > 1e-6f)
? fminf(unrealized / (plan_profit * equity + 1e-6f), 2.0f) : 0.0f;
? fmaxf(-2.0f, fminf(unrealized / (plan_profit * equity + 1e-6f), 2.0f)) : 0.0f;
/* [2] PnL vs stop (negative PnL vs stop → loss approaching) */
/* [2] PnL vs stop (negative PnL vs stop → loss approaching) [symmetric bound -2.0..2.0] */
pisv[PLAN_ISV_PNL_VS_STOP] = (plan_stop > 1e-6f)
? fminf(-unrealized / (plan_stop * equity + 1e-6f), 2.0f) : 0.0f;
? fmaxf(-2.0f, fminf(-unrealized / (plan_stop * equity + 1e-6f), 2.0f)) : 0.0f;
/* [3] Entry conviction (stable across the trade) */
pisv[PLAN_ISV_ENTRY_CONVICTION] = plan_conv_ent;

View File

@@ -846,11 +846,14 @@ extern "C" __global__ void experience_state_gather(
plan_isv[PLAN_ISV_PROGRESS] = (plan_tgt_bars > plan_thr_sg)
? fminf(f_hold_time / plan_tgt_bars, 2.0f) /* plan progress [0, 2] */
: 0.0f;
/* Symmetric clamp [-2, 2]: f_unrealized_pnl can be negative → fminf alone leaves
* an unbounded lower tail (feature jitter at policy input). Mirror of SP11
* symmetric reward cap (35db31089). See pearl_symmetric_clamp_audit. */
plan_isv[PLAN_ISV_PNL_VS_TARGET] = (plan_profit > 1e-6f)
? fminf(f_unrealized_pnl / (plan_profit * f_equity + 1e-6f), 2.0f) /* pnl vs target */
? fmaxf(-2.0f, fminf(f_unrealized_pnl / (plan_profit * f_equity + 1e-6f), 2.0f)) /* pnl vs target */
: 0.0f;
plan_isv[PLAN_ISV_PNL_VS_STOP] = (plan_stop > 1e-6f)
? fminf(-f_unrealized_pnl / (plan_stop * f_equity + 1e-6f), 2.0f) /* pnl vs stop */
? fmaxf(-2.0f, fminf(-f_unrealized_pnl / (plan_stop * f_equity + 1e-6f), 2.0f)) /* pnl vs stop */
: 0.0f;
plan_isv[PLAN_ISV_ENTRY_CONVICTION] = plan_conv; /* conviction at entry [0, 1] */

View File

@@ -5687,3 +5687,31 @@ new HEALTH_DIAG line, the follow-up commit will:
Per `feedback_no_partial_refactor`: instrumentation lands as one
atomic commit; removal lands as one atomic commit (with the fix).
## SP11 plan_isv symmetric clamp — 4 mirror sites (2026-05-04)
The implementer of `35db31089` (symmetric reward cap) flagged 4
additional asymmetric-clamp sites in `plan_isv` policy-input
features, mirroring the same bug class but on the feature side
rather than reward side:
| Site | Slot | Bug |
|---------------------------------------|-----------------------|----------------------------------------|
| `experience_kernels.cu:850` | `PLAN_ISV_PNL_VS_TARGET` | `fminf(.., 2.0)` capped above only |
| `experience_kernels.cu:853` | `PLAN_ISV_PNL_VS_STOP` | `fminf(.., 2.0)` capped above only |
| `backtest_plan_kernel.cu:164` | `PLAN_ISV_PNL_VS_TARGET` | mirror of 850 |
| `backtest_plan_kernel.cu:168` | `PLAN_ISV_PNL_VS_STOP` | mirror of 853 |
`unrealized` P&L can be negative (and `-unrealized` becomes negative
when the position is profitable). With only `fminf(x, 2.0)`, the
lower tail is unbounded, producing feature jitter at the policy
input under losing-trade conditions.
Fix: bilateral clamp via `fmaxf(-2.0, fminf(x, 2.0))`. These slots
feed the network state vector at line 956 of `experience_kernels.cu`
(consumer site) and at the `pisv` write-out in `backtest_plan_kernel`.
This is the policy-input mirror of the reward-side bug fixed in
`35db31089`. Both share the same architectural lesson — see
`pearl_symmetric_clamp_audit` (added in the SP11 close-out commit)
and `pearl_bounded_modifier_outputs_require_structural_activation`.