How to dynamically inject logic in a strategy at runtime? #292
-
Hi there! Thanks for the wonderful library. I've been experimented with it for a while now, and it has been great. Right now I'm trying to integrate an existing code base that has a concept of signals, and in order to avoid duplicated code logic, I'm trying to work with python inheritance or class factories to achieve this. Unfortunately, when I run the backtest no trades get executed. Any insights would be appreciated! Here's an example class Signal():
def __init__(self, symbol, data, **params):
self.symbol = symbol
self.data = data
self.params = dict(params, **self.defaults())
self.setup()
def __getattr__(self, name):
"""Look in params if attribute is missing"""
attr = self.__dict__.get("params", {}).get(name)
if attr is not None:
return attr
else:
raise AttributeError(name)
@abstractmethod
def setup(self):
pass
@abstractmethod
def buy(self):
pass
@abstractmethod
def sell(self):
pass
def I(self, func, *args, **kwargs):
"""attempt to have same signature as backtesting.py"""
return func(*args, **kwargs)
def defaults(self):
"""returns first value in param combos"""
store = {}
for k, v in self.param_combos().items():
store[k] = v[0]
return store
def param_combos(self):
return {}
class SmaCross(Signal):
def setup(self):
self.sma1 = self.I(SMA, self.data.Close, self.n1)
self.sma2 = self.I(SMA, self.data.Close, self.n2)
def next(self):
# If sma1 crosses above sma2, close any existing
# short trades, and buy the asset
if crossover(self.sma1, self.sma2):
self.position.close()
self.buy()
# Else, if sma1 crosses below sma2, close any existing
# long trades, and sell the asset
elif crossover(self.sma2, self.sma1):
self.position.close()
self.sell()
def param_combos(self):
return dict(n1=range(5, 30, 5), n2=range(10, 70, 5))
Then to create a from backtesting import Strategy
from signals import SmaCross
import pandas as pd
df = pd.read_csv("data.csv")
sig = SmaCross("AAPL", df)
MyBacktest = type('MyBacktest', (Strategy,), dict(
sig.params, init=sig.setup, next=sig.next
))
bt = Backtest(df, MyBacktest, cash=100_000, commission=.02)
stats = bt.run()
print(stats['# Trades'])
# >>> 0 If I copy and paste the demo from online and use it on the same dataset with same parameters, there are 34 trades, so I know there's not a problem with my dataset or parameter values. Something is wrong with the custom Thanks for your help! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
You're not calling |
Beta Was this translation helpful? Give feedback.
You're not calling
super()
methods in your overriding "init
" andnext
.Strategy
methods never fire.