import pandas as pd from strategies.base_strategy import BaseStrategy import logging class MaCrossStrategy(BaseStrategy): """ A strategy based on a fast Simple Moving Average (SMA) crossing a slow SMA. """ def calculate_signals(self, df: pd.DataFrame) -> pd.DataFrame: # Support multiple naming conventions: some configs use 'fast'/'slow' # while others use 'short_ma'/'long_ma'. Normalize here so both work. fast_ma_period = self.params.get('short_ma') or self.params.get('fast') or 0 slow_ma_period = self.params.get('long_ma') or self.params.get('slow') or 0 # If parameters are missing, return a neutral signal frame. if not fast_ma_period or not slow_ma_period: logging.warning(f"Missing MA period parameters (fast={fast_ma_period}, slow={slow_ma_period}).") df['signal'] = 0 return df if len(df) < slow_ma_period: logging.warning(f"Not enough data for MA periods {fast_ma_period}/{slow_ma_period}. Need {slow_ma_period}, have {len(df)}.") df['signal'] = 0 return df df['fast_sma'] = df['close'].rolling(window=fast_ma_period).mean() df['slow_sma'] = df['close'].rolling(window=slow_ma_period).mean() # Signal is 1 for Golden Cross (fast > slow), -1 for Death Cross df['signal'] = 0 df.loc[df['fast_sma'] > df['slow_sma'], 'signal'] = 1 df.loc[df['fast_sma'] < df['slow_sma'], 'signal'] = -1 return df