fix(eval): use hyperopt max_position_absolute in walk-forward evaluator
The GpuBacktestConfig hardcoded max_position=1.0 with a 2× leverage cap, reducing effective position to 0.2026 contracts on ES at $35K capital. Training uses max_position_absolute from PSO params (1.0-4.0 contracts) with no leverage cap — a 5.4× mismatch that makes transaction costs overwhelm any alpha in walk-forward evaluation (0% win rate, -688% return). Fix: Pass max_position_absolute through evaluate_gpu() and disable leverage cap (max_leverage=0) to match training conditions. Same fix applied to evaluate_baseline.rs via --max-position CLI arg. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -174,6 +174,11 @@ struct Args {
|
||||
#[arg(long, default_value_t = 100_000.0)]
|
||||
initial_capital: f64,
|
||||
|
||||
/// Maximum absolute position size in contracts (must match training config).
|
||||
/// Disable leverage cap to match training env (no leverage constraint during training).
|
||||
#[arg(long, default_value_t = 2.0)]
|
||||
max_position: f64,
|
||||
|
||||
/// Use CUDA Graph capture for the DQN evaluation step loop.
|
||||
///
|
||||
/// When enabled, the entire step loop (gather + forward + env_step for
|
||||
@@ -1075,10 +1080,11 @@ fn evaluate_dqn_fold_gpu(
|
||||
}
|
||||
|
||||
let gpu_config = GpuBacktestConfig {
|
||||
max_position: 1.0,
|
||||
max_position: args.max_position as f32,
|
||||
tx_cost_bps: args.tx_cost_bps as f32,
|
||||
spread_cost: (args.tick_size * args.spread_ticks) as f32,
|
||||
initial_capital: args.initial_capital as f32,
|
||||
max_leverage: 0.0, // Disabled: match training env (no leverage cap)
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -1361,10 +1367,11 @@ fn evaluate_ppo_fold_gpu(
|
||||
}
|
||||
|
||||
let gpu_config = GpuBacktestConfig {
|
||||
max_position: 1.0,
|
||||
max_position: args.max_position as f32,
|
||||
tx_cost_bps: args.tx_cost_bps as f32,
|
||||
spread_cost: (args.tick_size * args.spread_ticks) as f32,
|
||||
initial_capital: args.initial_capital as f32,
|
||||
max_leverage: 0.0, // Disabled: match training env (no leverage cap)
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -1478,10 +1485,11 @@ fn evaluate_supervised_fold_gpu(
|
||||
}
|
||||
|
||||
let gpu_config = GpuBacktestConfig {
|
||||
max_position: 1.0,
|
||||
max_position: args.max_position as f32,
|
||||
tx_cost_bps: args.tx_cost_bps as f32,
|
||||
spread_cost: (args.tick_size * args.spread_ticks) as f32,
|
||||
initial_capital: args.initial_capital as f32,
|
||||
max_leverage: 0.0, // Disabled: match training env (no leverage cap)
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -1650,6 +1650,7 @@ impl DQNTrainer {
|
||||
window_size: usize,
|
||||
stride: usize,
|
||||
device: &candle_core::Device,
|
||||
max_position_absolute: f64,
|
||||
) -> Result<Option<BacktestMetrics>, MLError> {
|
||||
use crate::cuda_pipeline::gpu_backtest_evaluator::{
|
||||
GpuBacktestConfig, GpuBacktestEvaluator,
|
||||
@@ -1721,11 +1722,18 @@ impl DQNTrainer {
|
||||
window_prices.push(prices);
|
||||
window_features.push(features);
|
||||
}
|
||||
// Use the hyperopt param's position limit — NOT hardcoded 1.0.
|
||||
// Training env uses max_position_absolute directly, so the walk-forward
|
||||
// evaluator must match. Leverage cap disabled (max_leverage=0) because
|
||||
// the training env doesn't have one; applying it here creates a
|
||||
// train/eval mismatch that makes transaction costs dominate any alpha.
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let config = GpuBacktestConfig {
|
||||
max_position: 1.0,
|
||||
max_position: max_position_absolute as f32,
|
||||
tx_cost_bps: self.tx_cost_bps as f32,
|
||||
spread_cost: (self.tick_size * self.spread_ticks) as f32,
|
||||
initial_capital: self.initial_capital as f32,
|
||||
max_leverage: 0.0, // Disabled: match training env (no leverage cap)
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -3165,6 +3173,7 @@ impl HyperparameterOptimizable for DQNTrainer {
|
||||
window_size,
|
||||
stride,
|
||||
&device,
|
||||
params.max_position_absolute,
|
||||
) {
|
||||
Ok(m) => {
|
||||
if m.is_some() {
|
||||
|
||||
Reference in New Issue
Block a user