class MomentumCrossover(Strategy):
"""Dual SMA crossover with RSI filter."""
# Optimisable parameters
fast = ctx.param("fast", 25, 5, 50)
slow = ctx.param("slow", 50, 20, 200)
rsi_thresh = ctx.param("rsi_thresh", 50, 30, 70)
def indicators(self, data):
self.fast_sma = ctx.indicator("SMA", close, self.fast)
self.slow_sma = ctx.indicator("SMA", close, self.slow)
self.rsi = ctx.indicator("RSI", close, 14)
def entry_signal(self):
trend = self.fast_sma > self.slow_sma
momentum = self.rsi > self.rsi_thresh
return trend & momentum # AND: both required
def exit_signal(self):
trend_lost = self.fast_sma < self.slow_sma
rsi_weak = self.rsi < self.rsi_thresh
return trend_lost | rsi_weak # OR: either triggers
def compute_weights(self, data):
w = np.where(self.entry_signal(), 1.0, 0.0)
w[self.exit_signal()] = 0.0
return ctx.fill_forward(w)