- 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
8.4 KiB
8.4 KiB
Test Fixtures Quick Start Guide
TL;DR
Before (Slow, 5-10ms per test):
let mut file_mapping = HashMap::new();
file_mapping.insert("ES.FUT".to_string(), "path/to/ES.FUT.dbn".to_string());
let data_source = DbnDataSource::new(file_mapping).await?;
let bars = data_source.load_ohlcv_bars("ES.FUT").await?;
After (Fast, 0.1μs per test):
use fixtures::get_es_fut_bars;
let bars = get_es_fut_bars().await?; // That's it!
Quick Examples
1. Load Real Data (Fastest Way)
use fixtures::{get_es_fut_bars, get_nq_fut_bars, get_cl_fut_bars};
#[tokio::test]
async fn test_my_strategy() -> anyhow::Result<()> {
let bars = get_es_fut_bars().await?; // Cached, fast
// Test your code...
Ok(())
}
2. Validate Data Quality
use helpers::{assert_valid_ohlcv, assert_chronological};
#[test]
fn test_data_quality() {
let bars = load_data();
assert_valid_ohlcv(&bars); // Validates price relationships
assert_chronological(&bars); // Validates timestamp order
}
3. Test Specific Market Conditions
use fixtures::{get_regime_sample, RegimeType};
#[tokio::test]
async fn test_trending_strategy() -> anyhow::Result<()> {
let bars = get_regime_sample(RegimeType::Trending).await?;
// bars now contain trending market data
Ok(())
}
4. Multi-Symbol Testing
use fixtures::get_multi_symbol_bars;
#[tokio::test]
async fn test_portfolio() -> anyhow::Result<()> {
let symbols = vec!["ES.FUT", "NQ.FUT"];
let data = get_multi_symbol_bars(&symbols).await?;
// data["ES.FUT"] → ES.FUT bars
// data["NQ.FUT"] → NQ.FUT bars
Ok(())
}
API Reference (One Page)
Load Data
| Function | Returns | Use Case |
|---|---|---|
get_es_fut_bars() |
Vec | E-mini S&P 500 (~390 bars) |
get_nq_fut_bars() |
Vec | E-mini NASDAQ-100 (~390 bars) |
get_cl_fut_bars() |
Vec | WTI Crude Oil (~1440 bars) |
get_multi_symbol_bars(&[symbols]) |
HashMap<String, Vec> | Multiple symbols at once |
get_bars_for_date(symbol, date) |
Vec | Specific date only |
get_regime_sample(regime_type) |
Vec | Trending/Ranging/Volatile/Stable |
Validate Data
| Function | Validates |
|---|---|
assert_valid_ohlcv(&bars) |
High≥Low, Open/Close within range, positive prices |
assert_chronological(&bars) |
Timestamps sorted ascending |
assert_price_range(&bars, symbol) |
Realistic price ranges (ES: 3000-6000) |
assert_no_large_gaps(&bars, max_min) |
No gaps > N minutes |
Validate Trades
| Function | Validates |
|---|---|
assert_valid_trade(&trade) |
Exit > Entry time, positive prices, PnL correct |
assert_valid_trade_sequence(&trades) |
No overlaps, chronological order |
Validate Metrics
| Function | Validates |
|---|---|
assert_sharpe_bounds(sharpe, min, max) |
Sharpe ratio realistic (-3 to 5) |
assert_drawdown_bounds(dd, max) |
Drawdown ≤ max% |
assert_win_rate_valid(rate) |
Win rate 0-100% |
Utilities
| Function | Returns | Use Case |
|---|---|---|
calculate_volatility(&bars) |
f64 | Annualized volatility % |
generate_quality_report(&bars) |
String | Comprehensive data analysis |
Regime Types
pub enum RegimeType {
Trending, // Strong directional movement (>1.5% change)
Ranging, // Bounded oscillation (<0.8% range)
Volatile, // High fluctuations (>0.5% std dev)
Stable, // Low volatility (<0.3% std dev)
}
Usage:
let trending = get_regime_sample(RegimeType::Trending).await?;
let ranging = get_regime_sample(RegimeType::Ranging).await?;
let volatile = get_regime_sample(RegimeType::Volatile).await?;
let stable = get_regime_sample(RegimeType::Stable).await?;
Performance
| Operation | Time | Notes |
|---|---|---|
| First call | 5-10ms | Load from DBN file |
| Subsequent calls | ~0.1μs | Read from cache |
| 100 tests | ~10ms | 50-100x faster |
Common Patterns
Pattern 1: Basic Strategy Test
use fixtures::get_es_fut_bars;
use helpers::assert_valid_ohlcv;
#[tokio::test]
async fn test_my_strategy() -> anyhow::Result<()> {
let bars = get_es_fut_bars().await?;
assert_valid_ohlcv(&bars);
let signals = my_strategy.generate_signals(&bars);
assert!(!signals.is_empty());
Ok(())
}
Pattern 2: Metrics Validation
use helpers::{assert_sharpe_bounds, assert_drawdown_bounds};
#[test]
fn test_backtest_metrics() {
let metrics = run_backtest();
assert_sharpe_bounds(metrics.sharpe, -3.0, 5.0);
assert_drawdown_bounds(metrics.max_dd, 50.0);
}
Pattern 3: Data Quality Report
use fixtures::get_es_fut_bars;
use helpers::generate_quality_report;
#[tokio::test]
async fn test_print_report() -> anyhow::Result<()> {
let bars = get_es_fut_bars().await?;
println!("{}", generate_quality_report(&bars));
Ok(())
}
Pattern 4: Multi-Regime Testing
use fixtures::{get_regime_sample, RegimeType};
#[tokio::test]
async fn test_all_regimes() -> anyhow::Result<()> {
for regime in &[
RegimeType::Trending,
RegimeType::Ranging,
RegimeType::Volatile,
RegimeType::Stable,
] {
let bars = get_regime_sample(*regime).await?;
test_strategy_on_regime(&bars, regime);
}
Ok(())
}
Troubleshooting
"File not found" error
# Check files exist
ls test_data/real/databento/*.dbn
# Should see:
# ES.FUT_ohlcv-1m_2024-01-02.dbn
# NQ.FUT_ohlcv-1m_2024-01-02.dbn
# CL.FUT_ohlcv-1m_2024-01-02.dbn
Tests still slow
// ❌ Don't bypass cache
let data_source = DbnDataSource::new(file_mapping).await?;
// ✅ Use cached fixtures
let bars = get_es_fut_bars().await?;
Need different date
// Not yet implemented (only 2024-01-02 available)
// Add more DBN files to test_data/real/databento/
Import Cheatsheet
// At top of test file
mod fixtures;
mod helpers;
// In test functions
use fixtures::{
get_es_fut_bars, get_nq_fut_bars, get_cl_fut_bars,
get_regime_sample, RegimeType,
get_multi_symbol_bars, get_bars_for_date,
};
use helpers::{
assert_valid_ohlcv, assert_chronological, assert_price_range,
assert_valid_trade, assert_valid_trade_sequence,
assert_sharpe_bounds, assert_drawdown_bounds,
calculate_volatility, generate_quality_report,
};
Full Example Test File
//! My strategy tests
mod fixtures;
mod helpers;
use anyhow::Result;
use fixtures::{get_es_fut_bars, get_regime_sample, RegimeType};
use helpers::{assert_valid_ohlcv, assert_chronological};
#[tokio::test]
async fn test_strategy_trending() -> Result<()> {
// Load trending market data (cached)
let bars = get_regime_sample(RegimeType::Trending).await?;
// Validate data quality
assert_valid_ohlcv(&bars);
assert_chronological(&bars);
// Test strategy
let signals = my_trend_strategy.generate(&bars);
assert!(!signals.is_empty(), "Should generate signals in trend");
Ok(())
}
#[tokio::test]
async fn test_strategy_ranging() -> Result<()> {
// Load ranging market data
let bars = get_regime_sample(RegimeType::Ranging).await?;
// Validate
assert_valid_ohlcv(&bars);
// Test
let signals = my_mean_reversion_strategy.generate(&bars);
assert!(!signals.is_empty(), "Should generate signals in range");
Ok(())
}
#[tokio::test]
async fn test_full_day() -> Result<()> {
// Load full day of real data
let bars = get_es_fut_bars().await?;
// Comprehensive validation
assert_valid_ohlcv(&bars);
assert_chronological(&bars);
// Run full backtest
let results = backtest(&bars);
// Validate metrics
use helpers::{assert_sharpe_bounds, assert_drawdown_bounds};
assert_sharpe_bounds(results.sharpe, -3.0, 5.0);
assert_drawdown_bounds(results.max_dd, 50.0);
Ok(())
}
Next Steps
- Copy pattern above for your tests
- Replace manual DBN loading with
get_es_fut_bars() - Add validation helpers to catch bugs early
- Run tests and enjoy 50-100x speedup!
More Info
- Full documentation:
fixtures/README.md - Performance analysis:
fixtures/PERFORMANCE.md - Examples:
fixtures_tests.rs
Questions? See comprehensive docs in fixtures/README.md
Quick start: Just call get_es_fut_bars().await? and you're done!