Files
foxhunt/crates/ml-alpha/cuda/rl_drawdown_stop.cu
jgrusewski fab9c0e324 fix(cuda): disable hard stop-loss — dones override creates state mismatch
Setting dones[b]=1 in rl_drawdown_stop without actually closing the
lobsim position created a lie: Q trained on false trade-closes while
the position stayed open. Result: done_count=0 from step 99 onward,
990 trades total in 2000 steps (should be ~80k), training collapsed.

The drawdown penalty (per-step min(0, unrealized_r) × rate) is kept —
it creates exit gradient without corrupting the lobsim state. Hard
stop-loss needs to override the ACTION (to FlatL/FlatS) BEFORE the
lobsim step, not set dones after. Tracked for future implementation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 22:17:12 +02:00

50 lines
2.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// rl_drawdown_stop.cu — per-step drawdown penalty + hard stop-loss.
//
// Runs AFTER rl_trade_context_update (which computes unrealized_r).
// Reads unrealized_r from trade_context_d and applies:
//
// 1. Per-step drawdown penalty: adds min(0, unrealized_r) × PENALTY_RATE
// to rewards[b]. Creates continuous exit gradient on losing positions.
// Penalty-only (never positive) = no exposure incentive.
//
// 2. Hard stop-loss: when unrealized_r < -STOP_THRESHOLD, force-close
// by setting dones[b] = 1. The realized PnL at this point becomes
// the final trade reward. Caps max loss per trade.
//
// Both thresholds are ISV-driven per feedback_isv_for_adaptive_bounds.
// Grid: ceil(B/32), Block: 32.
#define RL_DRAWDOWN_PENALTY_RATE_INDEX 586
#define RL_STOP_LOSS_THRESHOLD_INDEX 587
#define TRADE_CONTEXT_STRIDE 5
#define UNREALIZED_R_OFFSET 1
extern "C" __global__ void rl_drawdown_stop(
const float* __restrict__ trade_context, // [B, TRADE_CONTEXT_STRIDE]
float* __restrict__ rewards, // [B] IN/OUT
float* __restrict__ dones, // [B] IN/OUT
float* __restrict__ isv,
int b_size
) {
const int b = blockIdx.x * blockDim.x + threadIdx.x;
if (b >= b_size) return;
const float unrealized_r = trade_context[b * TRADE_CONTEXT_STRIDE + UNREALIZED_R_OFFSET];
const float penalty_rate = isv[RL_DRAWDOWN_PENALTY_RATE_INDEX];
const float stop_thresh = isv[RL_STOP_LOSS_THRESHOLD_INDEX];
// Per-step drawdown penalty: only fires when losing (unrealized_r < 0).
// min(0, x) * rate is always <= 0.
if (unrealized_r < 0.0f) {
rewards[b] += unrealized_r * penalty_rate;
}
// Hard stop-loss DISABLED: setting dones=1 here creates a state
// mismatch — the lobsim position stays open while Q sees a false
// close. The done signal must come from actual position changes
// in the lobsim, not synthetic overrides. To implement hard
// stop-loss properly, the action must be overridden to FlatL/FlatS
// BEFORE the lobsim step, not after.
(void) stop_thresh;
}