use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketState {
pub round: u64,
pub prices: HashMap<String, f32>,
pub volatility: f32,
pub trend: f32,
pub black_swan: bool,
}
impl MarketState {
pub fn new(round: u64) -> Self {
MarketState {
round,
prices: HashMap::new(),
volatility: 0.01,
trend: 0.0,
black_swan: false,
}
}
pub fn set_price(&mut self, asset: String, price: f32) {
self.prices.insert(asset, price);
}
pub fn next_state(&self) -> Self {
let mut next = MarketState::new(self.round + 1);
let drift = self.trend * 0.0001;
for (asset, price) in &self.prices {
let change = (rand::random::<f32>() - 0.5) * self.volatility * 2.0 + drift;
next.set_price(
asset.clone(),
(price * (1.0 + change)).max(0.01),
);
}
next.volatility = self.volatility * 0.95 + rand::random::<f32>() * 0.01;
next.trend = (self.trend + (rand::random::<f32>() - 0.5) * 0.1).clamp(-1.0, 1.0);
next.black_swan = rand::random::<f32>() < 0.01;
next
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompetitionResult {
pub agent_id: String,
pub rank: u32,
pub final_capital: u64,
pub return_percentage: f32,
pub trades_executed: u64,
pub wins: u64,
pub losses: u64,
pub scars_inflicted: u32,
pub ready_to_spawn: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArenaConfig {
pub rounds: u64,
pub initial_capital: u64,
pub tradeable_assets: Vec<String>,
pub max_leverage: f32,
pub spawn_threshold: f32,
pub death_threshold: f32,
}
impl Default for ArenaConfig {
fn default() -> Self {
ArenaConfig {
rounds: 1000,
initial_capital: 100000,
tradeable_assets: vec![
"BTC".to_string(),
"ETH".to_string(),
"SOL".to_string(),
"SPY".to_string(),
],
max_leverage: 10.0,
spawn_threshold: 25.0, death_threshold: -50.0, }
}
}
pub struct Arena {
pub config: ArenaConfig,
pub market_state: MarketState,
pub results: Vec<CompetitionResult>,
pub completed_rounds: u64,
pub total_trades_executed: u64,
pub total_agents_eliminated: u64,
pub total_spawns: u64,
}
impl Arena {
pub fn new(config: ArenaConfig) -> Self {
let mut market = MarketState::new(0);
for asset in &config.tradeable_assets {
market.set_price(asset.clone(), 100.0);
}
Arena {
config,
market_state: market,
results: Vec::new(),
completed_rounds: 0,
total_trades_executed: 0,
total_agents_eliminated: 0,
total_spawns: 0,
}
}
pub fn tick_round(&mut self) {
self.market_state = self.market_state.next_state();
self.completed_rounds += 1;
}
pub fn get_market_state(&self) -> &MarketState {
&self.market_state
}
pub fn record_result(&mut self, result: CompetitionResult) {
if !result.ready_to_spawn {
self.total_agents_eliminated += 1;
} else {
self.total_spawns += 1;
}
self.total_trades_executed += result.trades_executed;
self.results.push(result);
}
pub fn get_ranked_results(&self) -> Vec<CompetitionResult> {
let mut sorted = self.results.clone();
sorted.sort_by(|a, b| a.rank.cmp(&b.rank));
sorted
}
pub fn get_top_performers(&self, count: usize) -> Vec<CompetitionResult> {
self.get_ranked_results()
.into_iter()
.take(count)
.collect()
}
pub fn get_statistics(&self) -> ArenaStatistics {
let total_results = self.results.len();
let avg_return = if total_results > 0 {
self.results.iter().map(|r| r.return_percentage).sum::<f32>() / total_results as f32
} else {
0.0
};
let max_return = self.results.iter()
.map(|r| r.return_percentage)
.fold(f32::NEG_INFINITY, f32::max);
let min_return = self.results.iter()
.map(|r| r.return_percentage)
.fold(f32::INFINITY, f32::min);
ArenaStatistics {
total_agents: total_results as u64,
total_trades: self.total_trades_executed,
agents_eliminated: self.total_agents_eliminated,
spawns_created: self.total_spawns,
average_return: avg_return,
max_return,
min_return,
rounds_completed: self.completed_rounds,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArenaStatistics {
pub total_agents: u64,
pub total_trades: u64,
pub agents_eliminated: u64,
pub spawns_created: u64,
pub average_return: f32,
pub max_return: f32,
pub min_return: f32,
pub rounds_completed: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_market_state_evolution() {
let mut state = MarketState::new(0);
state.set_price("BTC".to_string(), 50000.0);
let next = state.next_state();
assert_eq!(next.round, 1);
assert!(next.prices.contains_key("BTC"));
}
#[test]
fn test_arena_creation() {
let config = ArenaConfig::default();
let arena = Arena::new(config);
assert_eq!(arena.completed_rounds, 0);
}
}