源码公式与预热窗口

finta 指标怎么算的:为什么有的指标前 13 行是 NaN,MACD 却少了柱状线

FinTA 把每个指标写成一个直接调用 pandas 滚动与指数平滑的方法,所以「预热多长、返回什么列、" 用哪种平滑」完全取决于那几行源码——同名指标在不同库里给出不同的数,根源就在这里。

本站逐项实测了这些口径:SMA(14) 前 13 行是 NaN," "RSI(14) 只有 1 行,EMA 第 0 行就有值;" "BBANDS 用的是样本标准差(ddof=1),ATR 用的是" "True Range 的简单均值而不是 Wilder 平滑;MACD 只返回两列,没有柱状线。

SMA 预热 13 行RSI 预热 1 行BBANDS ddof=1ATR 简单均值
预热长度:13 / 1 / 0SMA 前 13 行 NaN,RSI 只 1 行,EMA 第 0 行就有值
平滑方式:两种口径BBANDS 用样本标准差,ATR 用简单均值而非 Wilder
返回结构:Series 或 DataFrameMACD 只有两列,ICHIMOKU 的列名大小写还不统一
依据逐指标实测返回值与源码口径整理;具体数值以官方源码与 tests/test_unit.py 为准。

FinTA · 预热与 NaN

每个指标前面有多少行是 NaN

表 1:预热窗口实测(仓库自带 874 行 OHLCV,索引从 0 起)
指标调用首个有效行前置 NaN 数返回类型说明
TA.EMA(ohlc, 14)00Seriesadjust=True 时从第 0 行就有值——与 TA-Lib 前 19 行 NaN 的行为不同
TA.SMMA(ohlc, 14)00Series同样是平滑类,与 EMA 一样不给 NaN
TA.RSI(ohlc)11Series内部用 ewm,只在前 1 行为 NaN;与 TA-Lib 的 14 行预热差异明显
TA.SMA(ohlc, 14)1313Series标准 rolling(14) 语义:第 14 行才有第一个值
TA.WMA(ohlc, 14)1313Series同上
TA.ATR(ohlc)1313SeriesTrue Range 之后取 14 期均值,因此也是 13 行
TA.ADX(ohlc)1313Series但 TA-Lib 的 ADX 需要 27 行预热
TA.STOCH(ohlc)1313Series14 期高低区间
TA.WILLIAMS(ohlc)1313Series与 TA-Lib 一致
TA.MFI(ohlc)1313SeriesTA-Lib 是 14 行,差 1 行
TA.BBANDS(ohlc, 20)1919DataFrame(3 列)三列(上/中/下轨)的首个有效行相同
TA.OBV(ohlc)1共 14 个 NaNSeriesNaN 不在开头:1.3 把「收盘价不变」的 13 个平盘日输出为 NaN

读法:预热长度不是「周期数 − 1」这么简单——EMA/RSI 走指数平滑,值从很早就有;ATR/ADX 走滚动窗口,要等满窗。而 OBV 的 NaN 更特殊:它的 NaN 分散在中间,因为 1.3 在平盘日没有定义取值(master 已改为沿用上一值,见项目状态页)。

FinTA · 返回结构

每个指标返回的列名是什么

表 2:实测返回的列名与数据类型
返回对象列名 / 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 / PSARDataFrame(多列)取列前先 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 指标的平滑方式有哪些区别?从源码看八个常用指标的算法

表 3:八个常用指标的源码口径(读 finta/finta.py 得出)
指标用到的 pandas 原语关键参数与其他库的差异点
SMASeries.rolling(window).mean()默认 period=41无差异——三个库完全一致
EMASeries.ewm(span=period, adjust=adjust).mean()默认 period=9、adjust=Trueadjust=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;不返回柱状线
BBANDSrolling(period).mean() 与 rolling(period).std()(默认 ddof=1)默认 period=20、std_multiplier=2带宽比 TA-Lib/ta 大 √(20/19) ≈ 1.026 倍——本站用半宽做线性验证,实测比值与理论完全一致
ATRTrue Range 之后取 rolling(period).mean()默认 period=14是简单均值,不是 Wilder 递归——与手算「TR 的 14 期均值」完全一致(差 0.000000)
ADXDMI 差值经 ewm(adjust=adjust) 平滑默认 period=14与 TA-Lib 相关系数 0.9321、与 ta 只有 0.6361——分歧最大,且 adjust 无法对齐
STOCHrolling(14).min()/max() 直接归一化默认 period=14与 TA-Lib 的快 %K、与手写公式都完全一致

