Skip to content

Commit 9ff2fc5

Browse files
author
mouad.berqia
committed
Fix MultiBacktest runs with zero-trade datasets
1 parent 6e9016c commit 9ff2fc5

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

backtesting/lib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ def _mp_task_run(args):
608608
data_shm, strategy, bt_kwargs, run_kwargs = args
609609
dfs, shms = zip(*(SharedMemoryManager.shm2df(i) for i in data_shm))
610610
try:
611-
return [stats.filter(regex='^[^_]') if stats['# Trades'] else None
611+
return [stats.filter(regex='^[^_]')
612612
for stats in (Backtest(df, strategy, **bt_kwargs).run(**run_kwargs)
613613
for df in dfs)]
614614
finally:

backtesting/test/_test.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
import time
66
import unittest
7+
import warnings
78
from concurrent.futures.process import ProcessPoolExecutor
89
from contextlib import contextmanager
910
from glob import glob
@@ -982,6 +983,50 @@ def test_MultiBacktest(self):
982983
print(start_method, time.monotonic() - start_time)
983984
plot_heatmaps(heatmap.mean(axis=1), open_browser=False)
984985

986+
def test_MultiBacktest_keeps_zero_trade_runs(self):
987+
datasets = [GOOG[:-4], GOOG[:-3], GOOG[:-2], GOOG[:-1], GOOG]
988+
cases = {
989+
'all_false': ([False, False, False, False, False], [0, 0, 0, 0, 0]),
990+
'first_true_rest_false': ([True, False, False, False, False], [1, 0, 0, 0, 0]),
991+
'first_false_second_true': ([False, True, False, False, False], [0, 1, 0, 0, 0]),
992+
}
993+
994+
for name, (will_buys, expected_trades) in cases.items():
995+
class TestStrat(Strategy):
996+
def init(self):
997+
self.will_buy = will_buys[len(self.data.index) - 2144]
998+
self.has_bought = False
999+
1000+
def next(self):
1001+
if not self.will_buy:
1002+
return
1003+
if self.position:
1004+
self.position.close()
1005+
if not self.has_bought:
1006+
self.buy()
1007+
self.has_bought = True
1008+
1009+
with self.subTest(case=name), warnings.catch_warnings():
1010+
warnings.filterwarnings(
1011+
'ignore',
1012+
message='If you want to use multi-process optimization',
1013+
category=RuntimeWarning,
1014+
)
1015+
result = MultiBacktest(
1016+
datasets,
1017+
TestStrat,
1018+
cash=10_000,
1019+
commission=.002,
1020+
exclusive_orders=True,
1021+
).run()
1022+
1023+
self.assertIsInstance(result, pd.DataFrame)
1024+
self.assertEqual(result.columns.tolist(), [0, 1, 2, 3, 4])
1025+
self.assertIn('# Trades', result.index)
1026+
self.assertEqual(result.loc['# Trades'].astype(int).tolist(), expected_trades)
1027+
self.assertFalse(any(isinstance(value, pd.Series) for value in result.to_numpy().ravel()))
1028+
self.assertIn('Equity Final [$]', result.index)
1029+
9851030

9861031
class TestUtil(TestCase):
9871032
def test_as_str(self):

0 commit comments

Comments
 (0)