"""Mean reversion strategy — buy oversold, sell overbought using RSI.""" from datetime import datetime, timezone from shared.schemas.trading import MarketSnapshot, SentimentContext, SignalDirection, TradeSignal from shared.strategies.base import BaseStrategy class MeanReversionStrategy(BaseStrategy): """Trade on the assumption that extreme RSI readings will revert to the mean. **Buy signal** (LONG): RSI < 30 (oversold). **Sell signal** (SHORT): RSI > 70 (overbought). Signal strength is proportional to how far the RSI is from its threshold, clamped to [0, 1]. * Buy strength = ``(30 - rsi) / 30`` * Sell strength = ``(rsi - 70) / 30`` """ name: str = "mean_reversion" async def evaluate( self, ticker: str, market: MarketSnapshot, sentiment: SentimentContext | None = None, ) -> TradeSignal | None: if market.rsi is None: return None rsi = market.rsi if rsi < 30: direction = SignalDirection.LONG raw_strength = (30 - rsi) / 30 elif rsi > 70: direction = SignalDirection.SHORT raw_strength = (rsi - 70) / 30 else: # RSI in neutral territory — no opinion. return None strength = max(0.0, min(1.0, raw_strength)) return TradeSignal( ticker=ticker, direction=direction, strength=strength, strategy_sources=[self.name], timestamp=datetime.now(tz=timezone.utc), )