Skip to content

Commit 80a3ed5

Browse files
committed
BUG: Fix optimization hanging on MS Windows
Fixes #1256
1 parent 68f6478 commit 80a3ed5

File tree

4 files changed

+24
-6
lines changed

4 files changed

+24
-6
lines changed

backtesting/__init__.py

+20
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,23 @@
6868
from . import lib # noqa: F401
6969
from ._plotting import set_bokeh_output # noqa: F401
7070
from .backtesting import Backtest, Strategy # noqa: F401
71+
72+
73+
# Add overridable backtesting.Pool used for parallel optimization
74+
def Pool(processes=None, initializer=None, initargs=()):
75+
import multiprocessing as mp
76+
if mp.get_start_method() == 'spawn':
77+
import warnings
78+
warnings.warn(
79+
"If you want to use multi-process optimization with "
80+
"`multiprocessing.get_start_method() == 'spawn'` (e.g. on Windows),"
81+
"set `backtesting.Pool = multiprocessing.Pool` (or of the desired context) "
82+
"and hide `bt.optimize()` call behind a `if __name__ == '__main__'` guard. "
83+
"Currently using thread-based paralellism, "
84+
"which might be slightly slower for non-numpy / non-GIL-releasing code. "
85+
"See https://github.com/kernc/backtesting.py/issues/1256",
86+
category=RuntimeWarning, stacklevel=3)
87+
from multiprocessing.dummy import Pool
88+
return Pool(processes, initializer, initargs)
89+
else:
90+
return mp.Pool(processes, initializer, initargs)

backtesting/backtesting.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
from __future__ import annotations
1010

11-
import multiprocessing as mp
1211
import sys
1312
import warnings
1413
from abc import ABCMeta, abstractmethod
@@ -1501,9 +1500,9 @@ def _optimize_grid() -> Union[pd.Series, Tuple[pd.Series, pd.Series]]:
15011500
[p.values() for p in param_combos],
15021501
names=next(iter(param_combos)).keys()))
15031502

1504-
with mp.Pool() as pool, \
1503+
from . import Pool
1504+
with Pool() as pool, \
15051505
SharedMemoryManager() as smm:
1506-
15071506
with patch(self, '_data', None):
15081507
bt = copy(self) # bt._data will be reassigned in _mp_task worker
15091508
results = _tqdm(

backtesting/lib.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
from __future__ import annotations
1515

16-
import multiprocessing as mp
1716
import warnings
1817
from collections import OrderedDict
1918
from inspect import currentframe
@@ -569,7 +568,8 @@ def run(self, **kwargs):
569568
Wraps `backtesting.backtesting.Backtest.run`. Returns `pd.DataFrame` with
570569
currency indexes in columns.
571570
"""
572-
with mp.Pool() as pool, \
571+
from . import Pool
572+
with Pool() as pool, \
573573
SharedMemoryManager() as smm:
574574
shm = [smm.df2shm(df) for df in self._dfs]
575575
results = _tqdm(

backtesting/test/_test.py

-1
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,6 @@ def test_indicators_picklable(self):
10011001
class TestDocs(TestCase):
10021002
DOCS_DIR = os.path.join(os.path.dirname(__file__), '..', '..', 'doc')
10031003

1004-
@unittest.skipIf('win' in sys.platform, "Locks up with `ModuleNotFoundError: No module named '<run_path>'`")
10051004
@unittest.skipUnless(os.path.isdir(DOCS_DIR), "docs dir doesn't exist")
10061005
def test_examples(self):
10071006
examples = glob(os.path.join(self.DOCS_DIR, 'examples', '*.py'))

0 commit comments

Comments
 (0)