From 787418e779158bc1f70d57c65a1d74884a235c5c Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Tue, 26 May 2026 00:24:48 +0200 Subject: [PATCH] =?UTF-8?q?fix(rl):=20trail=20distance=20units=20mismatch?= =?UTF-8?q?=20=E2=80=94=20was=20400=C3=97=20too=20small?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trail_distance was computed from mean_abs_pnl (scaled reward space) but compared against mid-entry (price space). At reward_scale=0.0024, the trail was 0.002 price units = effectively zero, causing immediate trail-stop on every trade regardless of min_hold. Fix: divide by reward_scale to recover price-space magnitude: trail = k_init × (mean_abs_pnl / reward_scale) This gives trail ≈ 0.8 ticks at k_init=2.0 — reasonable for ES. avg_hold should now respect min_hold (100 steps) since trail stops won't fire on the first tick. Co-Authored-By: Claude Opus 4.7 --- crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu | 9 ++++++--- crates/ml-alpha/cuda/rl_unit_state_update.cu | 8 +++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu b/crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu index 5cff5f074..777528831 100644 --- a/crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu +++ b/crates/ml-alpha/cuda/rl_fused_reward_pipeline.cu @@ -31,6 +31,7 @@ // ISV slot indices (must match crates/ml-alpha/src/rl/isv_slots.rs) #define RL_TRAIL_K_INIT_INDEX 496 #define RL_MEAN_ABS_PNL_EMA_INDEX 423 +#define RL_REWARD_SCALE_INDEX 406 #define RL_PYRAMID_ADD_COUNT_INDEX 507 #define RL_STEP_COUNTER_ISV_INDEX 548 #define RL_ENTRY_COST_INDEX 532 @@ -104,9 +105,11 @@ extern "C" __global__ void rl_fused_reward_pipeline( const int current_step = (int)isv[RL_STEP_COUNTER_ISV_INDEX]; const int unit_prev = unit_prev_pos_lots[b]; - const float k_init = isv[RL_TRAIL_K_INIT_INDEX]; - const float mean_abs_pnl = isv[RL_MEAN_ABS_PNL_EMA_INDEX]; - const float bootstrap_d = k_init * (mean_abs_pnl + 1e-8f); + const float k_init = isv[RL_TRAIL_K_INIT_INDEX]; + const float mean_abs_pnl = isv[RL_MEAN_ABS_PNL_EMA_INDEX]; + const float reward_scale = isv[RL_REWARD_SCALE_INDEX]; + const float raw_magnitude = mean_abs_pnl / fmaxf(reward_scale, 1e-9f); + const float bootstrap_d = k_init * (raw_magnitude + 1e-8f); const int base = b * MAX_UNITS; diff --git a/crates/ml-alpha/cuda/rl_unit_state_update.cu b/crates/ml-alpha/cuda/rl_unit_state_update.cu index 2f19bd818..78ba0bbbd 100644 --- a/crates/ml-alpha/cuda/rl_unit_state_update.cu +++ b/crates/ml-alpha/cuda/rl_unit_state_update.cu @@ -26,6 +26,7 @@ #define MAX_UNITS 4 #define RL_TRAIL_K_INIT_INDEX 496 #define RL_MEAN_ABS_PNL_EMA_INDEX 423 +#define RL_REWARD_SCALE_INDEX 406 #define RL_PYRAMID_ADD_COUNT_INDEX 507 #define RL_STEP_COUNTER_ISV_INDEX 548 @@ -60,7 +61,12 @@ extern "C" __global__ void rl_unit_state_update( const float k_init = isv[RL_TRAIL_K_INIT_INDEX]; const float mean_abs_pnl = isv[RL_MEAN_ABS_PNL_EMA_INDEX]; - const float bootstrap_d = k_init * (mean_abs_pnl + 1e-8f); + const float reward_scale = isv[RL_REWARD_SCALE_INDEX]; + // mean_abs_pnl is in SCALED reward space. Convert to price space + // by dividing out the reward_scale: raw_magnitude ≈ scaled / scale. + // Trail distance must be in price units (compared against mid-entry). + const float raw_magnitude = mean_abs_pnl / fmaxf(reward_scale, 1e-9f); + const float bootstrap_d = k_init * (raw_magnitude + 1e-8f); const int base = b * MAX_UNITS;