70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
import os
|
|
from typing import Dict, Any
|
|
import strategies
|
|
from optimizer import run_optimized_backtest
|
|
|
|
|
|
class Optimizer:
|
|
"""Base optimizer wrapper tying a strategy to an optimization grid."""
|
|
name = 'BaseOptimizer'
|
|
strategy_cls = None
|
|
optimize_kwargs: Dict[str, Any] = None
|
|
|
|
@classmethod
|
|
def run(cls, symbol):
|
|
# pass optimizer name as report_tag so output files are unique per optimizer
|
|
return run_optimized_backtest(symbol, strategy_cls=cls.strategy_cls, optimize_kwargs=cls.optimize_kwargs, report_tag=cls.name)
|
|
|
|
|
|
class RsiOptimizer(Optimizer):
|
|
name = 'RsiOptimizer'
|
|
strategy_cls = strategies.RsiStrategy
|
|
def constraint(p):
|
|
return p.rsi_upper > p.rsi_lower
|
|
|
|
optimize_kwargs = dict(
|
|
rsi_period=range(7, 30, 2),
|
|
rsi_lower=range(20, 40, 5),
|
|
rsi_upper=range(60, 80, 5),
|
|
maximize='Return [%]',
|
|
constraint=constraint,
|
|
atr_period=range(10, 20, 2),
|
|
stop_loss_atr_multiplier=[2.0, 2.5, 3.0, 3.5, 4.0],
|
|
)
|
|
|
|
|
|
class CrossEmaOptimizer(Optimizer):
|
|
name = 'CrossEmaOptimizer'
|
|
strategy_cls = strategies.CrossEmaStrategy
|
|
|
|
def constraint(p):
|
|
# ensure long EMA period is greater than short EMA period
|
|
return p.long_ema > p.short_ema
|
|
|
|
optimize_kwargs = dict(
|
|
short_ema=range(5, 20, 3),
|
|
long_ema=range(20, 60, 5),
|
|
maximize='Return [%]',
|
|
constraint=constraint,
|
|
atr_period=range(10, 20, 2),
|
|
stop_loss_atr_multiplier=[2.0, 2.5, 3.0, 3.5],
|
|
finalize_trades=False, # run final backtest with best params
|
|
)
|
|
|
|
|
|
def get_optimizer_by_name(name: str):
|
|
mapping = {cls.name: cls for cls in (RsiOptimizer, CrossEmaOptimizer)}
|
|
# allow passing class name as well
|
|
if name in mapping:
|
|
return mapping[name]
|
|
# try attribute lookup in module
|
|
cls = getattr(__import__('optimizers'), name, None)
|
|
return cls
|
|
|
|
|
|
def run_optimizer(symbol: str, optimizer_name: str):
|
|
cls = get_optimizer_by_name(optimizer_name)
|
|
if cls is None:
|
|
raise ValueError(f"Optimizer '{optimizer_name}' not found")
|
|
return cls.run(symbol)
|