- Created data/examples/download_ml_training_data.rs using reqwest + Databento HTTP API - Downloaded 90 days × 4 symbols (ES.FUT, NQ.FUT, ZN.FUT, 6E.FUT) - Files saved to test_data/real/databento/ml_training/ - Total: 360 files, 15 MB compressed DBN format - Used existing Rust pattern from download_nq_fut.rs - API key loaded from .env file - 100% success rate (360/360 files) - Ready for ML training benchmarks Next: Create simplified training benchmark for RTX 3050 Ti GPU measurements
117 lines
3.4 KiB
Rust
117 lines
3.4 KiB
Rust
//! Multi-Day DBN Loading Example
|
|
//!
|
|
//! This example shows how to load data from multiple DBN files (multi-day backtests).
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! ```bash
|
|
//! cargo run --example dbn_multi_day_loading
|
|
//! ```
|
|
|
|
use backtesting_service::dbn_data_source::DbnDataSource;
|
|
use std::collections::HashMap;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
println!("=== DBN Multi-Day Loading Example ===\n");
|
|
|
|
// 1. Create file mapping with multiple files per symbol
|
|
let mut file_mapping = HashMap::new();
|
|
file_mapping.insert(
|
|
"ESH4".to_string(),
|
|
vec![
|
|
"test_data/real/databento/ESH4_ohlcv-1m_2024-01-03.dbn".to_string(),
|
|
"test_data/real/databento/ESH4_ohlcv-1m_2024-01-04.dbn".to_string(),
|
|
"test_data/real/databento/ESH4_ohlcv-1m_2024-01-05.dbn".to_string(),
|
|
],
|
|
);
|
|
|
|
println!("Creating data source with 3 files for ESH4...");
|
|
|
|
// 2. Create data source (multi-file mode)
|
|
let data_source = DbnDataSource::new_multi_file(file_mapping).await?;
|
|
|
|
println!("Files configured: {}", data_source.get_file_count("ESH4"));
|
|
|
|
// 3. Load all files (merged and sorted)
|
|
println!("\nLoading all days for ESH4...");
|
|
let start = std::time::Instant::now();
|
|
let bars = data_source.load_ohlcv_bars_all("ESH4").await?;
|
|
let duration = start.elapsed();
|
|
|
|
println!(
|
|
"✅ Loaded {} bars from {} files in {:?} ({:.2}ms)",
|
|
bars.len(),
|
|
3,
|
|
duration,
|
|
duration.as_secs_f64() * 1000.0
|
|
);
|
|
|
|
// 4. Analyze data by day
|
|
println!("\n=== Data by Day ===");
|
|
|
|
let mut day_stats: HashMap<String, Vec<_>> = HashMap::new();
|
|
for bar in &bars {
|
|
let day = bar.timestamp.format("%Y-%m-%d").to_string();
|
|
day_stats.entry(day).or_default().push(bar);
|
|
}
|
|
|
|
for (day, bars_for_day) in day_stats.iter() {
|
|
let first = bars_for_day.first().unwrap();
|
|
let last = bars_for_day.last().unwrap();
|
|
|
|
println!(
|
|
"{}: {} bars | {} to {} | Range: {} to {}",
|
|
day,
|
|
bars_for_day.len(),
|
|
first.timestamp.format("%H:%M"),
|
|
last.timestamp.format("%H:%M"),
|
|
first.close,
|
|
last.close
|
|
);
|
|
}
|
|
|
|
// 5. Verify chronological ordering across all days
|
|
println!("\n=== Cross-Day Validation ===");
|
|
|
|
let mut ordered = true;
|
|
for i in 1..bars.len() {
|
|
if bars[i].timestamp < bars[i - 1].timestamp {
|
|
println!(
|
|
"❌ Ordering issue at index {}: {} < {}",
|
|
i,
|
|
bars[i].timestamp,
|
|
bars[i - 1].timestamp
|
|
);
|
|
ordered = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ordered {
|
|
println!("✅ All bars properly ordered across {} days", day_stats.len());
|
|
}
|
|
|
|
// 6. Calculate daily returns
|
|
println!("\n=== Daily Returns ===");
|
|
|
|
for (day, bars_for_day) in day_stats.iter() {
|
|
if bars_for_day.len() > 1 {
|
|
let first_close = bars_for_day.first().unwrap().close;
|
|
let last_close = bars_for_day.last().unwrap().close;
|
|
|
|
let return_pct =
|
|
((last_close - first_close) / first_close * rust_decimal::Decimal::from(100))
|
|
.to_string()
|
|
.parse::<f64>()
|
|
.unwrap();
|
|
|
|
println!("{}: {:.2}% return", day, return_pct);
|
|
}
|
|
}
|
|
|
|
println!("\n✅ Example completed successfully!");
|
|
|
|
Ok(())
|
|
}
|