mirror of
https://github.com/KhizarImran/backtestingfx.git
synced 2026-07-27 20:17:44 +00:00
checkpoint: spread/commisions working
This commit is contained in:
@@ -3,6 +3,8 @@ use backtestingfx::broker::Broker;
|
||||
use backtestingfx::strategy::Strategy;
|
||||
use backtestingfx::engine::Engine;
|
||||
use backtestingfx::data::load_csv;
|
||||
use backtestingfx::stats::Stats;
|
||||
|
||||
|
||||
struct BuyEveryBar;
|
||||
|
||||
@@ -15,13 +17,13 @@ impl Strategy for BuyEveryBar {
|
||||
|
||||
fn main() {
|
||||
let data = load_csv("examples/data/eurusd_lse_1h.csv");
|
||||
let mut engine = Engine::new(data, 10000.0);
|
||||
let mut engine = Engine::new(data, 10_000.0, 0.0, 0.00010);
|
||||
let mut strategy = BuyEveryBar;
|
||||
|
||||
engine.run(&mut strategy); // main line that runs the strategy
|
||||
|
||||
println!("Final cash: {}", engine.broker.cash);
|
||||
println!("Trades executed: {}", engine.broker.trade_history.len());
|
||||
let stats = Stats::compute(&engine.broker, 10_000.0);
|
||||
println!("{}", stats);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+16
-7
@@ -4,32 +4,41 @@ use crate::types::{Position, Trade};
|
||||
pub struct Broker{
|
||||
pub cash: f64,
|
||||
pub positions: Vec<Position>,
|
||||
pub trade_history: Vec<Trade>
|
||||
pub trade_history: Vec<Trade>,
|
||||
pub commission: f64,
|
||||
pub spread: f64
|
||||
}
|
||||
|
||||
impl Broker {
|
||||
pub fn new(initial_cash: f64) -> Self { // does not need &mut because it initialises something new
|
||||
pub fn new(initial_cash: f64, commission: f64, spread: f64) -> Self { // does not need &mut because it initialises something new
|
||||
Broker {
|
||||
cash: initial_cash,
|
||||
positions: Vec::new(),
|
||||
trade_history: Vec::new()
|
||||
trade_history: Vec::new(),
|
||||
commission,
|
||||
spread
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buy(&mut self, price: f64, lot_size: f64, timestamp: i64) { // needs to modify the broker with new position. (.push works with the Vec::)
|
||||
let fill_price = price + self.spread; // buy at ask
|
||||
self.cash -= self.commission * lot_size; // pay commission
|
||||
|
||||
self.positions.push(Position {
|
||||
id: 0,
|
||||
entry_price: price,
|
||||
id: self.positions.len() as u64,
|
||||
entry_price: fill_price,
|
||||
lot_size,
|
||||
is_long: true,
|
||||
entry_timestamp: timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn sell(&mut self, price: f64, lot_size: f64, timestamp: i64) { // needs to modify the broker with new position. (.push works with the Vec::)
|
||||
pub fn sell(&mut self, price: f64, lot_size: f64, timestamp: i64) {
|
||||
let fill_price = price - self.spread; // buy at ask
|
||||
self.cash -= self.commission * lot_size; // pay commission // needs to modify the broker with new position. (.push works with the Vec::)
|
||||
self.positions.push(Position {
|
||||
id : 0,
|
||||
entry_price: price,
|
||||
entry_price: fill_price,
|
||||
lot_size,
|
||||
is_long: false,
|
||||
entry_timestamp: timestamp
|
||||
|
||||
+2
-2
@@ -8,10 +8,10 @@ pub struct Engine {
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new(data: Vec<Bar>, initial_cash: f64) -> Self {
|
||||
pub fn new(data: Vec<Bar>, initial_cash: f64, commission: f64, spread: f64) -> Self {
|
||||
Engine {
|
||||
data,
|
||||
broker : Broker::new(initial_cash) // it takes initial cash and not Broker as Engine is responsible for broker not the user
|
||||
broker : Broker::new(initial_cash, commission, spread) // it takes initial cash and not Broker as Engine is responsible for broker not the user
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod strategy;
|
||||
pub mod broker;
|
||||
pub mod engine;
|
||||
pub mod data;
|
||||
pub mod stats;
|
||||
@@ -0,0 +1,76 @@
|
||||
use crate::broker::Broker;
|
||||
|
||||
pub struct Stats {
|
||||
pub initial_cash: f64,
|
||||
pub final_cash: f64,
|
||||
pub total_return_pct: f64,
|
||||
pub num_trades: usize,
|
||||
pub num_wins: usize,
|
||||
pub win_rate_pct: f64,
|
||||
pub avg_pnl: f64,
|
||||
pub best_trade: f64,
|
||||
pub worst_trade: f64,
|
||||
pub profit_factor: f64,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub fn compute(broker: &Broker, initial_cash: f64) -> Self {
|
||||
let num_trades = broker.trade_history.len();
|
||||
let final_cash = broker.cash;
|
||||
let total_return_pct = (final_cash - initial_cash) / initial_cash * 100.0;
|
||||
|
||||
let num_wins = broker.trade_history.iter().filter(|t| t.pnl > 0.0).count();
|
||||
let win_rate_pct = if num_trades > 0 {
|
||||
num_wins as f64 / num_trades as f64 * 100.0
|
||||
} else { 0.0 };
|
||||
|
||||
let avg_pnl = if num_trades > 0 {
|
||||
broker.trade_history.iter().map(|t| t.pnl).sum::<f64>() / num_trades as f64
|
||||
} else { 0.0 };
|
||||
|
||||
let best_trade = broker.trade_history.iter().map(|t| t.pnl)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let worst_trade = broker.trade_history.iter().map(|t| t.pnl)
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
|
||||
let gross_profit: f64 = broker.trade_history.iter()
|
||||
.filter(|t| t.pnl > 0.0).map(|t| t.pnl).sum();
|
||||
let gross_loss: f64 = broker.trade_history.iter()
|
||||
.filter(|t| t.pnl < 0.0).map(|t| t.pnl.abs()).sum();
|
||||
let profit_factor = if gross_loss > 0.0 { gross_profit / gross_loss } else {
|
||||
f64::INFINITY };
|
||||
|
||||
Stats {
|
||||
initial_cash,
|
||||
final_cash,
|
||||
total_return_pct,
|
||||
num_trades,
|
||||
num_wins,
|
||||
win_rate_pct,
|
||||
avg_pnl,
|
||||
best_trade: if num_trades > 0 { best_trade } else { 0.0 },
|
||||
worst_trade: if num_trades > 0 { worst_trade } else { 0.0 },
|
||||
profit_factor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Stats {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f,
|
||||
"--- Backtest Results ---\n\
|
||||
Initial Cash: {:.2}\n\
|
||||
Final Cash: {:.2}\n\
|
||||
Total Return: {:.2}%\n\
|
||||
Trades: {}\n\
|
||||
Win Rate: {:.1}%\n\
|
||||
Avg PnL: {:.5}\n\
|
||||
Best Trade: {:.5}\n\
|
||||
Worst Trade: {:.5}\n\
|
||||
Profit Factor: {:.2}",
|
||||
self.initial_cash, self.final_cash, self.total_return_pct,
|
||||
self.num_trades, self.win_rate_pct, self.avg_pnl,
|
||||
self.best_trade, self.worst_trade, self.profit_factor
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user