@@ -59,8 +59,10 @@ def init(self):
59
59
60
60
def next (self ):
61
61
if crossover (self .sma1 , self .sma2 ):
62
+ self .position .close ()
62
63
self .buy ()
63
64
elif crossover (self .sma2 , self .sma1 ):
65
+ self .position .close ()
64
66
self .sell ()
65
67
66
68
@@ -154,52 +156,55 @@ def next(self, FIVE_DAYS=pd.Timedelta('3 days')):
154
156
assert not np .isnan (self .sma [- 1 ])
155
157
assert self .data .index [- 1 ]
156
158
157
- self .orders .is_long
158
- self .orders .is_short
159
- self .orders .entry
160
- self .orders .sl
161
- self .orders .tp
162
-
163
159
self .position
164
160
self .position .size
165
161
self .position .pl
166
162
self .position .pl_pct
167
- self .position .open_price
168
- self .position .open_time
169
163
self .position .is_long
170
164
171
165
if crossover (self .sma , self .data .Close ):
172
- self .orders .cancel ()
173
- self .sell ()
174
- assert not self .orders .is_long
175
- assert self .orders .is_short
176
- assert self .orders .entry
177
- assert not self .orders .sl
178
- assert not self .orders .tp
166
+ self .orders .cancel () # cancels only non-contingent
179
167
price = self .data .Close [- 1 ]
180
168
sl , tp = 1.05 * price , .9 * price
181
- self .sell (price , sl = sl , tp = tp )
182
- self .orders .set_entry (price )
183
- self .orders .set_sl (sl )
184
- self .orders .set_tp (tp )
185
- assert self .orders .entry == price
186
- assert self .orders .sl == sl
187
- assert self .orders .tp == tp
169
+
170
+ n_orders = len (self .orders )
171
+ self .sell (size = .21 , limit = price , stop = price , sl = sl , tp = tp )
172
+ assert len (self .orders ) == n_orders + 1
173
+
174
+ order = self .orders [- 1 ]
175
+ assert order .limit == price
176
+ assert order .stop == price
177
+ assert order .size == - .21
178
+ assert order .sl == sl
179
+ assert order .tp == tp
180
+ assert not order .is_contingent
188
181
189
182
elif self .position :
190
- assert not self .orders .entry
191
183
assert not self .position .is_long
192
- assert not not self .position .is_short
193
- assert self .position .open_price
184
+ assert self .position .is_short
194
185
assert self .position .pl
195
186
assert self .position .pl_pct
196
187
assert self .position .size < 0
197
- if self .data .index [- 1 ] - self .position .open_time > FIVE_DAYS :
198
- self .position .close ()
188
+
189
+ trade = self .trades [0 ]
190
+ if self .data .index [- 1 ] - self .data .index [trade .entry_bar ] > FIVE_DAYS :
191
+ assert not trade .is_long
192
+ assert trade .is_short
193
+ assert trade .size < 0
194
+ assert trade .entry_bar > 0
195
+ assert trade .exit_bar is None
196
+ assert trade .entry_price > 0
197
+ assert trade .exit_price is None
198
+ assert trade .pl / 1
199
+ assert trade .pl_pct / 1
200
+ assert trade .value > 0
201
+ assert trade .sl
202
+ assert trade .tp
203
+ self .position .close (.5 )
199
204
200
205
bt = Backtest (GOOG , Assertive )
201
206
stats = bt .run ()
202
- self .assertEqual (stats ['# Trades' ], 144 )
207
+ self .assertEqual (stats ['# Trades' ], 420 )
203
208
204
209
def test_broker_params (self ):
205
210
bt = Backtest (GOOG .iloc [:100 ], SmaCross ,
@@ -240,36 +245,42 @@ def test_compute_stats(self):
240
245
pd .Series ({
241
246
# NOTE: These values are also used on the website!
242
247
'# Trades' : 65 ,
243
- 'Avg. Drawdown Duration' : pd .Timedelta ('41 days 00:00:00' ),
244
- 'Avg. Drawdown [%]' : - 6.087158560194047 ,
248
+ 'Avg. Drawdown Duration' : pd .Timedelta ('40 days 00:00:00' ),
249
+ 'Avg. Drawdown [%]' : - 5.752030209842444 ,
245
250
'Avg. Trade Duration' : pd .Timedelta ('46 days 00:00:00' ),
246
- 'Avg. Trade [%]' : 3.0404430275631444 ,
247
- 'Best Trade [%]' : 54.05363186670138 ,
251
+ 'Avg. Trade [%]' : 3.097629974370268 ,
252
+ 'Best Trade [%]' : 53.59595229490424 ,
248
253
'Buy & Hold Return [%]' : 703.4582419772772 ,
249
- 'Calmar Ratio' : 0.0631443286380662 ,
254
+ 'Calmar Ratio' : 0.06715981043291917 ,
250
255
'Duration' : pd .Timedelta ('3116 days 00:00:00' ),
251
256
'End' : pd .Timestamp ('2013-03-01 00:00:00' ),
252
- 'Equity Final [$]' : 52624.29346696951 ,
253
- 'Equity Peak [$]' : 76908.27001642012 ,
254
- 'Expectancy [%]' : 8.774692825628644 ,
255
- 'Exposure Time [%]' : 93.93453145057767 ,
257
+ 'Equity Final [$]' : 49072.06999999996 ,
258
+ 'Equity Peak [$]' : 70073.97999999997 ,
259
+ 'Expectancy [%]' : 8.791710931051735 ,
260
+ 'Exposure Time [%]' : 93.99441340782123 ,
256
261
'Max. Drawdown Duration' : pd .Timedelta ('584 days 00:00:00' ),
257
- 'Max. Drawdown [%]' : - 48.15069053929621 ,
262
+ 'Max. Drawdown [%]' : - 46.123268579863776 ,
258
263
'Max. Trade Duration' : pd .Timedelta ('183 days 00:00:00' ),
259
- 'Return [%]' : 426.2429346696951 ,
260
- 'SQN' : 0.91553210127173 ,
261
- 'Sharpe Ratio' : 0.23169782960690408 ,
262
- 'Sortino Ratio' : 0.7096713270577958 ,
264
+ 'Return [%]' : 390.7206999999997 ,
265
+ 'SQN' : 0.9565100140529863 ,
266
+ 'Sharpe Ratio' : 0.2357610034211845 ,
267
+ 'Sortino Ratio' : 0.7355072888872161 ,
263
268
'Start' : pd .Timestamp ('2004-08-19 00:00:00' ),
264
269
'Win Rate [%]' : 46.15384615384615 ,
265
- 'Worst Trade [%]' : - 18.85561318387153 ,
270
+ 'Worst Trade [%]' : - 18.39887353835481 ,
266
271
}).sort_index ()
267
272
)
268
- self .assertTrue (
269
- stats ._trade_data .columns .equals (
270
- pd .Index (['Equity' , 'Exit Entry' , 'Exit Position' ,
271
- 'Entry Price' , 'Exit Price' , 'P/L' , 'Returns' ,
272
- 'Drawdown' , 'Drawdown Duration' ])))
273
+
274
+ self .assertSequenceEqual (
275
+ sorted (stats ['_equity_curve' ].columns ),
276
+ sorted (['Equity' , 'DrawdownPct' , 'DrawdownDuration' ]))
277
+
278
+ self .assertEqual (len (stats ['_trades' ]), 65 )
279
+
280
+ self .assertSequenceEqual (
281
+ sorted (stats ['_trades' ].columns ),
282
+ sorted (['Size' , 'EntryBar' , 'ExitBar' , 'EntryPrice' , 'ExitPrice' ,
283
+ 'PnL' , 'ReturnPct' , 'EntryTime' , 'ExitTime' , 'Duration' ]))
273
284
274
285
def test_compute_stats_bordercase (self ):
275
286
@@ -307,7 +318,7 @@ def next(self):
307
318
stats = Backtest (GOOG .iloc [:100 ], strategy ).run ()
308
319
309
320
self .assertFalse (np .isnan (stats ['Equity Final [$]' ]))
310
- self .assertFalse (stats . _trade_data ['Equity' ].isnull ().any ())
321
+ self .assertFalse (stats [ '_equity_curve' ] ['Equity' ].isnull ().any ())
311
322
self .assertEqual (stats ['_strategy' ].__class__ , strategy )
312
323
313
324
@@ -322,8 +333,6 @@ def coroutine(self):
322
333
assert self .position .size > 0
323
334
assert self .position .pl
324
335
assert self .position .pl_pct
325
- assert self .position .open_price > 0
326
- assert self .position .open_time
327
336
328
337
yield self .position .close ()
329
338
@@ -333,8 +342,6 @@ def coroutine(self):
333
342
assert not self .position .size
334
343
assert not self .position .pl
335
344
assert not self .position .pl_pct
336
- assert not self .position .open_price
337
- assert not self .position .open_time
338
345
339
346
class S (Strategy ):
340
347
def init (self ):
@@ -362,8 +369,8 @@ def test_optimize(self):
362
369
self .assertIsInstance (res , pd .Series )
363
370
364
371
res2 = bt .optimize (** OPT_PARAMS , maximize = lambda s : s ['SQN' ])
365
- self .assertSequenceEqual (res .filter (regex = '^[^_]' ).to_dict (),
366
- res2 .filter (regex = '^[^_]' ).to_dict ())
372
+ self .assertDictEqual (res .filter (regex = '^[^_]' ). fillna ( - 1 ).to_dict (),
373
+ res2 .filter (regex = '^[^_]' ). fillna ( - 1 ).to_dict ())
367
374
368
375
res3 , heatmap = bt .optimize (** OPT_PARAMS , return_heatmap = True ,
369
376
constraint = lambda d : d .slow > 2 * d .fast )
@@ -553,7 +560,7 @@ def init(self):
553
560
self .data .Close < sma )
554
561
555
562
stats = Backtest (GOOG , S ).run ()
556
- self .assertGreater (stats ['# Trades' ], 1000 )
563
+ self .assertEqual (stats ['# Trades' ], 2273 )
557
564
558
565
def test_TrailingStrategy (self ):
559
566
class S (TrailingStrategy ):
@@ -569,7 +576,7 @@ def next(self):
569
576
self .buy ()
570
577
571
578
stats = Backtest (GOOG , S ).run ()
572
- self .assertGreater (stats ['# Trades' ], 6 )
579
+ self .assertEqual (stats ['# Trades' ], 50 )
573
580
574
581
575
582
class TestUtil (TestCase ):
0 commit comments