|
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