From 9847a9956111887825cdc6f942b15b343d32fa75 Mon Sep 17 00:00:00 2001 From: KhizarImran Date: Sat, 13 Jun 2026 12:01:44 +0100 Subject: [PATCH] feat: stoploss and take profit implementation on the broker.rs --- examples/simple_strategy.rs | 2 +- src/broker.rs | 64 ++++++++++++++++++++++++++++++++++--- src/engine.rs | 1 + src/types.rs | 5 ++- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/examples/simple_strategy.rs b/examples/simple_strategy.rs index 10e7bc0..c63426e 100644 --- a/examples/simple_strategy.rs +++ b/examples/simple_strategy.rs @@ -10,7 +10,7 @@ struct BuyEveryBar; impl Strategy for BuyEveryBar { fn next (&mut self, bar: &Bar, broker: &mut Broker) { broker.close_all(bar.close, bar.timestamp); //closes any open positions - broker.buy(bar.close, 1.0, bar.timestamp); // can be more complicated with buy, sells, close position, close all etc. + broker.buy(bar.close, 1.0, bar.timestamp, None, None); // can be more complicated with buy, sells, close position, close all etc. } } diff --git a/src/broker.rs b/src/broker.rs index 98f3a86..e142f2b 100644 --- a/src/broker.rs +++ b/src/broker.rs @@ -1,5 +1,5 @@ -use crate::types::{Position, Trade}; +use crate::types::{Position, Trade, Bar}; pub struct Broker{ pub cash: f64, @@ -22,20 +22,21 @@ impl Broker { } } - 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::) + pub fn buy(&mut self, price: f64, lot_size: f64, timestamp: i64, stop_loss: Option, take_profit: Option) { // 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: self.positions.len() as u64, entry_price: fill_price, lot_size, is_long: true, entry_timestamp: timestamp, + stop_loss, + take_profit }); } - pub fn sell(&mut self, price: f64, lot_size: f64, timestamp: i64) { + pub fn sell(&mut self, price: f64, lot_size: f64, timestamp: i64, stop_loss: Option, take_profit: Option) { 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 { @@ -43,7 +44,9 @@ impl Broker { entry_price: fill_price, lot_size, is_long: false, - entry_timestamp: timestamp + entry_timestamp: timestamp, + stop_loss, + take_profit }); } @@ -116,4 +119,55 @@ impl Broker { self.cash + unrealized } + pub fn check_sl_tp(&mut self, bar: &Bar) { + let mut i = 0; + while i < self.positions.len() { + let fill = { + let p = &self.positions[i]; + if p.is_long{ + let sl_hit = p.stop_loss.map_or(false, |sl| bar.low <= sl); + let tp_hit = p.take_profit.map_or(false, |tp| bar.high >= tp); + if sl_hit {p.stop_loss} + else if tp_hit {p.take_profit} + else {None} + } else { + let sl_hit = p.stop_loss.map_or(false, |sl| bar.high >= sl); + let tp_hit = p.take_profit.map_or(false, |tp| bar.low <= tp); + if sl_hit {p.stop_loss} + else if tp_hit {p.take_profit} + else {None} + } + }; + + if let Some(fill_price) = fill { + let position = self.positions.remove(i); + let close_price = if position.is_long { + fill_price - self.spread + } else { + fill_price + self.spread + }; + + let pnl = if position.is_long { + (close_price - position.entry_price) * position.lot_size + } else { + (position.entry_price - close_price) * position.lot_size + }; + self.cash += pnl; + self.trade_history.push(Trade { + entry_price: position.entry_price, + exit_price: close_price, + lot_size: position.lot_size, + is_long: position.is_long, + pnl, + entry_timestamp: position.entry_timestamp, + exit_timestamp: bar.timestamp + }); + } else { + i+=1; + } + + } + + } + } diff --git a/src/engine.rs b/src/engine.rs index 2ba6e16..926957b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -21,6 +21,7 @@ impl Engine { pub fn run (&mut self, strategy: &mut dyn Strategy) -> Stats { strategy.init(&self.data); for bar in &self.data { + self.broker.check_sl_tp(bar); strategy.next(bar, &mut self.broker); self.equity_curve.push(self.broker.equity(bar.close)); } diff --git a/src/types.rs b/src/types.rs index 83adac7..156201c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -14,7 +14,10 @@ pub struct Position { // this is for the trading position pub entry_price: f64, pub lot_size: f64, pub is_long: bool, - pub entry_timestamp: i64 + pub entry_timestamp: i64, + pub stop_loss: Option, + pub take_profit: Option + } #[derive(Debug, Clone)]