The winner-ratchet condition (unrealized > initial_r) never fired because typical price moves rarely exceed 100% of the initial stop distance. Win/loss ratio measured at 1.01× (symmetric) despite the asymmetric decay being active. Fix: ISV-driven threshold factor (slot 546, default 0.25). Trail starts ratcheting when profit reaches 25% of initial_r — much more achievable. At 25%: a $12.50 initial_r only needs $3.12 profit before the winner-tracking activates. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
97 lines
3.9 KiB
Plaintext
97 lines
3.9 KiB
Plaintext
// rl_asymmetric_trail_decay.cu — structural asymmetric trail management.
|
||
//
|
||
// Runs EVERY STEP for each active unit. Automatically adjusts trail
|
||
// distance based on unrealized P&L direction:
|
||
//
|
||
// LOSING (mid moved against entry):
|
||
// trail *= loss_decay_rate per step (default 0.995)
|
||
// → halves in ~139 steps (~35 seconds)
|
||
// "Cut losers fast" — the longer a trade stays underwater,
|
||
// the tighter the stop gets, accelerating the exit.
|
||
//
|
||
// WINNING (unrealized > initial_r):
|
||
// trail = max(trail, unrealized_profit × win_trail_factor)
|
||
// → trail ratchets up with profit, never below current level
|
||
// "Let winners run" — the stop tracks 50% of open profit,
|
||
// locking in gains while giving room for continuation.
|
||
//
|
||
// NEUTRAL (between 0 and initial_r):
|
||
// trail unchanged — in the "proving zone" where the trade
|
||
// hasn't yet earned the right to a wider stop.
|
||
//
|
||
// This produces asymmetric P&L without the agent needing to LEARN
|
||
// when to tighten vs loosen — it's a structural edge baked into
|
||
// the mechanics. The agent's a7/a8 trail actions provide additional
|
||
// fine-tuning on top.
|
||
//
|
||
// Runs BEFORE rl_trail_stop_check so the updated trail distances
|
||
// are immediately used for breach detection.
|
||
//
|
||
// Per `feedback_no_atomicadd`: per-batch per-unit element-wise.
|
||
// Per `feedback_cpu_is_read_only`: pure device-side.
|
||
|
||
#include <stdint.h>
|
||
|
||
#define MAX_UNITS 4
|
||
#define RL_ASYM_LOSS_DECAY_RATE_INDEX 537
|
||
#define RL_ASYM_WIN_TRAIL_FACTOR_INDEX 538
|
||
#define RL_ASYM_WIN_THRESHOLD_INDEX 546
|
||
#define RL_TRAIL_MIN_INDEX 494
|
||
|
||
extern "C" __global__ void rl_asymmetric_trail_decay(
|
||
float* __restrict__ unit_trail_distance, // [B × MAX_UNITS] IN/OUT
|
||
const unsigned char* __restrict__ unit_active, // [B × MAX_UNITS]
|
||
const float* __restrict__ unit_entry_price, // [B × MAX_UNITS]
|
||
const float* __restrict__ unit_initial_r, // [B × MAX_UNITS]
|
||
const int* __restrict__ unit_lots, // [B × MAX_UNITS]
|
||
const float* __restrict__ bid_px, // [BOOK_LEVELS]
|
||
const float* __restrict__ ask_px, // [BOOK_LEVELS]
|
||
const float* __restrict__ isv,
|
||
int b_size
|
||
) {
|
||
const int b = blockIdx.x;
|
||
const int u = threadIdx.x;
|
||
if (b >= b_size || u >= MAX_UNITS) return;
|
||
|
||
const int idx = b * MAX_UNITS + u;
|
||
if (unit_active[idx] == 0) return;
|
||
|
||
const int lots = unit_lots[idx];
|
||
if (lots == 0) return;
|
||
|
||
const float entry = unit_entry_price[idx];
|
||
const float trail = unit_trail_distance[idx];
|
||
const float init_r = unit_initial_r[idx];
|
||
const float mid = 0.5f * (bid_px[0] + ask_px[0]);
|
||
|
||
const float loss_decay = isv[RL_ASYM_LOSS_DECAY_RATE_INDEX];
|
||
const float win_factor = isv[RL_ASYM_WIN_TRAIL_FACTOR_INDEX];
|
||
const float win_threshold = isv[RL_ASYM_WIN_THRESHOLD_INDEX];
|
||
const float trail_min = isv[RL_TRAIL_MIN_INDEX];
|
||
|
||
// Unrealized P&L in price units (positive = profitable).
|
||
const float direction = (lots > 0) ? 1.0f : -1.0f;
|
||
const float unrealized = direction * (mid - entry);
|
||
|
||
float new_trail = trail;
|
||
|
||
if (unrealized < 0.0f) {
|
||
// LOSING: tighten every step.
|
||
new_trail = trail * loss_decay;
|
||
} else if (unrealized > init_r * win_threshold) {
|
||
// WINNING beyond initial R: ratchet trail to track profit.
|
||
// Trail = max(current_trail, profit × win_factor).
|
||
// Never shrinks on winners — only grows.
|
||
const float profit_trail = unrealized * win_factor;
|
||
if (profit_trail > new_trail) {
|
||
new_trail = profit_trail;
|
||
}
|
||
}
|
||
// NEUTRAL (0 to init_r): trail unchanged — proving zone.
|
||
|
||
// Floor at trail_min.
|
||
new_trail = fmaxf(new_trail, trail_min);
|
||
|
||
unit_trail_distance[idx] = new_trail;
|
||
}
|