diff --git a/docs/plans/2026-03-11-cuda-backtest-gpu-residency-plan.md b/docs/plans/2026-03-11-cuda-backtest-gpu-residency-plan.md index 198751506..27b089e99 100644 --- a/docs/plans/2026-03-11-cuda-backtest-gpu-residency-plan.md +++ b/docs/plans/2026-03-11-cuda-backtest-gpu-residency-plan.md @@ -2028,16 +2028,16 @@ bitonic sort. Single download of extended WindowMetrics struct." Replace CPU backtest path in the standalone evaluation binary. **Files:** -- Modify: `bin/fxt/src/commands/evaluate_baseline.rs` (the evaluate_baseline binary entry point) -- Test: `SQLX_OFFLINE=true cargo check -p fxt --features cuda` +- Modify: `crates/ml/examples/evaluate_baseline.rs` (declared as `[[example]]` in `crates/ml/Cargo.toml:230`) +- Test: `SQLX_OFFLINE=true cargo check -p ml --example evaluate_baseline` - [ ] **Step 1: Add GPU evaluation path to evaluate_baseline** -In `bin/fxt/src/commands/evaluate_baseline.rs`, at the evaluation entry point where the +In `crates/ml/examples/evaluate_baseline.rs`, at the evaluation entry point where the `EvaluationEngine` is currently constructed, add a GPU path before the CPU path: ```rust -use crate::cuda_pipeline::gpu_backtest_evaluator::{GpuBacktestEvaluator, GpuBacktestConfig}; +use ml::cuda_pipeline::gpu_backtest_evaluator::{GpuBacktestEvaluator, GpuBacktestConfig}; // Try GPU evaluation first #[cfg(feature = "cuda")] @@ -2076,13 +2076,13 @@ if let Ok(device) = Device::new_cuda(0) { - [ ] **Step 2: Run check** -Run: `SQLX_OFFLINE=true cargo check -p fxt` +Run: `SQLX_OFFLINE=true cargo check -p ml --example evaluate_baseline` Expected: PASS (compiles with and without cuda feature) - [ ] **Step 3: Commit** ```bash -git add bin/fxt/src/commands/evaluate_baseline.rs +git add crates/ml/examples/evaluate_baseline.rs git commit -m "feat(eval): wire GPU backtester into evaluate_baseline binary Standalone evaluation now uses CUDA backtest kernel when available. @@ -2109,6 +2109,8 @@ use ml::cuda_pipeline::gpu_backtest_evaluator::{ GpuBacktestConfig, GpuBacktestEvaluator, WindowMetrics, }; use ml_dqn::evaluation::engine::EvaluationEngine; +use ml_dqn::evaluation::metrics::OHLCVBarF32; +use ml_core::common::action::FactoredAction; use candle_core::{Device, Tensor, DType}; /// Generate deterministic synthetic price data (random walk with drift). @@ -2184,15 +2186,22 @@ async fn test_gpu_vs_cpu_backtest_agreement() { ).expect("GPU evaluation"); // 3. Run CPU backtest with same always-Flat action + use ml_core::common::action::{ExposureLevel, OrderType, Urgency}; + let flat_action = FactoredAction::new(ExposureLevel::Flat, OrderType::Market, Urgency::Normal); + let mut cpu_engines: Vec = vec![ - EvaluationEngine::new(100_000.0), - EvaluationEngine::new(100_000.0), + EvaluationEngine::new_with_kelly(100_000.0, 1.0), + EvaluationEngine::new_with_kelly(100_000.0, 1.0), ]; - let all_closes = [&closes_1, &closes_2]; - for (engine, closes) in cpu_engines.iter_mut().zip(all_closes.iter()) { - for close in closes.iter() { - // Action 2 = Flat (no trading) - engine.process_bar_factored(*close, 2, 0.1, 0.0001); + let all_prices = [&prices_1, &prices_2]; + for (engine, window_prices) in cpu_engines.iter_mut().zip(all_prices.iter()) { + for (bar_idx, ohlc) in window_prices.iter().enumerate() { + let bar = OHLCVBarF32 { + timestamp: bar_idx as i64, + open: ohlc[0], high: ohlc[1], low: ohlc[2], close: ohlc[3], + volume: 1000.0, + }; + engine.process_bar_factored(bar_idx, &bar, &flat_action); } }