"""기술적 지표 계산 (인과 신호용).""" from __future__ import annotations import pandas as pd def ema(series: pd.Series, span: int) -> pd.Series: """지수이동평균을 계산한다.""" return series.ewm(span=span, adjust=False).mean() def sma(series: pd.Series, window: int) -> pd.Series: """단순이동평균을 계산한다.""" return series.rolling(window=window, min_periods=window).mean() def bollinger_bands( close: pd.Series, window: int = 20, num_std: float = 2.0, ) -> tuple[pd.Series, pd.Series, pd.Series]: """볼린저 밴드 (중심, 상단, 하단)를 계산한다.""" mid = sma(close, window) std = close.rolling(window=window, min_periods=window).std() upper = mid + num_std * std lower = mid - num_std * std return mid, upper, lower def rsi(close: pd.Series, period: int = 14) -> pd.Series: """RSI(상대강도지수)를 계산한다.""" delta = close.diff() gain = delta.clip(lower=0.0) loss = -delta.clip(upper=0.0) avg_gain = gain.ewm(alpha=1 / period, min_periods=period, adjust=False).mean() avg_loss = loss.ewm(alpha=1 / period, min_periods=period, adjust=False).mean() rs = avg_gain / avg_loss.replace(0, pd.NA) return 100 - (100 / (1 + rs)) def macd( close: pd.Series, fast: int = 12, slow: int = 26, signal: int = 9, ) -> tuple[pd.Series, pd.Series, pd.Series]: """MACD, 시그널, 히스토그램을 계산한다.""" ema_fast = ema(close, fast) ema_slow = ema(close, slow) macd_line = ema_fast - ema_slow signal_line = ema(macd_line, signal) hist = macd_line - signal_line return macd_line, signal_line, hist