133 lines
No EOL
3.8 KiB
Rust
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()
|
|
}
|
|
} |