From cb55c81dbc43df83ef4d5b717fe22b4d04a93d2e Mon Sep 17 00:00:00 2001 From: TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:44:20 +0900 Subject: feat(strategy): apply filters, conviction scoring, and ATR stops to all strategies --- .../strategy-engine/strategies/rsi_strategy.py | 32 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'services/strategy-engine/strategies/rsi_strategy.py') diff --git a/services/strategy-engine/strategies/rsi_strategy.py b/services/strategy-engine/strategies/rsi_strategy.py index 490a8a9..0ec6780 100644 --- a/services/strategy-engine/strategies/rsi_strategy.py +++ b/services/strategy-engine/strategies/rsi_strategy.py @@ -55,10 +55,34 @@ class RsiStrategy(BaseStrategy): if self._quantity <= 0: raise ValueError(f"Quantity must be positive, got {self._quantity}") + self._init_filters( + require_trend=False, + adx_threshold=float(params.get("adx_threshold", 25.0)), + min_volume_ratio=float(params.get("min_volume_ratio", 0.5)), + atr_stop_multiplier=float(params.get("atr_stop_multiplier", 2.0)), + atr_tp_multiplier=float(params.get("atr_tp_multiplier", 3.0)), + ) + def reset(self) -> None: self._closes.clear() + def _rsi_conviction(self, rsi_value: float) -> float: + """Map RSI value to conviction strength (0.0-1.0). + + For BUY (oversold): lower RSI = higher conviction. + For SELL (overbought): higher RSI = higher conviction. + Linear scale from the threshold to the extreme (0 or 100). + """ + if rsi_value < self._oversold: + # RSI 0 -> 1.0, RSI at oversold threshold -> 0.0 + return min(1.0, max(0.1, (self._oversold - rsi_value) / self._oversold)) + elif rsi_value > self._overbought: + # RSI 100 -> 1.0, RSI at overbought threshold -> 0.0 + return min(1.0, max(0.1, (rsi_value - self._overbought) / (100.0 - self._overbought))) + return 0.0 + def on_candle(self, candle: Candle) -> Signal | None: + self._update_filter_data(candle) self._closes.append(float(candle.close)) if len(self._closes) < self._period + 1: @@ -71,22 +95,26 @@ class RsiStrategy(BaseStrategy): return None if rsi_value < self._oversold: - return Signal( + signal = Signal( strategy=self.name, symbol=candle.symbol, side=OrderSide.BUY, price=candle.close, quantity=self._quantity, + conviction=self._rsi_conviction(rsi_value), reason=f"RSI {rsi_value:.2f} below oversold threshold {self._oversold}", ) + return self._apply_filters(signal) elif rsi_value > self._overbought: - return Signal( + signal = Signal( strategy=self.name, symbol=candle.symbol, side=OrderSide.SELL, price=candle.close, quantity=self._quantity, + conviction=self._rsi_conviction(rsi_value), reason=f"RSI {rsi_value:.2f} above overbought threshold {self._overbought}", ) + return self._apply_filters(signal) return None -- cgit v1.2.3