What is the fastest library/algorithm for calculating simple moving average? I wrote my own, but it takes too long on 330 000 items decimal dataset.
// simple moving average
int moving_average(double *values, double *&averages, int size, int periods)
{
double sum = 0;
for (int i = 0; i < size; i ++)
if (i < periods) {
sum += values[i];
averages[i] = (i == periods - 1) ? sum / (double)periods : 0;
} else {
sum = sum - values[i - periods] + values[i];
averages[i] = sum / (double)periods;
}
return (size - periods + 1 > 0) ? size - periods + 1 : 0;
}
One C function, 13 lines of codes, simple moving average. Example of usage:
double *values = new double[10]; // the input
double *averages = new double[10]; // the output
values[0] = 55;
values[1] = 113;
values[2] = 92.6;
...
values[9] = 23;
moving_average(values, averages, 10, 5); // 5-day moving average
/// <summary>
/// Fast low CPU usage moving average based on floating point math
/// Note: This algorithm algorithm compensates for floating point error by re-summing the buffer for every 1000 values
/// </summary>
public class FastMovingAverageDouble
{
/// <summary>
/// Adjust this as you see fit to suit the scenario
/// </summary>
const int MaximumWindowSize = 100;
/// <summary>
/// Adjust this as you see fit
/// </summary>
const int RecalculateEveryXValues = 1000;
/// <summary>
/// Initializes moving average for specified window size
/// </summary>
/// <param name="_WindowSize">Size of moving average window between 2 and MaximumWindowSize
/// Note: this value should not be too large and also bear in mind the possibility of overflow and floating point error as this class internally keeps a sum of the values within the window</param>
public FastMovingAverageDouble(int _WindowSize)
{
if (_WindowSize < 2)
{
_WindowSize = 2;
}
else if (_WindowSize > MaximumWindowSize)
{
_WindowSize = MaximumWindowSize;
}
m_WindowSize = _WindowSize;
}
private object SyncRoot = new object();
private Queue<double> Buffer = new Queue<double>();
private int m_WindowSize;
private double m_MovingAverage = 0d;
private double MovingSum = 0d;
private bool BufferFull;
private int Counter = 0;
/// <summary>
/// Calculated moving average
/// </summary>
public double MovingAverage
{
get
{
lock (SyncRoot)
{
return m_MovingAverage;
}
}
}
/// <summary>
/// Size of moving average window set by constructor during intialization
/// </summary>
public int WindowSize
{
get
{
return m_WindowSize;
}
}
/// <summary>
/// Add new value to sequence and recalculate moving average seee <see cref="MovingAverage"/>
/// </summary>
/// <param name="NewValue">New value to be added</param>
public void AddValue(int NewValue)
{
lock (SyncRoot)
{
Buffer.Enqueue(NewValue);
MovingSum += NewValue;
if (!BufferFull)
{
int BufferSize = Buffer.Count;
BufferFull = BufferSize == WindowSize;
m_MovingAverage = MovingSum / BufferSize;
}
else
{
Counter += 1;
if (Counter > RecalculateEveryXValues)
{
MovingSum = 0;
foreach (double BufferValue in Buffer)
{
MovingSum += BufferValue;
}
Counter = 0;
}
MovingSum -= Buffer.Dequeue();
m_MovingAverage = MovingSum / WindowSize;
}
}
}
}
The current (accepted) solution contains an inner loop. It would be more efficient to remove this as well. You can see how this is achieved here:
How to efficiently calculate a moving Standard Deviation
You don't need to keep a running queue. Just pick the latest new entry to the window and drop off the older entry. Notice that this only uses one loop and no extra storage other than a sum.
// n is the window for your Simple Moving Average
public List<double> GetMovingAverages(List<Price> prices, int n)
{
var movingAverages = new double[prices.Count];
var runningTotal = 0.0d;
for (int i = 0; i < prices.Count; ++i)
{
runningTotal += prices[i].Value;
if( i - n >= 0) {
var lost = prices[i - n].Value;
runningTotal -= lost;
movingAverages[i] = runningTotal / n;
}
}
return movingAverages.ToList();
}
Tested with Dotnet Core 3 & Linq:
int period = 20;
for(int k=0;data.Count()-period;k++){
decimal summe = data.Skip(k).Take(period).Sum();
summe /= (decimal)period;
}
It does rely on Linq and its internal optimization, did not time it.
Uses Skip() and Take() as a "rangeBetween" solution for moving average and then divide the summe by the period quantity.
*The for loop is upper capped to avoid incomplete sum operations.
Reference (C# Microsoft): Skip(), Take(), Sum();
How aboutQueue
?
using System.Collections.Generic;
using System.Linq;
public class MovingAverage
{
private readonly Queue<decimal> _queue;
private readonly int _period;
public MovingAverage(int period)
{
_period = period;
_queue = new Queue<decimal>(period);
}
public decimal Compute(decimal x)
{
if (_queue.Count >= _period)
{
_queue.Dequeue();
}
_queue.Enqueue(x);
return _queue.Average();
}
}
Usage:
MovingAverage ma = new MovingAverage(3);
foreach(var val in new decimal[] { 1,2,3,4,5,6,7,8,9 })
{
Console.WriteLine(ma.Compute(val));
}