pandas-ta 的指标列有没有用未来数据?怎么用代码判断
不要去猜指标公式。用数据本身做实验:把数据截断到时刻 t,只算到 t,再和「一次算完全部数据」在第 t 行的取值比。如果对不上,就说明这一列的值会因为后来的数据而变化。
def is_causal(df, fn, col):
"""fn: 传入 DataFrame 返回 Series/DataFrame 的调用;col: 要检查的列名。"""
full = fn(df)[col]
diffs = 0
for t in range(warmup, len(df)):
part = fn(df.iloc[: t + 1])[col] # 只喂到 t
if not np.isclose(part.iloc[-1], full.iloc[t], equal_nan=True):
diffs += 1
return diffs # 0 表示这一列是因果的(不含未来数据)
| 为什么用这个方法 | 说明 | 局限 | |
|---|---|---|---|
| 不需要读实现 | 只依赖「输入变了输出会不会变」这一条可观测性质 | 需要能反复调用,计算量随数据长度增长 | |
| 能定位到列 | 多列指标(如 Ichimoku)可以逐列判定,而不是整块下结论 | 列数多时耗时明显 | |
| 能当回归测试 | 升级版本后重跑一次,就能发现新引入的非因果列 | 需要一份有代表性的数据样本 | |
| 注意点 | 本方法判定的是实现层面的因果性 | — | 有些指标在数学定义上就用未来窗口(如 DPO),会被正确地判为非因果 |
pandas-ta 哪些指标列通过、哪些不通过?
下表是本站对 120 行合成 OHLCV 数据、逐个时点增量重算的实测结果(比对区间为第 61–120 个时点,共 60 次比对)。
| 指标 | 检查的列 | 不一致时点数 | 判定 |
|---|---|---|---|
sma(length=20) | SMA_20 | 0 | 因果,可安全使用 |
rsi(length=14) | RSI_14 | 0 | 因果 |
macd() | MACD_12_26_9 等 3 列 | 0 | 因果 |
bbands(length=20) | 5 列 | 0 | 因果 |
supertrend() | 4 列 | 0 | 因果 |
tsi() | 2 列 | 0 | 因果 |
tsignals() | 输出即原 df 的各列 | 0 | 不改变原数据 |
ichimoku() | ITS_9 / IKS_26 / ISA_9 / ISB_26 | 各 0 | 这四列在默认 append_span=False 下不做前瞻位移 |
ichimoku() | ICS_26(Chikou Span) | 34 | 非因果:默认 include_chikou=True,会向后位移 |
dpo() | DPO_20 | 49 | 非因果:默认 centered=True,用中心化窗口 |
其它振荡类(willr / stoch / cci 等) | 未逐项检查 | — | 本站没有覆盖全部指标,请对你实际用到的那几个单独跑一遍 |
| 多列指标的逐列差异 | 按列分别统计,而不是整块判定 | — | 同一个指标里可能只有部分列非因果,必须逐列看 |
pandas-ta 的非因果列,三种处理方式怎么选?
判定出来后不一定要放弃这个指标,但绝不能把它原样当成信号。下表给出三种处置方式与适用情形。
| 处置方式 | 怎么做 | 适用情形 | 代价 |
|---|---|---|---|
| ① 关掉前瞻参数 | df.ta.dpo(length=20, centered=False);df.ta.ichimoku(include_chikou=False) | 你只是不想要未来信息,指标本身仍要保留 | 指标含义会变,与教科书里的同名指标不再等价,要重新解释 |
| ② 自行位移对齐 | 把整列 shift(n) 到「在信息可得时点才可见」的位置,n 从实现里推导 | 保留原定义,但需要一个明确的可用时点 | 位移量算错会引入新的偏差;建议用上一节的判定代码回归 |
| ③ 从信号池剔除 | 不把这些列放进信号逻辑,只在事后研究中使用 | 指标本身是「事后描述型」(例如 Chikou 用于回顾) | 放弃这部分信息在信号里的作用 |
- 先确认它到底在哪个时点才可得 对非因果列,用判定代码找出「第 t 行的值在第几行之后才稳定」——这个差值就是需要位移的量。实测中 DPO 与 Chikou 的位移都由定义决定(中心化窗口、向后 kijun 根)。
- 再决定是关参数还是做位移 如果只想要趋势信息,直接关前瞻参数最省事;如果要对齐教科书定义,就做位移并在注释里写清位移依据。
- 最后在回测里做一次对照 同一策略跑两遍:一遍带非因果列、一遍不带。如果两者绩效差距很大,说明原策略的核心收益来自未来信息,这个结论比任何参数优化都重要。
指标值算对了,信号就写对了吗?
指标因果不等于信号因果。下面这张表是从指标值到下单动作之间最容易错位的四个环节。
| 环节 | 常见错误 | 为什么会出错 | 正确做法 |
|---|---|---|---|
| 成交时点 | 用当根 K 线的收盘价成交,但信号也来自当根收盘价 | 收盘价在收盘那一刻才知道,你不可能用同一个价格成交 | 信号在 t 根收盘生成,成交记为 t+1 根开盘(或 t 根收盘加滑点,但要明确写出来) |
| 位移符号 | 以为 shift(1) 是「往前看」,实际是「往后挪」 | pandas 的 shift(1) 把值下移一行,等于「让 t 行看到 t−1 行的值」 | 在代码注释里写清「shift 后第 t 行代表 t−1 时刻的信息」,用一段断言验证首行是 NaN |
| 预热期 | 从最开头的一根 K 线就开始下单 | 前 N 根里指标是 NaN,比较结果不可靠 | 丢弃预热期内的信号,并记录丢了多少根 |
| 参数越多越危险 | 在历史上反复调参直到曲线好看 | 参数越多,越容易拟合噪声 | 用样本外区间或滚动窗口验证,并保留一份调参记录 |
接回测之前要做哪些审计?
把下面七项做成一张检查表,每次改策略都过一遍。前四项是数据层,后三项是信号层。
| # | 检查项 | 判据 | 不通过的典型后果 |
|---|---|---|---|
| 1 | 列名契约 | 必需列齐全、索引为递增日期且无重复 | 静默不加列或整列 NaN |
| 2 | 缺口处理 | 在算指标之前完成 dropna,或明确记录保留策略 | 依赖 rolling 的指标大面积 NaN |
| 3 | 复权口径一致 | 指标与收益计算使用同一套价格口径 | 指标看到的波动与实现的收益不是同一件事 |
| 4 | 预热期隔离 | 预热期内的信号被丢弃并计数 | 最早一批信号建立在 NaN 之上 |
| 5 | 指标因果性 | 用增量重算代码逐列检查并留读数 | 回测结果被未来数据抬高 |
| 6 | 成交时点 | 信号时点与成交时点的关系被显式写出并断言 | 同根收盘价既发信号又成交 |
| 7 | 样本外验证 | 至少留出一段未参与调参的区间 | 参数过拟合,上线后失效 |
| 8 | 交易成本建模 | 佣金、滑点、冲击成本至少给出一组假设并写在结论里 | 回测收益被高估,与实盘差距无法解释 |
| 9 | 极端行情检查 | 把涨跌停、停牌、跳空区间单独看一遍结果 | 策略在特殊区间失效但在全样本上看不出来 |
| 10 | 信号密度检查 | 统计每期产生多少信号,确认不是「几乎每根都发信号」 | 信号过密通常意味着阈值失效或存在信息泄露 |
| 11 | 可复现性检查 | 在干净环境里用锁定版本重跑一次,比对结果 | 结论无法复现,等于没有结论 |
pandas-ta 未来函数常见问答:哪些列有问题、怎么自己验证
下面的回答都指向可核验的官方文件或本站实测;与官方表述冲突时,以官方仓库与文档为准。
pandas-ta 有未来函数吗?
有,但集中在可预期的位置:实测 DPO 在默认 centered=True 下、Ichimoku 在默认 include_chikou=True 下的 ICS_26 列会被判为非因果。其余本站检查过的常用指标(SMA/RSI/MACD/BBands/Supertrend/TSI)在实测中均无不一致。
DPI/DPO 这类指标是不是有 bug?
不是。DPO 的定义本身就是中心化窗口(用未来若干根 K 线做参照),非因果是它的定义特征。问题只在于你不能把它原样当成可交易的信号。
多周期数据会对不齐吗?
会。把日线指标与周线指标放在同一张表上时,周线值在一周内是不变的(或只在周末更新),对齐方式决定了下单时看到的是哪一期数值。本站不给统一答案,但要求把对齐规则写进代码注释并做断言。
怎么自己验证我用的指标?
用本页给出的判定代码:把数据截断到时刻 t 重新计算,与全量结果比对第 t 行的取值。60 次比对里只要出现不一致,就说明该列受到未来数据影响。
Ichimoku 的 ISA / ISB 也有问题吗?
在默认 append_span=False 的实测中,ISA_9 与 ISB_26 的不一致时点数为 0;被判为非因果的是 ICS_26(Chikou Span)。若你把 append_span 打开做前瞻位移,需要重新判定。
用 shift(1) 处理一下就行吗?
可能不够。位移量应当由「该列的值在第几行之后才稳定」推导,而不是凭经验写 1。用判定代码求出的差值才是依据;位移后建议再回归一次。
预热期要留多长才够?
以「最长窗口 + 信号窗口」为下限。本站实测 SMA(20) 的起始非空值在第 20 根,ATR(14) 在第 15 根,而 MACD 需要先跑满慢线再加信号窗口。保守做法是把预热期设为最长窗口的两倍,并统计被丢弃的信号条数写进记录。
本站的回测建议是什么?
本站不给策略建议,也不评价任何策略收益。只提供一份七项审计清单,用来确认你的回测里不存在你自己都不知道的信息泄露。