84 lines
3.3 KiB
Python
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 [] |