2025-06-07 14:01:20 +08:00

84 lines
3.3 KiB
Python

"""
Moving Average Convergence Divergence (MACD) indicator implementation.
"""
from typing import List
import pandas as pd
from ..base import BaseIndicator
from ..result import IndicatorResult
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') -> List[IndicatorResult]:
"""
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:
List of indicator results with MACD, signal, and histogram values
"""
# Validate input data
if not self.validate_dataframe(df, slow_period):
return []
try:
# Calculate fast and slow EMAs
df['ema_fast'] = df[price_column].ewm(span=fast_period, adjust=False).mean()
df['ema_slow'] = df[price_column].ewm(span=slow_period, adjust=False).mean()
# Calculate MACD line
df['macd'] = df['ema_fast'] - df['ema_slow']
# Calculate signal line (EMA of MACD)
df['signal'] = df['macd'].ewm(span=signal_period, adjust=False).mean()
# Calculate histogram
df['histogram'] = df['macd'] - df['signal']
# Convert results to IndicatorResult objects
results = []
for i, (timestamp, row) in enumerate(df.iterrows()):
# Only return results after minimum period
if i >= slow_period - 1:
if not (pd.isna(row['macd']) or pd.isna(row['signal']) or pd.isna(row['histogram'])):
result = IndicatorResult(
timestamp=timestamp,
symbol=row['symbol'],
timeframe=row['timeframe'],
values={
'macd': row['macd'],
'signal': row['signal'],
'histogram': row['histogram']
},
metadata={
'fast_period': fast_period,
'slow_period': slow_period,
'signal_period': signal_period,
'price_column': price_column
}
)
results.append(result)
return results
except Exception as e:
if self.logger:
self.logger.error(f"Error calculating MACD: {e}")
return []