added trade-tracking and example rust strats

This commit is contained in:
Boki 2025-07-03 22:55:23 -04:00
parent 0a4702d12a
commit 3a7557c8f4
15 changed files with 2108 additions and 29 deletions

View file

@ -14,7 +14,7 @@ use crate::{
use super::{
BacktestConfig, BacktestState, EventQueue, BacktestEvent, EventType,
Strategy, Signal, SignalType, BacktestResult,
Strategy, Signal, SignalType, BacktestResult, TradeTracker,
};
pub struct BacktestEngine {
@ -38,6 +38,9 @@ pub struct BacktestEngine {
// Price tracking
last_prices: HashMap<String, f64>,
// Trade tracking
trade_tracker: TradeTracker,
}
impl BacktestEngine {
@ -67,6 +70,7 @@ impl BacktestEngine {
profitable_trades: 0,
total_pnl: 0.0,
last_prices: HashMap::new(),
trade_tracker: TradeTracker::new(),
}
}
@ -394,6 +398,9 @@ impl BacktestEngine {
// Record the fill with symbol and side information
self.state.write().record_fill(order.symbol.clone(), order.side, fill.clone());
// Track trades
self.trade_tracker.process_fill(&order.symbol, order.side, &fill);
// Update cash
let cash_change = match order.side {
crate::Side::Buy => -(fill.quantity * fill.price + fill.commission),
@ -410,6 +417,13 @@ impl BacktestEngine {
}
}
eprintln!("Fill processed: {} {} @ {} (side: {:?})",
fill.quantity, order.symbol, fill.price, order.side);
eprintln!("Current position after fill: {}",
self.position_tracker.get_position(&order.symbol)
.map(|p| p.quantity)
.unwrap_or(0.0));
// Update metrics
self.total_trades += 1;
if update.resulting_position.realized_pnl > 0.0 {
@ -470,24 +484,43 @@ impl BacktestEngine {
let total_pnl = realized_pnl + unrealized_pnl;
let total_return = (total_pnl / self.config.initial_capital) * 100.0;
// Get completed trades from trade tracker for metrics
let completed_trades = self.trade_tracker.get_completed_trades();
// Calculate metrics from completed trades
let completed_trade_count = completed_trades.len();
let profitable_trades = completed_trades.iter().filter(|t| t.pnl > 0.0).count();
let total_wins: f64 = completed_trades.iter().filter(|t| t.pnl > 0.0).map(|t| t.pnl).sum();
let total_losses: f64 = completed_trades.iter().filter(|t| t.pnl < 0.0).map(|t| t.pnl.abs()).sum();
let avg_win = if profitable_trades > 0 { total_wins / profitable_trades as f64 } else { 0.0 };
let avg_loss = if completed_trade_count > profitable_trades {
total_losses / (completed_trade_count - profitable_trades) as f64
} else { 0.0 };
let profit_factor = if total_losses > 0.0 { total_wins / total_losses } else { 0.0 };
// For the API, return all fills (not just completed trades)
// This shows all trading activity
let all_fills = state.completed_trades.clone();
let total_trades = all_fills.len();
BacktestResult {
config: self.config.clone(),
metrics: super::BacktestMetrics {
total_return,
total_trades: self.total_trades,
profitable_trades: self.profitable_trades,
win_rate: if self.total_trades > 0 {
(self.profitable_trades as f64 / self.total_trades as f64) * 100.0
total_trades,
profitable_trades,
win_rate: if completed_trade_count > 0 {
(profitable_trades as f64 / completed_trade_count as f64) * 100.0
} else { 0.0 },
profit_factor: 0.0, // TODO: Calculate properly
profit_factor,
sharpe_ratio: 0.0, // TODO: Calculate properly
max_drawdown: 0.0, // TODO: Calculate properly
total_pnl,
avg_win: 0.0, // TODO: Calculate properly
avg_loss: 0.0, // TODO: Calculate properly
avg_win,
avg_loss,
},
equity_curve: state.equity_curve.clone(),
trades: state.completed_trades.clone(),
trades: all_fills,
final_positions: self.position_tracker.get_all_positions()
.into_iter()
.map(|p| (p.symbol.clone(), p))