|
8 | 8 |
|
9 | 9 | from __future__ import annotations
|
10 | 10 |
|
11 |
| -import multiprocessing as mp |
12 | 11 | import sys
|
13 | 12 | import warnings
|
14 | 13 | from abc import ABCMeta, abstractmethod
|
|
24 | 23 | from numpy.random import default_rng
|
25 | 24 |
|
26 | 25 | from ._plotting import plot # noqa: I001
|
27 |
| -from ._stats import compute_stats |
| 26 | +from ._stats import compute_stats, dummy_stats |
28 | 27 | from ._util import (
|
29 | 28 | SharedMemoryManager, _as_str, _Indicator, _Data, _batch, _indicator_warmup_nbars,
|
30 | 29 | _strategy_indicators, patch, try_, _tqdm,
|
@@ -211,7 +210,7 @@ def next(self):
|
211 | 210 | """
|
212 | 211 |
|
213 | 212 | class __FULL_EQUITY(float): # noqa: N801
|
214 |
| - def __repr__(self): return '.9999' |
| 213 | + def __repr__(self): return '.9999' # noqa: E704 |
215 | 214 | _FULL_EQUITY = __FULL_EQUITY(1 - sys.float_info.epsilon)
|
216 | 215 |
|
217 | 216 | def buy(self, *,
|
@@ -449,7 +448,7 @@ def __repr__(self):
|
449 | 448 | ('tp', self.__tp_price),
|
450 | 449 | ('contingent', self.is_contingent),
|
451 | 450 | ('tag', self.__tag),
|
452 |
| - ) if value is not None)) |
| 451 | + ) if value is not None)) # noqa: E126 |
453 | 452 |
|
454 | 453 | def cancel(self):
|
455 | 454 | """Cancel the order."""
|
@@ -578,7 +577,7 @@ def __init__(self, broker: '_Broker', size: int, entry_price: float, entry_bar,
|
578 | 577 | def __repr__(self):
|
579 | 578 | return f'<Trade size={self.__size} time={self.__entry_bar}-{self.__exit_bar or ""} ' \
|
580 | 579 | f'price={self.__entry_price}-{self.__exit_price or ""} pl={self.pl:.0f}' \
|
581 |
| - f'{" tag="+str(self.__tag) if self.__tag is not None else ""}>' |
| 580 | + f'{" tag=" + str(self.__tag) if self.__tag is not None else ""}>' |
582 | 581 |
|
583 | 582 | def _replace(self, **kwargs):
|
584 | 583 | for k, v in kwargs.items():
|
@@ -1309,7 +1308,8 @@ def run(self, **kwargs) -> pd.Series:
|
1309 | 1308 | # np.nan >= 3 is not invalid; it's False.
|
1310 | 1309 | with np.errstate(invalid='ignore'):
|
1311 | 1310 |
|
1312 |
| - for i in _tqdm(range(start, len(self._data)), desc=self.run.__qualname__): |
| 1311 | + for i in _tqdm(range(start, len(self._data)), desc=self.run.__qualname__, |
| 1312 | + unit='bar', mininterval=2, miniters=100): |
1313 | 1313 | # Prepare data and indicators for `next` call
|
1314 | 1314 | data._set_length(i + 1)
|
1315 | 1315 | for attr, indicator in indicator_attrs:
|
@@ -1425,9 +1425,7 @@ def optimize(self, *,
|
1425 | 1425 | maximize_key = None
|
1426 | 1426 | if isinstance(maximize, str):
|
1427 | 1427 | maximize_key = str(maximize)
|
1428 |
| - stats_keys = compute_stats( |
1429 |
| - [], np.r_[[np.nan]], pd.DataFrame({col: [np.nan] for col in ('Close',)}), None, 0).index |
1430 |
| - if maximize not in stats_keys: |
| 1428 | + if maximize not in dummy_stats().index: |
1431 | 1429 | raise ValueError('`maximize`, if str, must match a key in pd.Series '
|
1432 | 1430 | 'result of backtest.run()')
|
1433 | 1431 |
|
@@ -1503,9 +1501,9 @@ def _optimize_grid() -> Union[pd.Series, Tuple[pd.Series, pd.Series]]:
|
1503 | 1501 | [p.values() for p in param_combos],
|
1504 | 1502 | names=next(iter(param_combos)).keys()))
|
1505 | 1503 |
|
1506 |
| - with mp.Pool() as pool, \ |
| 1504 | + from . import Pool |
| 1505 | + with Pool() as pool, \ |
1507 | 1506 | SharedMemoryManager() as smm:
|
1508 |
| - |
1509 | 1507 | with patch(self, '_data', None):
|
1510 | 1508 | bt = copy(self) # bt._data will be reassigned in _mp_task worker
|
1511 | 1509 | results = _tqdm(
|
@@ -1567,7 +1565,8 @@ def memoized_run(tup):
|
1567 | 1565 | stats = self.run(**dict(tup))
|
1568 | 1566 | return -maximize(stats)
|
1569 | 1567 |
|
1570 |
| - progress = iter(_tqdm(repeat(None), total=max_tries, leave=False, desc='Backtest.optimize')) |
| 1568 | + progress = iter(_tqdm(repeat(None), total=max_tries, leave=False, |
| 1569 | + desc=self.optimize.__qualname__, mininterval=2)) |
1571 | 1570 | _names = tuple(kwargs.keys())
|
1572 | 1571 |
|
1573 | 1572 | def objective_function(x):
|
|
0 commit comments