Incorrect outputs for RSI, BBANDS, BOP, MFI, CMO, STOCHRSI
Elyasnz opened this issue · 5 comments
first of all, thanks for the great library
im having trouble calculating StochasticRsi, rsi, mfi, cmo values for some symbols using TA-Lib version 0.4.26 but the values are ok in websites like tradingview or binance
i think there is a problem when numbers are very small but note that the functions work with higher time-frames like Daily or above
CODE
import requests
from pandas import DataFrame, to_datetime
from talib import RSI, STOCHRSI, MFI, CMO
from mplfinance import plot
# symbol='ATABTC'
# symbol='CELRBTC'
# symbol='CHRBTC'
# symbol='DOGEBTC'
# symbol='GALABTC'
# symbol='GRTBTC'
symbol='HBARBTC'
# symbol='IOTABTC'
# symbol='TRXBTC'
# symbol='VETBTC'
# symbol='WAXPBTC'
# symbol='XEMBTC'
# symbol='XLMBTC'
# symbol='ZILBTC'
data = json.loads(requests.get(
f'https://www.binance.com/api/v1/klines?symbol={symbol}&interval=15m&limit=10000&startTime=1681603200000&endTime=1682491357000'
).text)
data = DataFrame(
[
(item[0] // 1000, float(item[1]), float(item[2]), float(item[3]), float(item[4]), float(item[5]))
for item in data
],
columns=('timestamp', 'open', 'high', 'low', 'close', 'volume')
)
data.index = to_datetime(data['timestamp'], unit='s')
plot(
data[['open', 'high', 'low', 'close', 'volume']],
warn_too_much_data=10000,
type='candle'
)
print('RSI')
print(RSI(data.close, timeperiod=14))
print()
print('STOCHRSI')
print(STOCHRSI(data.close, timeperiod=14, fastk_period=3, fastd_period=3, fastd_matype=0))
print()
print('MFI')
print(MFI(data.high, data.low, data.close, data.volume, timeperiod=14))
print()
print('CMO')
print(CMO(data.close, timeperiod=14))
OUTPUT
RSI
timestamp
2023-04-16 00:00:00 NaN
2023-04-16 00:15:00 NaN
2023-04-16 00:30:00 NaN
2023-04-16 00:45:00 NaN
2023-04-16 01:00:00 NaN
...
2023-04-26 05:30:00 0.0
2023-04-26 05:45:00 0.0
2023-04-26 06:00:00 0.0
2023-04-26 06:15:00 0.0
2023-04-26 06:30:00 0.0
Length: 987, dtype: float64
STOCHRSI
(timestamp
2023-04-16 00:00:00 NaN
2023-04-16 00:15:00 NaN
2023-04-16 00:30:00 NaN
2023-04-16 00:45:00 NaN
2023-04-16 01:00:00 NaN
...
2023-04-26 05:30:00 0.0
2023-04-26 05:45:00 0.0
2023-04-26 06:00:00 0.0
2023-04-26 06:15:00 0.0
2023-04-26 06:30:00 0.0
Length: 987, dtype: float64, timestamp
2023-04-16 00:00:00 NaN
2023-04-16 00:15:00 NaN
2023-04-16 00:30:00 NaN
2023-04-16 00:45:00 NaN
2023-04-16 01:00:00 NaN
...
2023-04-26 05:30:00 0.0
2023-04-26 05:45:00 0.0
2023-04-26 06:00:00 0.0
2023-04-26 06:15:00 0.0
2023-04-26 06:30:00 0.0
Length: 987, dtype: float64)
MFI
timestamp
2023-04-16 00:00:00 NaN
2023-04-16 00:15:00 NaN
2023-04-16 00:30:00 NaN
2023-04-16 00:45:00 NaN
2023-04-16 01:00:00 NaN
...
2023-04-26 05:30:00 0.000000
2023-04-26 05:45:00 0.000000
2023-04-26 06:00:00 0.000000
2023-04-26 06:15:00 44.627119
2023-04-26 06:30:00 36.565340
Length: 987, dtype: float64
CMO
timestamp
2023-04-16 00:00:00 NaN
2023-04-16 00:15:00 NaN
2023-04-16 00:30:00 NaN
2023-04-16 00:45:00 NaN
2023-04-16 01:00:00 NaN
...
2023-04-26 05:30:00 0.0
2023-04-26 05:45:00 0.0
2023-04-26 06:00:00 0.0
2023-04-26 06:15:00 0.0
2023-04-26 06:30:00 0.0
Length: 987, dtype: float64
This is due to passing "very small numbers" to the underlying TA-Lib C library.
You can workaround it two ways:
- In Python, you can do something similar to this comment (#157 (comment)), where you adjust the prices before and after calling TA-Lib.
rsi = talib.RSI(prices * 100000) / 100000
- Compile your own TA-Lib C library with a modification to the
TA_IS_ZERO
macro as mentioned in this comment (#157 (comment)).
It has been proposed to upstream that patch as part of our attempts to take ownership of the TA-Lib C library in https://github.com/ta-lib/ta-lib, but I am not yet familiar enough to say that change is without impact.
thanks for your response
please take a look at this part of the C library
it looks like they fixed this issue but how can i upgrade my C library and is it safe to do so?
just checked the commit time of that change and it was back in 2009
I'm wondering why the problem still persists
after some digging into the codes and indicators here is a little help for anyone still struggling with these issues
first of all, let me say the output of some indicators are incorrect just in the case of "very small numbers" and some indicators are incorrect all the time
FirstCase (very small numbers)
I'm using HBAR/BTC
in M15
timeframe for the "very small numbers" case
with this snippet, you can change the backend C library to handle this problem
(I have commented out the rm
lines please be careful with them (you can add your own flags on compile) )
#! /usr/bin/env bash
#rm ta-lib-0.4.0-src.tar.gz
#rm ta-lib -r
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar xvfz ta-lib-0.4.0-src.tar.gz
cd ta-lib
sed -i 's/0.00000001/0.000000000000000000000000001/g' src/ta_func/ta_utility.h
./configure
make
make install
cd ..
#rm ta-lib-0.4.0-src.tar.gz
#rm ta-lib -r
after doing this indicators like RSI
, BBANDS
and BOP
will be fixed right away
SecondCase (incorrect indicators)
for this part, the indicators are mostly copied from pandas_ta library
and also note that indicators mentioned in this section will be implemented with Python code so there is a little overhead on that but at least the output is correct :)
MFI
this indicator is mostly incorrect with very small numbers and changing TA_IS_ZERO
didn't fix the problem so i looked at pandas_ta.mfi
# talib mfi incorrect for very small numbers
typical_price = (data.high + data.low + data.close) / 3.0
typical_price_diff = typical_price.diff()
raw_money_flow = typical_price * data.volume
psum = raw_money_flow.mask(typical_price_diff < 0).fillna(0).rolling(self.timeperiod).sum()
nsum = raw_money_flow.mask(typical_price_diff > 0).fillna(0).rolling(self.timeperiod).sum()
mfi = 100 * psum / (psum + nsum)
CMO
Talib implementation of this indicator is like RSI
(with a little shift) (and is also incorrect with "very small numbers") so I looked at pandas_ta.cmo
# talib CMO is incorrect and is like RSI
mom = data.close.diff()
positive = mom.copy().clip(lower=0).rolling(self.timeperiod).sum()
negative = mom.copy().clip(upper=0).abs().rolling(self.timeperiod).sum()
cmo = 100 * (positive - negative) / (positive + negative)
STOCHRSI
honestly, I didn't understand the Talib implementation of this indicator and did not find any output like Talib`s output so I looked at pandas_ta.stochrsi
note that this snippet uses talib RSI
so be careful with "very small numbers" (first case)
from talib import RSI, SMA
from sys import float_info as sflt
# talib STOCHRSI is incorrect
rsi_ = RSI(data.close, self.timeperiod)
_rolling = rsi_.rolling(self.timeperiod)
lowest_rsi = _rolling.min()
highest_rsi = _rolling.max()
stoch = 100 * (rsi_ - lowest_rsi)
_diff = highest_rsi - lowest_rsi
if _diff.eq(0).any().any():
_diff += sflt.epsilon
stoch /= _diff
stochrsi_k = SMA(stoch, self.fastk_period)
stochrsi_d = SMA(stochrsi_k, self.fastd_period)
and also a little note
in tradingview APO
indicator uses ema
so if you want that make sure to pass matype=1
kwarg
Double Checking
after these changes, I also checked other indicators (ones that I needed) and didn't find any problems with them
feel free to take a look at the indicator chart pictures