Skip to content

Commit 7dafab9

Browse files
authored
ENH: Return stats from MultiBacktest.optimize (#1308)
Return per-dataset best-run statistics by default and optionally return heatmaps alongside them.
1 parent ca2e261 commit 7dafab9

1 file changed

Lines changed: 21 additions & 7 deletions

File tree

backtesting/lib.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,9 @@ class MultiBacktest:
576576
from backtesting.test import EURUSD, BTCUSD, SmaCross
577577
btm = MultiBacktest([EURUSD, BTCUSD], SmaCross)
578578
stats_per_ticker: pd.DataFrame = btm.run(fast=10, slow=20)
579-
heatmap_per_ticker: pd.DataFrame = btm.optimize(...)
579+
stats_per_ticker, heatmap_per_ticker = btm.optimize(
580+
fast=range(10, 30, 10), slow=range(20, 40, 10),
581+
return_heatmap=True)
580582
"""
581583
def __init__(self, df_list, strategy_cls, **kwargs):
582584
self._dfs = df_list
@@ -615,24 +617,36 @@ def _mp_task_run(args):
615617
for shmem in chain(*shms):
616618
shmem.close()
617619

618-
def optimize(self, **kwargs) -> pd.DataFrame:
620+
def optimize(self, *, return_heatmap: bool = False, **kwargs) -> Union[
621+
pd.DataFrame, tuple[pd.DataFrame, pd.DataFrame]]:
619622
"""
620-
Wraps `backtesting.backtesting.Backtest.optimize`, but returns `pd.DataFrame` with
621-
currency indexes in columns.
623+
Wraps `backtesting.backtesting.Backtest.optimize` and returns a
624+
`pd.DataFrame` of best-run statistics, with datasets in columns.
625+
626+
If `return_heatmap` is `True`, also returns a second `pd.DataFrame`
627+
containing optimization heatmaps, with datasets in columns.
622628
623-
heamap: pd.DataFrame = btm.optimize(...)
629+
stats, heatmap = btm.optimize(
630+
fast=range(10, 30, 10), slow=range(20, 40, 10),
631+
return_heatmap=True)
624632
from backtesting.plot import plot_heatmaps
625633
plot_heatmaps(heatmap.mean(axis=1))
626634
"""
635+
best_stats = []
627636
heatmaps = []
628637
# Simple loop since bt.optimize already does its own multiprocessing
629638
for df in _tqdm(self._dfs, desc=self.__class__.__name__, mininterval=2):
630639
bt = Backtest(df, self._strategy, **self._bt_kwargs)
631-
_best_stats, heatmap = bt.optimize( # type: ignore
640+
stats, heatmap = bt.optimize( # type: ignore
632641
return_heatmap=True, return_optimization=False, **kwargs)
642+
best_stats.append(stats.filter(regex='^[^_]'))
633643
heatmaps.append(heatmap)
644+
645+
stats = pd.DataFrame(dict(zip(count(), best_stats)))
634646
heatmap = pd.DataFrame(dict(zip(count(), heatmaps)))
635-
return heatmap
647+
if return_heatmap:
648+
return stats, heatmap
649+
return stats
636650

637651

638652
# NOTE: Don't put anything below this __all__ list

0 commit comments

Comments
 (0)