use crate::finance::agent::{FinanceAgent, FinanceAgentStatus};
use crate::finance::ml::models::q_net::SimpleQNet;
use crate::finance::ml::traits::{MarketState, TradeAction};
pub struct MLFinanceAgent {
pub agent: FinanceAgent,
pub q_net: SimpleQNet,
pub epsilon: f32,
pub mutation_rate: f32,
pub mutation_strength: f32,
}
impl MLFinanceAgent {
pub fn new(
agent: FinanceAgent,
input_size: usize,
hidden_size: usize,
epsilon: f32,
mutation_rate: f32,
mutation_strength: f32,
) -> Result<Self, String> {
let q_net = SimpleQNet::new(input_size, hidden_size)
.map_err(|e| format!("Failed to create QNet: {:?}", e))?;
Ok(MLFinanceAgent {
agent,
q_net,
epsilon,
mutation_rate,
mutation_strength,
})
}
pub fn decide_trade(&self, market_state: &MarketState) -> TradeAction {
if rand::random::<f32>() < self.epsilon {
let rand_action = rand::random::<u32>() % 3;
match rand_action {
0 => TradeAction::Buy,
1 => TradeAction::Sell,
_ => TradeAction::Hold,
}
} else {
let mut state_vector = market_state.prices.clone();
while state_vector.len() < 5 {
state_vector.push(0.0);
}
state_vector.truncate(5);
match self.q_net.forward_pass(&state_vector) {
Ok(q_values) => {
let mut best_idx = 2;
for (idx, value) in q_values.iter().enumerate() {
if idx < q_values.len() && idx < 3 {
if idx == 0 || value > &q_values[best_idx] {
best_idx = idx;
}
}
}
let best_action = match best_idx {
0 => TradeAction::Buy,
1 => TradeAction::Sell,
_ => TradeAction::Hold,
};
best_action
}
Err(_) => TradeAction::Hold, }
}
}
pub fn execute_trade_ml(
&mut self,
market_state: &MarketState,
loss_occurred: bool,
loss_amount: u64,
) -> TradeAction {
let action = self.decide_trade(market_state);
if loss_occurred {
self.inflict_scar(loss_amount);
}
action
}
fn inflict_scar(&mut self, loss_amount: u64) {
self.agent.metrics.scar_count += 1;
let damage_factor = 0.01 * self.agent.metrics.scar_count as f32;
println!(
"🔴 Scar inflicted! Count: {} | Damage factor: {:.4} | Loss: {}",
self.agent.metrics.scar_count, damage_factor, loss_amount
);
if self.agent.metrics.scar_count >= self.agent.max_scars {
self.agent.status = FinanceAgentStatus::TerminallyDamaged;
println!("💀 Agent reached terminal damage level!");
}
}
pub fn spawn_offspring(&self) -> Result<MLFinanceAgent, String> {
let offspring_agent = FinanceAgent::new(
format!("{}_gen{}", self.agent.strategy, self.agent.metrics.generation + 1),
self.agent.metrics.capital / 2, self.agent.metrics.generation + 1,
);
let offspring = MLFinanceAgent::new(
offspring_agent,
5, 64, self.epsilon,
self.mutation_rate,
self.mutation_strength,
)?;
Ok(offspring)
}
pub fn decay_epsilon(&mut self, decay_rate: f32) {
self.epsilon = (self.epsilon * decay_rate).max(0.1);
}
}
pub struct MLAgentArena {
pub agents: Vec<MLFinanceAgent>,
pub round: u32,
}
impl MLAgentArena {
pub fn new(agents: Vec<MLFinanceAgent>) -> Self {
MLAgentArena { agents, round: 0 }
}
pub fn rank_agents(&mut self) {
self.agents.sort_by(|a, b| {
b.agent.metrics.capital.cmp(&a.agent.metrics.capital)
});
}
pub fn evolve(&mut self) -> Result<(), String> {
self.rank_agents();
let total_agents = self.agents.len();
let survivors = (total_agents as f32 * 0.5) as usize;
if survivors == 0 {
return Ok(()); }
let mut next_generation = Vec::new();
for i in 0..survivors.min(self.agents.len()) {
next_generation.push(self.agents[i].clone_agent());
}
let survivor_count = next_generation.len();
for i in 0..survivor_count {
if let Ok(offspring) = next_generation[i].spawn_offspring() {
next_generation.push(offspring);
}
}
self.agents = next_generation;
self.round += 1;
println!("⚡ Evolution Round {}: {} agents → {} agents",
self.round, total_agents, self.agents.len());
Ok(())
}
}
impl MLFinanceAgent {
fn clone_agent(&self) -> Self {
MLFinanceAgent {
agent: FinanceAgent::new(
self.agent.strategy.clone(),
self.agent.metrics.capital,
self.agent.metrics.generation,
),
q_net: self.q_net.clone(),
epsilon: self.epsilon,
mutation_rate: self.mutation_rate,
mutation_strength: self.mutation_strength,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_epsilon_decay() {
let agent = FinanceAgent::new("test".to_string(), 1000, 0);
let mut ml_agent = MLFinanceAgent::new(agent, 5, 64, 1.0, 0.1, 0.5)
.expect("Failed to create ML agent");
ml_agent.decay_epsilon(0.99);
assert!(ml_agent.epsilon < 1.0);
assert!(ml_agent.epsilon > 0.1);
}
#[test]
fn test_arena_ranking() {
let agent = FinanceAgent::new("agent1".to_string(), 1000, 0);
let ml_agent = MLFinanceAgent::new(agent, 5, 64, 0.5, 0.1, 0.5)
.expect("Failed to create ML agent");
let mut arena = MLAgentArena::new(vec![ml_agent]);
arena.rank_agents();
assert_eq!(arena.agents.len(), 1);
}
}