使用JavaScript计算指数移动平均值(EMA)

10

你好,是否可能在JavaScript中计算EMA?

我要应用的EMA公式如下:

EMA = 数组[i] * K + EMA(上一个) * (1 - K)

其中K是平滑因子:

K = 2 / (N + 1)

N是我想要考虑的值的范围。

所以,如果我有这样的值数组,并且这些值随着时间增长:

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

目标是拥有一个函数,返回EMA数组,因为除了第一个“Range”值之外的任何这些值都具有此EMA,对于数据中的每个项目,我都有相关的EMA值。 通过这种方式,我可以使用所有值或仅使用最后一个值来“预测”下一个值。

function EMACalc(Array,Range) {
var k = 2/(Range + 1);
...
}
我不知道如何实现这个,希望能得到帮助。

你不应该使用 ArrayRange 作为参数名称,因为这会覆盖原生的 JS 对象。 - Daniel Reina
问题:通过 EMA = array[i] * K + EMA(previous) * (1 – K),您的意思是 EMA[i] = array[i] * K + EMA[i - 1] * (1 – K) 吗? - Daniel Reina
好的@DanielG.Reina,我明白了,是的,EMA[i]听起来更好。因为要计算前i个项目的EMA,你需要知道第i-1个项目的EMA!对于第一个元素,可以使用相同元素的值。 - Jorman Franzini
3个回答

25

我不确定我完全理解您的需求,但我将为您提供一个函数的代码,该函数返回一个数组,其中计算了每个索引大于0的EMA(第一个索引没有任何先前的EMA计算,并将返回输入的第一个值)。

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;
}

这应该可以做到。


谢谢@Daniel,我尝试了这篇文章中的值https://docs.oracle.com/cd/E12032_01/doc/epm.921/html_ir_studio/frameset.htm?/cd/E12032_01/doc/epm.921/html_ir_studio/ir_studio-15-74.html,但除了第一个项目外都是NaN。 - Jorman Franzini
抱歉,发现错误了,你的函数中写的是 mRange 但实际应该是 Range。 :D - Jorman Franzini
1
计算 k 时,我忘记将 Range 更改为 mRange。如果还有其他问题,请告诉我。如果您将信息复制为文本,请在调用函数之前将值解析为数字(整数或浮点数),因为该函数期望一个数字数组。 - Daniel Reina
太好了 :) 你能验证一下我的答案吗?谢谢! - Daniel Reina
1
我也试过了...我永远无法得到Binance输出的EMA数值。每个价格都不同。尝试了20种不同的方法。最后试了你的方法 - 仍是一样,每个价格都不同。我试着提供21、51、101个周期的K线。也试过20、50和100个。什么都没用……我完全不知道这些人如何计算EMA。我在这上面浪费了两天时间……快要放弃了…… - Lachezar Raychev
不起作用,Binance/Tradingview 的数字不同。 - synkro

6
以下是另一种实现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));


1
警告:我认为这里存在一个微妙的错误,导致产生了错误的结果。在lambda映射中a[i-1]的值不是先前计算出的ema,而是先前的输入值。数组不会被map操作符改变。该错误可以在以下codepen中清楚地发现:http://codepen.io/corolla/pen/QpwRmz我相信@dgrcode的答案是正确的。 - corolla
@corolla 是的,你说得对。我已经相应地更正了我的代码。感谢提醒。 - Redu
1
我从 https://github.com/jonschlinkert/exponential-moving-average 复制了数值,但是计算结果与那里的输出不匹配。你有任何想法哪个是正确的吗? - Blair Zajac
如果你检查上述代码,会发现它返回与输入相同长度的数组。它试图计算前 r 个项目的 EMA。你提到的代码从索引 r-1 开始。它们的结果会趋于一致,但我认为这个更好。 - Redu

2
我喜欢递归,这里是一个使用递归的EMA函数示例。不需要维护数组。
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接