Files
Chan/chan/indicators/engine.py
T
PorterandCursor 2c1232555e refactor: 缠论引擎迁入 chan/ 分层解耦,指标外置
将核心结构、指标与分析拆到 chan/{core,indicators,analysis,pipeline};
根目录保留兼容 shim;strategies 改为从 chan 包导入;买卖点经 bsp_macd 与 MACD 接合。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 14:47:13 +08:00

75 lines
2.6 KiB
Python

"""从 OHLC DataFrame 计算指标,不触碰缠论结构对象。"""
from __future__ import annotations
from typing import Optional
import talib.abstract as ta
import pandas as pd
from .config import IndicatorConfig
from .store import IndicatorStore
def _volume_ratio(df: pd.DataFrame, window: int = 10) -> pd.Series:
vol = df["volume"].astype(float)
ma = vol.rolling(window=window).mean()
ratio = vol / ma
return ratio.fillna(1.0)
class IndicatorEngine:
def __init__(self, config: Optional[IndicatorConfig] = None):
self.config = config or IndicatorConfig()
def compute(self, df: pd.DataFrame, config: Optional[IndicatorConfig] = None) -> IndicatorStore:
cfg = config or self.config
out = df.copy()
fast, slow, period = cfg.macd_fast, cfg.macd_slow, cfg.macd_signal
macd = ta.MACD(out, fastperiod=fast, slowperiod=slow, signalperiod=period)
out["macd"] = macd["macd"]
out["macdsignal"] = macd["macdsignal"]
out["macdhist"] = macd["macdhist"]
for period_n in cfg.ema_periods:
out[f"ema{period_n}"] = ta.EMA(out, timeperiod=period_n)
out["rsi"] = ta.RSI(out, timeperiod=cfg.rsi_period)
out["atr"] = ta.ATR(out, timeperiod=cfg.atr_period)
out["volume_ratio"] = _volume_ratio(out)
bb_middle = ta.SMA(out, timeperiod=cfg.bb_middle_sma_period)
for bb_period, nbup, nbdn, suffix in cfg.bbands:
bb = ta.BBANDS(
out,
timeperiod=bb_period,
nbdevup=nbup,
nbdevdn=nbdn,
matype=0,
)
bbp = (out["close"] - bb["lowerband"]) / (bb["upperband"] - bb["lowerband"])
if suffix == "2633":
out["bb2633upper"] = bb["upperband"]
out["bb2633lower"] = bb["lowerband"]
out["bb2633middle"] = bb["middleband"]
out["bbp2633"] = bbp
elif suffix == "365":
out["bbup365"] = bb["upperband"]
out["bblow365"] = bb["lowerband"]
out["bbp365"] = bbp
elif suffix == "120":
out["bbup120"] = bb["upperband"]
out["bblow120"] = bb["lowerband"]
out["bbp120"] = bbp
elif suffix == "30":
out["bbup30"] = bb["upperband"]
out["bblow30"] = bb["lowerband"]
out["bbmiddle30"] = bb_middle
out["bbp30"] = bbp
elif suffix == "302":
out["bbup302"] = bb["upperband"]
out["bblow302"] = bb["lowerband"]
out["bbp302"] = bbp
return IndicatorStore(out)