cinar / indicatorts

IndicatorTS - Stock technical indicators and strategies in TypeScript for browser and server programs.
MIT License
260 stars 43 forks source link

Division by zero error #472

Open ebadran opened 1 month ago

ebadran commented 1 month ago

Currently, the divide function in src/helper/numArray.ts does not handle division by zero errors.

Some assets have limited trading volume, and therefore may have a day's open, close, high, and low prices all equal. In these cases, applying certain indicators (e.g. the Random Index KDJ) to those stocks will result in their functions returning arrays of NaNs.

Steps to reproduce the behavior:

  1. Obtain the historical OHLC data from the CELU stock, from 09-Aug-2019 until the current date.
  2. Apply the KDJ indicator to that stock's data: const { k, d, j } = kdj(highs, lows, closings, defaultConfig);
  3. The result will be arrays of NaNs.

Additional context:

The above error will also occur with the following indicators:

Suggestion:

A quick fix would be to refactor the divide function in src/helper/numArray.ts so that it returns 1 when dividing by zero:

export function divide(values1: number[], values2: number[]): number[] {
  checkSameLength(values1, values2);

  const result = new Array<number>(values1.length);

  for (let i = 0; i < result.length; i++) {
    if (values2[i] === 0) {
      result[i] = 1;
    } else {
      result[i] = values1[i] / values2[i];
    }
  }

  return result;
}

Alternatively, you could divide by 0.0000001:

result[i] = values1[i] / (values2[i] || 0.0000001);

I hope this helps.

Best regards,

cinar commented 1 month ago

Thank you very much for reporting it. Yes, it sounds like that condition will impact multiple indicators here. We can certainly do that, but I am wondering what is the common practice for these, as this seems to be a normal case. Let me do some research and get back to you.

ebadran commented 1 month ago

Thanks, Onur.

I noticed that the Random Index (KDJ), Chaikin Oscillator and Accumulation Distribution have this calculation in common:

((Closing Price - Low Price) - (High Price - Closing Price)) / (High Price - Low Price)

In the context of an asset having all OHLC values equal, we’re essentially dividing zero by zero, so it might make sense for the divide formula to return 1.

So a light-handed approach would be to conditionally return 1 if both the numerator and denominator are zero; and setting the denominator value to 1 if only the denominator is zero.