diff --git a/crates/ml/src/cuda_pipeline/experience_kernels.cu b/crates/ml/src/cuda_pipeline/experience_kernels.cu index b74d69f95..42051e5bb 100644 --- a/crates/ml/src/cuda_pipeline/experience_kernels.cu +++ b/crates/ml/src/cuda_pipeline/experience_kernels.cu @@ -1010,13 +1010,22 @@ extern "C" __global__ void experience_action_select( /* Output Q-gap for conviction-based position sizing. * Q-gap = Q(best_direction) - Q(Flat). Higher = more conviction. - * Used by env_step to scale target_position. */ + * Used by env_step to scale target_position. + * + * D7/N7: During contrarian override, the action selected is argmin rather + * than argmax. The raw Q(best) - Q(Flat) gap would mislead the position + * sizer into full conviction for what's actually the "deliberately wrong" + * action. Zero out the gap so contrarian trades size minimal. */ if (out_q_gaps != NULL) { - int flat_idx = 3; /* Flat = index 3 for direction(4): Short(0)/Hold(1)/Long(2)/Flat(3) */ - float q_best = q_b0[argmax_n(q_b0, b0_size)]; - float q_flat = q_b0[flat_idx]; - float gap = q_best - q_flat; - out_q_gaps[i] = (gap > 0.0f) ? gap : 0.0f; + if (contrarian_active != 0) { + out_q_gaps[i] = 0.0f; + } else { + int flat_idx = 3; /* Flat = index 3 for direction(4): Short(0)/Hold(1)/Long(2)/Flat(3) */ + float q_best = q_b0[argmax_n(q_b0, b0_size)]; + float q_flat = q_b0[flat_idx]; + float gap = q_best - q_flat; + out_q_gaps[i] = (gap > 0.0f) ? gap : 0.0f; + } } }