将核心结构、指标与分析拆到 chan/{core,indicators,analysis,pipeline};
根目录保留兼容 shim;strategies 改为从 chan 包导入;买卖点经 bsp_macd 与 MACD 接合。
Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
2.6 KiB
Python
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)
|