指标函数要求什么样的数据?
pandas-ta 不取数据,它假设你的 DataFrame 里已经有正确的列。下表列出各类指标需要的列,以及缺列时实测会发生什么——这一列比「需要什么」更重要。
| 指标类别 | 需要的列 | 缺列时的实测行为 | 你该怎么防 |
|---|---|---|---|
单序列类(sma / ema / rsi / bbands) | close 或显式传入的序列 | 拿不到 close 时该次计算失败或被跳过 | 先断言 close 存在;更稳的是显式传序列 |
带高低价的(atr / stoch / adx / psar) | high、low、close | 缺 high 或 low 时同样被跳过 | 做一次集合断言 {'high','low','close'} ⊆ columns |
成交量类(obv / mfi / vwap) | 外加 volume | 实测:静默返回原 DataFrame,一列都不加,也不报错 | 成交量类指标单独做列断言 |
K 线形态(cdl_pattern) | open、high、low、close | 缺列时跳过 | 形态依赖 open 列,容易被忽略 |
| 列名大小写 | Close 与 close 都可用 | 实测大写列名一样能算出 SMA_20 | 但大小写混用的表(Close 与 volume 并存)容易埋坑,建议统一小写 |
| 注意点 | MultiIndex 列(yfinance 常见)需要先展平 | 展平前指标函数找不到列 | 见下方「列结构」一节 |
append=True 之后 df.columns 没有任何变化——如果你没断言列数,会以为「指标算完了」,实际上一个都没算。为什么不报错,却一列都没加上?
下面的复现步骤都是本站实测的真实行为。把它们加进你的数据检查函数,能提前拦掉大部分「算了但没算」的问题。
| 失败类型 | 复现方式(实测) | 观察到的结果 | 加什么断言 |
|---|---|---|---|
| ① 缺列 | 把 DataFrame 的 volume 列删掉,再执行 d.ta.obv(append=True) | 形状仍为 60×4,没有新增任何列,无异常 | assert {"high","low","close"} <= set(df.columns) |
| ② 数据不足 | 取 10 行数据执行 d.ta.sma(length=20) 与 d.ta.rsi(length=14) | 形状仍为 10×5,没有新增任何列,无异常 | assert len(df) >= 需要的窗口长度 |
| ③ 缺口(这一种会静默出 NaN) | 对仅含交易日的日线执行 resample('D') 后会插入周末空行 | 官方文档记载:300 行日线中含周末缺口时,224 个指标里 80 多个整列 NaN | 先 df = df.dropna() 再算(必须赋值回去) |
- 把列断言写成函数 在管道入口加一个
check_ohlcv(df):断言必需列存在、索引为日期且递增、high >= max(open, close) >= min(open, close) >= low、无重复日期。返回一份检查结论,而不是抛异常。 - 把长度断言写进指标封装 包一层
safe_ta(df, fn, **kw):调用前记录len(df)与set(df.columns),调用后比对是否真的新增了列;没新增就记一条日志。这样「静默失败」会变成可查记录。 - 把缺口处理前置 取数或重采样之后、算指标之前做一次
dropna();若业务上不能丢行(例如需要保留交易日对齐),就改成只在指标输入序列上做dropna()再对齐回原索引。 - 把这三条写进项目 README 让后来的协作者知道:这里不报错不等于算对了。本站的建议是把它写成「数据契约」一节,与指标默认参数放在一起。
yfinance 取来的 MultiIndex 列怎么处理?
pandas-ta 只认单层列名。直接从 yf.download() 拿到的表在批量下载时列是多层的,必须先展平。下表给出可复制的处理顺序。
| 步骤 | 做什么 | 命令示例 | 为什么必须做 |
|---|---|---|---|
| ① 看列结构 | 先打印列的层级数 | print(df.columns.nlevels) | 多层列下 df["close"] 取不到单列 |
| ② 展平多层列 | 批量下载时通常只保留价格层 | df.columns = df.columns.get_level_values(0) | 取下价格层(Open/High/Low/Close/Volume) |
| ③ 统一小写 | 把列名规范成小写 | df.columns = [c.lower() for c in df.columns] | 虽然大写也能用,但混用容易埋坑 |
| ④ 只留需要的列 | 丢掉 adj close 等多余列或明确保留策略 | df = df[["open","high","low","close","volume"]] | 避免后续误用复权列 |
| ⑤ 排序并去重索引 | 保证日期递增且不重复 | df = df[~df.index.duplicated(keep="last")].sort_index() | 指标按顺序滚动,乱序会全错 |
| ⑥ 处理缺口 | 决定 dropna 还是保留 | df = df.dropna() | 缺口会让依赖 rolling 的指标整列 NaN |
| 注意点 | 复权语义要与回测口径一致 | — | 用复权价算指标、用未复权价估收益是常见错误 |
指标要跑多少行才有可用的数值?
指标不是从最开头就有值的。下面是本站实测的起始非空位置,用来估算你至少需要多少历史数据。
| 指标 | 参数 | 起始非空位置(实测) | 留数据时怎么算 |
|---|---|---|---|
sma | length=20 | 0 基索引 19(第 20 行) | 至少留 length 行 |
atr | length=14 | 0 基索引 14 | RMA 平滑,先消耗 14 行 |
rsi | length=14 | 60 行数据里出现 46 个非空值 | 约需 length + 1 行 |
macd | 12/26/9 | 需要先跑满慢线窗口再加信号窗口 | 保守留 26 + 9 行以上 |
bbands | 20 / 2.0 | 与 sma 相同(中轨即均线) | 至少留 length 行 |
ichimoku | 9/26/26 | 以最长窗口为准,另有前瞻位移 | 留 26 × 2 行以上更稳 |
| 注意点 | 以上是「无缺口」情况下的读数 | — | 有缺口时起点会后移,因为 rolling 需要连续非 NaN |
自己用 pandas 复算,能对上吗?
很多人会想「我用 pandas 自己写公式不就行了」。可以,但必须知道哪些能对上、哪些对不上。下表是本站用 pandas 3.0.6 逐项复算的最大绝对差。
| 指标 | 复算公式 | 最大绝对差 | 结论与用法 |
|---|---|---|---|
sma(20) | close.rolling(20).mean() | 0.0 | 完全一致,可以互换 |
bbands(20, 2) 中轨 | close.rolling(20).mean() | 0.0 | 一致 |
bbands(20, 2) 上轨 | mid + 2 × std(ddof=0) | 1e-12 | 吻合 → 库用 ddof=0(与签名一致) |
bbands(20, 2) 上轨(另一种口径) | mid + 2 × std(ddof=1) | 0.1677 | 不吻合,排除 ddof=1。自己复算时用错这个会整体偏移 |
ema(20) | close.ewm(span=20, adjust=False).mean() | 0.0593 | 不完全一致:种子与递推口径不同,别假定相等 |
rsi(14) | 朴素 Wilder(ewm(alpha=1/14)) | 18.81 | 差异很大:原因在平滑与种子细节,必须直接用库的输出 |
macd(12,26,9) | ewm(span=12) − ewm(span=26) | 0.3821 | 同样受 EMA 口径影响 |
atr(14) | TR.rolling(14).mean() / TR.ewm(alpha=1/14) | 0.0889 / 0.0545 | 两种都不完全一致;列名 ATRr_14 里的 r 表示 RMA 平滑 |
接指标之前要检查哪些项?
把下面的检查项做成一个函数,在每次算指标前调用。它拦掉的是不会报错但结果错的问题。
| 检查项 | 判据 | 不通过的后果 | 处置 |
|---|---|---|---|
| 必需列存在 | {'open','high','low','close','volume'} ⊆ columns | 静默不加列 | 补列或改传显式序列 |
| 索引是日期且递增 | isinstance(index, DatetimeIndex) 且 index.is_monotonic_increasing | 滚动窗口按错顺序计算 | 排序并转日期索引 |
| 索引无重复 | index.has_duplicates == False | 重采样与对齐出错 | 去重 |
| 行数大于最大窗口 | len(df) > max_window | 指标整列 NaN 或静默无输出 | 补历史数据或缩短窗口 |
| OHLC 自洽 | high ≥ max(open, close)、low ≤ min(open, close) | ATR / PSAR 等高低价指标失真 | 修正数据或标注为异常 |
| 缺口比例可接受 | df.isna().mean() < 阈值 | 依赖 rolling 的指标大面积 NaN | 先 dropna() 再算 |
| 数值列可运算 | 用 df.dtypes 确认 OHLCV 五列都是数值型 | 对象型列会让计算报错或静默跳过 | 用 pd.to_numeric(errors="coerce") 转换并记录被强转的行数 |
| 成交量单位一致 | 确认单位(股 / 手 / 千股)在整条管道里统一 | 成交量类指标数值量级错乱但不会报错 | 在取数层统一单位,并写进数据字典 |
pandas-ta 数据问题常见问答:为什么没加列、为什么全是 NaN
下面的回答都指向可核验的官方文件或本站实测;与官方表述冲突时,以官方仓库与文档为准。
为什么我算完指标,DataFrame 一列都没多?
两种最可能:①列名不对或缺列(例如没有 volume 却算 OBV);②行数少于指标窗口。本站实测这两种情况都不抛异常,只是什么都不加。先打印 df.columns 与 len(df)。
列名一定要小写吗?
不一定。实测 Close 这样的大写列名同样能算出 SMA_20。但大小写混用(Close 与 volume 并存)容易埋坑,建议统一成小写。
从 yfinance 拿的数据为什么直接用不了?
批量下载时列是 MultiIndex(多层),而指标函数只认单层列名。需要先取价格层、转小写、排序去重、处理缺口,顺序见本页「列结构」一节。
全是 NaN 是算错了吗?
通常是三类原因:数据长度不够、数据里有缺口、或者你取错了列。官方 quickstart 明确记载:含周末缺口的日线上,224 个指标里有 80 多个会整列 NaN,而 ema/rma/atr 仍出值。官方给的解法是在计算之前 dropna(),并且必须赋值回去。
dropna() 放在计算前还是计算后?
放在计算之前。放在后面只能删掉 NaN,不能改变「滚动窗口看不到足够连续数据」这个事实。另外 daily.dropna() 会返回副本,写成单独一行等于没做。
自己用 pandas 写公式靠谱吗?
取决于指标。本站实测:SMA 与 BBands 中轨完全一致;BBands 上轨用 ddof=0 才吻合;但 EMA 差 0.06、RSI 差 18.8、MACD 差 0.38、ATR 差 0.05–0.09。所以简单均线可以自己写,振荡类建议直接用库输出。