diff --git a/crates/ml/src/cuda_pipeline/backtest_env_kernel.cu b/crates/ml/src/cuda_pipeline/backtest_env_kernel.cu index 62ea60d83..6d5998338 100644 --- a/crates/ml/src/cuda_pipeline/backtest_env_kernel.cu +++ b/crates/ml/src/cuda_pipeline/backtest_env_kernel.cu @@ -261,14 +261,22 @@ extern "C" __global__ void backtest_env_step( // Re-encode position -> exposure_idx -> factored action to ensure metrics // kernel counts real position changes, not model-requested actions. { - float f_actual_frac = position / fmaxf(max_position, 0.01f); - int actual_exp_idx = (int)roundf((f_actual_frac + 1.0f) * 0.5f * (float)(b0_size - 1)); - if (actual_exp_idx < 0) actual_exp_idx = 0; - if (actual_exp_idx >= b0_size) actual_exp_idx = b0_size - 1; - // Preserve original order/urgency from model action - int orig_order = (b1_size > 0 && b2_size > 0) ? (action_val / b2_size) % b1_size : 0; - int orig_urgency = (b2_size > 0) ? action_val % b2_size : 0; - int actual_action = actual_exp_idx * b1_size * b2_size + orig_order * b2_size + orig_urgency; + /* Re-encode actual position as 4-branch factored action. + * Recover direction and magnitude from position, preserve order/urgency. */ + float pos_sign = position; + int actual_dir = (pos_sign > 0.001f) ? 2 : (pos_sign < -0.001f) ? 0 : 1; + float abs_pos = fabsf(position) / fmaxf(max_position, 0.01f); + int actual_mag; + if (abs_pos < 0.375f) actual_mag = 0; /* ~0.25 -> Quarter */ + else if (abs_pos < 0.75f) actual_mag = 1; /* ~0.50 -> Half */ + else actual_mag = 2; /* ~1.00 -> Full */ + if (actual_dir == 1) actual_mag = 0; /* Flat: magnitude irrelevant */ + int orig_order = decode_order_4b(action_val, b2_size, b3_size); + int orig_urgency = decode_urgency_4b(action_val, b3_size); + int actual_action = actual_dir * b1_size * b2_size * b3_size + + actual_mag * b2_size * b3_size + + orig_order * b3_size + + orig_urgency; actions_history[w * max_len + current_step] = actual_action; } }