Calculer un écart type mobile

J’ai trouvé l’extrait de code suivant ici sur stackoverflow, mais j’ai le problème que le stdev devient NaN. Une idée de comment réparer ça?

public static void AddBollingerBands(ref SortedList<DateTime, Dictionary> data, int period, int factor) { double total_average = 0; double total_squares = 0; for (int i = 0; i = period - 1) { double total_bollinger = 0; double average = total_average / period; double stdev = Math.Sqrt((total_squares - Math.Pow(total_average,2)/period) / period); data.Values[i]["bollinger_average"] = average; data.Values[i]["bollinger_top"] = average + factor * stdev; data.Values[i]["bollinger_bottom"] = average - factor * stdev; total_average -= data.Values[i - period + 1]["close"]; total_squares -= Math.Pow(data.Values[i - period + 1]["close"], 2); } } } 

Une variante numériquement plus stable est préférable pour les calculs de moyenne incrémentale / moyenne mobile et d’écart type. Une façon de faire est d’utiliser l’algorithme de Knuth, comme indiqué dans le bloc de code ci-dessous:

 public class MovingAverageCalculator { public MovingAverageCalculator(int period) { _period = period; _window = new double[period]; } public double Average { get { return _average; } } public double StandardDeviation { get { var variance = Variance; if (variance >= double.Epsilon) { var sd = Math.Sqrt(variance); return double.IsNaN(sd) ? 0.0 : sd; } return 0.0; } } public double Variance { get { var n = N; return n > 1 ? _variance_sum / (n - 1) : 0.0; } } public bool HasFullPeriod { get { return _num_added >= _period; } } public IEnumerable Observations { get { return _window.Take(N); } } public int N { get { return Math.Min(_num_added, _period); } } public void AddObservation(double observation) { // Window is treated as a circular buffer. var ndx = _num_added % _period; var old = _window[ndx]; // get value to remove from window _window[ndx] = observation; // add new observation in its place. _num_added++; // Update average and standard deviation using deltas var old_avg = _average; if (_num_added <= _period) { var delta = observation - old_avg; _average += delta / _num_added; _variance_sum += (delta * (observation - _average)); } else // use delta vs removed observation. { var delta = observation - old; _average += delta / _period; _variance_sum += (delta * ((observation - _average) + (old - old_avg))); } } private readonly int _period; private readonly double[] _window; private int _num_added; private double _average; private double _variance_sum; } 

Vous pouvez ensuite l'utiliser de la manière suivante dans votre exemple de code:

 public static void AddBollingerBands(ref SortedList> data, int period, int factor) { var moving_avg = new MovingAverageCalculator(period); for (int i = 0; i < data.Count(); i++) { moving_avg.AddObservation(data.Values[i]["close"]); if (moving_avg.HasFullPeriod) { var average = moving_avg.Average; var limit = factor * moving_avg.StandardDeviation; data.Values[i]["bollinger_average"] = average; data.Values[i]["bollinger_top"] = average + limit; data.Values[i]["bollinger_bottom"] = average - limit; } } } 

Pour que stdev devienne NaN, quelque chose ne va pas dans cette tâche:

 double stdev = Math.Sqrt((total_squares - Math.Pow(total_average,2)/period) / period); 

Vous ne pouvez pas diviser par zéro, alors assurez-vous que la period n’est pas définie sur celle-là. Le moyen le plus simple à résoudre consiste à imprimer toutes les variables avant d’appeler cette ligne et à voir si quelque chose est déjà NaN ou mathématiquement inutilisable