refactor(ml): consolidate 13 duplicate OHLCVBar definitions into single canonical type
Created ml/src/types/ohlcv.rs as the single source of truth for OHLCVBar (DateTime<Utc> 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 <noreply@anthropic.com>
This commit is contained in:
@@ -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<ml::evaluation::metrics::OHLCVBar> = bars
|
||||
let eval_bars: Vec<ml::evaluation::metrics::OHLCVBarF32> = 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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<RegimeOHLCVBar>,
|
||||
bar_buffer_adx: Vec<OHLCVBar>,
|
||||
|
||||
/// OHLCV bar buffer for adaptive features (requires historical bars with DateTime timestamp)
|
||||
bar_buffer_adaptive: Vec<ExtractionOHLCVBar>,
|
||||
bar_buffer_adaptive: Vec<OHLCVBar>,
|
||||
}
|
||||
|
||||
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<ExtractionOHLCVBar> = messages
|
||||
let bars: Vec<OHLCVBar> = 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<ExtractionOHLCVBar> {
|
||||
fn convert_message_to_bar(&self, msg: &ProcessedMessage) -> Option<OHLCVBar> {
|
||||
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::<chrono::Utc>::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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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
|
||||
///
|
||||
|
||||
@@ -11,16 +11,7 @@
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// OHLCV Bar representation
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct OHLCVBar {
|
||||
pub timestamp: DateTime<Utc>,
|
||||
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)
|
||||
///
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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];
|
||||
|
||||
|
||||
@@ -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<Utc>,
|
||||
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)]
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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<PriceOHLCVBar> = self
|
||||
let price_bars: VecDeque<OHLCVBar> = self
|
||||
.bars
|
||||
.iter()
|
||||
.map(|b| PriceOHLCVBar {
|
||||
.map(|b| OHLCVBar {
|
||||
timestamp: b.timestamp,
|
||||
open: b.open,
|
||||
high: b.high,
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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)]
|
||||
|
||||
@@ -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::<chrono::Utc>::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::<chrono::Utc>::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::<chrono::Utc>::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::<chrono::Utc>::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 },
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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)]
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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::<chrono::Utc>::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)
|
||||
///
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<Utc>,
|
||||
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
|
||||
///
|
||||
|
||||
@@ -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<Utc>,
|
||||
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<RegimeState, OrchestratorError> {
|
||||
// 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<crate::regime::trending::OHLCVBar> = 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<Bar> {
|
||||
fn create_test_bars(count: usize, base_price: f64) -> Vec<OHLCVBar> {
|
||||
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,
|
||||
|
||||
@@ -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<chrono::Utc>,
|
||||
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)]
|
||||
|
||||
@@ -17,16 +17,7 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
/// OHLCV bar structure for trending analysis
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OHLCVBar {
|
||||
pub timestamp: chrono::DateTime<chrono::Utc>,
|
||||
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)]
|
||||
|
||||
@@ -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<Utc>,
|
||||
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 {
|
||||
|
||||
8
ml/src/types/mod.rs
Normal file
8
ml/src/types/mod.rs
Normal file
@@ -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;
|
||||
41
ml/src/types/ohlcv.rs
Normal file
41
ml/src/types/ohlcv.rs
Normal file
@@ -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<Utc>,
|
||||
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::<Utc>::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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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<OHLCVBar> {
|
||||
fn create_synthetic_bars(count: usize) -> Vec<OHLCVBarF32> {
|
||||
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,
|
||||
|
||||
@@ -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<OHLCVBar> {
|
||||
fn create_test_bars() -> Vec<OHLCVBarF32> {
|
||||
vec![
|
||||
OHLCVBar {
|
||||
OHLCVBarF32 {
|
||||
timestamp: 1000,
|
||||
open: 100.0,
|
||||
high: 100.0,
|
||||
@@ -339,7 +339,7 @@ fn create_test_bars() -> Vec<OHLCVBar> {
|
||||
close: 100.0,
|
||||
volume: 1000.0,
|
||||
},
|
||||
OHLCVBar {
|
||||
OHLCVBarF32 {
|
||||
timestamp: 2000,
|
||||
open: 100.5,
|
||||
high: 100.5,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Vec<f32>> {
|
||||
fn build_features_from_loader(loader: &RealDataLoader, bars: &[ml::types::OHLCVBar]) -> Vec<Vec<f32>> {
|
||||
let feat_matrix = loader
|
||||
.extract_features(bars)
|
||||
.expect("extract_features failed");
|
||||
|
||||
Reference in New Issue
Block a user