@@ -249,9 +249,9 @@ def next(self):
249249 ORDER_BAR = 2
250250 stats = Backtest (SHORT_DATA , S , cash = CASH , spread = SPREAD , commission = COMMISSION ).run ()
251251 trade_open_price = SHORT_DATA ['Open' ].iloc [ORDER_BAR ]
252- self .assertEqual (stats ['_trades' ]['EntryPrice' ].iloc [0 ], trade_open_price * (1 + SPREAD / 2 ))
252+ self .assertEqual (stats ['_trades' ]['EntryPrice' ].iloc [0 ], trade_open_price * (1 + SPREAD ))
253253 self .assertEqual (stats ['_equity_curve' ]['Equity' ].iloc [2 :4 ].round (2 ).tolist (),
254- [9734.52 , 9750.10 ])
254+ [9685.31 , 9652.42 ])
255255
256256 stats = Backtest (SHORT_DATA , S , cash = CASH , commission = (100 , COMMISSION )).run ()
257257 self .assertEqual (stats ['_equity_curve' ]['Equity' ].iloc [2 :4 ].round (2 ).tolist (),
@@ -262,7 +262,7 @@ def next(self):
262262 self .assertEqual (stats ['_equity_curve' ]['Equity' ].iloc [2 :4 ].round (2 ).tolist (),
263263 [9781.28 , 9846.04 ])
264264
265- def test_spread_is_split_between_entry_and_exit (self ):
265+ def test_spread_is_applied_at_entry_and_exit (self ):
266266 class Long (Strategy ):
267267 def init (self ):
268268 pass
@@ -287,99 +287,11 @@ def next(self):
287287 long_trade = Backtest (data , Long , spread = .02 ).run ()._trades .iloc [0 ]
288288 short_trade = Backtest (data , Short , spread = .02 ).run ()._trades .iloc [0 ]
289289
290- self .assertEqual ((long_trade .EntryPrice , long_trade .ExitPrice ), (101 . , 99 . ))
291- self .assertEqual ((short_trade .EntryPrice , short_trade .ExitPrice ), (99 . , 101 . ))
292- self .assertEqual ((long_trade .PnL , short_trade .PnL ), (- 20 . , - 20 . ))
290+ self .assertEqual ((long_trade .EntryPrice , long_trade .ExitPrice ), (102 . , 98 . ))
291+ self .assertEqual ((short_trade .EntryPrice , short_trade .ExitPrice ), (98 . , 102 . ))
292+ self .assertEqual ((long_trade .PnL , short_trade .PnL ), (- 40 . , - 40 . ))
293293
294- def test_open_trade_pl_includes_entry_commission (self ):
295- class S (Strategy ):
296- def init (self ):
297- self .open_pl = self .open_pl_pct = None
298- self .position_pl = self .position_pl_pct = None
299-
300- def next (self ):
301- if len (self .data ) == 2 :
302- self .buy (size = 10 )
303- elif self .position :
304- trade = self .trades [0 ]
305- self .open_pl = trade .pl
306- self .open_pl_pct = trade .pl_pct
307- self .position_pl = self .position .pl
308- self .position_pl_pct = self .position .pl_pct
309- self .position .close ()
310-
311- index = pd .date_range ('2020' , periods = 5 )
312- data = pd .DataFrame ({column : 100. for column in ('Open' , 'High' , 'Low' , 'Close' )},
313- index = index )
314- stats = Backtest (data , S , cash = 10_000 , commission = (5 , .01 )).run ()
315-
316- self .assertEqual (stats ._strategy .open_pl , - 15. )
317- self .assertEqual (stats ._strategy .open_pl_pct , - .015 )
318- self .assertEqual (stats ._strategy .position_pl , - 15. )
319- self .assertEqual (stats ._strategy .position_pl_pct , - 1.5 )
320- self .assertEqual (stats ._trades .Commission .iloc [0 ], 30. )
321- self .assertEqual (stats ._trades .PnL .iloc [0 ], - 30. )
322-
323- def test_partial_close_allocates_entry_commission (self ):
324- class S (Strategy ):
325- def init (self ):
326- self .remaining_pl = None
327- self .accounting_delta = None
328-
329- def next (self ):
330- if len (self .data ) == 2 :
331- self .buy (size = 10 )
332- elif len (self .data ) == 3 :
333- self .position .close (.4 )
334- elif self .position :
335- self .remaining_pl = self .trades [0 ].pl
336- self .accounting_delta = (
337- self .closed_trades [0 ].pl + self .trades [0 ].pl ,
338- self .equity - 10_000 ,
339- )
340- self .position .close ()
341-
342- index = pd .date_range ('2020' , periods = 6 )
343- data = pd .DataFrame ({column : 100. for column in ('Open' , 'High' , 'Low' , 'Close' )},
344- index = index )
345- stats = Backtest (data , S , cash = 10_000 , commission = (5 , .01 )).run ()
346-
347- self .assertEqual (stats ._strategy .remaining_pl , - 9. )
348- self .assertEqual (stats ._strategy .accounting_delta , (- 24. , - 24. ))
349- self .assertEqual (stats ['Commissions [$]' ], 35. )
350- self .assertEqual (stats ._trades .Commission .tolist (), [15. , 20. ])
351- self .assertEqual (stats ._trades .PnL .tolist (), [- 15. , - 20. ])
352-
353- def test_callable_entry_commission_is_not_recomputed_at_close (self ):
354- class Commission :
355- def __init__ (self ):
356- self .calls = []
357-
358- def __call__ (self , size , price ):
359- self .calls .append ((size , price ))
360- return len (self .calls )
361-
362- class S (Strategy ):
363- def init (self ):
364- pass
365-
366- def next (self ):
367- if len (self .data ) == 2 :
368- self .buy (size = 10 )
369- elif self .position :
370- self .position .close ()
371-
372- commission = Commission ()
373- index = pd .date_range ('2020' , periods = 5 )
374- data = pd .DataFrame ({column : 100. for column in ('Open' , 'High' , 'Low' , 'Close' )},
375- index = index )
376- stats = Backtest (data , S , cash = 10_000 , commission = commission ).run ()
377-
378- self .assertEqual (len (commission .calls ), 3 )
379- self .assertEqual (stats ._trades .Commission .iloc [0 ], 5. )
380- self .assertEqual (stats ._trades .PnL .iloc [0 ], - 5. )
381-
382- def test_reversal_applies_half_spread_once_per_fill (self ):
294+ def test_reversal_applies_spread_at_each_transaction (self ):
383295 class S (Strategy ):
384296 def init (self ):
385297 pass
@@ -398,10 +310,10 @@ def next(self):
398310 trades = Backtest (data , S , spread = .02 ).run ()._trades
399311
400312 self .assertEqual (trades [['Size' , 'EntryPrice' , 'ExitPrice' ]].values .tolist (),
401- [[10. , 101 . , 99 . ], [- 5. , 99 . , 101 . ]])
402- self .assertEqual (trades .PnL .tolist (), [- 20 . , - 10 . ])
313+ [[10. , 102 . , 98 . ], [- 5. , 98 . , 102 . ]])
314+ self .assertEqual (trades .PnL .tolist (), [- 40 . , - 20 . ])
403315
404- def test_stop_exit_pays_half_spread (self ):
316+ def test_stop_exit_pays_spread (self ):
405317 class S (_S ):
406318 def next (self ):
407319 if len (self .data ) == 2 :
@@ -415,9 +327,9 @@ def next(self):
415327 }, index = pd .date_range ('2020' , periods = 5 ))
416328 trade = Backtest (data , S , spread = .02 ).run ()._trades .iloc [0 ]
417329
418- self .assertEqual (trade .EntryPrice , 101 . )
330+ self .assertEqual (trade .EntryPrice , 102 . )
419331 self .assertEqual (trade .SL , 95. )
420- self .assertEqual (trade .ExitPrice , 94.05 )
332+ self .assertEqual (trade .ExitPrice , 93.1 )
421333
422334 def test_commissions (self ):
423335 class S (_S ):
0 commit comments