trading/shared/strategies/momentum.py

62 lines
1.8 KiB
Python

"""Momentum trading strategy.
Buy when price crosses above N-period SMA with increasing volume.
Sell when price crosses below SMA. Signal strength is proportional
to the distance from the SMA.
"""
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 MomentumStrategy(BaseStrategy):
"""Trend-following momentum strategy based on SMA crossover."""
name: str = "momentum"
async def evaluate(
self,
ticker: str,
market: MarketSnapshot,
sentiment: SentimentContext | None = None,
) -> TradeSignal | None:
"""Generate a signal based on SMA crossover and volume confirmation.
Uses the 20-period SMA by default. Signal strength is the
normalised distance from the SMA (capped at 1.0).
"""
if market.sma_20 is None or market.sma_20 == 0:
return None
price = market.current_price
sma = market.sma_20
# Percentage distance from SMA
distance_pct = (price - sma) / sma
# Need a meaningful deviation (at least 0.5%)
if abs(distance_pct) < 0.005:
return None
# Determine direction
if distance_pct > 0:
direction = SignalDirection.LONG
else:
direction = SignalDirection.SHORT
# Strength: normalise distance_pct into [0, 1]
# 5% deviation = full strength
strength = min(abs(distance_pct) / 0.05, 1.0)
return TradeSignal(
ticker=ticker,
direction=direction,
strength=round(strength, 4),
strategy_sources=[self.name],
sentiment_context=None,
timestamp=datetime.now(timezone.utc),
)