ta / 预热窗口与 NaN

ta 指标前面为什么是 NaN:43 个类的预热长度实测表

把 OHLCV 喂进 ta 之后,第一件让人困惑的事是「为什么结果前面一堆空值」。答案是 滚动窗口还没填满:RSIIndicator(window=14) 需要 14 根 K 线才出第一个值, 所以前 13 行是 NaN。

但麻烦在于每个指标的预热长度都不一样——本站实测从 0 行(OBV、ADI) 到 47 行(STC、Mass Index)。更麻烦的是,ATR 和 ADX 的表头不是 NaN 而是 0, 用 isna() 根本筛不掉。这一页给出全部 43 个类的实测数字。

预热 0–47 行43 个类逐个实测fillna 有陷阱核验 ta 0.11.0
默认 fillna=False滚动窗口填满前是 NaN(min_periods=window)
fillna=True窗口不满也算,用部分窗口的均值补上
0 值表头ATR / ADX 用 np.zeros 初始化,前几行是 0.0
dropna()把任何 0 值当缺失,含 0 的整行会被删掉
依据 ta 源码(ta/utils.py、ta/volatility.py)与本机实测整理的语义示意;读数见本页表格,不是官方文档结论。

三种机制

ta 的缺失值有三种来源,别当成同一回事

机制哪些指标表头表现怎么判断可用性
滚动窗口未满大部分趋势/动量/波动率指标(SMA、RSI、CCI、BB、DC、UI…)NaN,长度 = 窗口 − 1用 isna() 或 first_valid_index()
多级窗口叠加MACD、PPO、PVO、TSI、StochRSI、STC、Mass Index、AroonNaN 更长:MACD 信号线 33 行、TSI 37 行、STC 与 Mass Index 47 行同上,但要知道「基础指标出值 ≠ 派生列出值」
数组零初始化AverageTrueRange、ADXIndicator0.0 而不是 NaNisna() 无效;要按窗口手动切片或把 0 换掉
平滑类无预热OBV、ADI、NVI、Daily Return、Cumulative Return几乎立即出值(0 或 1 行 NaN)可直接使用,但要理解它们的基准点

为什么会有 0 值表头:看 AverageTrueRange._run() 的实现就能看到——它先用 np.zeros(len(close)) 建数组,再从第 window-1 位开始用递归公式填充。前 window-1 位从来没被赋值,自然就是 0。这和「NaN 表示缺失」的常见约定完全相反。

预热长度表

43 个指标类各自的预热长度是多少

类别类名最长预热对应输出列特殊情况
趋势PSARIndicator212psar_up表头为 0:psar_down_indicator、psar_up_indicator
趋势MassIndex47mass_index输出 1 条
趋势STCIndicator47stc输出 1 条
趋势TRIXIndicator40trix输出 1 条
动量TSIIndicator37tsi输出 1 条
动量AwesomeOscillatorIndicator33awesome_oscillator输出 1 条
动量PercentagePriceOscillator33ppo_hist输出 3 条
动量PercentageVolumeOscillator33pvo_hist输出 3 条
趋势MACD33macd_diff输出 3 条
动量StochRSIIndicator30stochrsi_d输出 3 条
动量UltimateOscillator28ultimate_oscillator输出 1 条
趋势IchimokuIndicator25ichimoku_a输出 4 条
动量StochasticOscillator15stoch_signal输出 2 条
动量ROCIndicator14roc输出 1 条
趋势AroonIndicator14aroon_down输出 3 条
趋势KSTIndicator14kst输出 3 条
趋势VortexIndicator14vortex_indicator_diff输出 3 条
成交量EaseOfMovementIndicator14sma_ease_of_movement输出 2 条
成交量ForceIndexIndicator14force_index输出 1 条
动量KAMAIndicator13kama输出 1 条
动量RSIIndicator13rsi输出 1 条
动量WilliamsRIndicator13williams_r输出 1 条
趋势CCIIndicator13cci输出 1 条
趋势DPOIndicator13dpo输出 1 条
趋势EMAIndicator13ema_indicator输出 1 条
趋势SMAIndicator13sma_indicator输出 1 条
趋势WMAIndicator13wma输出 1 条
波动率BollingerBands13bollinger_hband表头为 0:bollinger_hband_indicator、bollinger_lband_indicator
波动率DonchianChannel13donchian_channel_hband输出 5 条
波动率KeltnerChannel13keltner_channel_mband表头为 0:keltner_channel_hband_indicator、keltner_channel_lband_indicator
波动率UlcerIndex13ulcer_index输出 1 条
成交量ChaikinMoneyFlowIndicator13chaikin_money_flow输出 1 条
成交量MFIIndicator13money_flow_index输出 1 条
成交量VolumeWeightedAveragePrice13volume_weighted_average_price输出 1 条
成交量VolumePriceTrendIndicator1volume_price_trend输出 1 条
其他DailyLogReturnIndicator1daily_log_return输出 1 条
其他DailyReturnIndicator1daily_return输出 1 条
趋势ADXIndicator0adx表头为 0:adx、adx_neg、adx_pos
波动率AverageTrueRange0average_true_range表头为 0:average_true_range
成交量AccDistIndexIndicator0acc_dist_index输出 1 条
成交量NegativeVolumeIndexIndicator0negative_volume_index输出 1 条
成交量OnBalanceVolumeIndicator0on_balance_volume输出 1 条
其他CumulativeReturnIndicator0cumulative_return表头为 0:cumulative_return

