"""Value strategy — trade on fundamental valuation metrics.""" from datetime import datetime, timezone from shared.schemas.trading import MarketSnapshot, SentimentContext, SignalDirection, TradeSignal from shared.strategies.base import BaseStrategy class ValueStrategy(BaseStrategy): """Generate signals from fundamental financial data. Computes a composite score from PEG ratio, P/E ratio, EPS, revenue growth, profit margin, and debt-to-equity ratio. **Buy signal** (LONG): Composite score > 0.3 (undervalued). **Sell signal** (SHORT): Composite score < -0.3 (overvalued). Signal strength = ``abs(score) / 2.0``, clamped to [0, 1]. """ name: str = "value" async def evaluate( self, ticker: str, market: MarketSnapshot, sentiment: SentimentContext | None = None, ) -> TradeSignal | None: if market.fundamentals is None: return None f = market.fundamentals if f.peg_ratio is None or f.pe_ratio is None: return None score = 0.0 # PEG ratio scoring if f.peg_ratio < 1.0: score += 0.3 elif f.peg_ratio > 3.0: score -= 0.3 # P/E ratio scoring if f.pe_ratio < 15: score += 0.3 elif f.pe_ratio > 40: score -= 0.3 # EPS scoring if f.eps_ttm is not None: if f.eps_ttm > 0: score += 0.2 elif f.eps_ttm < 0: score -= 0.3 # Revenue growth scoring if f.revenue_growth_yoy is not None: if f.revenue_growth_yoy > 0.1: score += 0.2 elif f.revenue_growth_yoy < -0.1: score -= 0.2 # Profit margin scoring if f.profit_margin is not None: if f.profit_margin > 0.15: score += 0.1 elif f.profit_margin < 0: score -= 0.2 # Debt-to-equity scoring if f.debt_to_equity is not None: if f.debt_to_equity > 3.0: score -= 0.2 elif f.debt_to_equity < 0.5: score += 0.1 # Determine direction if score > 0.3: direction = SignalDirection.LONG elif score < -0.3: direction = SignalDirection.SHORT else: return None strength = max(0.0, min(1.0, abs(score) / 2.0)) return TradeSignal( ticker=ticker, direction=direction, strength=strength, strategy_sources=[self.name], timestamp=datetime.now(tz=timezone.utc), )