stock-bot/apps/stock/core/src/backtest/mod.rs
2025-07-03 21:45:08 -04:00

133 lines
No EOL
3.8 KiB
Rust

use crate::{MarketUpdate, Order, Fill, TradingMode, MarketDataSource, ExecutionHandler, TimeProvider, Side};
use crate::positions::PositionTracker;
use crate::risk::RiskEngine;
use crate::orderbook::OrderBookManager;
use chrono::{DateTime, Utc};
use std::collections::{BTreeMap, VecDeque};
use std::sync::Arc;
use parking_lot::RwLock;
use serde::{Serialize, Deserialize};
pub mod engine;
pub mod event;
pub mod strategy;
pub mod results;
pub use engine::BacktestEngine;
pub use event::{BacktestEvent, EventType};
pub use strategy::{Strategy, Signal, SignalType};
pub use results::{BacktestResult, BacktestMetrics};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletedTrade {
pub symbol: String,
pub side: Side,
pub timestamp: DateTime<Utc>,
pub price: f64,
pub quantity: f64,
pub commission: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BacktestConfig {
pub name: String,
pub symbols: Vec<String>,
pub start_time: DateTime<Utc>,
pub end_time: DateTime<Utc>,
pub initial_capital: f64,
pub commission: f64,
pub slippage: f64,
pub data_frequency: String,
}
#[derive(Debug, Clone)]
pub struct BacktestState {
pub current_time: DateTime<Utc>,
pub portfolio_value: f64,
pub cash: f64,
pub equity_curve: Vec<(DateTime<Utc>, f64)>,
pub pending_orders: BTreeMap<String, Order>,
pub completed_trades: Vec<CompletedTrade>,
}
impl BacktestState {
pub fn new(initial_capital: f64, start_time: DateTime<Utc>) -> Self {
Self {
current_time: start_time,
portfolio_value: initial_capital,
cash: initial_capital,
equity_curve: vec![(start_time, initial_capital)],
pending_orders: BTreeMap::new(),
completed_trades: Vec::new(),
}
}
pub fn update_portfolio_value(&mut self, value: f64) {
self.portfolio_value = value;
self.equity_curve.push((self.current_time, value));
}
pub fn add_pending_order(&mut self, order: Order) {
self.pending_orders.insert(order.id.clone(), order);
}
pub fn remove_pending_order(&mut self, order_id: &str) -> Option<Order> {
self.pending_orders.remove(order_id)
}
pub fn record_fill(&mut self, symbol: String, side: Side, fill: Fill) {
self.completed_trades.push(CompletedTrade {
symbol,
side,
timestamp: fill.timestamp,
price: fill.price,
quantity: fill.quantity,
commission: fill.commission,
});
}
}
// Event queue for deterministic replay
#[derive(Debug)]
pub struct EventQueue {
events: VecDeque<BacktestEvent>,
}
impl EventQueue {
pub fn new() -> Self {
Self {
events: VecDeque::new(),
}
}
pub fn push(&mut self, event: BacktestEvent) {
// Insert in time order
let pos = self.events.iter().position(|e| e.timestamp > event.timestamp)
.unwrap_or(self.events.len());
self.events.insert(pos, event);
}
pub fn pop_until(&mut self, timestamp: DateTime<Utc>) -> Vec<BacktestEvent> {
let mut events = Vec::new();
while let Some(event) = self.events.front() {
if event.timestamp <= timestamp {
events.push(self.events.pop_front().unwrap());
} else {
break;
}
}
events
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn peek_next(&self) -> Option<&BacktestEvent> {
self.events.front()
}
}