读法:「最长预热」是该类所有输出列里前导 NaN 最多的那一条(也就是整类可用所需的最小行数)。按这份实测表,要保证全部 43 个类都出值,样本至少需要 448 行(400 行样本 + 47 行预热);只想用 RSI 的话 14 行就够。数字来自 400 行合成 OHLCV(固定随机种子),以你实际数据的长度与缺口情况为准。

fillna 语义

fillna=True 到底填了什么:数值级验证

对比项fillna=False(默认)fillna=True
布尔带中轨(window=20)前导 NaN19 行0 行
第 6 行(索引 5)的值NaN102.600107
同一位置的真实含义窗口未满,不该有值= 该标的前 6 根 K 线收盘价的算术平均(本机复算:102.600107)
第 20 行(索引 19)的值102.366137102.366137(与默认一致)
实现机制Pandas rolling 的 min_periods=windowmin_periods=0——窗口不满也照样算
用在特征工程里得到带 NaN 的列,需要自行 dropna 或填充得到无 NaN 的列,但头部若干行是「用 1 根、2 根…K 线算出来的」

这是本站认为最值得记住的一条:fillna=True 不是「用均值/前值把 NaN 补上」,而是让窗口在不完整时就开始计算。它确实消灭了 NaN,代价是头部若干行的统计含义与后续行不同——一个 20 日均线的前 19 个值,分别是用 1、2、…、19 根 K 线算出来的。把这类列直接喂进模型或回测,等于在头部混入了尺度不同的样本。

零值表头

哪些指标的表头是 0 而不是 NaN

输出列所属类表头行为为什么建议
average_true_range()AverageTrueRange前 13 行 0.0,全列无 NaN数组零初始化 + 递归填充当成缺失值处理,别参与计算
adx() / adx_pos() / adx_neg()ADXIndicator无 NaN,但表头 15–27 行为 0同类实现方式同上;注意 ADX 的 0 会被误读成「无趋势」
bollinger_hband_indicator() / lband_indicator()BollingerBands只有 0 / 1,前 30 行为 0这是信号标记列,不是价格0 是合法取值,别当缺失值删
psar_up_indicator() / psar_down_indicator()PSARIndicator只有 0 / 1同上(标记列)同上
keltner_channel_kchi/kcliKeltnerChannel只有 0 / 1同上(标记列)同上
on_balance_volume() / acc_dist_index() / negative_volume_index()成交量模块从 0 累加,所以首值接近 0 不是缺失累积量指标以某点为基准看差分或变化率,别直接比绝对值

区分两类 0:一类是「初始化留下的 0」(ATR、ADX)——那是缺失值,必须处理;另一类是「标记列的 0」(各种 *_indicator)——那是合法的信号取值,删掉就丢了信息。两种 0 在同一张表里并存,这就是为什么不能无脑 df != 0。

dropna 行为

ta.utils.dropna() 会删掉哪些行

调用方式输入行数输出行数发生了什么
ta.utils.dropna(df)(数据干净、无 0 无缺失)400400原样返回
同上,但某一天 volume = 0(例如停牌)400399该行被整行删除——0 被当成缺失值
同上,但某一天 close = 0(数据源异常)400399该行被删除(这一种是期望行为)
同上,但某一天 volume = NaN400399该行被删除
dropna() 用在 add_all_ta_features 的输出上400086 列里每一行都至少有一列是 NaN,于是全部行被删光

