ta-lib-python icon indicating copy to clipboard operation
ta-lib-python copied to clipboard

Outputs of functions are rounded to 0 (when prices are very small)

Open welsh01 opened this issue 7 years ago • 13 comments

Hey!

I implemented TA-Lib to work on cryptocurrency markets. Given some BTC-ALTCOIN markets, prices can be very low. As example: BTC-DOGE has prices like 0.00000022. RSI function returns 0 values.

Lets take 20 closing prices stored as numpy array:

array([ 2.40000000e-07, 2.40000000e-07, 2.40000000e-07, 2.40000000e-07, 2.40000000e-07, 2.30000000e-07, 2.40000000e-07, 2.40000000e-07, 2.40000000e-07, 2.40000000e-07, 2.30000000e-07, 2.40000000e-07, 2.30000000e-07, 2.40000000e-07, 2.30000000e-07, 2.40000000e-07, 2.40000000e-07, 2.30000000e-07, 2.30000000e-07, 2.30000000e-07])

RSI function returns: array([ nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, 0., 0., 0., 0., 0., 0.])

Number types in the close price array are numpy.float64. Is there any way to change the method, how outputs from talib functions are rounded?

Can anyone help? Would be nice to get it to work for all markets. Thanks!

welsh01 avatar Oct 05 '17 17:10 welsh01

See #151. I'm not sure if you have enough precision in those numbers, but perhaps you can do something like this:

rsi = talib.RSI(prices * 100000) / 100000

mrjbq7 avatar Oct 09 '17 18:10 mrjbq7

Thanks for your answer!

In fact i read #151 after i opened that issue. I tried it few days ago and it worked.

For RSI you just need to multply the prices. Division is not needed :)

welsh01 avatar Oct 09 '17 19:10 welsh01

Haha oh right. :-)

mrjbq7 avatar Oct 09 '17 19:10 mrjbq7

Is there any chance this issue with small numbers will be fixed? I had the same issue with bollinger bands and just spent half a day wondering if i was doing something wrong with the RSI function... Also, you make me worried when you say stuff like you wonder about precision... To me it seems quite a simple problem to fix, but i don't know C so i can't fix it myself.

A lot of people trade bitcoins & altcoins now, so it's not such a small problem.

hippylover avatar Oct 16 '17 22:10 hippylover

If you can replicate a difference in behavior between calling the C function with small numbers and calling the Python function with small numbers then I will fix it. If its an underlying issue with precision using double-precision floating point numbers and moving averages or whatever, then it's not something I intend to fix...

mrjbq7 avatar Oct 16 '17 22:10 mrjbq7

Well i would'nt know how to call the C function :P To be frank i don't really know if the underlying lib is to blame or this lib(I assume you do?). I suppose if it was this lib one would only have to use Decimal... maybe.

hippylover avatar Oct 16 '17 22:10 hippylover

Also, why wouldn't you change it? Is it just a question of effort? Wouldn't a quick sed command fix the C code? :)

If it's not so much effort i might be able to get a friend to fix it. If the C lib is to blame i suppose "making a github"(what is the term) and sharing the code could happen if that would be ok with you.

hippylover avatar Oct 16 '17 22:10 hippylover

Are you on a 32-bit or 64-bit python?

mrjbq7 avatar Oct 16 '17 23:10 mrjbq7

Using your test-case, showing how it "fails" for small numbers and works if you scale it up:

>>> a = np.array([0.00000024, 0.00000024, 0.00000024,
                  0.00000024, 0.00000024, 0.00000023,
                  0.00000024, 0.00000024, 0.00000024,
                  0.00000024, 0.00000023, 0.00000024,
                  0.00000023, 0.00000024, 0.00000023,
                  0.00000024, 0.00000024, 0.00000023,
                  0.00000023, 0.00000023])
>>> ta.RSI(a, 10)
array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,   0.,
         0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.])
>>> ta.RSI(a * 100000, 10)
array([         nan,          nan,          nan,          nan,
                nan,          nan,          nan,          nan,
                nan,          nan,  33.33333333,  51.35135135,
        39.49191686,  51.84807025,  42.25953803,  52.10182441,
        52.10182441,  43.04366487,  43.04366487,  43.04366487])

I wrote a little test-case in C:

