"""Moving Average Crossover strategy.""" import pandas as pd from decimal import Decimal from typing import Optional, Dict, Any from src.strategies.base import BaseStrategy, StrategySignal, SignalType from src.data.indicators import get_indicators class MovingAverageStrategy(BaseStrategy): """Moving average crossover strategy.""" def __init__(self, name: str, parameters: Optional[Dict[str, Any]] = None, timeframes: Optional[list] = None): """Initialize moving average strategy. Parameters: fast_period: Fast MA period (default 10) slow_period: Slow MA period (default 30) ma_type: MA type - 'sma' or 'ema' (default 'ema') """ super().__init__(name, parameters, timeframes) self.fast_period = self.parameters.get('fast_period', 10) self.slow_period = self.parameters.get('slow_period', 30) self.ma_type = self.parameters.get('ma_type', 'ema') self.indicators = get_indicators() self._price_history = [] async def on_tick(self, symbol: str, price: Decimal, timeframe: str, data: Dict[str, Any]) -> Optional[StrategySignal]: """Generate signal based on MA crossover.""" # Add price to history self._price_history.append(float(price)) if len(self._price_history) < self.slow_period + 1: return None # Calculate MAs prices = pd.Series(self._price_history[-self.slow_period-1:]) if self.ma_type == 'sma': fast_ma = self.indicators.sma(prices, self.fast_period) slow_ma = self.indicators.sma(prices, self.slow_period) else: fast_ma = self.indicators.ema(prices, self.fast_period) slow_ma = self.indicators.ema(prices, self.slow_period) if len(fast_ma) < 2 or len(slow_ma) < 2: return None # Check for crossover fast_current = fast_ma.iloc[-1] fast_prev = fast_ma.iloc[-2] slow_current = slow_ma.iloc[-1] slow_prev = slow_ma.iloc[-2] # Bullish crossover if fast_prev <= slow_prev and fast_current > slow_current: return StrategySignal( signal_type=SignalType.BUY, symbol=symbol, strength=min(1.0, (fast_current - slow_current) / slow_current), price=price, metadata={'fast_ma': float(fast_current), 'slow_ma': float(slow_current)} ) # Bearish crossover elif fast_prev >= slow_prev and fast_current < slow_current: return StrategySignal( signal_type=SignalType.SELL, symbol=symbol, strength=min(1.0, (slow_current - fast_current) / slow_current), price=price, metadata={'fast_ma': float(fast_current), 'slow_ma': float(slow_current)} ) return None def on_signal(self, signal: StrategySignal) -> Optional[StrategySignal]: """Process signal.""" return signal if self.should_execute(signal) else None