源码里有两次「隐蔽的替换」:dropna() 先执行 df[number_cols] = df[number_cols][df[number_cols] < math.exp(709)] 和 df[number_cols] = df[number_cols][df[number_cols] != 0.0],第二步把所有 0 值变成 NaN,然后才 dropna()。官方 README 的用法是「先 dropna 清洗原始数据,再 add_all_ta_features」——顺序反了会把整个数据集删空。

处理策略

NaN 该怎么处理:按目的选做法

你的目的推荐做法代价 / 注意点
接进机器学习管道用默认 fillna=False,然后按最长预热行数整体截掉头部会损失 47 行样本;换指标组合可以减少损失(只有 STC/Mass Index 需要 47 行)
只想快速看某只标的现状用 fillna=True,头部值不要当真头部若干行统计含义不同,不要拿去做结论
回测用 fillna=False,并对每个指标单独按其预热长度对齐起点不同指标起点不同,信号时间必须取最晚的那个,否则会用未来数据
处理停牌 / 无成交日不要用 ta.utils.dropna();自己按规则过滤该函数会把合法的 0 成交量行删掉,造成时间序列断裂
只想用一个指标直接用该指标的类,跳过 wrapper需要自己知道它的预热长度(本页表格里有)

一条通用原则:先确定样本的可用起点,再开始算指标。把 43 个指标一起算、再用 dropna() 收尾,会同时遇到「删空」和「起点不一致」两个问题。回测场景下起点不一致还可能引入未来数据,详见回测边界页。想先看每个指标的窗口与输出列,去指标目录;要一次性拿到 86 列,看批量特征页。

FAQ

关于 ta 预热窗口与 NaN 的常见问题

为什么 ta 算出来的指标前面是 NaN?
因为滚动窗口还没填满。默认 fillna=False 时,Pandas 的 rolling 用 min_periods=window,前 window-1 行就是 NaN。RSIIndicator(window=14) 前 13 行是 NaN,第 14 行起有值。以官方实现与你实际窗口参数为准。
fillna=True 是不是就是把 NaN 填成 0 或均值?
不是。本站核验了源码:fillna=True 只是把 min_periods 从窗口长度改成 0,让窗口不满也照常计算。所以 20 日布林带中轨的前 19 个值,分别是用 1、2、…、19 根 K 线的均值——数值上等于「部分窗口均值」,不是常数填充。本机复算第 6 行等于前 6 根收盘价均值,完全吻合。
ATR 算出来全是 0 是 bug 吗?
不是 bug,是实现的副作用。AverageTrueRange._run() 用 np.zeros(len(close)) 初始化,再从第 window-1 位开始递归填充,所以前 window-1 位是 0.0 而不是 NaN。ADXIndicator 有同样的特点。把这些 0 当真实值参与计算会得到错误的仓位或止损水平。
ta.utils.dropna() 为什么把我一半数据删了?
因为它的实现会先把所有数值列里的 0 替换成 NaN,然后再删行。只要你的数据里有停牌日成交量 0、或某个指标天然取 0,这些行都会被删。本机实测:一份 400 行数据里只要有一天 volume=0,输出就从 400 行变成 399 行。用它之前先确认数据里没有合法的 0 值;以你实际的 ta.utils.dropna 源码版本为准。
样本要准备多少行才够?
按本机实测,43 个类里预热最长的是 47 行(STCIndicator 与 MassIndex),所以要全部指标都出值,至少需要 47 + 1 行有效数据;实践中本站在 400 行样本上跑完了全部指标。只用 RSI 的话 14 行就够。样本长度以你的指标组合与官方实现为准。
不同指标的 NaN 起点不同,能不能直接对齐全表?
可以对齐,但必须按最长的那个预热长度统一切掉头部,而不是各自处理。在回测场景里这一点尤其重要:如果某个指标在你的第一个信号日还没有值,而你用后来的值回填,就等于引入了未来数据。详见回测边界页。
这些 NaN 长度会随版本变化吗?
会。预热长度取决于窗口参数的默认值和具体实现方式,而 ta 的默认参数写在各类的 __init__ 签名里(本页数字取的是本机 ta 0.11.0 的默认值)。升级版本或自己传不同的 window 后,请以你的实际输出为准重新测量。

接下来读哪一页?

知道有多少 NaN 之后,下一个问题是「数字本身对不对」。数值验证页用三路手算把 RSI、MACD、布林带和 ATR 拆开对照。