"""News-driven trading strategy. Buy on strong positive sentiment (score > 0.7, confidence > 0.6), sell on strong negative sentiment. Signal strength is the product of sentiment score and confidence, with a decay factor for stale news. """ 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 NewsDrivenStrategy(BaseStrategy): """Sentiment-based strategy driven by scored news articles.""" name: str = "news_driven" def __init__( self, positive_threshold: float = 0.7, negative_threshold: float = -0.7, min_confidence: float = 0.6, min_articles: int = 1, ) -> None: self.positive_threshold = positive_threshold self.negative_threshold = negative_threshold self.min_confidence = min_confidence self.min_articles = min_articles async def evaluate( self, ticker: str, market: MarketSnapshot, sentiment: SentimentContext | None = None, ) -> TradeSignal | None: """Generate a signal based on aggregated news sentiment.""" if sentiment is None: return None if sentiment.article_count < self.min_articles: return None if sentiment.avg_confidence < self.min_confidence: return None score = sentiment.avg_score if score > self.positive_threshold: direction = SignalDirection.LONG elif score < self.negative_threshold: direction = SignalDirection.SHORT else: return None # Strength = |score| * confidence (both in [0, 1]) strength = abs(score) * sentiment.avg_confidence strength = min(max(strength, 0.0), 1.0) return TradeSignal( ticker=ticker, direction=direction, strength=round(strength, 4), strategy_sources=[self.name], sentiment_context={ "avg_score": sentiment.avg_score, "article_count": sentiment.article_count, "avg_confidence": sentiment.avg_confidence, }, timestamp=datetime.now(timezone.utc), )