343 lines
11 KiB
Rust
343 lines
11 KiB
Rust
//! Sample data for AlgoArena demos.
|
|
|
|
use algoarena_shared::{AgentConfig, AgentParameters, AssetConfig, StrategyType, TournamentConfig};
|
|
|
|
/// Create a sample tournament configuration.
|
|
#[must_use]
|
|
pub fn create_sample_tournament() -> TournamentConfig {
|
|
TournamentConfig {
|
|
name: "Strategy Showdown 2026".to_string(),
|
|
num_steps: 252, // One trading year
|
|
assets: create_diverse_assets(),
|
|
agents: create_competing_agents(),
|
|
transaction_cost_bps: 10.0,
|
|
seed: Some(42),
|
|
}
|
|
}
|
|
|
|
/// Create a diverse set of assets for trading.
|
|
#[must_use]
|
|
pub fn create_diverse_assets() -> Vec<AssetConfig> {
|
|
vec![
|
|
AssetConfig {
|
|
symbol: "SPY".to_string(),
|
|
initial_price: 500.0,
|
|
volatility: 0.012, // ~19% annual
|
|
drift: 0.0003, // ~7.5% annual
|
|
},
|
|
AssetConfig {
|
|
symbol: "QQQ".to_string(),
|
|
initial_price: 450.0,
|
|
volatility: 0.015, // ~24% annual
|
|
drift: 0.0004, // ~10% annual
|
|
},
|
|
AssetConfig {
|
|
symbol: "TLT".to_string(),
|
|
initial_price: 95.0,
|
|
volatility: 0.010, // ~16% annual
|
|
drift: 0.0001, // ~2.5% annual
|
|
},
|
|
AssetConfig {
|
|
symbol: "GLD".to_string(),
|
|
initial_price: 200.0,
|
|
volatility: 0.008, // ~13% annual
|
|
drift: 0.0002, // ~5% annual
|
|
},
|
|
]
|
|
}
|
|
|
|
/// Create a set of competing agents with different strategies.
|
|
#[must_use]
|
|
pub fn create_competing_agents() -> Vec<AgentConfig> {
|
|
vec![
|
|
// Momentum Strategies
|
|
AgentConfig {
|
|
id: "momentum_fast".to_string(),
|
|
name: "Fast Momentum".to_string(),
|
|
strategy: StrategyType::Momentum,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
lookback: 10,
|
|
momentum_threshold: 0.015,
|
|
position_size: 0.15,
|
|
..Default::default()
|
|
},
|
|
},
|
|
AgentConfig {
|
|
id: "momentum_slow".to_string(),
|
|
name: "Slow Momentum".to_string(),
|
|
strategy: StrategyType::Momentum,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
lookback: 30,
|
|
momentum_threshold: 0.03,
|
|
position_size: 0.2,
|
|
..Default::default()
|
|
},
|
|
},
|
|
// Mean Reversion Strategies
|
|
AgentConfig {
|
|
id: "reversion_tight".to_string(),
|
|
name: "Tight Reverter".to_string(),
|
|
strategy: StrategyType::MeanReversion,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
lookback: 15,
|
|
reversion_threshold: 1.5,
|
|
position_size: 0.1,
|
|
..Default::default()
|
|
},
|
|
},
|
|
AgentConfig {
|
|
id: "reversion_wide".to_string(),
|
|
name: "Wide Reverter".to_string(),
|
|
strategy: StrategyType::MeanReversion,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
lookback: 30,
|
|
reversion_threshold: 2.5,
|
|
position_size: 0.15,
|
|
..Default::default()
|
|
},
|
|
},
|
|
// Trend Following Strategies
|
|
AgentConfig {
|
|
id: "trend_classic".to_string(),
|
|
name: "Classic Trend".to_string(),
|
|
strategy: StrategyType::TrendFollowing,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
short_ma_period: 10,
|
|
long_ma_period: 50,
|
|
position_size: 0.15,
|
|
..Default::default()
|
|
},
|
|
},
|
|
AgentConfig {
|
|
id: "trend_fast".to_string(),
|
|
name: "Fast Trend".to_string(),
|
|
strategy: StrategyType::TrendFollowing,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
short_ma_period: 5,
|
|
long_ma_period: 20,
|
|
position_size: 0.2,
|
|
..Default::default()
|
|
},
|
|
},
|
|
// Passive Strategy
|
|
AgentConfig {
|
|
id: "buyhold".to_string(),
|
|
name: "Buy & Hold".to_string(),
|
|
strategy: StrategyType::BuyAndHold,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
// Random Baseline
|
|
AgentConfig {
|
|
id: "random".to_string(),
|
|
name: "Random Walker".to_string(),
|
|
strategy: StrategyType::Random,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
]
|
|
}
|
|
|
|
/// Create a quick demo tournament (fewer steps for testing).
|
|
#[must_use]
|
|
pub fn create_quick_tournament() -> TournamentConfig {
|
|
TournamentConfig {
|
|
name: "Quick Demo".to_string(),
|
|
num_steps: 50, // ~2 months
|
|
assets: vec![
|
|
AssetConfig {
|
|
symbol: "SPY".to_string(),
|
|
initial_price: 500.0,
|
|
volatility: 0.012,
|
|
drift: 0.0003,
|
|
},
|
|
AssetConfig {
|
|
symbol: "QQQ".to_string(),
|
|
initial_price: 450.0,
|
|
volatility: 0.015,
|
|
drift: 0.0004,
|
|
},
|
|
],
|
|
agents: vec![
|
|
AgentConfig {
|
|
id: "momentum".to_string(),
|
|
name: "Momentum".to_string(),
|
|
strategy: StrategyType::Momentum,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
AgentConfig {
|
|
id: "reversion".to_string(),
|
|
name: "Mean Reversion".to_string(),
|
|
strategy: StrategyType::MeanReversion,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
AgentConfig {
|
|
id: "buyhold".to_string(),
|
|
name: "Buy & Hold".to_string(),
|
|
strategy: StrategyType::BuyAndHold,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
],
|
|
transaction_cost_bps: 10.0,
|
|
seed: Some(42),
|
|
}
|
|
}
|
|
|
|
/// Create a high-volatility tournament.
|
|
#[must_use]
|
|
pub fn create_volatile_tournament() -> TournamentConfig {
|
|
TournamentConfig {
|
|
name: "Volatility Challenge".to_string(),
|
|
num_steps: 100,
|
|
assets: vec![
|
|
AssetConfig {
|
|
symbol: "CRYPTO".to_string(),
|
|
initial_price: 50000.0,
|
|
volatility: 0.04, // ~63% annual
|
|
drift: 0.001, // ~25% annual
|
|
},
|
|
AssetConfig {
|
|
symbol: "MEME".to_string(),
|
|
initial_price: 10.0,
|
|
volatility: 0.08, // ~127% annual
|
|
drift: 0.0, // No drift
|
|
},
|
|
],
|
|
agents: create_competing_agents(),
|
|
transaction_cost_bps: 25.0, // Higher costs
|
|
seed: Some(123),
|
|
}
|
|
}
|
|
|
|
/// Create a low-volatility tournament.
|
|
#[must_use]
|
|
pub fn create_stable_tournament() -> TournamentConfig {
|
|
TournamentConfig {
|
|
name: "Stability Test".to_string(),
|
|
num_steps: 200,
|
|
assets: vec![
|
|
AssetConfig {
|
|
symbol: "BOND1".to_string(),
|
|
initial_price: 100.0,
|
|
volatility: 0.003, // ~5% annual
|
|
drift: 0.0001, // ~2.5% annual
|
|
},
|
|
AssetConfig {
|
|
symbol: "BOND2".to_string(),
|
|
initial_price: 100.0,
|
|
volatility: 0.004, // ~6% annual
|
|
drift: 0.00015, // ~3.8% annual
|
|
},
|
|
],
|
|
agents: vec![
|
|
AgentConfig {
|
|
id: "reversion".to_string(),
|
|
name: "Mean Reversion".to_string(),
|
|
strategy: StrategyType::MeanReversion,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters {
|
|
reversion_threshold: 1.0, // Tighter for low vol
|
|
..Default::default()
|
|
},
|
|
},
|
|
AgentConfig {
|
|
id: "trend".to_string(),
|
|
name: "Trend Following".to_string(),
|
|
strategy: StrategyType::TrendFollowing,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
AgentConfig {
|
|
id: "buyhold".to_string(),
|
|
name: "Buy & Hold".to_string(),
|
|
strategy: StrategyType::BuyAndHold,
|
|
initial_capital: 100_000.0,
|
|
parameters: AgentParameters::default(),
|
|
},
|
|
],
|
|
transaction_cost_bps: 5.0, // Lower costs for bonds
|
|
seed: Some(456),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_sample_tournament() {
|
|
let config = create_sample_tournament();
|
|
assert_eq!(config.num_steps, 252);
|
|
assert!(!config.assets.is_empty());
|
|
assert!(!config.agents.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_diverse_assets() {
|
|
let assets = create_diverse_assets();
|
|
assert_eq!(assets.len(), 4);
|
|
|
|
// Check all have positive prices
|
|
for asset in &assets {
|
|
assert!(asset.initial_price > 0.0);
|
|
assert!(asset.volatility > 0.0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_competing_agents() {
|
|
let agents = create_competing_agents();
|
|
assert_eq!(agents.len(), 8);
|
|
|
|
// Check all have positive capital
|
|
for agent in &agents {
|
|
assert!(agent.initial_capital > 0.0);
|
|
}
|
|
|
|
// Check unique IDs
|
|
let ids: Vec<_> = agents.iter().map(|a| &a.id).collect();
|
|
let unique_ids: std::collections::HashSet<_> = ids.iter().collect();
|
|
assert_eq!(ids.len(), unique_ids.len());
|
|
}
|
|
|
|
#[test]
|
|
fn test_quick_tournament() {
|
|
let config = create_quick_tournament();
|
|
assert_eq!(config.num_steps, 50);
|
|
assert_eq!(config.agents.len(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_volatile_tournament() {
|
|
let config = create_volatile_tournament();
|
|
assert!(config.assets[0].volatility > 0.03); // High volatility
|
|
}
|
|
|
|
#[test]
|
|
fn test_stable_tournament() {
|
|
let config = create_stable_tournament();
|
|
assert!(config.assets[0].volatility < 0.01); // Low volatility
|
|
}
|
|
|
|
#[test]
|
|
fn test_strategy_diversity() {
|
|
let agents = create_competing_agents();
|
|
let strategies: std::collections::HashSet<_> = agents.iter().map(|a| a.strategy).collect();
|
|
|
|
assert!(strategies.contains(&StrategyType::Momentum));
|
|
assert!(strategies.contains(&StrategyType::MeanReversion));
|
|
assert!(strategies.contains(&StrategyType::TrendFollowing));
|
|
assert!(strategies.contains(&StrategyType::BuyAndHold));
|
|
assert!(strategies.contains(&StrategyType::Random));
|
|
}
|
|
}
|