Skip to content

Commit 2a7bb97

Browse files
committed
✨: Add option to pass initial configs
1 parent f0a1d49 commit 2a7bb97

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

backtesting/backtesting.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@ def optimize(self, *,
12621262
acq_type: str = 'auto',
12631263
use_constrained_model: bool = False,
12641264
return_configs: int = 100,
1265+
init_configs: list[dict] | None = None,
12651266
**kwargs) -> Tuple[pd.Series, List]:
12661267
"""
12671268
Optimize strategy parameters to an optimal combination.
@@ -1313,6 +1314,9 @@ def optimize(self, *,
13131314
13141315
`return_configs` is the number of close-to-optimal configurations to return.
13151316
1317+
`init_configs` can be a list of dicts with initial configurations. The number of generated
1318+
initial configurations will be reduced accordingly.
1319+
13161320
Additional keyword arguments represent strategy arguments with
13171321
list-like collections of possible values. For example, the following
13181322
code finds and returns the "best" of the 7 admissible (of the
@@ -1453,8 +1457,22 @@ def eval_run(config: sp.Configuration):
14531457
initial_runs = min(max_tries, n_initial_points or 20 + 3 * len(kwargs))
14541458

14551459
if init_strategy == 'latin_hypercube':
1456-
lhs = LatinHypercubeSampler(space, initial_runs, criterion='maximin')
1457-
initial_configs = lhs.generate(return_config=True)
1460+
given_initial_configs = []
1461+
if init_configs:
1462+
for config in init_configs:
1463+
try:
1464+
given_initial_configs.append(sp.Configuration(configuration_space=space, values=config))
1465+
except Exception:
1466+
logging.warning("Invalid externally given initial config discovered")
1467+
continue
1468+
initial_runs_to_generate = initial_runs - len(given_initial_configs)
1469+
if initial_runs_to_generate > 0:
1470+
lhs = LatinHypercubeSampler(space, initial_runs_to_generate, criterion='maximin')
1471+
generated_initial_configs = lhs.generate(return_config=True)
1472+
else:
1473+
generated_initial_configs = []
1474+
1475+
initial_configs = given_initial_configs + generated_initial_configs
14581476

14591477
valid_initial_configs = []
14601478
for config in initial_configs:

backtesting/test/_test.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,15 +572,39 @@ def test_method_skopt(self):
572572

573573
def test_method_openbox(self):
574574
bt = Backtest(GOOG.iloc[:100], SmaCross)
575-
res = bt.optimize(
575+
res, best_configs = bt.optimize(
576576
fast=range(2, 20), slow=np.arange(2, 20, dtype=object), blubb=[-2.0],
577577
constraint=lambda p: p.fast < p.slow,
578578
max_tries=30,
579579
method='openbox',
580580
return_optimization=False,
581581
return_heatmap=False,
582-
random_state=2)
582+
random_state=2,
583+
)
584+
self.assertIsInstance(res, pd.Series)
585+
self.assertIsInstance(best_configs, list)
586+
self.assertEqual(len(best_configs), 30)
587+
588+
def test_method_openbox_with_given_initial_configs(self):
589+
bt = Backtest(GOOG.iloc[:100], SmaCross)
590+
res, best_configs = bt.optimize(
591+
fast=range(2, 20), slow=np.arange(2, 20, dtype=object), blubb=[-2.0],
592+
constraint=lambda p: p.fast < p.slow,
593+
max_tries=30,
594+
method='openbox',
595+
init_strategy='latin_hypercube',
596+
init_configs=[
597+
{'fast': 2, 'slow': 3, 'blubb': -2.0},
598+
{'fast': 2, 'slow': 4, 'blubb': -2.0},
599+
{'fast': 2, 'slow': 5, 'blubb': -2.0},
600+
],
601+
return_optimization=False,
602+
return_heatmap=False,
603+
random_state=2,
604+
)
583605
self.assertIsInstance(res, pd.Series)
606+
self.assertIsInstance(best_configs, list)
607+
self.assertEqual(len(best_configs), 30)
584608

585609
# def test_method_openbox_parallel(self):
586610
# bt = Backtest(GOOG.iloc[:100], SmaCross)

0 commit comments

Comments
 (0)