在c#中计算队列的指数移动平均

本文关键字:指数 移动 队列 计算 | 更新日期: 2023-09-27 18:17:27

我有一个简单的类来计算我添加的值的移动平均值。我这样使用它:

MovingAverage ma = new MovingAverage();
ma.push(value1);
ma.push(value2);
... 
Console.Writeline(average.Average);
//the class
public class MovingAverage
{
    public int Period = 5;
    private Queue<double> Quotes = new Queue<double>();
    public void Push(double quote)
    {
        if (Quotes.Count == Period)
            Quotes.Dequeue();
        Quotes.Enqueue(quote);
    }
    public void Clear()
    {
        Quotes.Clear();
    }
    public double Average { get { if (Quotes.Count == 0) return 0; return Quotes.Average(); } }
    public double ExponentialMovingAverage
    {
        get
        {
            ???
        }
    }
}

我想扩展这个类也返回ExponentialMovingAverage。你怎么写返回的指数平均值的排队项目在引号?

我意识到您需要在类中添加Alpha属性,但我不确定如何完成计算的数学。

在c#中计算队列的指数移动平均

LINQ:

return Quotes.DefaultIfEmpty()
             .Aggregate((ema, nextQuote) => alpha * nextQuote + (1 - alpha) * ema);
我要指出的是,对于实时财务数据,这非常低效。更好的方法是缓存以前的EMA值,并使用上述(恒定时间)递归公式在新报价上更新它。

不需要指数移动平均线的队列,因为你只需要跟踪之前的EMA。

public class ExponentialMovingAverageIndicator
{
    private bool _isInitialized;
    private readonly int _lookback;
    private readonly double _weightingMultiplier;
    private double _previousAverage;
    public double Average { get; private set; }
    public double Slope { get; private set; }
    public ExponentialMovingAverageIndicator(int lookback)
    {
        _lookback = lookback;
        _weightingMultiplier = 2.0/(lookback + 1);
    }
    public void AddDataPoint(double dataPoint)
    {
        if (!_isInitialized)
        {
            Average = dataPoint;
            Slope = 0;
            _previousAverage = Average;
            _isInitialized = true;
            return;
        }
        Average = ((dataPoint - _previousAverage)*_weightingMultiplier) + _previousAverage;
        Slope = Average - _previousAverage;
        //update previous average
        _previousAverage = Average;
    }
}

这是@MattWolf答案的最小版本,使用了稍微不同的API,并且使用了c# 7。

public sealed class FloatExponentialMovingAverageCalculator
{
    private readonly float _alpha;
    private float _lastAverage = float.NaN;
    public FloatExponentialMovingAverageCalculator(int lookBack) => _alpha = 2f / (lookBack + 1);
    public float NextValue(float value) => _lastAverage = float.IsNaN(_lastAverage)
        ? value
        : (value - _lastAverage)*_alpha + _lastAverage;
}

我认为@Ani的答案需要一个小的调整。初始值将被设置为"alpha * nextQuote"而不仅仅是"nextQuote"。最简单的修复方法是将初始种子值设置为匹配第一个记录,然后第一次迭代变成alpha * S1 + (1 - alpha) * S1:

return Quotes
  .DefaultIfEmpty()
  .Aggregate(Quotes.FirstOrDefault() ?? 0.0,
(ema, nextQuote) => alpha * nextQuote + (1 - alpha) * ema);