@@ -10,8 +10,8 @@ use trading_core::traits::{Broker, Strategy};
1010use trading_core:: types:: { Bar , BarSeries , Side , SignalType , Timeframe } ;
1111use trading_risk:: { RiskConfig , RiskManager } ;
1212
13- use crate :: statistics:: { BacktestStats , TradeRecord } ;
1413use crate :: report:: BacktestReport ;
14+ use crate :: statistics:: { BacktestStats , TradeRecord } ;
1515
1616/// Backtest configuration.
1717#[ derive( Debug , Clone , Serialize , Deserialize ) ]
@@ -101,65 +101,73 @@ impl BacktestEngine {
101101 if skip {
102102 // Don't process this signal, but continue processing the bar
103103 } else {
104- // Evaluate with risk manager
105- let current_price = Decimal :: try_from ( bar. close ) . unwrap_or ( dec ! ( 0 ) ) ;
106- let portfolio = broker. get_account ( ) . await . unwrap ( ) ;
107- let decision = risk_manager. evaluate_signal ( & portfolio, & signal, current_price) ;
104+ // Evaluate with risk manager
105+ let current_price = Decimal :: try_from ( bar. close ) . unwrap_or ( dec ! ( 0 ) ) ;
106+ let portfolio = broker. get_account ( ) . await . unwrap ( ) ;
107+ let decision =
108+ risk_manager. evaluate_signal ( & portfolio, & signal, current_price) ;
108109
109- if let Some ( order_request) = decision. order ( ) {
110- // Submit and execute order
111- if let Ok ( order) = broker. submit_order ( order_request. clone ( ) ) . await {
112- if let Ok ( filled) = broker. execute_at_price ( order. id , current_price) {
113- let fill_price = filled. filled_avg_price . unwrap_or ( current_price) ;
114- let fill_qty = filled. filled_quantity ;
110+ if let Some ( order_request) = decision. order ( ) {
111+ // Submit and execute order
112+ if let Ok ( order) = broker. submit_order ( order_request. clone ( ) ) . await {
113+ if let Ok ( filled) = broker. execute_at_price ( order. id , current_price)
114+ {
115+ let fill_price =
116+ filled. filled_avg_price . unwrap_or ( current_price) ;
117+ let fill_qty = filled. filled_quantity ;
115118
116- // Calculate P&L for closing trades
117- let pnl = match order_request. side {
118- Side :: Buy => {
119- // Opening a long position
120- let entry = open_positions. entry ( symbol. clone ( ) ) . or_insert ( ( Decimal :: ZERO , Decimal :: ZERO ) ) ;
121- // Weighted average entry price
122- if entry. 1 + fill_qty > Decimal :: ZERO {
123- entry. 0 = ( entry. 0 * entry. 1 + fill_price * fill_qty) / ( entry. 1 + fill_qty) ;
119+ // Calculate P&L for closing trades
120+ let pnl = match order_request. side {
121+ Side :: Buy => {
122+ // Opening a long position
123+ let entry = open_positions
124+ . entry ( symbol. clone ( ) )
125+ . or_insert ( ( Decimal :: ZERO , Decimal :: ZERO ) ) ;
126+ // Weighted average entry price
127+ if entry. 1 + fill_qty > Decimal :: ZERO {
128+ entry. 0 = ( entry. 0 * entry. 1
129+ + fill_price * fill_qty)
130+ / ( entry. 1 + fill_qty) ;
131+ }
132+ entry. 1 += fill_qty;
133+ None
124134 }
125- entry. 1 += fill_qty;
126- None
127- }
128- Side :: Sell => {
129- // Closing (or reducing) a long position
130- if let Some ( entry) = open_positions. get_mut ( & symbol) {
131- if entry. 1 > Decimal :: ZERO {
132- let close_qty = fill_qty. min ( entry. 1 ) ;
133- let trade_pnl = ( fill_price - entry. 0 ) * close_qty;
134- entry. 1 -= close_qty;
135- if entry. 1 <= Decimal :: ZERO {
136- open_positions. remove ( & symbol) ;
135+ Side :: Sell => {
136+ // Closing (or reducing) a long position
137+ if let Some ( entry) = open_positions. get_mut ( & symbol) {
138+ if entry. 1 > Decimal :: ZERO {
139+ let close_qty = fill_qty. min ( entry. 1 ) ;
140+ let trade_pnl =
141+ ( fill_price - entry. 0 ) * close_qty;
142+ entry. 1 -= close_qty;
143+ if entry. 1 <= Decimal :: ZERO {
144+ open_positions. remove ( & symbol) ;
145+ }
146+ Some ( trade_pnl)
147+ } else {
148+ None
137149 }
138- Some ( trade_pnl)
139150 } else {
140151 None
141152 }
142- } else {
143- None
144153 }
145- }
146- } ;
154+ } ;
147155
148- // Record trade
149- let trade = TradeRecord {
150- symbol : symbol. clone ( ) ,
151- side : order_request. side ,
152- quantity : fill_qty,
153- price : fill_price,
154- timestamp : DateTime :: from_timestamp_millis ( timestamp)
155- . unwrap_or_else ( || Utc :: now ( ) ) ,
156- signal_type : signal. signal_type ,
157- pnl,
158- } ;
159- stats. add_trade ( trade) ;
156+ // Record trade
157+ let trade = TradeRecord {
158+ symbol : symbol. clone ( ) ,
159+ side : order_request. side ,
160+ quantity : fill_qty,
161+ price : fill_price,
162+ timestamp : DateTime :: from_timestamp_millis ( timestamp)
163+ . unwrap_or_else ( Utc :: now) ,
164+ signal_type : signal. signal_type ,
165+ pnl,
166+ } ;
167+ stats. add_trade ( trade) ;
168+ }
160169 }
161170 }
162- }
163171 } // else (not skipped)
164172 }
165173 }
@@ -192,7 +200,7 @@ impl BacktestEngine {
192200 quantity : * quantity,
193201 price : close_price,
194202 timestamp : DateTime :: from_timestamp_millis ( last_bar. timestamp )
195- . unwrap_or_else ( || Utc :: now ( ) ) ,
203+ . unwrap_or_else ( Utc :: now) ,
196204 signal_type : SignalType :: CloseLong ,
197205 pnl : Some ( pnl) ,
198206 } ;
0 commit comments