Skip to content
All library documents

Why Python and TA-Lib Indicators Can Differ at Machine Precision

Article Quant Q&A · Author: user165494

Summary

The document compares a Python implementation of a simple moving average with TA-Lib’s output, after the author initially framed the question around matching RSI. Across the displayed observations, the values are usually identical or differ by very small amounts near floating-point precision. The example computes the average using a loop and a window mean, then subtracts the Python result from TA-Lib’s result to inspect the residuals.

The example illustrates that mathematically equivalent indicator calculations need not produce bit-for-bit identical results. Operation order, implementation details, and floating-point rounding can create tiny discrepancies across libraries. However, the document contains no response explaining the source of the differences or a procedure for reproducing TA-Lib’s exact arithmetic. Its evidence is one code example and output sample, so it does not establish behavior for other inputs, indicators, platforms, or library versions.

Key ideas

  • Equivalent rolling-average formulas can produce tiny differences in floating-point output.
  • The example compares a loop-based Python average with TA-Lib and displays their residuals.
  • The original RSI question is simplified to a simple moving average for the comparison.
  • Exact matching can depend on implementation details and the order of arithmetic operations.
  • The example does not identify the cause of each discrepancy or prove how other indicators behave.

Tags

Full text
# Is it possible to match talib's RSI results down to machine precision using just python?


# Is it possible to match talib's RSI results down to machine precision using just python?












I want to match talib's RSI with just python down to machine precision and I'm struggling. Out of curiosity I also tried a bunch of libraries like tulipy and pandas_ta and the gaps are similar.

Anyone has any suggestions?

In the code snippet below, you can comment out all the relevant tulipy lines if you don't want to install it.

edit: Switched from RSI to a simple moving average for simplicity. The machine precision numeric gaps between c and python are similar anyway. The simple moving average c source code by talib: https://pastebin.com/WuMWBFtF

outputs:

```
                                 talib                                mine                              errors
0                                  nan                                 nan                                 nan
1                                  nan                                 nan                                 nan
2                                  nan                                 nan                                 nan
3                                  nan                                 nan                                 nan
4                                  nan                                 nan                                 nan
5                                  nan                                 nan                                 nan
6                                  nan                                 nan                                 nan
7                                  nan                                 nan                                 nan
8                                  nan                                 nan                                 nan
9                                  nan                                 nan                                 nan
10                                 nan                                 nan                                 nan
11  0.15516036417806491898296883391595  0.15516036417806497449412006517377 -0.00000000000000005551115123125783
12  0.05205880767301387240797438948903  0.05205880767301387240797438948903  0.00000000000000000000000000000000
13 -0.11350055963883017018378751572527 -0.11350055963883017018378751572527  0.00000000000000000000000000000000
14 -0.15543593887235351846953790300176 -0.15543593887235351846953790300176  0.00000000000000000000000000000000
15 -0.01614653775592672646510550293897 -0.01614653775592670911787074317090 -0.00000000000000001734723475976807
16 -0.00741072772658941687079492410817 -0.00741072772658940819717754422413 -0.00000000000000000867361737988404
17  0.18007029396551640920698389436438  0.18007029396551640920698389436438  0.00000000000000000000000000000000
18  0.13157654447972574884850871512754  0.13157654447972577660408433075645 -0.00000000000000002775557561562891
19  0.23623677929370121009178262738715  0.23623677929370123784735824301606 -0.00000000000000002775557561562891
20  0.09932199565529327422996885843531  0.09932199565529331586333228187868 -0.00000000000000004163336342344337
21 -0.05095804509442600910285037230096 -0.05095804509442600910285037230096  0.00000000000000000000000000000000
22 -0.08092279219264598977279234759408 -0.08092279219264594813942892415071 -0.00000000000000004163336342344337
23 -0.23319625415699421489001963436749 -0.23319625415699415937886840310966 -0.00000000000000005551115123125783
24  0.00495671069283426524165747650841  0.00495671069283432075280870776623 -0.00000000000000005551115123125783
25  0.23119641920851810579229379527533  0.23119641920851816130344502653315 -0.00000000000000005551115123125783
26  0.25282673483697731819930254459905  0.25282673483697731819930254459905  0.00000000000000000000000000000000
27  0.03160457726895595648164416502368  0.03160457726895599117611368455982 -0.00000000000000003469446951953614
28  0.01566407883863546804392719025145  0.01566407883863554437176013323096 -0.00000000000000007632783294297951
29 -0.15181719995420131508190308977646 -0.15181719995420125957075185851863 -0.00000000000000005551115123125783
30 -0.18723656696915602637432129995432 -0.18723656696915602637432129995432  0.00000000000000000000000000000000
31 -0.07811486111860536929452081267300 -0.07811486111860532766115738922963 -0.00000000000000004163336342344337
32 -0.20444686349359716959206423325668 -0.20444686349359711408091300199885 -0.00000000000000005551115123125783
33 -0.24196515292630801918782879056380 -0.24196515292630796367667755930597 -0.00000000000000005551115123125783
34 -0.14324766151539661263036862237641 -0.14324766151539658487479300674750 -0.00000000000000002775557561562891
35 -0.01668741793313034682544326869902 -0.01668741793313028784484508548758 -0.00000000000000005898059818321144
36 -0.20155891969265707364122874878376 -0.20155891969265701813007751752593 -0.00000000000000005551115123125783
37 -0.33901998761756196865135848383943 -0.33901998761756191314020725258160 -0.00000000000000005551115123125783
38 -0.25610064767786538952876185248897 -0.25610064767786527850645938997332 -0.00000000000000011102230246251565
39 -0.00627059309831806688251276682422 -0.00627059309831808509710926458069  0.00000000000000001821459649775647
```

and the code:

```
import numpy as np
import pandas as pd
import talib
np.random.seed(999)
period = 12
ts = pd.Series(np.random.randn(1000))

pd.set_option('display.max_rows', 10000)
pd.set_option('display.max_columns', 10000)
pd.set_option('display.width', 1000)
pd.set_option('display.float_format', lambda x: '%.32f' % x)

def manual_sma(prices, n):
    prices = prices.astype(np.double)
    ma = np.zeros_like(prices)
    ma[:n] = np.nan

    for i in range(n-1, len(prices)):
        ma[i] = np.nanmean(prices[i-n+1:i+1])
    return pd.Series(ma)

my_sma = manual_sma(ts, period)
pandas_sma = ts.rolling(period, min_periods=period).mean()
talib_sma = talib.SMA(ts, timeperiod=period)

df = pd.concat((talib_sma, my_sma), axis=1)
df.columns = ['talib', 'mine']
df['errors'] = df['talib'] - df['mine']
print(df.iloc[:40])
```

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.