"""Mean reversion trading strategy. Buy when RSI < 30 (oversold), sell when RSI > 70 (overbought). Signal strength is proportional to RSI extremity. """ from __future__ import annotations from datetime import datetime, timezone from shared.schemas.trading import MarketSnapshot, SentimentContext, SignalDirection, TradeSignal from shared.strategies.base import BaseStrategy class MeanReversionStrategy(BaseStrategy): """Contrarian strategy based on RSI extremes.""" name: str = "mean_reversion" def __init__( self, oversold_threshold: float = 30.0, overbought_threshold: float = 70.0, ) -> None: self.oversold_threshold = oversold_threshold self.overbought_threshold = overbought_threshold async def evaluate( self, ticker: str, market: MarketSnapshot, sentiment: SentimentContext | None = None, ) -> TradeSignal | None: """Generate a signal when RSI indicates oversold/overbought conditions.""" if market.rsi is None: return None rsi = market.rsi if rsi < self.oversold_threshold: direction = SignalDirection.LONG # Strength proportional to how oversold: RSI 0 -> strength 1.0, RSI 30 -> strength 0.0 strength = (self.oversold_threshold - rsi) / self.oversold_threshold elif rsi > self.overbought_threshold: direction = SignalDirection.SHORT # Strength proportional to how overbought: RSI 100 -> strength 1.0, RSI 70 -> strength 0.0 strength = (rsi - self.overbought_threshold) / (100.0 - self.overbought_threshold) else: return None strength = min(max(strength, 0.0), 1.0) return TradeSignal( ticker=ticker, direction=direction, strength=round(strength, 4), strategy_sources=[self.name], sentiment_context=None, timestamp=datetime.now(timezone.utc), )