From 1934367bfaf8dcccd31ec80fe2d3034da94c515a Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 20 Feb 2026 18:14:42 +0100 Subject: [PATCH] refactor(ml): consolidate 13 duplicate OHLCVBar definitions into single canonical type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created ml/src/types/ohlcv.rs as the single source of truth for OHLCVBar (DateTime timestamp, f64 OHLCV fields). Replaced all 13 duplicate definitions across features/, regime/, real_data_loader, and evaluation/ with imports from crate::types::OHLCVBar. Key changes: - New: ml/src/types/mod.rs + ohlcv.rs with canonical OHLCVBar (derives: Debug, Clone, Copy, PartialEq, Serialize, Deserialize + Default) - Renamed: evaluation::metrics::OHLCVBar → OHLCVBarF32 (genuinely different type: f32 fields, i64 timestamp for compact backtesting) - Eliminated all import aliases (ExtractionOHLCVBar, RegimeOHLCVBar, PriceOHLCVBar, VolumeOHLCVBar) in dbn_sequence_loader.rs and pipeline.rs - Renamed regime::orchestrator::Bar → OHLCVBar (same fields, just aliased) - Updated 39 files total (13 definitions removed, imports normalized) 1883 lib tests passing, compilation clean. Co-Authored-By: Claude Opus 4.6 --- ml/examples/backtest_dqn.rs | 10 +-- ml/examples/evaluate_production_checkpoint.rs | 4 +- ml/examples/feature_importance_analysis.rs | 5 +- ml/examples/simple_dqn_eval.rs | 6 +- ml/examples/train_continuous_ppo_parquet.rs | 2 +- ml/examples/train_dqn.rs | 4 +- ml/src/data_loaders/dbn_sequence_loader.rs | 25 ++++---- ml/src/data_validation/corrector.rs | 2 +- ml/src/data_validation/rules.rs | 3 +- ml/src/data_validation/validator.rs | 3 +- ml/src/evaluation/engine.rs | 6 +- ml/src/evaluation/metrics.rs | 6 +- ml/src/features/adx_features.rs | 11 +--- ml/src/features/alternative_bars.rs | 11 +--- ml/src/features/extraction.rs | 12 +--- ml/src/features/feature_extraction.rs | 16 +---- ml/src/features/mod.rs | 5 +- ml/src/features/pipeline.rs | 14 ++--- ml/src/features/price_features.rs | 11 +--- ml/src/features/regime_adx.rs | 20 ++---- ml/src/features/statistical_features.rs | 11 +--- ml/src/features/volume_features.rs | 24 +------- ml/src/hyperopt/adapters/dqn.rs | 4 +- ml/src/lib.rs | 1 + ml/src/real_data_loader.rs | 14 +---- ml/src/regime/orchestrator.rs | 61 ++++--------------- ml/src/regime/ranging.rs | 11 +--- ml/src/regime/trending.rs | 11 +--- ml/src/regime/volatile.rs | 13 +--- ml/src/types/mod.rs | 8 +++ ml/src/types/ohlcv.rs | 41 +++++++++++++ ml/tests/common/validation_helpers.rs | 3 +- ml/tests/data_validation_tests.rs | 3 +- ml/tests/dqn_tensor_shape_validation.rs | 6 +- ml/tests/evaluation_net_pnl_bug3_test.rs | 30 ++++----- ml/tests/feature_cache_tests.rs | 3 +- ml/tests/kelly_position_sizing_integration.rs | 18 +++--- ml/tests/ppo_dual_phase_backtest_tests.rs | 6 +- ml/tests/validation_real_data_test.rs | 2 +- 39 files changed, 171 insertions(+), 275 deletions(-) create mode 100644 ml/src/types/mod.rs create mode 100644 ml/src/types/ohlcv.rs diff --git a/ml/examples/backtest_dqn.rs b/ml/examples/backtest_dqn.rs index 7a0170f5e..b5f711231 100644 --- a/ml/examples/backtest_dqn.rs +++ b/ml/examples/backtest_dqn.rs @@ -304,8 +304,8 @@ fn run_backtest( _ => ml::evaluation::engine::Action::Hold, }; - // Convert OHLCVBar to evaluation OHLCVBar - let eval_bar = ml::evaluation::metrics::OHLCVBar { + // Convert OHLCVBar to evaluation OHLCVBarF32 + let eval_bar = ml::evaluation::metrics::OHLCVBarF32 { timestamp: bar.timestamp.timestamp(), open: bar.open as f32, high: bar.high as f32, @@ -321,7 +321,7 @@ fn run_backtest( // Close any open position at end if engine.current_position.is_some() { let last_bar = &bars[bars.len() - 1]; - let eval_bar = ml::evaluation::metrics::OHLCVBar { + let eval_bar = ml::evaluation::metrics::OHLCVBarF32 { timestamp: last_bar.timestamp.timestamp(), open: last_bar.open as f32, high: last_bar.high as f32, @@ -336,9 +336,9 @@ fn run_backtest( info!("✅ Backtest complete ({:.2}s)", elapsed.as_secs_f64()); // Calculate performance metrics - let eval_bars: Vec = bars + let eval_bars: Vec = bars .iter() - .map(|b| ml::evaluation::metrics::OHLCVBar { + .map(|b| ml::evaluation::metrics::OHLCVBarF32 { timestamp: b.timestamp.timestamp(), open: b.open as f32, high: b.high as f32, diff --git a/ml/examples/evaluate_production_checkpoint.rs b/ml/examples/evaluate_production_checkpoint.rs index e4cc96b9b..cbd2b2268 100644 --- a/ml/examples/evaluate_production_checkpoint.rs +++ b/ml/examples/evaluate_production_checkpoint.rs @@ -8,7 +8,7 @@ use candle_core::{Device, Tensor}; use ml::data_loaders::load_parquet_data; use ml::dqn::dqn::WorkingDQN; use ml::evaluation::engine::{Action, EvaluationEngine}; -use ml::evaluation::metrics::{OHLCVBar, PerformanceMetrics}; +use ml::evaluation::metrics::{OHLCVBarF32, PerformanceMetrics}; use std::path::Path; use tracing::info; @@ -105,7 +105,7 @@ async fn main() -> Result<()> { let action = Action::from(action_idx); // Create OHLCV bar - let bar = OHLCVBar { + let bar = OHLCVBarF32 { timestamp: bar_idx as i64, open: close_price, high: close_price, diff --git a/ml/examples/feature_importance_analysis.rs b/ml/examples/feature_importance_analysis.rs index c36aa4c6e..7fc10be62 100644 --- a/ml/examples/feature_importance_analysis.rs +++ b/ml/examples/feature_importance_analysis.rs @@ -10,7 +10,8 @@ //! ``` use anyhow::{Context, Result}; -use ml::real_data_loader::{OHLCVBar, RealDataLoader}; +use ml::real_data_loader::RealDataLoader; +use ml::types::OHLCVBar; use std::collections::HashMap; use tracing::{info, warn}; use tracing_subscriber::FmtSubscriber; @@ -31,7 +32,7 @@ async fn main() -> Result<()> { info!("Analyzing 36 features vs baseline 16 features"); // Load real market data - let data_loader = RealDataLoader::new(); + let data_loader = RealDataLoader::new_from_workspace()?; let mut file_mapping = HashMap::new(); file_mapping.insert( "6E.FUT".to_string(), diff --git a/ml/examples/simple_dqn_eval.rs b/ml/examples/simple_dqn_eval.rs index 17d1467c1..18f234396 100644 --- a/ml/examples/simple_dqn_eval.rs +++ b/ml/examples/simple_dqn_eval.rs @@ -16,7 +16,7 @@ use clap::Parser; use ml::data_loaders::load_parquet_data; use ml::dqn::dqn::{WorkingDQN, WorkingDQNConfig}; use ml::evaluation::engine::{Action, EvaluationEngine}; -use ml::evaluation::metrics::{OHLCVBar, PerformanceMetrics}; +use ml::evaluation::metrics::{OHLCVBarF32, PerformanceMetrics}; use ml::features::extraction::compute_dqn_features; use std::path::PathBuf; use tracing::{info, warn}; @@ -122,7 +122,7 @@ fn main() -> Result<()> { let action = Action::from(action_idx); // Process action through evaluation engine - let ohlcv_bar = OHLCVBar { + let ohlcv_bar = OHLCVBarF32 { timestamp: bar.timestamp, open: bar.open, high: bar.high, @@ -141,7 +141,7 @@ fn main() -> Result<()> { // Close any remaining position if let Some(last_bar) = bars.last() { - let ohlcv_bar = OHLCVBar { + let ohlcv_bar = OHLCVBarF32 { timestamp: last_bar.timestamp, open: last_bar.open, high: last_bar.high, diff --git a/ml/examples/train_continuous_ppo_parquet.rs b/ml/examples/train_continuous_ppo_parquet.rs index 64346c39f..62bf35d55 100644 --- a/ml/examples/train_continuous_ppo_parquet.rs +++ b/ml/examples/train_continuous_ppo_parquet.rs @@ -50,7 +50,7 @@ use ml::ppo::continuous_ppo::{ use ml::ppo::flow_policy::FlowPolicyConfig; use ml::ppo::gae::GAEConfig; use ml::evaluation::engine::{Action, EvaluationEngine}; -use ml::evaluation::metrics::{PerformanceMetrics, OHLCVBar as MetricsOHLCVBar}; +use ml::evaluation::metrics::{PerformanceMetrics, OHLCVBarF32 as MetricsOHLCVBar}; /// Results from dual-phase backtesting (exploration vs exploitation) #[derive(Debug, Clone)] diff --git a/ml/examples/train_dqn.rs b/ml/examples/train_dqn.rs index 9db0327bb..deeedaa65 100644 --- a/ml/examples/train_dqn.rs +++ b/ml/examples/train_dqn.rs @@ -1016,8 +1016,8 @@ async fn main() -> Result<()> { }; // Create OHLCV bar - use ml::evaluation::metrics::OHLCVBar; - let bar = OHLCVBar { + use ml::evaluation::metrics::OHLCVBarF32; + let bar = OHLCVBarF32 { timestamp: bar_idx as i64, open: close_price as f32, high: close_price as f32, diff --git a/ml/src/data_loaders/dbn_sequence_loader.rs b/ml/src/data_loaders/dbn_sequence_loader.rs index 5d980244b..aa62cf845 100644 --- a/ml/src/data_loaders/dbn_sequence_loader.rs +++ b/ml/src/data_loaders/dbn_sequence_loader.rs @@ -40,16 +40,17 @@ use tracing::{debug, info, warn}; use crate::data_loaders::dbn_tick_adapter::Tick; use crate::features::alternative_bars::{ - DollarBarSampler, ImbalanceBarSampler, OHLCVBar, RunBarSampler, TickBarSampler, + DollarBarSampler, ImbalanceBarSampler, RunBarSampler, TickBarSampler, VolumeBarSampler, }; use crate::features::normalization::FeatureNormalizer; +use crate::types::OHLCVBar; // Wave D regime detection feature extractors use crate::ensemble::MarketRegime; -use crate::features::extraction::{extract_ml_features, OHLCVBar as ExtractionOHLCVBar}; +use crate::features::extraction::extract_ml_features; use crate::features::regime_adaptive::RegimeAdaptiveFeatures; -use crate::features::regime_adx::{OHLCVBar as RegimeOHLCVBar, RegimeADXFeatures}; +use crate::features::regime_adx::RegimeADXFeatures; use crate::features::regime_cusum::RegimeCUSUMFeatures; use crate::features::regime_transition::RegimeTransitionFeatures; @@ -112,10 +113,10 @@ pub struct DbnSequenceLoader { current_regime: MarketRegime, /// OHLCV bar buffer for ADX (requires historical bars with i64 timestamp) - bar_buffer_adx: Vec, + bar_buffer_adx: Vec, /// OHLCV bar buffer for adaptive features (requires historical bars with DateTime timestamp) - bar_buffer_adaptive: Vec, + bar_buffer_adaptive: Vec, } impl std::fmt::Debug for DbnSequenceLoader { @@ -995,7 +996,7 @@ impl DbnSequenceLoader { // This eliminates 43 zero-padded features (19.1% junk data) // Step 1: Convert ProcessedMessage to OHLCVBar format for production pipeline - let bars: Vec = messages + let bars: Vec = messages .iter() .filter_map(|msg| self.convert_message_to_bar(msg)) .collect(); @@ -1120,7 +1121,7 @@ impl DbnSequenceLoader { /// Convert ProcessedMessage to OHLCVBar format for production feature extraction /// /// REFACTOR (Wave 5 Agent 26): Helper method to bridge DbnSequenceLoader with production pipeline - fn convert_message_to_bar(&self, msg: &ProcessedMessage) -> Option { + fn convert_message_to_bar(&self, msg: &ProcessedMessage) -> Option { match msg { ProcessedMessage::Ohlcv { timestamp, @@ -1139,7 +1140,7 @@ impl DbnSequenceLoader { let timestamp_dt = chrono::DateTime::from_timestamp(secs, nsec).unwrap_or_else(chrono::Utc::now); - Some(ExtractionOHLCVBar { + Some(OHLCVBar { timestamp: timestamp_dt, open: open.to_f64(), high: high.to_f64(), @@ -1342,9 +1343,9 @@ impl DbnSequenceLoader { } // ADX & Directional Indicators (indices 211-215, 5 features) - // Create OHLCVBar for ADX calculation (requires historical bars with i64 timestamp) - let current_bar_adx = RegimeOHLCVBar { - timestamp: 0, // Not used in feature calculation + // Create OHLCVBar for ADX calculation + let current_bar_adx = OHLCVBar { + timestamp: chrono::DateTime::::from_timestamp(0, 0).unwrap(), // Not used in feature calculation open: open.to_f64(), high: high.to_f64(), low: low.to_f64(), @@ -1372,7 +1373,7 @@ impl DbnSequenceLoader { // Adaptive Strategy Metrics (indices 221-224, 4 features) // Create OHLCVBar for adaptive features (requires DateTime timestamp) - let current_bar_adaptive = ExtractionOHLCVBar { + let current_bar_adaptive = OHLCVBar { timestamp: chrono::Utc::now(), // Use current time open: open.to_f64(), high: high.to_f64(), diff --git a/ml/src/data_validation/corrector.rs b/ml/src/data_validation/corrector.rs index 08cebc5c0..a992c0afe 100644 --- a/ml/src/data_validation/corrector.rs +++ b/ml/src/data_validation/corrector.rs @@ -10,7 +10,7 @@ //! All corrections are conservative and preserve data integrity. //! Original data is never modified in-place. -use crate::real_data_loader::OHLCVBar; +use crate::types::OHLCVBar; use anyhow::Result; /// Data corrector for automatic fixes diff --git a/ml/src/data_validation/rules.rs b/ml/src/data_validation/rules.rs index 4ec59815f..c71b1aeb5 100644 --- a/ml/src/data_validation/rules.rs +++ b/ml/src/data_validation/rules.rs @@ -4,7 +4,8 @@ //! Each rule implements the `ValidationRule` trait and can be composed //! into a comprehensive validation pipeline. -use crate::real_data_loader::{Indicators, OHLCVBar}; +use crate::real_data_loader::Indicators; +use crate::types::OHLCVBar; use anyhow::Result; /// Validation error information diff --git a/ml/src/data_validation/validator.rs b/ml/src/data_validation/validator.rs index 99853c9d3..10b8b288d 100644 --- a/ml/src/data_validation/validator.rs +++ b/ml/src/data_validation/validator.rs @@ -3,7 +3,8 @@ //! Orchestrates multiple validation rules and generates comprehensive reports. use super::rules::{Severity, ValidationError, ValidationRule}; -use crate::real_data_loader::{Indicators, OHLCVBar}; +use crate::real_data_loader::Indicators; +use crate::types::OHLCVBar; use anyhow::Result; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/ml/src/evaluation/engine.rs b/ml/src/evaluation/engine.rs index 76302d707..9a2e6a282 100644 --- a/ml/src/evaluation/engine.rs +++ b/ml/src/evaluation/engine.rs @@ -2,7 +2,7 @@ //! //! Tracks positions, executes trades based on DQN actions, and records trade history. -use super::metrics::OHLCVBar; +use super::metrics::OHLCVBarF32; use serde::{Deserialize, Serialize}; /// Trading action from DQN model @@ -87,7 +87,7 @@ impl EvaluationEngine { /// * `bar_idx` - Index of current bar in the dataset /// * `bar` - Current OHLCV bar /// * `action` - Action selected by DQN model - pub fn process_bar(&mut self, bar_idx: usize, bar: &OHLCVBar, action: Action) { + pub fn process_bar(&mut self, bar_idx: usize, bar: &OHLCVBarF32, action: Action) { // Update action counts self.action_counts[action as usize] += 1; @@ -137,7 +137,7 @@ impl EvaluationEngine { } /// Close current position and record trade - pub fn close_position(&mut self, exit_bar_idx: usize, exit_bar: &OHLCVBar) { + pub fn close_position(&mut self, exit_bar_idx: usize, exit_bar: &OHLCVBarF32) { if let Some(pos) = self.current_position.take() { // Calculate base PnL (for 1 contract) let base_pnl = match pos.direction { diff --git a/ml/src/evaluation/metrics.rs b/ml/src/evaluation/metrics.rs index b7e492158..3220830d4 100644 --- a/ml/src/evaluation/metrics.rs +++ b/ml/src/evaluation/metrics.rs @@ -50,7 +50,7 @@ impl PerformanceMetrics { /// /// # Returns /// Comprehensive performance metrics - pub fn from_trades(trades: &[Trade], initial_capital: f32, _bars: &[OHLCVBar]) -> Self { + pub fn from_trades(trades: &[Trade], initial_capital: f32, _bars: &[OHLCVBarF32]) -> Self { if trades.is_empty() { return Self { total_return_pct: 0.0, @@ -317,9 +317,9 @@ fn calculate_benchmark_metrics(_returns: &[f64], _annualized_return: f64) -> (f6 (0.0, 0.0, 0.0) } -/// OHLCV bar structure (placeholder - should match actual implementation) +/// Compact f32 OHLCV bar for backtesting evaluation #[derive(Debug, Clone)] -pub struct OHLCVBar { +pub struct OHLCVBarF32 { pub timestamp: i64, pub open: f32, pub high: f32, diff --git a/ml/src/features/adx_features.rs b/ml/src/features/adx_features.rs index e88478771..29149d45e 100644 --- a/ml/src/features/adx_features.rs +++ b/ml/src/features/adx_features.rs @@ -45,16 +45,7 @@ use std::collections::VecDeque; -/// OHLCV bar structure (compatible with other Wave D features) -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +pub use crate::types::OHLCVBar; /// ADX feature extractor using Wilder's 14-period algorithm /// diff --git a/ml/src/features/alternative_bars.rs b/ml/src/features/alternative_bars.rs index 011ed7dee..3b89a1ffc 100644 --- a/ml/src/features/alternative_bars.rs +++ b/ml/src/features/alternative_bars.rs @@ -11,16 +11,7 @@ use chrono::{DateTime, Utc}; -/// OHLCV Bar representation -#[derive(Debug, Clone, PartialEq)] -pub struct OHLCVBar { - pub timestamp: DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +pub use crate::types::OHLCVBar; /// Tick Bar Sampler - Aggregates every N ticks (PRIMARY IMPLEMENTATION - Agent B3) /// diff --git a/ml/src/features/extraction.rs b/ml/src/features/extraction.rs index 1924cfd9f..775e3b919 100644 --- a/ml/src/features/extraction.rs +++ b/ml/src/features/extraction.rs @@ -21,6 +21,7 @@ //! ``` use crate::features::microstructure::{AmihudIlliquidity, CorwinSchultzSpread, RollMeasure}; +pub use crate::types::OHLCVBar; use anyhow::{Context, Result}; use chrono::{Datelike, Timelike}; use common::features::{BollingerBands, ATR, EMA, MACD, RSI}; @@ -46,17 +47,6 @@ use data::providers::databento::mbp10::Mbp10Snapshot; /// - 1.0.0: Initial 51-feature implementation (43 base + 8 OFI) pub const FEATURE_EXTRACTION_VERSION: &str = "1.0.0"; -/// OHLCV bar data structure (compatible with real_data_loader) -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} - /// Feature extraction result: 51-dimensional feature vector per bar pub type FeatureVector = [f64; 51]; diff --git a/ml/src/features/feature_extraction.rs b/ml/src/features/feature_extraction.rs index 28d1f089e..d47f91e66 100644 --- a/ml/src/features/feature_extraction.rs +++ b/ml/src/features/feature_extraction.rs @@ -4,20 +4,10 @@ //! Phase 1: Implements 15 core features (5 OHLCV + 10 technical indicators) //! Phase 2-4: Will add 241 additional engineered features +pub use crate::types::OHLCVBar; use crate::MLError; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; - -/// OHLCV bar structure (from real_data_loader) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OHLCVBar { - pub timestamp: DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +#[cfg(test)] +use chrono::Utc; /// Feature extractor with technical indicators #[derive(Debug)] diff --git a/ml/src/features/mod.rs b/ml/src/features/mod.rs index 32ab7af8b..9d5f28f83 100644 --- a/ml/src/features/mod.rs +++ b/ml/src/features/mod.rs @@ -37,7 +37,8 @@ pub mod volume_features; // Wave C: Volume-based features (10 features) // Feature configuration (Wave C) pub use config::{FeatureConfig, FeatureGroup, FeatureIndices, FeaturePhase}; -pub use extraction::{extract_ml_features, FeatureVector, OHLCVBar}; +pub use extraction::{extract_ml_features, FeatureVector}; +pub use crate::types::OHLCVBar; pub use minio_integration::{ cache_exists, compute_data_hash, download_cache_metadata, download_features_from_minio, list_cached_features, upload_cache_metadata, upload_features_to_minio, CacheMetadata, @@ -54,7 +55,7 @@ pub use unified::{ // Alternative bar sampling (tick, volume, dollar, imbalance, run bars) pub use alternative_bars::{ - DollarBarSampler, ImbalanceBarSampler, OHLCVBar as AltBar, RunBarSampler, TickBarSampler, + DollarBarSampler, ImbalanceBarSampler, RunBarSampler, TickBarSampler, VolumeBarSampler, }; diff --git a/ml/src/features/pipeline.rs b/ml/src/features/pipeline.rs index f9b7476f1..512eeb24d 100644 --- a/ml/src/features/pipeline.rs +++ b/ml/src/features/pipeline.rs @@ -52,14 +52,14 @@ use anyhow::{Context, Result}; use std::collections::VecDeque; -use crate::features::extraction::OHLCVBar; use crate::features::microstructure_features::{ BuySellImbalance, HighLowSpread, InterArrivalTime, KyleLambda, PriceImpact, TickCount, VarianceRatio, VolumeWeightedSpread, }; -use crate::features::price_features::{OHLCVBar as PriceOHLCVBar, PriceFeatureExtractor}; +use crate::features::price_features::PriceFeatureExtractor; use crate::features::time_features::TimeFeatureExtractor; -use crate::features::volume_features::{OHLCVBar as VolumeOHLCVBar, VolumeFeatureExtractor}; +use crate::features::volume_features::VolumeFeatureExtractor; +use crate::types::OHLCVBar; /// Wrapper around VecDeque that provides lazy allocation for bars /// @@ -228,8 +228,8 @@ impl FeatureExtractionPipeline { // Note: BarsBuffer (RingBuffer) automatically maintains window size // via circular overwriting, no need for manual pop_front - // Convert to VolumeOHLCVBar for volume extractor - let volume_bar = VolumeOHLCVBar { + // Convert to OHLCVBar for volume extractor + let volume_bar = OHLCVBar { timestamp: bar.timestamp, open: bar.open, high: bar.high, @@ -345,10 +345,10 @@ impl FeatureExtractionPipeline { // Price features (15) if self.config.enable_price { // Convert extraction::OHLCVBar to price_features::OHLCVBar - let price_bars: VecDeque = self + let price_bars: VecDeque = self .bars .iter() - .map(|b| PriceOHLCVBar { + .map(|b| OHLCVBar { timestamp: b.timestamp, open: b.open, high: b.high, diff --git a/ml/src/features/price_features.rs b/ml/src/features/price_features.rs index 1538fd60b..5fe91cd3d 100644 --- a/ml/src/features/price_features.rs +++ b/ml/src/features/price_features.rs @@ -18,16 +18,7 @@ use std::collections::VecDeque; -/// OHLCV bar structure (compatible with extraction.rs) -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +pub use crate::types::OHLCVBar; /// Price-based feature extractor for all 15 features #[derive(Debug)] diff --git a/ml/src/features/regime_adx.rs b/ml/src/features/regime_adx.rs index ace30ce44..f0769a7e0 100644 --- a/ml/src/features/regime_adx.rs +++ b/ml/src/features/regime_adx.rs @@ -30,16 +30,7 @@ //! - Memory footprint: <200 bytes per symbol //! - Cache-friendly: Sequential access patterns -/// OHLCV bar structure for ADX feature extraction -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: i64, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +pub use crate::types::OHLCVBar; /// ADX Feature Extractor with Wilder's Smoothing /// @@ -355,7 +346,7 @@ mod tests { /// Helper to create a test bar fn create_test_bar(open: f64, high: f64, low: f64, close: f64, volume: f64) -> OHLCVBar { OHLCVBar { - timestamp: 0, + timestamp: chrono::DateTime::::from_timestamp(0, 0).unwrap(), open, high, low, @@ -402,7 +393,7 @@ mod tests { // Second bar with NaN high (simulates corrupted DBN data) let bar2_nan = OHLCVBar { - timestamp: 0, + timestamp: chrono::DateTime::::from_timestamp(0, 0).unwrap(), open: 101.0, high: f64::NAN, // NaN input - triggers the fix low: 99.0, @@ -450,7 +441,7 @@ mod tests { // Second bar with Inf low (simulates price anomaly) let bar2_inf = OHLCVBar { - timestamp: 0, + timestamp: chrono::DateTime::::from_timestamp(0, 0).unwrap(), open: 101.0, high: 103.0, low: f64::INFINITY, // Inf input - triggers the fix @@ -473,9 +464,10 @@ mod tests { let mut adx = RegimeADXFeatures::new(14); // Feed 10 bars with various NaN values + let epoch = chrono::DateTime::::from_timestamp(0, 0).unwrap(); for i in 0..10 { let bar = OHLCVBar { - timestamp: i, + timestamp: epoch, open: if i % 2 == 0 { 100.0 } else { f64::NAN }, high: if i % 3 == 0 { 102.0 } else { f64::NAN }, low: if i % 4 == 0 { 98.0 } else { f64::NAN }, diff --git a/ml/src/features/statistical_features.rs b/ml/src/features/statistical_features.rs index 2bc8e6fae..9714f94f1 100644 --- a/ml/src/features/statistical_features.rs +++ b/ml/src/features/statistical_features.rs @@ -24,16 +24,7 @@ use std::collections::VecDeque; -/// OHLCV bar structure (compatible with price_features.rs) -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +pub use crate::types::OHLCVBar; /// Statistical feature extractor with efficient rolling window algorithms #[derive(Debug)] diff --git a/ml/src/features/volume_features.rs b/ml/src/features/volume_features.rs index e3097d907..65d121145 100644 --- a/ml/src/features/volume_features.rs +++ b/ml/src/features/volume_features.rs @@ -30,29 +30,7 @@ use anyhow::Result; use std::collections::VecDeque; -/// OHLCV bar data structure (matches extraction.rs) -#[derive(Debug, Clone, Copy)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} - -impl Default for OHLCVBar { - fn default() -> Self { - Self { - timestamp: chrono::DateTime::::from_timestamp(0, 0).unwrap(), - open: 0.0, - high: 0.0, - low: 0.0, - close: 0.0, - volume: 0.0, - } - } -} +pub use crate::types::OHLCVBar; /// Volume feature extractor with stateful rolling windows (Wave G17 optimized) /// diff --git a/ml/src/hyperopt/adapters/dqn.rs b/ml/src/hyperopt/adapters/dqn.rs index 60e26e922..9f943dc17 100644 --- a/ml/src/hyperopt/adapters/dqn.rs +++ b/ml/src/hyperopt/adapters/dqn.rs @@ -51,7 +51,7 @@ use tracing::info; use chrono::Utc; use crate::evaluation::engine::{Action, EvaluationEngine}; -use crate::evaluation::metrics::{OHLCVBar, PerformanceMetrics}; +use crate::evaluation::metrics::{OHLCVBarF32, PerformanceMetrics}; use crate::hyperopt::paths::TrainingPaths; use crate::hyperopt::traits::{HyperparameterOptimizable, ParameterSpace}; use crate::trainers::dqn::{DQNHyperparameters, DQNTrainer as InternalDQNTrainer}; @@ -2452,7 +2452,7 @@ impl HyperparameterOptimizable for DQNTrainer { // Create OHLCV bar (we only have close price from validation data) // For backtest purposes, we set open=high=low=close - let bar = OHLCVBar { + let bar = OHLCVBarF32 { timestamp: bar_idx as i64, // Use bar index as timestamp open: close_price as f32, high: close_price as f32, diff --git a/ml/src/lib.rs b/ml/src/lib.rs index 532411941..cf50629d1 100644 --- a/ml/src/lib.rs +++ b/ml/src/lib.rs @@ -948,6 +948,7 @@ pub use tft::{ QuantizedTemporalFusionTransformer, QuantizedVariableSelectionNetwork, }; pub mod trainers; // ML model trainers with gRPC integration +pub mod types; // Canonical shared types (OHLCVBar, etc.) pub mod transformers; pub mod universe; diff --git a/ml/src/real_data_loader.rs b/ml/src/real_data_loader.rs index d3798cea0..d6bb1b323 100644 --- a/ml/src/real_data_loader.rs +++ b/ml/src/real_data_loader.rs @@ -29,24 +29,14 @@ //! ``` use anyhow::{Context, Result}; -use chrono::{DateTime, Utc}; +use chrono::DateTime; use dbn::decode::{DbnDecoder, DecodeRecordRef}; use dbn::OhlcvMsg; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::{Path, PathBuf}; use tracing::{debug, info}; -/// OHLCV bar - standard candlestick data structure -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OHLCVBar { - pub timestamp: DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +use crate::types::OHLCVBar; /// Feature matrix for ML model input /// diff --git a/ml/src/regime/orchestrator.rs b/ml/src/regime/orchestrator.rs index e6b6f73ca..7b2efd5b7 100644 --- a/ml/src/regime/orchestrator.rs +++ b/ml/src/regime/orchestrator.rs @@ -40,6 +40,7 @@ use crate::regime::{ trending::{TrendingClassifier, TrendingSignal}, volatile::{VolatileClassifier, VolatileSignal}, }; +use crate::types::OHLCVBar; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; @@ -66,17 +67,6 @@ pub enum OrchestratorError { DetectionFailed(String), } -/// OHLCV bar structure for regime detection -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Bar { - pub timestamp: DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} - /// Regime state output #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RegimeState { @@ -246,7 +236,7 @@ impl RegimeOrchestrator { pub async fn detect_and_persist( &mut self, symbol: &str, - bars: &[Bar], + bars: &[OHLCVBar], ) -> Result { // Validate input if bars.len() < self.min_bars { @@ -276,22 +266,9 @@ impl RegimeOrchestrator { // Step 2: If break detected OR forced detection, classify regime let regime = if break_detected || prev_regime.is_none() { - // Convert bars to classifier format - let ohlcv_bars: Vec = bars - .iter() - .map(|b| crate::regime::trending::OHLCVBar { - timestamp: b.timestamp, - open: b.open, - high: b.high, - low: b.low, - close: b.close, - volume: b.volume, - }) - .collect(); - - // Query regime classifiers - let trending_signal = if let Some(last_bar) = ohlcv_bars.last() { - self.trending_classifier.classify(last_bar.clone()) + // Query regime classifiers (all use canonical OHLCVBar) + let trending_signal = if let Some(&last_bar) = bars.last() { + self.trending_classifier.classify(last_bar) } else { TrendingSignal::Ranging { adx: 0.0, @@ -299,30 +276,14 @@ impl RegimeOrchestrator { } }; - let ranging_signal = if let Some(last_bar) = ohlcv_bars.last() { - let ranging_bar = crate::regime::ranging::OHLCVBar { - timestamp: last_bar.timestamp, - open: last_bar.open, - high: last_bar.high, - low: last_bar.low, - close: last_bar.close, - volume: last_bar.volume, - }; - self.ranging_classifier.classify(ranging_bar) + let ranging_signal = if let Some(&last_bar) = bars.last() { + self.ranging_classifier.classify(last_bar) } else { crate::regime::ranging::RangingSignal::NotRanging }; - let volatile_signal = if let Some(last_bar) = ohlcv_bars.last() { - let volatile_bar = crate::regime::volatile::OHLCVBar { - timestamp: last_bar.timestamp, - open: last_bar.open, - high: last_bar.high, - low: last_bar.low, - close: last_bar.close, - volume: last_bar.volume, - }; - self.volatile_classifier.classify(volatile_bar) + let volatile_signal = if let Some(&last_bar) = bars.last() { + self.volatile_classifier.classify(last_bar) } else { VolatileSignal::Low }; @@ -471,12 +432,12 @@ impl RegimeOrchestrator { mod tests { use super::*; - fn create_test_bars(count: usize, base_price: f64) -> Vec { + fn create_test_bars(count: usize, base_price: f64) -> Vec { let base_time = Utc::now(); (0..count) .map(|i| { let price = base_price + (i as f64 * 0.1); - Bar { + OHLCVBar { timestamp: base_time + chrono::Duration::seconds(i as i64 * 60), open: price, high: price + 0.5, diff --git a/ml/src/regime/ranging.rs b/ml/src/regime/ranging.rs index 738b0117b..cdba6e761 100644 --- a/ml/src/regime/ranging.rs +++ b/ml/src/regime/ranging.rs @@ -11,16 +11,7 @@ use serde::{Deserialize, Serialize}; use std::collections::VecDeque; -/// OHLCV bar structure (from feature_extraction) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +use crate::types::OHLCVBar; /// Ranging regime signal #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] diff --git a/ml/src/regime/trending.rs b/ml/src/regime/trending.rs index 0df9a989a..ddcf8d8b8 100644 --- a/ml/src/regime/trending.rs +++ b/ml/src/regime/trending.rs @@ -17,16 +17,7 @@ use std::collections::VecDeque; -/// OHLCV bar structure for trending analysis -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: chrono::DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +use crate::types::OHLCVBar; /// Trending signal output #[derive(Debug, Clone, PartialEq)] diff --git a/ml/src/regime/volatile.rs b/ml/src/regime/volatile.rs index 1cfbac1c5..f78d7d4af 100644 --- a/ml/src/regime/volatile.rs +++ b/ml/src/regime/volatile.rs @@ -20,19 +20,9 @@ //! - Reuses `compute_garman_klass_volatility()` from `ml/src/features/price_features.rs` //! - Reuses ATR calculation logic from `ml/src/features/feature_extraction.rs` -use chrono::{DateTime, Utc}; use std::collections::VecDeque; -/// OHLCV bar structure (compatible with price_features.rs) -#[derive(Debug, Clone)] -pub struct OHLCVBar { - pub timestamp: DateTime, - pub open: f64, - pub high: f64, - pub low: f64, - pub close: f64, - pub volume: f64, -} +use crate::types::OHLCVBar; /// Volatile signal output #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -322,6 +312,7 @@ fn safe_clip(value: f64, min: f64, max: f64) -> f64 { #[cfg(test)] mod tests { use super::*; + use chrono::Utc; // Test helper functions fn create_bar(open: f64, high: f64, low: f64, close: f64, volume: f64) -> OHLCVBar { diff --git a/ml/src/types/mod.rs b/ml/src/types/mod.rs new file mode 100644 index 000000000..44c7fd0d9 --- /dev/null +++ b/ml/src/types/mod.rs @@ -0,0 +1,8 @@ +//! Core types shared across the ML crate. +//! +//! This module provides canonical type definitions to eliminate duplication. +//! All modules should import shared types from here rather than defining their own. + +pub mod ohlcv; + +pub use ohlcv::OHLCVBar; diff --git a/ml/src/types/ohlcv.rs b/ml/src/types/ohlcv.rs new file mode 100644 index 000000000..0ecf76e08 --- /dev/null +++ b/ml/src/types/ohlcv.rs @@ -0,0 +1,41 @@ +//! Canonical OHLCV bar type for the ML crate. + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +/// Canonical OHLCV bar representation. +/// +/// This is the **single source of truth** for OHLCV bar data across the ML crate. +/// All feature extractors, regime detectors, data loaders, and evaluation code +/// should use this type rather than defining local copies. +/// +/// # Fields +/// - `timestamp`: UTC timestamp of the bar +/// - `open`, `high`, `low`, `close`: Price fields (f64) +/// - `volume`: Trade volume (f64) +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub struct OHLCVBar { + pub timestamp: DateTime, + pub open: f64, + pub high: f64, + pub low: f64, + pub close: f64, + pub volume: f64, +} + +impl Default for OHLCVBar { + fn default() -> Self { + Self { + // UNIX epoch — from_timestamp(0, 0) is infallible for these inputs + timestamp: match DateTime::::from_timestamp(0, 0) { + Some(ts) => ts, + None => Utc::now(), + }, + open: 0.0, + high: 0.0, + low: 0.0, + close: 0.0, + volume: 0.0, + } + } +} diff --git a/ml/tests/common/validation_helpers.rs b/ml/tests/common/validation_helpers.rs index 9b473f6a7..0f11d90b6 100644 --- a/ml/tests/common/validation_helpers.rs +++ b/ml/tests/common/validation_helpers.rs @@ -25,7 +25,8 @@ use ml::data_validation::rules::{ CompletenessRule, ContinuityRule, IndicatorRule, IntegrityRule, TimestampRule, }; use ml::data_validation::validator::{DataValidator, ValidationResult}; -use ml::real_data_loader::{Indicators, OHLCVBar}; +use ml::real_data_loader::Indicators; +use ml::types::OHLCVBar; // ============================================================================ // Test Configuration Helpers diff --git a/ml/tests/data_validation_tests.rs b/ml/tests/data_validation_tests.rs index ee7b5c5fb..9d146c1ab 100644 --- a/ml/tests/data_validation_tests.rs +++ b/ml/tests/data_validation_tests.rs @@ -22,7 +22,8 @@ use ml::data_validation::rules::{ CompletenessRule, ContinuityRule, IndicatorRule, IntegrityRule, TimestampRule, }; use ml::data_validation::validator::{DataValidator, ValidationReport, ValidationResult}; -use ml::real_data_loader::{Indicators, OHLCVBar, RealDataLoader}; +use ml::real_data_loader::{Indicators, RealDataLoader}; +use ml::types::OHLCVBar; /// Test 1: OHLCV integrity validation (high≥low, volume≥0, price relationships) #[tokio::test] diff --git a/ml/tests/dqn_tensor_shape_validation.rs b/ml/tests/dqn_tensor_shape_validation.rs index 0aad7bb4b..12040912e 100644 --- a/ml/tests/dqn_tensor_shape_validation.rs +++ b/ml/tests/dqn_tensor_shape_validation.rs @@ -20,20 +20,20 @@ use anyhow::Result; use chrono::Utc; use ml::evaluation::engine::{Action, EvaluationEngine, Trade}; -use ml::evaluation::metrics::{OHLCVBar, PerformanceMetrics}; +use ml::evaluation::metrics::{OHLCVBarF32, PerformanceMetrics}; // ================================================================================================ // TEST UTILITIES // ================================================================================================ /// Generate synthetic OHLCV bars for testing -fn create_synthetic_bars(count: usize) -> Vec { +fn create_synthetic_bars(count: usize) -> Vec { let mut bars = Vec::with_capacity(count); let base_price = 100.0; for i in 0..count { let price = base_price + (i as f32) * 0.5; - bars.push(OHLCVBar { + bars.push(OHLCVBarF32 { timestamp: Utc::now().timestamp(), open: price - 0.1, high: price + 0.2, diff --git a/ml/tests/evaluation_net_pnl_bug3_test.rs b/ml/tests/evaluation_net_pnl_bug3_test.rs index 46e073bc0..4751458e0 100644 --- a/ml/tests/evaluation_net_pnl_bug3_test.rs +++ b/ml/tests/evaluation_net_pnl_bug3_test.rs @@ -4,7 +4,7 @@ //! not GROSS P&L. Ensures all metrics (Sharpe, win rate, returns) are accurate. use ml::evaluation::engine::{Action, EvaluationEngine, PositionDirection}; -use ml::evaluation::metrics::{OHLCVBar, PerformanceMetrics}; +use ml::evaluation::metrics::{OHLCVBarF32, PerformanceMetrics}; /// Test that Trade struct includes transaction cost fields #[test] @@ -57,7 +57,7 @@ fn test_trade_struct_has_cost_fields() { #[test] fn test_losing_trade_after_costs() { let bars = vec![ - OHLCVBar { + OHLCVBarF32 { timestamp: 1000, open: 100.0, high: 100.0, @@ -65,7 +65,7 @@ fn test_losing_trade_after_costs() { close: 100.0, volume: 1000.0, }, - OHLCVBar { + OHLCVBarF32 { timestamp: 2000, open: 100.2, high: 100.2, @@ -147,13 +147,13 @@ fn test_metrics_use_net_pnl() { fn test_win_rate_uses_net_pnl() { let bars = vec![ // Trade 1: Large profit (0.8) - still profitable after costs - OHLCVBar { timestamp: 1000, open: 100.0, high: 100.0, low: 100.0, close: 100.0, volume: 1000.0 }, - OHLCVBar { timestamp: 2000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, + OHLCVBarF32 { timestamp: 1000, open: 100.0, high: 100.0, low: 100.0, close: 100.0, volume: 1000.0 }, + OHLCVBarF32 { timestamp: 2000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, // Hold to avoid opening new position - OHLCVBar { timestamp: 3000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, + OHLCVBarF32 { timestamp: 3000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, // Trade 2: Small profit (0.2) - becomes loss after costs - OHLCVBar { timestamp: 4000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, - OHLCVBar { timestamp: 5000, open: 101.0, high: 101.0, low: 101.0, close: 101.0, volume: 1000.0 }, + OHLCVBarF32 { timestamp: 4000, open: 100.8, high: 100.8, low: 100.8, close: 100.8, volume: 1000.0 }, + OHLCVBarF32 { timestamp: 5000, open: 101.0, high: 101.0, low: 101.0, close: 101.0, volume: 1000.0 }, ]; let mut engine = EvaluationEngine::new(10000.0); @@ -199,7 +199,7 @@ fn test_win_rate_uses_net_pnl() { #[test] fn test_short_position_transaction_costs() { let bars = vec![ - OHLCVBar { + OHLCVBarF32 { timestamp: 1000, open: 100.0, high: 100.0, @@ -207,7 +207,7 @@ fn test_short_position_transaction_costs() { close: 100.0, volume: 1000.0, }, - OHLCVBar { + OHLCVBarF32 { timestamp: 2000, open: 99.5, high: 99.5, @@ -295,7 +295,7 @@ fn test_zero_profit_after_costs() { // Set up a trade that exactly breaks even after costs // Gross profit = transaction costs let bars = vec![ - OHLCVBar { + OHLCVBarF32 { timestamp: 1000, open: 100.0, high: 100.0, @@ -303,7 +303,7 @@ fn test_zero_profit_after_costs() { close: 100.0, volume: 1000.0, }, - OHLCVBar { + OHLCVBarF32 { timestamp: 2000, open: 100.3, high: 100.3, @@ -329,9 +329,9 @@ fn test_zero_profit_after_costs() { } // Helper function to create standard test bars -fn create_test_bars() -> Vec { +fn create_test_bars() -> Vec { vec![ - OHLCVBar { + OHLCVBarF32 { timestamp: 1000, open: 100.0, high: 100.0, @@ -339,7 +339,7 @@ fn create_test_bars() -> Vec { close: 100.0, volume: 1000.0, }, - OHLCVBar { + OHLCVBarF32 { timestamp: 2000, open: 100.5, high: 100.5, diff --git a/ml/tests/feature_cache_tests.rs b/ml/tests/feature_cache_tests.rs index 8e7cdd41b..49ba282dd 100644 --- a/ml/tests/feature_cache_tests.rs +++ b/ml/tests/feature_cache_tests.rs @@ -13,7 +13,8 @@ use anyhow::Result; use chrono::Utc; -use ml::real_data_loader::{OHLCVBar, RealDataLoader}; +use ml::real_data_loader::RealDataLoader; +use ml::types::OHLCVBar; use std::path::PathBuf; use tempfile::TempDir; diff --git a/ml/tests/kelly_position_sizing_integration.rs b/ml/tests/kelly_position_sizing_integration.rs index 6ffb61a22..aee88737a 100644 --- a/ml/tests/kelly_position_sizing_integration.rs +++ b/ml/tests/kelly_position_sizing_integration.rs @@ -4,7 +4,7 @@ // in the backtesting pipeline (PortfolioTracker → EvaluationEngine → Hyperopt). use ml::evaluation::engine::{Action, EvaluationEngine}; -use ml::evaluation::metrics::OHLCVBar; +use ml::evaluation::metrics::OHLCVBarF32; #[test] fn test_kelly_scales_pnl_correctly() { @@ -12,7 +12,7 @@ fn test_kelly_scales_pnl_correctly() { let mut engine_full = EvaluationEngine::new_with_kelly(10_000.0, 1.0); // Open long position at $100 - let bar1 = OHLCVBar { + let bar1 = OHLCVBarF32 { timestamp: 0, open: 100.0, high: 100.0, @@ -23,7 +23,7 @@ fn test_kelly_scales_pnl_correctly() { engine_full.process_bar(0, &bar1, Action::Buy); // Close at $110 (10% gain) - let bar2 = OHLCVBar { + let bar2 = OHLCVBarF32 { timestamp: 1, open: 110.0, high: 110.0, @@ -63,7 +63,7 @@ fn test_kelly_scales_losses_correctly() { // Full Kelly (1.0) let mut engine_full = EvaluationEngine::new_with_kelly(10_000.0, 1.0); - let bar1 = OHLCVBar { + let bar1 = OHLCVBarF32 { timestamp: 0, open: 100.0, high: 100.0, @@ -74,7 +74,7 @@ fn test_kelly_scales_losses_correctly() { engine_full.process_bar(0, &bar1, Action::Buy); // Price drops to $90 (-10% loss) - let bar2 = OHLCVBar { + let bar2 = OHLCVBarF32 { timestamp: 1, open: 90.0, high: 90.0, @@ -106,7 +106,7 @@ fn test_kelly_with_short_positions() { let mut engine_full = EvaluationEngine::new_with_kelly(10_000.0, 1.0); // Short at $100 - let bar1 = OHLCVBar { + let bar1 = OHLCVBarF32 { timestamp: 0, open: 100.0, high: 100.0, @@ -117,7 +117,7 @@ fn test_kelly_with_short_positions() { engine_full.process_bar(0, &bar1, Action::Sell); // Cover at $90 (10% profit for short) - let bar2 = OHLCVBar { + let bar2 = OHLCVBarF32 { timestamp: 1, open: 90.0, high: 90.0, @@ -146,7 +146,7 @@ fn test_default_new_uses_full_kelly() { // Verify that EvaluationEngine::new() defaults to Kelly=1.0 let mut engine_default = EvaluationEngine::new(10_000.0); - let bar1 = OHLCVBar { + let bar1 = OHLCVBarF32 { timestamp: 0, open: 100.0, high: 100.0, @@ -156,7 +156,7 @@ fn test_default_new_uses_full_kelly() { }; engine_default.process_bar(0, &bar1, Action::Buy); - let bar2 = OHLCVBar { + let bar2 = OHLCVBarF32 { timestamp: 1, open: 110.0, high: 110.0, diff --git a/ml/tests/ppo_dual_phase_backtest_tests.rs b/ml/tests/ppo_dual_phase_backtest_tests.rs index 0780988b0..fbd349a12 100644 --- a/ml/tests/ppo_dual_phase_backtest_tests.rs +++ b/ml/tests/ppo_dual_phase_backtest_tests.rs @@ -9,7 +9,7 @@ //! 3. Run tests and verify all pass use ml::evaluation::engine::{Action, EvaluationEngine}; -use ml::evaluation::metrics::{PerformanceMetrics, OHLCVBar}; +use ml::evaluation::metrics::{PerformanceMetrics, OHLCVBarF32}; /// Results from dual-phase backtesting (exploration vs exploitation) #[derive(Debug, Clone)] @@ -76,8 +76,8 @@ impl DualPhaseBacktestResults { } // Helper function to create a dummy OHLCVBar for testing -fn create_test_bar(price: f32) -> OHLCVBar { - OHLCVBar { +fn create_test_bar(price: f32) -> OHLCVBarF32 { + OHLCVBarF32 { timestamp: 0, open: price, high: price + 1.0, diff --git a/ml/tests/validation_real_data_test.rs b/ml/tests/validation_real_data_test.rs index 33e754138..1de284b0d 100644 --- a/ml/tests/validation_real_data_test.rs +++ b/ml/tests/validation_real_data_test.rs @@ -24,7 +24,7 @@ use ml::validation::{ /// 8-10: MACD (line, signal, histogram = line - signal) /// 11-13: Bollinger Bands (upper, middle, lower) /// 14: ATR(14) -fn build_features_from_loader(loader: &RealDataLoader, bars: &[ml::real_data_loader::OHLCVBar]) -> Vec> { +fn build_features_from_loader(loader: &RealDataLoader, bars: &[ml::types::OHLCVBar]) -> Vec> { let feat_matrix = loader .extract_features(bars) .expect("extract_features failed");