源码公式与预热窗口
finta 指标怎么算的:为什么有的指标前 13 行是 NaN,MACD 却少了柱状线
FinTA 把每个指标写成一个直接调用 pandas 滚动与指数平滑的方法,所以「预热多长、返回什么列、" 用哪种平滑」完全取决于那几行源码——同名指标在不同库里给出不同的数,根源就在这里。
本站逐项实测了这些口径:SMA(14) 前 13 行是 NaN,"
"RSI(14) 只有 1 行,EMA 第 0 行就有值;"
"BBANDS 用的是样本标准差(ddof=1),ATR 用的是"
"True Range 的简单均值而不是 Wilder 平滑;MACD 只返回两列,没有柱状线。
FinTA · 预热与 NaN
每个指标前面有多少行是 NaN
| 指标调用 | 首个有效行 | 前置 NaN 数 | 返回类型 | 说明 |
|---|---|---|---|---|
TA.EMA(ohlc, 14) | 0 | 0 | Series | adjust=True 时从第 0 行就有值——与 TA-Lib 前 19 行 NaN 的行为不同 |
TA.SMMA(ohlc, 14) | 0 | 0 | Series | 同样是平滑类,与 EMA 一样不给 NaN |
TA.RSI(ohlc) | 1 | 1 | Series | 内部用 ewm,只在前 1 行为 NaN;与 TA-Lib 的 14 行预热差异明显 |
TA.SMA(ohlc, 14) | 13 | 13 | Series | 标准 rolling(14) 语义:第 14 行才有第一个值 |
TA.WMA(ohlc, 14) | 13 | 13 | Series | 同上 |
TA.ATR(ohlc) | 13 | 13 | Series | True Range 之后取 14 期均值,因此也是 13 行 |
TA.ADX(ohlc) | 13 | 13 | Series | 但 TA-Lib 的 ADX 需要 27 行预热 |
TA.STOCH(ohlc) | 13 | 13 | Series | 14 期高低区间 |
TA.WILLIAMS(ohlc) | 13 | 13 | Series | 与 TA-Lib 一致 |
TA.MFI(ohlc) | 13 | 13 | Series | TA-Lib 是 14 行,差 1 行 |
TA.BBANDS(ohlc, 20) | 19 | 19 | DataFrame(3 列) | 三列(上/中/下轨)的首个有效行相同 |
TA.OBV(ohlc) | 1 | 共 14 个 NaN | Series | NaN 不在开头:1.3 把「收盘价不变」的 13 个平盘日输出为 NaN |
读法:预热长度不是「周期数 − 1」这么简单——EMA/RSI 走指数平滑,值从很早就有;ATR/ADX 走滚动窗口,要等满窗。而 OBV 的 NaN 更特殊:它的 NaN 分散在中间,因为 1.3 在平盘日没有定义取值(master 已改为沿用上一值,见项目状态页)。
FinTA · 返回结构
每个指标返回的列名是什么
| 返回对象 | 列名 / Series 名称(实测) | 备注 |
|---|---|---|
TA.SMA(ohlc, 14) | Series,name = 14 period SMA | 名称里带周期数,便于直接赋值给 DataFrame |
TA.RSI(ohlc) | Series,name = 14 period RSI | 同上;默认周期 14 |
TA.ATR(ohlc) | Series,name = 14 period ATR | 同上 |
TA.ADX(ohlc) | Series,name = 14 period ADX. | 名称末尾带一个点,字符串匹配时容易漏 |
TA.STOCH(ohlc) | Series,name = 14 period STOCH %K | 名为 %K,实际没有 %D 列 |
TA.MACD(ohlc) | DataFrame,列 ['MACD', 'SIGNAL'] | 只有两列:没有柱状线(histogram);需要柱状线要自己算 MACD − SIGNAL |
TA.BBANDS(ohlc) | DataFrame,列 ['BB_UPPER', 'BB_MIDDLE', 'BB_LOWER'] | 三列,顺序固定 |
TA.KC(ohlc) | DataFrame,列 ['KC_UPPER', 'KC_LOWER'] | 只有上下轨,没有中轨 |
TA.DMI(ohlc) | DataFrame,列 ['DI+', 'DI-'] | 列名含 +/-,用点号取列不方便 |
TA.ICHIMOKU(ohlc) | DataFrame,列 ['TENKAN', 'KIJUN', 'senkou_span_a', 'SENKOU', 'CHIKOU'] | 同一张表里列名大小写风格不一致(senkou_span_a 小写、其余大写) |
TA.CMO / TSI / CHANDELIER / APZ / PSAR | DataFrame(多列) | 取列前先 print(list(r.columns)) 确认 |
TA.OBV / SAR / VFI / TP / MOM / ROC / ER / WILLIAMS 等 | Series | 单列输出 |
别凭记忆写列名。本站建议在接入指标时统一做一次 print(type(r), getattr(r, 'name', None) or list(r.columns)),把列名固化进你的代码常量——仓库已归档,这些名字不会变,但也不会有人帮你写文档。
FinTA · 平滑口径
FinTA 指标的平滑方式有哪些区别?从源码看八个常用指标的算法
| 指标 | 用到的 pandas 原语 | 关键参数 | 与其他库的差异点 |
|---|---|---|---|
| SMA | Series.rolling(window).mean() | 默认 period=41 | 无差异——三个库完全一致 |
| EMA | Series.ewm(span=period, adjust=adjust).mean() | 默认 period=9、adjust=True | adjust=False 时与 ta 完全一致;adjust=True 只影响前段 |
| RSI | 涨跌幅 ewm(span=period, adjust=adjust) 后再算比值 | 默认 period=14、adjust=True | 与 ta 完全一致;与 TA-Lib 只在预热段不同;改成 adjust=False 反而偏离更多 |
| MACD | 两条 ewm 之差,再对差值做 ewm | 默认 12 / 26 / 9、adjust=True | 与 TA-Lib 相关系数 0.999999;不返回柱状线 |
| BBANDS | rolling(period).mean() 与 rolling(period).std()(默认 ddof=1) | 默认 period=20、std_multiplier=2 | 带宽比 TA-Lib/ta 大 √(20/19) ≈ 1.026 倍——本站用半宽做线性验证,实测比值与理论完全一致 |
| ATR | True Range 之后取 rolling(period).mean() | 默认 period=14 | 是简单均值,不是 Wilder 递归——与手算「TR 的 14 期均值」完全一致(差 0.000000) |
| ADX | DMI 差值经 ewm(adjust=adjust) 平滑 | 默认 period=14 | 与 TA-Lib 相关系数 0.9321、与 ta 只有 0.6361——分歧最大,且 adjust 无法对齐 |
| STOCH | rolling(14).min()/max() 直接归一化 | 默认 period=14 | 与 TA-Lib 的快 %K、与手写公式都完全一致 |
把「差异点」一列读一遍可以得到一个判断规则:只要指标用的是滚动窗口(rolling),FinTA 与其他库通常能对上;只要用了指数平滑(ewm),就有了「怎么播种、怎么归一化」的自由度,不同库的选择不同。所以遇到数值不一致时,先看它是不是 ewm 类指标。
FinTA · adjust 开关
adjust 参数该不该改?同一个开关在两个指标上方向相反
| 指标 | 默认 adjust=True 时 | 改成 adjust=False 后 | 结论 |
|---|---|---|---|
TA.EMA(ohlc, 20) | 与 ta 最大差 2.107(只在前段) | 与 ta 完全一致(差 0.000000) | 想和 ta 对齐可以改 |
TA.RSI(ohlc, 14) | 与 TA-Lib 最大差 4.740,相关系数 0.999759 | 与 TA-Lib 最大差 涨到 23.353,相关系数降到 0.978169 | 不要改——默认值才是更接近主流的一个 |
TA.ADX(ohlc, 14) | 与 TA-Lib 最大差 14.51 | 与 TA-Lib 最大差 涨到 30.54 | 不要改,而且改了也对不齐(相关系数 0.9069 < 0.9321) |
这是一个「看起来能调、实际不能统一调」的开关。它的语义是 pandas 的 ewm(adjust=...):True 表示用加权归一化(前段更接近简单均值),False 表示纯递归。不同指标与外部库的接近程度不一样,所以本站的建议是:先保留默认值,只有在做过交叉验证之后才针对单个指标调整,并且把调整记进实验笔记——否则同一份代码在两个环境里会给出不同的数。
FinTA · 核对方法
怎么确认自己的指标算对了
五步核对
TA.SMA(ohlc, 14).isna().sum(),实测应为 13;如果得到 0,说明数据行数不够或处理方式与预期不同。TA.SMA(ohlc, 14).values[-1] 比较,两者应完全相等(滚动均值没有平滑自由度,是最可靠的对照指标)。tests/test_unit.py 用精确浮点断言,例如 TA.SMA(ohlc, 14).values[-1] == 6922.33922063;用仓库自带的 CSV 跑一遍即可确认安装正确。ewm 类指标单独做一次跨库对照:把它与另一个库(或手写递归)比一遍,把最大差记下来;不要用「看起来形状差不多」作为结论。adjust 取值、预热长度、数据行序要求——否则三个月后无法解释结果变化。# 核对脚本(本机实测通过;CSV 用仓库自带的 tests/data/bittrex_btc-usdt.csv)
import inspect, pandas as pd
from finta import TA
ohlc = pd.read_csv('bittrex_btc-usdt.csv', index_col='date', parse_dates=True).sort_index()
# 1) 预热长度
print(TA.SMA(ohlc, 14).isna().sum()) # 13
print(TA.RSI(ohlc).isna().sum()) # 1
print(TA.EMA(ohlc, 20).isna().sum()) # 0
# 2) 手算对照:滚动均值没有自由度,必须完全相等
manual = ohlc['close'].iloc[-14:].mean()
print(float(TA.SMA(ohlc, 14).iloc[-1]), manual) # 6922.3392206... 6922.3392206...
# 3) 官方断言值
assert round(TA.SMA(ohlc, 14).values[-1], 8) == 6922.33922063
# 4) 返回结构(别凭记忆写列名)
for name in ('MACD', 'BBANDS', 'KC', 'DMI', 'ICHIMOKU', 'STOCH', 'ADX'):
r = getattr(TA, name)(ohlc)
print(name, type(r).__name__, getattr(r, 'name', None) or list(r.columns))
# MACD DataFrame ['MACD', 'SIGNAL']
# BBANDS DataFrame ['BB_UPPER', 'BB_MIDDLE', 'BB_LOWER']
# KC DataFrame ['KC_UPPER', 'KC_LOWER']
# DMI DataFrame ['DI+', 'DI-']
# ICHIMOKU DataFrame ['TENKAN', 'KIJUN', 'senkou_span_a', 'SENKOU', 'CHIKOU']
# STOCH Series 14 period STOCH %K
# ADX Series 14 period ADX.
第 2 步是最值得养成的习惯:滚动均值类指标必须与手算值完全相等。如果不等,问题一定在数据(行序、列、行数)而不在指标——这也是本站把数据契约单列一页的原因。
FinTA · 口径不一致
哪些同名指标在不同库里算法不同?七个常见差异清单
| 指标 | FinTA 的做法 | 常见其他做法 | 本站实测的差距 |
|---|---|---|---|
| ATR | True Range 的简单移动平均 | Wilder 平滑(等同于 ewm(alpha=1/N)) | 最后一根 328.57 vs 336.40,最大差 561.69,相关系数 0.9887 |
| 布林带 | 标准差用样本口径 ddof=1 | 多数库与 TA-Lib 用总体口径 ddof=0 | 半宽比值恒为 √(20/19),最后一根上轨 8212.80 vs 8184.89 |
| ADX | DMI 差值经 ewm(adjust=True) | Wilder 递推 | 与 TA-Lib 相关系数 0.9321、与 ta 0.6361——本页表格里分歧最大的一项 |
| EMA | ewm(adjust=True),第 0 行就有值 | 通常用 adjust=False 并给前 N 行 NaN | 中后段完全一致,差异只在前 1/3(最大 2.245) |
| MFI | 14 期窗口,13 行预热 | TA-Lib 为 14 行预热 | 数值完全一致,只差 1 行预热长度 |
| CCI | 默认 period=20 | TA-Lib 调用时常传 14 | 同为 20 时完全一致;周期不同时最大差 223.23(差的是参数不是算法) |
| OBV | 1.3 把平盘日输出为 NaN | 多数库把平盘日视为不变、沿用上一值 | 1.3 自身就有 14 个 NaN;与 TA-Lib 的差仅 0.0003,与 ta 差 4.85 |
这张表可以直接当成排查清单:当你发现两个库的数不一致时,按「参数是否相同 → 是不是 ewm 类 → 标准差口径 → 是不是平盘/缺口处理」这个顺序查,通常四步之内就能定位。完整的逐指标对照读数见交叉验证页。
FAQ
关于 finta 指标计算口径的常见问题
为什么 SMA 前 13 行是 NaN,RSI 却只有 1 行?
SMA 走 rolling(14).mean(),不满 14 个观测就不给值;RSI 走 ewm 指数平滑,从第 1 行开始就有递推值。本站实测:EMA、SMMA 甚至连 NaN 都没有,第 0 行就有值——这与 TA-Lib 前 19 行 NaN 的行为差别很大。MACD 为什么没有柱状线?
TA.MACD 只返回 ['MACD', 'SIGNAL'] 两列。TA-Lib 的 MACD 返回三个输出(含 histogram)。需要柱状线时自己算 MACD − SIGNAL 即可,注意这与 TA-Lib 的 histogram 定义是一致的。布林带的宽度为什么比别的软件大?
2 × rolling.std(ddof=1) 完全一致,与 ddof=0 的比值恒为 √(20/19) ≈ 1.026——是固定倍数,不是随机噪声。adjust 参数改成 False 会更准吗?
EMA 改成 False 后与 ta 完全一致;但 RSI 改成 False 后与 TA-Lib 的最大差从 4.74 涨到 23.35;ADX 改成 False 后最大差从 14.51 涨到 30.54。建议保留默认值,只有在做过交叉验证之后针对单个指标调整,并把调整记入实验笔记。ATR 为什么和别的不一样?
ta 都用递推平滑。本站实测:FinTA 的结果与手算「TR 的 14 期均值」完全相等(差 0.000000),与 TA-Lib 最后一根相差 7.8 个点。两者都叫 ATR,但口径不同——用哪个取决于你要与谁对齐。返回的列名能不能直接从官方文档查到?
BB_UPPER / BB_LOWER,实际还有 BB_MIDDLE),没有统一的列名清单。本站的实测表可以作为参考,但建议在你自己的代码里先打印一次列名再使用——这也是本页给出核对脚本的原因。全部结论以官方源码为准。相关阅读:与 TA-Lib 交叉验证、数据契约与索引顺序。本页结论绑定 2026-10-12 的实测环境(Python 3.11.9),数值口径以官方源码为准。