Files
rustytorch/demos/algoarena-shared/src/lib.rs
T
2026-03-04 00:08:42 +00:00

431 lines
12 KiB
Rust

//! Shared types for AlgoArena - Strategy Backtesting Battleground.
//!
//! This crate defines the IPC types for multi-agent trading simulation.
use serde::{Deserialize, Serialize};
// ============================================================================
// Market Types
// ============================================================================
/// Market state snapshot.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketState {
/// Current timestamp (step).
pub step: usize,
/// Current prices for each asset.
pub prices: Vec<f64>,
/// Price history (for lookback).
pub price_history: Vec<Vec<f64>>,
/// Volume for each asset.
pub volumes: Vec<f64>,
/// Market indicators.
pub indicators: MarketIndicators,
}
/// Market-wide indicators.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MarketIndicators {
/// Market return (%).
pub market_return: f64,
/// Volatility.
pub volatility: f64,
/// Trend strength (positive = bullish, negative = bearish).
pub trend: f64,
}
/// Asset configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetConfig {
/// Asset symbol.
pub symbol: String,
/// Initial price.
pub initial_price: f64,
/// Daily volatility.
pub volatility: f64,
/// Expected drift (daily).
pub drift: f64,
}
// ============================================================================
// Order and Execution Types
// ============================================================================
/// Order side.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum OrderSide {
/// Buy.
Buy,
/// Sell.
Sell,
}
/// Order type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum OrderType {
/// Market order.
Market,
/// Limit order.
Limit,
}
/// A trading order.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Order {
/// Agent ID.
pub agent_id: String,
/// Asset index.
pub asset_idx: usize,
/// Side.
pub side: OrderSide,
/// Order type.
pub order_type: OrderType,
/// Quantity (shares).
pub quantity: f64,
/// Limit price (for limit orders).
pub limit_price: Option<f64>,
}
/// Order execution result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Execution {
/// Original order.
pub order: Order,
/// Filled quantity.
pub filled_quantity: f64,
/// Average fill price.
pub fill_price: f64,
/// Transaction cost.
pub transaction_cost: f64,
/// Slippage.
pub slippage: f64,
/// Whether fully filled.
pub is_filled: bool,
}
// ============================================================================
// Agent Types
// ============================================================================
/// Strategy type for an agent.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum StrategyType {
/// Buy and hold.
BuyAndHold,
/// Momentum following.
Momentum,
/// Mean reversion.
MeanReversion,
/// Trend following (moving average crossover).
TrendFollowing,
/// Random (baseline).
Random,
/// Custom / RL-based.
Custom,
}
/// Agent configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
/// Agent ID.
pub id: String,
/// Agent name.
pub name: String,
/// Strategy type.
pub strategy: StrategyType,
/// Initial capital.
pub initial_capital: f64,
/// Parameters.
pub parameters: AgentParameters,
}
/// Agent parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentParameters {
/// Lookback window.
pub lookback: usize,
/// Position sizing (fraction of capital).
pub position_size: f64,
/// Stop loss (%).
pub stop_loss: Option<f64>,
/// Take profit (%).
pub take_profit: Option<f64>,
/// Short MA period (for trend following).
pub short_ma_period: usize,
/// Long MA period.
pub long_ma_period: usize,
/// Threshold for mean reversion.
pub reversion_threshold: f64,
/// Momentum threshold.
pub momentum_threshold: f64,
}
impl Default for AgentParameters {
fn default() -> Self {
Self {
lookback: 20,
position_size: 0.1,
stop_loss: Some(0.05),
take_profit: Some(0.10),
short_ma_period: 10,
long_ma_period: 50,
reversion_threshold: 2.0,
momentum_threshold: 0.01,
}
}
}
/// Agent state during simulation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentState {
/// Agent ID.
pub agent_id: String,
/// Current cash.
pub cash: f64,
/// Holdings (shares per asset).
pub holdings: Vec<f64>,
/// Portfolio value.
pub portfolio_value: f64,
/// P&L history.
pub pnl_history: Vec<f64>,
/// Number of trades.
pub trade_count: usize,
}
// ============================================================================
// Tournament Types
// ============================================================================
/// Tournament configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TournamentConfig {
/// Tournament name.
pub name: String,
/// Number of simulation steps.
pub num_steps: usize,
/// Asset configurations.
pub assets: Vec<AssetConfig>,
/// Agent configurations.
pub agents: Vec<AgentConfig>,
/// Transaction cost (basis points).
pub transaction_cost_bps: f64,
/// Random seed.
pub seed: Option<u64>,
}
/// Tournament result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TournamentResult {
/// Tournament config.
pub config: TournamentConfig,
/// Agent results (sorted by rank).
pub rankings: Vec<AgentResult>,
/// Final price paths.
pub price_paths: Vec<Vec<f64>>,
/// Market statistics.
pub market_stats: MarketStats,
}
/// Individual agent result.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentResult {
/// Agent ID.
pub agent_id: String,
/// Agent name.
pub agent_name: String,
/// Strategy type.
pub strategy: StrategyType,
/// Final rank (1 = best).
pub rank: usize,
/// Final portfolio value.
pub final_value: f64,
/// Total return (%).
pub total_return: f64,
/// Sharpe ratio.
pub sharpe_ratio: f64,
/// Maximum drawdown (%).
pub max_drawdown: f64,
/// Win rate (% of profitable trades).
pub win_rate: f64,
/// Number of trades.
pub trade_count: usize,
/// P&L history.
pub pnl_history: Vec<f64>,
}
/// Market statistics.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketStats {
/// Market return over period.
pub market_return: f64,
/// Market volatility.
pub market_volatility: f64,
/// Best performing asset.
pub best_asset: String,
/// Worst performing asset.
pub worst_asset: String,
}
// ============================================================================
// Sample Data Functions
// ============================================================================
/// Create default asset configurations.
#[must_use]
pub fn default_assets() -> Vec<AssetConfig> {
vec![
AssetConfig {
symbol: "SPY".to_string(),
initial_price: 500.0,
volatility: 0.012,
drift: 0.0003,
},
AssetConfig {
symbol: "QQQ".to_string(),
initial_price: 400.0,
volatility: 0.015,
drift: 0.0004,
},
AssetConfig {
symbol: "TLT".to_string(),
initial_price: 90.0,
volatility: 0.010,
drift: 0.0001,
},
]
}
/// Create default agent configurations.
#[must_use]
pub fn default_agents() -> Vec<AgentConfig> {
vec![
AgentConfig {
id: "momentum_1".to_string(),
name: "Momentum Master".to_string(),
strategy: StrategyType::Momentum,
initial_capital: 100_000.0,
parameters: AgentParameters {
lookback: 20,
momentum_threshold: 0.02,
..Default::default()
},
},
AgentConfig {
id: "reversion_1".to_string(),
name: "Mean Machine".to_string(),
strategy: StrategyType::MeanReversion,
initial_capital: 100_000.0,
parameters: AgentParameters {
lookback: 30,
reversion_threshold: 2.0,
..Default::default()
},
},
AgentConfig {
id: "trend_1".to_string(),
name: "Trend Tracker".to_string(),
strategy: StrategyType::TrendFollowing,
initial_capital: 100_000.0,
parameters: AgentParameters {
short_ma_period: 10,
long_ma_period: 50,
..Default::default()
},
},
AgentConfig {
id: "buyhold_1".to_string(),
name: "Buy & Hold".to_string(),
strategy: StrategyType::BuyAndHold,
initial_capital: 100_000.0,
parameters: AgentParameters::default(),
},
AgentConfig {
id: "random_1".to_string(),
name: "Random Walker".to_string(),
strategy: StrategyType::Random,
initial_capital: 100_000.0,
parameters: AgentParameters::default(),
},
]
}
/// Create a sample tournament configuration.
#[must_use]
pub fn sample_tournament() -> TournamentConfig {
TournamentConfig {
name: "Strategy Showdown".to_string(),
num_steps: 252, // One year
assets: default_assets(),
agents: default_agents(),
transaction_cost_bps: 10.0,
seed: Some(42),
}
}
// ============================================================================
// Tests
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_order_creation() {
let order = Order {
agent_id: "test".to_string(),
asset_idx: 0,
side: OrderSide::Buy,
order_type: OrderType::Market,
quantity: 10.0,
limit_price: None,
};
assert_eq!(order.side, OrderSide::Buy);
assert_eq!(order.quantity, 10.0);
}
#[test]
fn test_agent_parameters_default() {
let params = AgentParameters::default();
assert_eq!(params.lookback, 20);
assert!(params.stop_loss.is_some());
}
#[test]
fn test_default_assets() {
let assets = default_assets();
assert_eq!(assets.len(), 3);
assert_eq!(assets[0].symbol, "SPY");
}
#[test]
fn test_default_agents() {
let agents = default_agents();
assert_eq!(agents.len(), 5);
// Check strategy diversity
let strategies: Vec<_> = agents.iter().map(|a| a.strategy).collect();
assert!(strategies.contains(&StrategyType::Momentum));
assert!(strategies.contains(&StrategyType::MeanReversion));
}
#[test]
fn test_sample_tournament() {
let tournament = sample_tournament();
assert_eq!(tournament.num_steps, 252);
assert!(!tournament.assets.is_empty());
assert!(!tournament.agents.is_empty());
}
#[test]
fn test_serialization() {
let config = sample_tournament();
let json = serde_json::to_string(&config).unwrap();
assert!(json.contains("Strategy Showdown"));
let parsed: TournamentConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name, config.name);
}
}