feat(cuda): hard margin floor — force Hold when equity < $20k drawdown

Broker hard constraint: $35k capital, ES maintenance margin ~$14.4k.
If cumulative realized_pnl drops below -$20k, ALL trading actions
are overridden to Hold (if flat) or FlatL/FlatS (if positioned).
ISV-driven at slot 587 (RL_MARGIN_FLOOR_USD_INDEX, bootstrap 20000).

Fires FIRST in the confidence gate — before warmup check, before
exploration slots, before everything. The broker will liquidate at
margin call; this prevents the model from reaching that point.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-28 01:47:41 +02:00
parent a9c74bac33
commit 05bee20adb
3 changed files with 32 additions and 2 deletions

View File

@@ -34,6 +34,7 @@
#define RL_HOLD_TARGET_FRAC_INDEX 575
#define RL_HOLD_FRAC_EMA_INDEX 576
#define RL_CONF_GATE_MAX_HOLD_FRAC_INDEX 584
#define RL_MARGIN_FLOOR_USD_INDEX 587
#define THRESHOLD_MIN 0.05f
#define THRESHOLD_MAX 0.95f
#define HOLD_EMA_ALPHA 0.1f
@@ -51,6 +52,26 @@ extern "C" __global__ void rl_confidence_gate(
const int b = blockIdx.x;
if (b >= b_size) return;
// ── Margin floor: force Hold when cumulative loss exceeds limit.
// Reads realized_pnl from PosFlat (offset 8). If the sim has lost
// more than the margin floor, ALL non-Hold actions become Hold.
// This is a HARD constraint — broker will liquidate at margin call.
// Fires before warmup check, exploration slots, everything.
{
const unsigned char* p = pos_state + b * pos_bytes;
const float realized = *(const float*)(p + 8);
const float margin_floor = isv[RL_MARGIN_FLOOR_USD_INDEX];
if (realized < -margin_floor) {
const int lots = *(const int*)(p + 0);
if (lots != 0) {
actions[b] = (lots > 0) ? 3 : 4; // force flat
} else {
actions[b] = ACTION_HOLD; // stay flat, don't re-enter
}
return;
}
}
const int current_step = (int)isv[RL_STEP_COUNTER_ISV_INDEX];
const int warmup = (int)isv[RL_GATE_WARMUP_STEPS_INDEX];
if (current_step < warmup) return;

View File

@@ -1127,5 +1127,11 @@ pub const RL_DONE_WIN_MAGNITUDE_EMA_INDEX: usize = 585;
/// per-step tail maxes.
pub const RL_DONE_LOSS_MAGNITUDE_EMA_INDEX: usize = 586;
/// Maximum allowed drawdown in USD before force-Hold (margin floor).
/// $35k capital, ES maintenance margin ~$14.4k → max drawdown ~$20k.
/// The confidence gate reads realized_pnl from PosFlat — if loss
/// exceeds this, ALL non-Hold actions are overridden to Hold.
pub const RL_MARGIN_FLOOR_USD_INDEX: usize = 587;
/// Last RL-allocated slot index (exclusive).
pub const RL_SLOTS_END: usize = 587;
pub const RL_SLOTS_END: usize = 588;

View File

@@ -2673,7 +2673,7 @@ impl IntegratedTrainer {
// (slot, value) pair — pure device write, no HtoD per
// `feedback_no_htod_htoh_only_mapped_pinned`.
{
let isv_constants: [(usize, f32); 111] = [
let isv_constants: [(usize, f32); 112] = [
// Static seeds for the adaptive reward-clamp controller —
// these are the initial values that
// `rl_reward_clamp_controller` will replace once it
@@ -2840,6 +2840,9 @@ impl IntegratedTrainer {
(crate::rl::isv_slots::RL_SAC_ALPHA_INDEX, 0.01),
(crate::rl::isv_slots::RL_SAC_ENTROPY_TARGET_INDEX, 1.68),
(crate::rl::isv_slots::RL_CONF_GATE_MAX_HOLD_FRAC_INDEX, 0.85),
// Hard margin floor: $35k capital, ~$14.4k ES maintenance.
// Max drawdown ~$20k before broker liquidates.
(crate::rl::isv_slots::RL_MARGIN_FLOOR_USD_INDEX, 20000.0),
];
for (slot, value) in isv_constants.iter() {
let slot_i32 = *slot as i32;