Files
foxhunt/services/backtesting_service/tests/fixtures/QUICKSTART.md
jgrusewski e8a68ee39f Download 360 DBN files (36.3 MB) using Rust databento client
- 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
2025-10-13 13:30:02 +02:00

359 lines
8.4 KiB
Markdown

# Test Fixtures Quick Start Guide
## TL;DR
**Before** (Slow, 5-10ms per test):
```rust
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):
```rust
use fixtures::get_es_fut_bars;
let bars = get_es_fut_bars().await?; // That's it!
```
---
## Quick Examples
### 1. Load Real Data (Fastest Way)
```rust
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
```rust
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
```rust
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
```rust
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<MarketData> | E-mini S&P 500 (~390 bars) |
| `get_nq_fut_bars()` | Vec<MarketData> | E-mini NASDAQ-100 (~390 bars) |
| `get_cl_fut_bars()` | Vec<MarketData> | WTI Crude Oil (~1440 bars) |
| `get_multi_symbol_bars(&[symbols])` | HashMap<String, Vec> | Multiple symbols at once |
| `get_bars_for_date(symbol, date)` | Vec<MarketData> | Specific date only |
| `get_regime_sample(regime_type)` | Vec<MarketData> | 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
```rust
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**:
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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
```bash
# 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
```rust
// ❌ 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
```rust
// Not yet implemented (only 2024-01-02 available)
// Add more DBN files to test_data/real/databento/
```
---
## Import Cheatsheet
```rust
// 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
```rust
//! 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
1. **Copy pattern above** for your tests
2. **Replace manual DBN loading** with `get_es_fut_bars()`
3. **Add validation helpers** to catch bugs early
4. **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!