Calculating Exponential Moving Average (EMA) using javascript
Asked Answered
T

4

11

Hi Is possible to calculate EMA in javascript?

The formula for EMA that I'm trying to apply is this

EMA = array[i] * K + EMA(previous) * (1 – K)

Where K is the smooth factor:

K = 2/(N + 1)

And N is the Range of value that I wanna consider

So if I've an array of value like this, and this value grow during the times:

var data = [15,18,12,14,16,11,6,18,15,16];

the goal is to have a function, that return the array of the EMA, because any of this value, expect the very fist "Range" value, have this EMA, for each item on data, I've the related EMA value. In that way I can use all or use only the last one to "predict" the next one.

function EMACalc(Array,Range) {
var k = 2/(Range + 1);
...
}

I can't figure out how to achieve this, any help would be apreciated

Tropopause answered 15/10, 2016 at 8:52 Comment(3)
You shouldn't be using Array and Range as parameter names, as you are overwriting native JS objectsRefrigerant
Question: by EMA = array[i] * K + EMA(previous) * (1 – K) you mean EMA[i] = array[i] * K + EMA[i - 1] * (1 – K)?Refrigerant
Ok @DanielG.Reina, got it, and yes, the EMA[i] sound better. Because to calc the EMA for the i items you should know the EMA for the i-1 item! For the very 1st element can be used the value of the same element in itTropopause
R
26

I don't know if I completely understood what you need, but I will give you the code for a function that returns an array with the EMA computed for each index > 0 (the first index doesn't have any previous EMA computed, and will return the first value of the input).

function EMACalc(mArray,mRange) {
  var k = 2/(mRange + 1);
  // first item is just the same as the first item in the input
  emaArray = [mArray[0]];
  // for the rest of the items, they are computed with the previous one
  for (var i = 1; i < mArray.length; i++) {
    emaArray.push(mArray[i] * k + emaArray[i - 1] * (1 - k));
  }
  return emaArray;
}

This should do it.

Refrigerant answered 15/10, 2016 at 9:27 Comment(6)
Tnx @Daniel, I tried with the value in this article docs.oracle.com/cd/E12032_01/doc/epm.921/html_ir_studio/… but give NaN except for the 1st item.Tropopause
Ops, sorry found the error, your function say mRange but k have Range :DTropopause
I forgot to change Range to mRange when computing k. Let me know if there is any other problem. If you copy the info as text, remember to parse the values to numbers (integer or float) before calling the function, as it expects an array of numbersRefrigerant
great :) could you validate my answer then? Thanks!Refrigerant
tried that as well... I can never get the EMA numbers that binance is outputting. Every price is different. Tried it in 20 different ways. Finally tried yours - same, Every price is different. I tried to supply 21,51,101 period cnadles. Tried also 20,50 and 100. Nothing is working... I have no idea how these guys calculate EMA. 2 days I lost on this... about to give up...Hamulus
Doesn't work, Binance/Tradingview numbers are different.Iredale
P
6

The following can be another way of implementing the EMA.

var getEMA = (a,r) => a.reduce((p,n,i) => i ? p.concat(2*n/(r+1) + p[p.length-1]*(r-1)/(r+1)) : p, [a[0]]),
      data = [15,18,12,14,16,11,6,18,15,16],
     range = 3;

console.log(getEMA(data,range));
Parsifal answered 15/10, 2016 at 11:52 Comment(4)
Warning: I believe there is a subtle error here producing wrong results. The value of a[i-1] in the lambda to map is not the previously calculated ema, it is the previous input value. The array is not mutated by the map operator. The error can be clearly spotted in the following codepen: codepen.io/corolla/pen/QpwRmz I believe the answer by @dgrcode is correct.Specialism
@Specialism Yes you are right. I have corrected my code accordingly. Thanks for the heads up.Parsifal
I copied in the values from github.com/jonschlinkert/exponential-moving-average and this calculation doesn't match the output there. Any idea on which one is correct?Nidia
@Blair Zajac If you check above code returns same length array as the input. It attempts to calculate EMA for the first range (r) many items too. The code that you refer starts after index r-1. Their results converge but i believe this is better.Parsifal
J
2

I like recursion, so here's an example of an EMA function that uses it. No need to maintain arrays.

function weightMultiplier(N) { return 2 / (N + 1) }

function ema(tIndex, N, array) {
    if (!array[tIndex-1] || (tIndex) - (N) < 0) return undefined;
    const k = weightMultiplier(N);
    const price = array[tIndex];
    const yEMA = ema(tIndex-1, N, array) || array[tIndex-1]
    return (price - yEMA) * k + yEMA
}
Jeramey answered 24/7, 2020 at 20:56 Comment(0)
B
0

I'm providing a new answer to this as the way this is calculated in technical analysis/indicators (binance, tradingview, etc) slightly varies from the answers already posted here.

The difference is that the SMA (Simple Moving Average) is used as a starting point to calculate the EMA.

For a list of candles, ticks, prices, where the period or interval is 5 we should expect:

  • 1st Tick - undefined
  • 2nd Tick - undefined
  • 3rd Tick - undefined
  • 4th Tick - undefined
  • 5th Tick - SMA (sum of first 5 ticks / period)
  • 6th Tick - EMA (((price - prevEMA) * exponent) + prevEMA)

Calculate Exponential Moving Average for technical analysis:

const calcEMA = (prices) => {
  let sum = 0
    let averages = []
    let prevEMA = undefined
    let tickIndex = 1

    for (const price of prices) {

      if (tickIndex < period) {
        tickIndex++
          sum = sum + price
          averages.push(undefined)
      } else {

        if (prevEMA) {
          // EMA
          prevEMA = ((price - prevEMA) * exponent) + prevEMA
        } else {
          // SMA
          prevEMA = ((sum + price) / period)
        }

        averages.push(prevEMA)

      }
    }
  return averages
}

calcEMA([
    81.59, 81.06, 82.87, 83.0, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54, 86.89, 87.77, 87.29,
])
Balkanize answered 22/7 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.