52 lines
2.2 KiB
Python
Raw Normal View History

2025-06-07 14:01:20 +08:00
"""
Moving Average Convergence Divergence (MACD) indicator implementation.
"""
import pandas as pd
from ..base import BaseIndicator
class MACDIndicator(BaseIndicator):
"""
Moving Average Convergence Divergence (MACD) technical indicator.
Calculates trend-following momentum indicator that shows the relationship
between two moving averages of a security's price.
Handles sparse data appropriately without interpolation.
"""
def calculate(self, df: pd.DataFrame, fast_period: int = 12,
slow_period: int = 26, signal_period: int = 9,
price_column: str = 'close') -> pd.DataFrame:
2025-06-07 14:01:20 +08:00
"""
Calculate Moving Average Convergence Divergence (MACD).
Args:
df: DataFrame with OHLCV data
fast_period: Fast EMA period (default 12)
slow_period: Slow EMA period (default 26)
signal_period: Signal line EMA period (default 9)
price_column: Price column to use ('open', 'high', 'low', 'close')
Returns:
DataFrame with MACD values and metadata, indexed by timestamp
2025-06-07 14:01:20 +08:00
"""
# Validate input data
if not self.validate_dataframe(df, slow_period):
return pd.DataFrame()
2025-06-07 14:01:20 +08:00
try:
df = df.copy()
df['macd'] = df[price_column].ewm(span=fast_period, adjust=False).mean() - \
df[price_column].ewm(span=slow_period, adjust=False).mean()
2025-06-07 14:01:20 +08:00
df['signal'] = df['macd'].ewm(span=signal_period, adjust=False).mean()
df['histogram'] = df['macd'] - df['signal']
# Only keep rows with valid MACD, and only 'timestamp', 'macd', 'signal', 'histogram' columns
result_df = df.loc[df['macd'].notna() & df['signal'].notna() & df['histogram'].notna(), ['timestamp', 'macd', 'signal', 'histogram']].copy()
# Only keep rows after enough data for MACD and signal
min_required = max(slow_period, signal_period)
result_df = result_df.iloc[min_required-1:]
result_df.set_index('timestamp', inplace=True)
return result_df
except Exception:
return pd.DataFrame()