#include <assert.h>
#include <stdio.h>
#include "ta-lib/ta_defs.h"
#include "ta-lib/ta_common.h"
#include "ta-lib/ta_abstract.h"
#include "ta-lib/ta_func.h"

int main(int argc, char** argv) {

    double prices[] = {
      0.00000024, 0.00000024, 0.00000024,
      0.00000024, 0.00000024, 0.00000023,
      0.00000024, 0.00000024, 0.00000024,
      0.00000024, 0.00000023, 0.00000024,
      0.00000023, 0.00000024, 0.00000023,
      0.00000024, 0.00000024, 0.00000023,
      0.00000023, 0.00000023 };

    int outBegIdx;
    int outNBElement;
    double outReal[100] = { } ;

    int lookback = TA_RSI_Lookback(10);

    TA_RetCode ret = TA_RSI(0, 19, prices, 10, &outBegIdx, &outNBElement,
                            &outReal[lookback]);

    assert(ret == 0);

    for (int i = 0; i < outNBElement; i++) {
        printf("%d: %f\n", outBegIdx+i, outReal[outBegIdx+i]);
    }
}

Compiled on macOS, with this output, for the unscaled output:

$ clang foo.c -lta_lib
$ ./a.out 
10: 0.000000
11: 0.000000
12: 0.000000
13: 0.000000
14: 0.000000
15: 0.000000
16: 0.000000
17: 0.000000
18: 0.000000
19: 0.000000

So the problem isn't in this Python wrapper, but maybe in C. I confirm also that test case produces this output with "scaled up" numbers (0.024 instead of 0.00000024):

$ ./a.out
10: 33.333333
11: 51.351351
12: 39.491917
13: 51.848070
14: 42.259538
15: 52.101824
16: 52.101824
17: 43.043665
18: 43.043665
19: 43.043665

So perhaps the problem is in the underlying C code.

Looking through the code for the function here:

https://sourceforge.net/p/ta-lib/code/HEAD/tree/trunk/ta-lib/c/src/ta_func/ta_RSI.c#l156

It's hard to tell which line might be causing problems, I mean this one is suspicious:

https://sourceforge.net/p/ta-lib/code/HEAD/tree/trunk/ta-lib/c/src/ta_func/ta_RSI.c#l361

Because of the TA_IS_ZERO macro which is defined here:

https://sourceforge.net/p/ta-lib/code/HEAD/tree/trunk/ta-lib/c/src/ta_func/ta_utility.h#l259

But that uses a very small epsilon which shouldn't apply here...

mrjbq7 avatar Oct 16 '17 23:10 mrjbq7

Well i don't know. I'm using 64 bit python for what it's worth.

hippylover avatar Oct 21 '17 11:10 hippylover

As @mrjbq7 guessed, it's indeed caused by using TA_IS_ZERO, which is defined as #define TA_IS_ZERO(v) (((-0.00000001)<v)&&(v<0.00000001)) ta_RSI.c uses it as: if( !TA_IS_ZERO(tempValue1) ) outReal[outIdx++] = 100.0*(prevGain/tempValue1); else outReal[outIdx++] = 0.0;

For the test case test_RSI, the first tempValue1 is 2.9999999999999954e-09, which is considered as 0 by TA_IS_ZERO because it's less than 1.0000000000000000e-08.

To fix this, one can change TA_IS_ZERO to add more precision, such as change it to #define TA_IS_ZERO(v) (((-0.000000000001)<v)&&(v<0.000000000001))

then recompile and link, it will be ok.

colinzuo avatar Oct 23 '17 08:10 colinzuo

Confirmed, all issues solved (tested with both RSI, BBANDS) editing these 2 lines in ta_utility.h and recompiling.

#define TA_IS_ZERO(v) (((-0.00000001)<v)&&(v<0.00000001)) #define TA_IS_ZERO_OR_NEG(v) (v<0.00000001)

changed to:

#define TA_IS_ZERO(v) (((-0.000000000000000001)<v)&&(v<0.000000000000000001)) #define TA_IS_ZERO_OR_NEG(v) (v<0.000000000000000001)

Thanks

dsilletti avatar Feb 21 '18 13:02 dsilletti

if anyone is still struggling with this issue take a look at this comment

Elyasnz avatar May 24 '23 08:05 Elyasnz