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

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

  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!