8
votes

Calculez la moyenne mobile exponentielle sur une file d'attente en C #

J'ai une classe simple pour calculer la moyenne mobile des valeurs que j'ajoute. Je l'utilise comme ceci:

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


0 commentaires

4 Réponses :


18
votes

Que diriez-vous de LINQ:

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


2 commentaires

alpha est indéfini. Qu'avez-vous mis pour ça?


@Leviti "Le coefficient α représente le degré de diminution de la pondération, un facteur de lissage constant entre 0 et 1. Un α plus élevé réduit les observations plus anciennes plus rapidement."



7
votes

Ne pas avoir besoin d'une file d'attente pour une moyenne mobile exponentielle, car il vous suffit de garder une trace de l'EMA précédent. xxx


1 commentaires

Vous n'utilisez pas votre champ _lookback pour quoi que ce soit. Il suffirait de l'utiliser que dans la CTOR.



5
votes

Voici une version minimale de la réponse de @ Mattwolf avec une API légèrement différente et en utilisant 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;
}


0 commentaires

0
votes

Je pense qu'il y a un petit tweak nécessaire à la réponse de @ Ani. La valeur initiale serait définie sur "Alpha * NextQuote" au lieu de "NextQuote". La solution la plus facile est de définir la valeur de la graine initiale pour correspondre au premier enregistrement, puis la première itération devient Alpha * S1 + (1 - alpha) * S1:

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


0 commentaires