Download backtest/indicators.py from pigracing/zg: direct link, hf CLI and curl.
- Browser
- Download file 6.85 kB
-
https://huggingface.co/spaces/pigracing/zg/resolve/main/backtest/indicators.py
- Command line
-
hf download hf://spaces/pigracing/zg/backtest/indicators.py
-
curl -L -o indicators.py https://huggingface.co/spaces/pigracing/zg/resolve/main/backtest/indicators.py
6.85 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| 技术指标计算模块 | |
| 包含:MACD, KDJ, RSI, 布林带, 均线, 量比, 换手率, 振幅, 波动率等 | |
| """ | |
| import numpy as np | |
| import pandas as pd | |
| import config | |
| def calc_all_indicators(df: pd.DataFrame) -> pd.DataFrame: | |
| """一次性计算全部技术指标,返回带新列的 DataFrame""" | |
| df = df.copy() | |
| # 均线 | |
| for ma in config.MA_PERIODS: | |
| df[f"ma{ma}"] = df["close"].rolling(ma).mean() | |
| # 布林带 | |
| df = calc_bollinger(df) | |
| # MACD | |
| df = calc_macd(df) | |
| # KDJ | |
| df = calc_kdj(df) | |
| # RSI | |
| df["rsi14"] = calc_rsi(df["close"], 14) | |
| # 成交量指标 | |
| df["volume_ma5"] = df["volume"].rolling(5).mean() | |
| df["volume_ma20"] = df["volume"].rolling(20).mean() | |
| df["volume_ma60"] = df["volume"].rolling(60).mean() | |
| # 量比(当前量 / 20日均量) | |
| df["vol_ratio"] = df["volume"] / df["volume"].shift(1).rolling(20).mean() | |
| # 换手率(已有 turnover 列) | |
| if "turnover" in df.columns: | |
| df["turnover_ma5"] = df["turnover"].rolling(5).mean() | |
| df["turnover_ma20"] = df["turnover"].rolling(20).mean() | |
| # 振幅(使用前一日收盘价) | |
| df["pre_close"] = df["close"].shift(1) | |
| df["amplitude"] = (df["high"] - df["low"]) / df["pre_close"] * 100 | |
| # 涨跌幅 | |
| df["pct_chg"] = df["close"].pct_change() * 100 | |
| # 波动率(近20日涨跌幅绝对值均值) | |
| df["volatility_20d"] = df["pct_chg"].abs().rolling(20).mean() | |
| # 最高价/最低价 滚动 | |
| df["high_20"] = df["high"].rolling(20).max() | |
| df["low_20"] = df["low"].rolling(20).min() | |
| df["high_60"] = df["high"].rolling(60).max() | |
| df["low_60"] = df["low"].rolling(60).min() | |
| # 获利盘比例近似 | |
| df["profit_ratio"] = calc_profit_ratio(df) | |
| # 筹码集中度近似(使用布林带宽度的逆指标和价格位置组合) | |
| df["chip_concentration"] = calc_chip_concentration(df) | |
| # V2.1.1: 两融指标(如有数据列) | |
| if "margin_net_buy" in df.columns: | |
| df["margin_ma5"] = df["margin_net_buy"].rolling(5).mean() | |
| df["margin_ma20"] = df["margin_net_buy"].rolling(20).mean() | |
| if "margin_short" in df.columns: | |
| df["margin_short_ma5"] = df["margin_short"].rolling(5).mean() | |
| return df | |
| def calc_bollinger(df: pd.DataFrame, period: int = None, k: float = None) -> pd.DataFrame: | |
| """布林带指标""" | |
| period = period or config.BOLLINGER_PERIOD | |
| k = k or config.BOLLINGER_STD | |
| df = df.copy() | |
| df["boll_mid"] = df["close"].rolling(period).mean() | |
| df["boll_std"] = df["close"].rolling(period).std() | |
| df["boll_upper"] = df["boll_mid"] + k * df["boll_std"] | |
| df["boll_lower"] = df["boll_mid"] - k * df["boll_std"] | |
| df["boll_width"] = (df["boll_upper"] - df["boll_lower"]) / df["boll_mid"] | |
| df["boll_pct"] = (df["close"] - df["boll_lower"]) / (df["boll_upper"] - df["boll_lower"]) | |
| df["boll_pct"] = df["boll_pct"].clip(0, 1) | |
| return df | |
| def calc_macd(df: pd.DataFrame) -> pd.DataFrame: | |
| """MACD 指标""" | |
| df = df.copy() | |
| fast = config.MACD_FAST | |
| slow = config.MACD_SLOW | |
| signal = config.MACD_SIGNAL | |
| ema_fast = df["close"].ewm(span=fast, adjust=False).mean() | |
| ema_slow = df["close"].ewm(span=slow, adjust=False).mean() | |
| df["macd_dif"] = ema_fast - ema_slow | |
| df["macd_dea"] = df["macd_dif"].ewm(span=signal, adjust=False).mean() | |
| df["macd_bar"] = 2 * (df["macd_dif"] - df["macd_dea"]) | |
| return df | |
| def calc_kdj(df: pd.DataFrame) -> pd.DataFrame: | |
| """KDJ 指标""" | |
| df = df.copy() | |
| period = config.KDJ_PERIOD | |
| low_min = df["low"].rolling(period).min() | |
| high_max = df["high"].rolling(period).max() | |
| rsv = (df["close"] - low_min) / (high_max - low_min) * 100 | |
| rsv = rsv.fillna(50) | |
| df["kdj_k"] = rsv.ewm(com=2, adjust=False).mean() | |
| df["kdj_d"] = df["kdj_k"].ewm(com=2, adjust=False).mean() | |
| df["kdj_j"] = 3 * df["kdj_k"] - 2 * df["kdj_d"] | |
| return df | |
| def calc_rsi(close_series: pd.Series, period: int = 14) -> pd.Series: | |
| """RSI 指标""" | |
| delta = close_series.diff() | |
| gain = delta.where(delta > 0, 0.0) | |
| loss = (-delta).where(delta < 0, 0.0) | |
| avg_gain = gain.ewm(span=period, adjust=False).mean() | |
| avg_loss = loss.ewm(span=period, adjust=False).mean() | |
| rs = avg_gain / avg_loss.replace(0, np.nan) | |
| rsi = 100 - (100 / (1 + rs)) | |
| return rsi | |
| def calc_profit_ratio(df: pd.DataFrame) -> pd.Series: | |
| """ | |
| 获利盘比例近似计算 | |
| 基于:当前价格在60日区间内的位置 | |
| profit_ratio = (close - low_60) / (high_60 - low_60) | |
| """ | |
| low_60 = df["low"].rolling(60).min() | |
| high_60 = df["high"].rolling(60).max() | |
| ratio = (df["close"] - low_60) / (high_60 - low_60).replace(0, np.nan) | |
| return ratio.clip(0, 1) | |
| def calc_chip_concentration(df: pd.DataFrame) -> pd.Series: | |
| """ | |
| 筹码集中度近似计算 | |
| 当价格在低位且振幅收窄、成交量萎缩时,视为筹码集中 | |
| 指标含义:值越高表示筹码越集中(0~1) | |
| """ | |
| low_60 = df["low"].rolling(60).min() | |
| high_60 = df["high"].rolling(60).max() | |
| # 价格在低位(0~0.3区间为新低附近集中) | |
| price_pos = (df["close"] - low_60) / (high_60 - low_60).replace(0, np.nan) | |
| # 振幅收窄(近10日振幅均值与60日振幅均值的比值) | |
| amp_10 = (df["high"] - df["low"]).rolling(10).mean() | |
| amp_60 = (df["high"] - df["low"]).rolling(60).mean() | |
| amp_ratio = amp_10 / amp_60.replace(0, np.nan) | |
| amp_ratio = amp_ratio.clip(0, 1) | |
| # 成交量萎缩(近5日均量是60日均量的比例,越低越集中) | |
| vol_ratio = df["volume"].rolling(5).mean() / df["volume"].rolling(60).mean().replace(0, np.nan) | |
| vol_ratio = 1 - vol_ratio.clip(0, 1) # 反转:量越小值越大 | |
| # 加权综合 | |
| concentration = (1 - price_pos.fillna(0.5)) * 0.4 + (1 - amp_ratio.fillna(0.5)) * 0.3 + vol_ratio.fillna(0.5) * 0.3 | |
| return concentration.clip(0, 1) | |
| def get_support_resistance(df: pd.DataFrame) -> dict: | |
| """ | |
| 识别支撑位和压力位 | |
| 基于前期高点和低点 | |
| """ | |
| if len(df) < 60: | |
| return {"support": None, "resistance": None} | |
| recent = df.tail(60) | |
| # 支撑位:60日最低点 | |
| support = recent["low"].min() | |
| # 压力位:60日最高点 | |
| resistance = recent["high"].max() | |
| # 均线支撑:20日线位置 | |
| ma20 = recent["close"].rolling(20).mean().iloc[-1] | |
| return { | |
| "support": round(support, 2), | |
| "resistance": round(resistance, 2), | |
| "ma20": round(ma20, 2), | |
| } | |