Move all step_synthetic/dqn_replay_step alloc_zeros to persistent trainer fields (ss_* prefix). Enables CUDA Graph capture of the replay training step — all device pointers are now stable across steps. Introduces reduce_axis0_free() to resolve borrow-checker E0502 when both source (per-batch scratch) and destination (reduced grad) are self fields passed to the same function. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
110 lines
4.5 KiB
Plaintext
110 lines
4.5 KiB
Plaintext
// rl_multires_features_update.cu — per-batch multi-resolution features
|
||
// derived from real-time timestamp deltas across 3 horizons.
|
||
//
|
||
// Maintains streaming EMA state per (batch, horizon, feature) and
|
||
// outputs 12 normalized features [B × 3 × 4]:
|
||
// per horizon h ∈ {1s, 10s, 600s}:
|
||
// [0] price_change = EMA of (mid - prev_mid) / σ_price
|
||
// [1] vol_realized = sqrt(EMA of (mid - prev_mid)²)
|
||
// [2] oflow_imb = EMA of (bid_sz[0] - ask_sz[0]) / (bid_sz[0] + ask_sz[0])
|
||
// [3] trade_burst = EMA of event_rate (1/dt_seconds, capped at 1000)
|
||
//
|
||
// The EMA alpha per horizon = dt / horizon_seconds (time-weighted
|
||
// exponential: a 1-second event on a 10s horizon contributes α=0.1).
|
||
// This gives identical time-constant semantics to a circular buffer
|
||
// with O(1) state per feature.
|
||
//
|
||
// State buffers (persistent across steps):
|
||
// multires_state_d [B × 3 × 4]: running EMA values
|
||
// prev_mid_d [B]: previous step's mid price
|
||
// prev_ts_ns_d [B]: previous step's timestamp (for dt computation)
|
||
//
|
||
// One thread per batch. No shared memory.
|
||
// Per `feedback_no_atomicadd`, `feedback_cpu_is_read_only`.
|
||
|
||
#include <stdint.h>
|
||
|
||
#define N_HORIZONS 3
|
||
#define N_FEATURES 4
|
||
#define MULTIRES_DIM (N_HORIZONS * N_FEATURES)
|
||
|
||
#define RL_MULTIRES_HORIZON_1_INDEX 521
|
||
|
||
extern "C" __global__ void rl_multires_features_update(
|
||
float* __restrict__ multires_state, // [B × MULTIRES_DIM] IN/OUT
|
||
float* __restrict__ multires_output, // [B × MULTIRES_DIM] OUT (normalized)
|
||
float* __restrict__ prev_mid, // [B] IN/OUT
|
||
unsigned long long* __restrict__ prev_ts_ns, // [B] IN/OUT
|
||
const float* __restrict__ bid_px, // [BOOK_LEVELS]
|
||
const float* __restrict__ ask_px, // [BOOK_LEVELS]
|
||
const float* __restrict__ bid_sz, // [BOOK_LEVELS]
|
||
const float* __restrict__ ask_sz, // [BOOK_LEVELS]
|
||
const float* __restrict__ isv,
|
||
const unsigned long long* __restrict__ current_ts_ns_ptr,
|
||
int b_size
|
||
) {
|
||
const int b = blockIdx.x * blockDim.x + threadIdx.x;
|
||
if (b >= b_size) return;
|
||
|
||
const float mid = 0.5f * (bid_px[0] + ask_px[0]);
|
||
const float old_mid = prev_mid[b];
|
||
const unsigned long long current_ts_ns = current_ts_ns_ptr[0];
|
||
const unsigned long long old_ts = prev_ts_ns[b];
|
||
|
||
// dt in seconds (protect against zero/backward timestamps).
|
||
const float dt = (current_ts_ns > old_ts)
|
||
? (float)(current_ts_ns - old_ts) * 1e-9f
|
||
: 1e-6f;
|
||
|
||
const float price_delta = mid - old_mid;
|
||
const float best_bid_sz = bid_sz[0];
|
||
const float best_ask_sz = ask_sz[0];
|
||
const float total_sz = best_bid_sz + best_ask_sz + 1e-8f;
|
||
const float imbalance = (best_bid_sz - best_ask_sz) / total_sz;
|
||
const float event_rate = fminf(1.0f / (dt + 1e-8f), 1000.0f);
|
||
|
||
const int base = b * MULTIRES_DIM;
|
||
|
||
for (int h = 0; h < N_HORIZONS; ++h) {
|
||
const float horizon_s = isv[RL_MULTIRES_HORIZON_1_INDEX + h];
|
||
const float alpha = fminf(dt / (horizon_s + 1e-8f), 1.0f);
|
||
const float one_minus_alpha = 1.0f - alpha;
|
||
|
||
const int off = base + h * N_FEATURES;
|
||
|
||
// EMA updates.
|
||
float ema_price = multires_state[off + 0];
|
||
float ema_vol_sq = multires_state[off + 1];
|
||
float ema_imb = multires_state[off + 2];
|
||
float ema_burst = multires_state[off + 3];
|
||
|
||
// First-observation bootstrap (sentinel = 0 for all EMAs).
|
||
if (old_ts == 0ULL) {
|
||
ema_price = price_delta;
|
||
ema_vol_sq = price_delta * price_delta;
|
||
ema_imb = imbalance;
|
||
ema_burst = event_rate;
|
||
} else {
|
||
ema_price = one_minus_alpha * ema_price + alpha * price_delta;
|
||
ema_vol_sq = one_minus_alpha * ema_vol_sq + alpha * (price_delta * price_delta);
|
||
ema_imb = one_minus_alpha * ema_imb + alpha * imbalance;
|
||
ema_burst = one_minus_alpha * ema_burst + alpha * event_rate;
|
||
}
|
||
|
||
multires_state[off + 0] = ema_price;
|
||
multires_state[off + 1] = ema_vol_sq;
|
||
multires_state[off + 2] = ema_imb;
|
||
multires_state[off + 3] = ema_burst;
|
||
|
||
// Normalized output.
|
||
const float vol = sqrtf(ema_vol_sq + 1e-8f);
|
||
multires_output[off + 0] = ema_price / (vol + 1e-8f);
|
||
multires_output[off + 1] = vol;
|
||
multires_output[off + 2] = ema_imb;
|
||
multires_output[off + 3] = ema_burst / 100.0f;
|
||
}
|
||
|
||
prev_mid[b] = mid;
|
||
prev_ts_ns[b] = current_ts_ns;
|
||
}
|