把「差异点」一列读一遍可以得到一个判断规则:只要指标用的是滚动窗口(rolling),FinTA 与其他库通常能对上;只要用了指数平滑(ewm),就有了「怎么播种、怎么归一化」的自由度,不同库的选择不同。所以遇到数值不一致时,先看它是不是 ewm 类指标。

FinTA · adjust 开关

adjust 参数该不该改?同一个开关在两个指标上方向相反

表 4:把 adjust 从默认的 True 改成 False 之后的实测影响
指标默认 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,说明数据行数不够或处理方式与预期不同。
  • 再用手算对一遍:取最后 14 行的收盘价求均值,与 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 · 口径不一致

    哪些同名指标在不同库里算法不同?七个常见差异清单

    表 6:FinTA 与其他库同名不同算法的指标
    指标FinTA 的做法常见其他做法本站实测的差距
    ATRTrue 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
    ADXDMI 差值经 ewm(adjust=True)Wilder 递推与 TA-Lib 相关系数 0.9321、与 ta 0.6361——本页表格里分歧最大的一项
    EMAewm(adjust=True),第 0 行就有值通常用 adjust=False 并给前 N 行 NaN中后段完全一致,差异只在前 1/3(最大 2.245)
    MFI14 期窗口,13 行预热TA-Lib 为 14 行预热数值完全一致,只差 1 行预热长度
    CCI默认 period=20TA-Lib 调用时常传 14同为 20 时完全一致;周期不同时最大差 223.23(差的是参数不是算法)
    OBV1.3 把平盘日输出为 NaN多数库把平盘日视为不变、沿用上一值1.3 自身就有 14 个 NaN;与 TA-Lib 的差仅 0.0003,与 ta 差 4.85

    这张表可以直接当成排查清单:当你发现两个库的数不一致时,按「参数是否相同 → 是不是 ewm 类 → 标准差口径 → 是不是平盘/缺口处理」这个顺序查,通常四步之内就能定位。完整的逐指标对照读数见交叉验证页。

    FAQ

    关于 finta 指标计算口径的常见问题

    为什么 SMA 前 13 行是 NaN,RSI 却只有 1 行?
    因为它们用的 pandas 原语不同。SMA 走 rolling(14).mean(),不满 14 个观测就不给值;RSI 走 ewm 指数平滑,从第 1 行开始就有递推值。本站实测:EMA、SMMA 甚至连 NaN 都没有,第 0 行就有值——这与 TA-Lib 前 19 行 NaN 的行为差别很大。
    MACD 为什么没有柱状线?
    FinTA 的 TA.MACD 只返回 ['MACD', 'SIGNAL'] 两列。TA-Lib 的 MACD 返回三个输出(含 histogram)。需要柱状线时自己算 MACD − SIGNAL 即可,注意这与 TA-Lib 的 histogram 定义是一致的。
    布林带的宽度为什么比别的软件大?
    因为 FinTA 用的是样本标准差(ddof=1),而 TA-Lib 与多数软件用总体标准差(ddof=0)。本站做过线性验证:FinTA 的半宽与 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 为什么和别的不一样?
    因为 FinTA 的 ATR 是 True Range 的简单 14 期均值,而 Wilder 原始定义与 TA-Lib、ta 都用递推平滑。本站实测:FinTA 的结果与手算「TR 的 14 期均值」完全相等(差 0.000000),与 TA-Lib 最后一根相差 7.8 个点。两者都叫 ATR,但口径不同——用哪个取决于你要与谁对齐。
    返回的列名能不能直接从官方文档查到?
    查不全。README 只给了一行注释(例如 BBANDS 会返回 BB_UPPER / BB_LOWER,实际还有 BB_MIDDLE),没有统一的列名清单。本站的实测表可以作为参考,但建议在你自己的代码里先打印一次列名再使用——这也是本页给出核对脚本的原因。全部结论以官方源码为准。

    相关阅读:与 TA-Lib 交叉验证、数据契约与索引顺序。本页结论绑定 2026-10-12 的实测环境(Python 3.11.9),数值口径以官方源码为准。

    接下来读哪一页?

    口径清楚了,下一步是把它和别的库对上数——交叉验证页给出同一份数据、同一周期下FinTA 与 TA-Lib、ta 的逐指标对照表,以及差异出在哪一步。