简体   繁体   English

在 C# 中计算队列上的指数移动平均线

[英]Calculate Exponential Moving Average on a Queue in C#

I have a simple class for calculating the moving average of values I add to it.我有一个简单的类来计算我添加到它的值的移动平均值。 I use it like this:我像这样使用它:

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
        {
            ???
        }
    }
}

I would like to extend this class to also return the ExponentialMovingAverage.我想扩展这个类以返回 ExponentialMovingAverage。 How would you write return the Exponential Average of the Queued items in Quotes?您将如何编写返回报价中排队项目的指数平均值?

I realize you will need to add an Alpha property to the class but I'm not sure how to complete the math for the calculation.我意识到您需要在类中添加一个 Alpha 属性,但我不确定如何完成计算的数学运算。

How about with LINQ:使用 LINQ 怎么样:

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

I would point out that for real-time financial data, this is highly inefficient.我要指出的是,对于实时财务数据,这是非常低效的。 A much better way would be to cache the previous EMA value and update it on a new quote with the above (constant-time) recurrence-formula.更好的方法是缓存之前的 EMA 值并使用上述(恒定时间)重复公式在新报价上更新它。

Do do not need a queue for an Exponential Moving Average because you only need to keep track of the previous 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;
    }
}

Here's a minimal version of @MattWolf's answer with a slightly different API, and using C# 7.这是@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;
}

I think there's a small tweak required to @Ani's answer.我认为@Ani 的回答需要进行一些小调整。 The initial value would get set to "alpha * nextQuote" instead of just "nextQuote".初始值将设置为“alpha * nextQuote”,而不仅仅是“nextQuote”。 Easiest fix is to set the initial seed value to match the first record, then the first iteration becomes alpha * S1 + (1 - alpha) * S1:最简单的解决方法是设置初始种子值以匹配第一条记录,然后第一次迭代变为 alpha * S1 + (1 - alpha) * S1:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM