Files
foxhunt/crates/ml-alpha/cuda/rl_inventory_beta_controller.cu
jgrusewski 285d42aa7b feat(rl): adaptive risk-management stack — 5 layers, all ISV-driven
Layered risk stack per spec docs/superpowers/specs/2026-05-30-adaptive-risk-management-design.md:

  Layer 1 (CMDP)        hard session-DD, cooldown, max-open, inventory limits
  Layer 2 (IQN τ)       risk-averse action selection adapts to session drawdown
  Layer 3 (Inventory)   Avellaneda-Stoikov penalty β scales with reward magnitude
                        and inventory variance
  Layer 4 (Kelly)       half-Kelly fraction sizing from observed win-rate +
                        R-multiple, warmup-gated until cumulative_dones >= 1000
  Layer D (Trail)       wire dead a7/a8 (TrailTighten/Loosen) via independent
                        ISV factors, replacing the symmetric reciprocal

Architecture: every threshold ISV-driven (22 new slots, RL_SLOTS_END 662→684).
Every adaptive bound follows the canonical Wiener-α blend with floor 0.4,
sentinel-zero bootstrap, and asymmetric Schulman where applicable.

Kernels:
  rl_cmdp_constraints_check        session pnl + cooldown + consec-loss tracking
  rl_iqn_action_tau_controller     τ = clamp(0.5 - 5·dd_frac, τ_min, 1.0)
  rl_inventory_beta_controller     β_target = 0.01·E|reward| / (2·σ_inventory)
  rl_kelly_fraction_controller     f = clamp(safety · (p·b - q)/b, 0, 1)
  rl_win_rate_ema_update           closed-trade win-rate EMA from rewards + dones
  rl_avg_win_loss_ema_update       separate avg-win and avg-loss EMAs
  rl_inventory_variance_update     Welford variance of net-position-per-batch

Integration:
- All 7 new cubins loaded in IntegratedTrainer + launched per step in spec order
  (CMDP after reward pipeline, before actions_to_market_targets reads override
  flags; Layer 2/3/4 controllers in rl_fused_controllers.cu).
- actions_to_market_targets.cu: Layer 1 hard overrides (DD-triggered → 0 lots;
  cooldown → 0 lots; max-open → block opening actions; inventory cap → block
  one-sided expansion) and Layer 4 Kelly fraction scaling on target lots.
- rl_fused_reward_pipeline.cu: Layer 3 inventory penalty term in reward shaping.
- rl_trail_mutate.cu: a7 multiplies trail by RL_TRAIL_TIGHTEN_FACTOR_INDEX,
  a8 by RL_TRAIL_LOOSEN_FACTOR_INDEX (was symmetric reciprocal — pearls
  pearl_dead_trail_stop_actions_a7_a8).

Validation:
- 12 GPU-oracle invariants pass (tests/risk_stack_invariants.rs):
  G1-G4 CMDP, G5-G7 IQN τ, G8-G9 inventory β, G10-G12 Kelly.
- 20/20 trade_management_kernels.rs tests pass (a7/a8 migrated).
- 5/5 controller_adaptive_floors.rs tests still pass.
- integrated_trainer_smoke passes (end-to-end pipeline launch).
- Local 1k smoke b=128: completes 1000/1000 steps, no NaN, controllers steady.
- compute-sanitizer memcheck (5 steps b=128): ERROR SUMMARY: 0 errors.

Plan: docs/superpowers/plans/2026-05-30-adaptive-risk-management-plan.md
2026-05-30 20:52:28 +02:00

58 lines
2.3 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_inventory_beta_controller.cu — Layer 3: adaptive inventory penalty β.
//
// Spec: docs/superpowers/specs/2026-05-30-adaptive-risk-management-design.md
//
// Scales the inventory penalty `reward_shaped = reward_raw - β × |net_pos|`
// so that at ~2σ inventory the penalty is ~1% of typical reward magnitude.
//
// β_target = 0.01 × E[|reward|] / (2 × σ_inventory)
//
// Sentinel-zero bootstrap per `pearl_first_observation_bootstrap`: hold β
// at 0 until BOTH reward magnitude EMA and inventory variance EMA have
// real data. After bootstrap, Wiener-α blend with floor 0.4 per
// `pearl_wiener_alpha_floor_for_nonstationary`.
//
// Per `feedback_no_atomicadd`: single-thread single-block.
// Per `feedback_cpu_is_read_only`: pure device kernel.
// Per `feedback_isv_for_adaptive_bounds`: every threshold via ISV slot.
#include <stdint.h>
#define RL_INVENTORY_PENALTY_BETA_INDEX 674
#define RL_INVENTORY_VARIANCE_EMA_INDEX 675
#define RL_REWARD_MAGNITUDE_EMA_INDEX 614
#define RL_WIENER_ALPHA_FLOOR_INDEX 659
// 1% of typical reward magnitude at 2σ inventory — Avellaneda-Stoikov
// canonical "noticeable but not dominant" calibration. Structural ratio.
#define BETA_TARGET_FRAC_OF_REWARD 0.01f
#define BETA_SIGMA_MULTIPLIER 2.0f
extern "C" __global__ void rl_inventory_beta_controller(
float* __restrict__ isv
) {
if (threadIdx.x != 0 || threadIdx.y != 0 || threadIdx.z != 0) return;
if (blockIdx.x != 0 || blockIdx.y != 0 || blockIdx.z != 0) return;
const float reward_mag = isv[RL_REWARD_MAGNITUDE_EMA_INDEX];
const float inv_var = isv[RL_INVENTORY_VARIANCE_EMA_INDEX];
// Dead-signal hold: need real data on both inputs.
if (inv_var <= 0.0f || reward_mag <= 0.0f) return;
const float inv_std = sqrtf(inv_var);
const float beta_target = BETA_TARGET_FRAC_OF_REWARD * reward_mag
/ (BETA_SIGMA_MULTIPLIER * inv_std);
const float prev = isv[RL_INVENTORY_PENALTY_BETA_INDEX];
const float a_floor = isv[RL_WIENER_ALPHA_FLOOR_INDEX];
if (prev == 0.0f) {
// First-observation bootstrap.
isv[RL_INVENTORY_PENALTY_BETA_INDEX] = beta_target;
} else {
isv[RL_INVENTORY_PENALTY_BETA_INDEX] =
(1.0f - a_floor) * prev + a_floor * beta_target;
}
}