diff --git a/src/indicators/__init__.py b/src/indicators/__init__.py index e69de29..2304ec9 100644 --- a/src/indicators/__init__.py +++ b/src/indicators/__init__.py @@ -0,0 +1 @@ +# Initialize indicators package diff --git a/src/indicators/sunny_bands.py b/src/indicators/sunny_bands.py new file mode 100644 index 0000000..2c566bc --- /dev/null +++ b/src/indicators/sunny_bands.py @@ -0,0 +1,83 @@ +import numpy as np +import pandas as pd + +class SunnyBands: + def __init__(self, length: int = 50, atr_multiplier: float = 1.5, smooth_factor: int = 2): + """ + Initialize SunnyBands indicator + + Args: + length (int): DMA Length (default: 50) + atr_multiplier (float): ATR Multiplier (default: 1.5) + smooth_factor (int): Smoothing Factor (default: 2) + """ + self.length = length + self.atr_multiplier = atr_multiplier + self.smooth_factor = smooth_factor + + def calculate_atr(self, high: pd.Series, low: pd.Series, close: pd.Series) -> pd.Series: + """Calculate Average True Range""" + tr1 = high - low + tr2 = abs(high - close.shift(1)) + tr3 = abs(low - close.shift(1)) + tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) + return tr.rolling(window=self.length).mean() + + def calculate(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Calculate SunnyBands indicator + + Args: + df (pd.DataFrame): DataFrame with 'high', 'low', 'close' columns + + Returns: + pd.DataFrame: DataFrame with added columns for DMA and bands + """ + # Calculate DMA (Double EMA) + ema1 = df['close'].ewm(span=self.length, adjust=False).mean() + dma = ema1.ewm(span=self.smooth_factor, adjust=False).mean() + + # Calculate ATR + atr = self.calculate_atr(df['high'], df['low'], df['close']) + + # Calculate Bands + upper_band = dma + (atr * self.atr_multiplier) + lower_band = dma - (atr * self.atr_multiplier) + + # Generate signals + bullish = (df['close'] > lower_band) & (df['close'].shift(1) <= lower_band) + bearish = (df['close'] < upper_band) & (df['close'].shift(1) >= upper_band) + + # Add results to DataFrame + results = pd.DataFrame({ + 'dma': dma, + 'upper_band': upper_band, + 'lower_band': lower_band, + 'bullish_signal': bullish, + 'bearish_signal': bearish + }) + + return results + + def get_signals(self, df: pd.DataFrame) -> dict: + """ + Get the current trading signals based on SunnyBands + + Args: + df (pd.DataFrame): DataFrame with price data + + Returns: + dict: Dictionary containing current signals and band values + """ + results = self.calculate(df) + + # Get latest values + current = results.iloc[-1] + + return { + 'dma': current['dma'], + 'upper_band': current['upper_band'], + 'lower_band': current['lower_band'], + 'is_bullish': current['bullish_signal'], + 'is_bearish': current['bearish_signal'] + }