如何在C#中更快地计算简单移动平均线?
计算简单移动平均线的最快库/算法是什么?我自己写的,但在330 000项十进制数据集上花费的时间太长了如何在C#中更快地计算简单移动平均线?,c#,algorithm,financial,moving-average,C#,Algorithm,Financial,Moving Average,计算简单移动平均线的最快库/算法是什么?我自己写的,但在330 000项十进制数据集上花费的时间太长了 时段/时间(毫秒) 20/300 60/1500 120/3500 以下是我的方法代码: public decimal MA_Simple(int period, int ii) { if (period != 0 && ii > period) { //stp.Start(); decimal summ = 0;
- 时段/时间(毫秒)
- 20/300李>
- 60/1500李>
- 120/3500
public decimal MA_Simple(int period, int ii) {
if (period != 0 && ii > period) {
//stp.Start();
decimal summ = 0;
for (int i = ii; i > ii - period; i--) {
summ = summ + Data.Close[i];
}
summ = summ / period;
//stp.Stop();
//if (ii == 1500) System.Windows.Forms.MessageBox.Show((stp.ElapsedTicks * 1000.0) / Stopwatch.Frequency + " ms");
return summ;
} else return -1;
}
数据.Close[]
是一个固定大小(1000 000)的十进制数组。如果数据是静态的,您可以对数组进行预处理,以快速进行移动平均查询:
decimal[] GetCSum(decimal[] data) {
decimal csum[] = new decimal[data.Length];
decimal cursum = 0;
for(int i=0; i<data.Length; i++) {
cursum += data[i];
csum[i] = cursum;
}
return csum;
}
decimal[]GetCSum(decimal[]data){
十进制csum[]=新的十进制[data.Length];
十进制游标=0;
对于(inti=0;i,您的主要问题是每次迭代都会浪费太多的信息。
如果你想跑得这么快,你需要保持一个与帧长相同大小的缓冲区
此代码将为整个数据集运行移动平均值:
(不是真正的C#但你应该明白)
decimal buffer[]=新的十进制[句点];
十进制输出[]=新的十进制[data.Length];
当前_指数=0;
对于(int i=0;i这是我在应用程序中使用的MA
double[] MovingAverage(int period, double[] source)
{
var ma = new double[source.Length];
double sum = 0;
for (int bar = 0; bar < period; bar++)
sum += source[bar];
ma[period - 1] = sum/period;
for (int bar = period; bar < source.Length; bar++)
ma[bar] = ma[bar - 1] + source[bar]/period
- source[bar - period]/period;
return ma;
}
double[]移动平均值(整数周期,double[]源)
{
var ma=新的双精度[源.长度];
双和=0;
用于(int bar=0;bar
一旦对整个数据系列进行了计算,您就可以立即获取特定值。以下是我如何尝试的。但警告我是一个完全的业余爱好者,因此这可能是完全错误的
List<decimal> MovingAverage(int period, decimal[] Data)
{
decimal[] interval = new decimal[period];
List<decimal> MAs = new List<decimal>();
for (int i=0, i < Data.length, i++)
{
interval[i % period] = Data[i];
if (i > period - 1)
{
MAs.Add(interval.Average());
}
}
return MAs;
}
列表移动平均值(整数周期,十进制[]数据)
{
十进制[]间隔=新的十进制[期间];
List MAs=新列表();
对于(int i=0,i周期-1)
{
MAs.Add(interval.Average());
}
}
返回MAs;
}
应返回包含数据移动平均值的小数列表。当前(接受的)解决方案包含一个内部循环。删除此项也会更有效。您可以在此处看到如何实现此目的:
//
///基于浮点数学的快速低CPU使用率移动平均
///注意:此算法通过每1000个值重新求和缓冲区来补偿浮点错误
///
公共类FastMovingAverageDouble
{
///
///根据您认为合适的情况进行调整,以适应场景
///
const int maximumWindowsSize=100;
///
///根据您的需要进行调整
///
const int recreacteeveryxvalue=1000;
///
///初始化指定窗口大小的移动平均值
///
///移动平均窗口的大小介于2和MaximumWindowSize之间
///注意:这个值不应该太大,同时也要注意溢出和浮点错误的可能性,因为这个类在内部保持窗口中的值的总和
公共FastMovingAverageDouble(int\u WindowsSize)
{
如果(_WindowSize<2)
{
_WindowSize=2;
}
否则如果(_WindowSize>MaximumWindowSize)
{
_窗口大小=最大窗口大小;
}
m_WindowSize=_WindowSize;
}
私有对象SyncRoot=新对象();
专用队列缓冲区=新队列();
私有int m_窗口大小;
私人双移动平均值=0d;
专用双动sum=0d;
私人布尔缓冲区满;
专用整数计数器=0;
///
///计算移动平均线
///
公共双动平均
{
得到
{
锁定(同步根)
{
返回移动平均值;
}
}
}
///
///初始化期间构造函数设置的移动平均值窗口的大小
///
公共int窗口大小
{
得到
{
返回m_WindowSize;
}
}
///
///向序列添加新值并重新计算移动平均seee
///
///新增价值
公共void AddValue(int NewValue)
{
锁定(同步根)
{
Buffer.Enqueue(NewValue);
MovingSum+=新值;
如果(!BufferFull)
{
int BufferSize=Buffer.Count;
BufferFull=BufferSize==WindowsSize;
m_MovingAverage=MovingSum/缓冲区大小;
}
其他的
{
计数器+=1;
如果(计数器>重新计算值)
{
MovingSum=0;
foreach(缓冲区中的双缓冲值)
{
MovingSum+=缓冲值;
}
计数器=0;
}
MovingSum-=Buffer.Dequeue();
m_MovingAverage=MovingSum/WindowsSize;
}
}
}
}
现在,库中有一个名为的类可以为您执行此操作。如果您只想在最后的“X”项上执行此操作,请改用
两者都将计算运行平均值、方差和标准偏差,只需一次通过,不存储额外的数据副本。公共类移动平均值
public class MovingAverage
{
private Queue<Decimal> samples = new Queue<Decimal>();
private int windowSize = 16;
private Decimal sampleAccumulator;
public Decimal Average { get; private set; }
/// <summary>
/// Computes a new windowed average each time a new sample arrives
/// </summary>
/// <param name="newSample"></param>
public void ComputeAverage(Decimal newSample)
{
sampleAccumulator += newSample;
samples.Enqueue(newSample);
if (samples.Count > windowSize)
{
sampleAccumulator -= samples.Dequeue();
}
Average = sampleAccumulator / samples.Count;
}
}
{
私有队列样本=新队列();
私有int windowSize=16;
专用十进制采样累加器;
公共十进制平均值{get;private set;}
///
///每次新样本到达时计算新的窗口平均值
///
///
公共无效计算平均值(十进制新闻示例)
{
样本累加器+=新样本;
样本。排队(新闻样本);
如果(samples.Count>windowSize)
{
sampleAccumulator-=samples.Dequeue();
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
double[] MovingAverage(int period, double[] source)
{
var ma = new double[source.Length];
double sum = 0;
for (int bar = 0; bar < period; bar++)
sum += source[bar];
ma[period - 1] = sum/period;
for (int bar = period; bar < source.Length; bar++)
ma[bar] = ma[bar - 1] + source[bar]/period
- source[bar - period]/period;
return ma;
}
List<decimal> MovingAverage(int period, decimal[] Data)
{
decimal[] interval = new decimal[period];
List<decimal> MAs = new List<decimal>();
for (int i=0, i < Data.length, i++)
{
interval[i % period] = Data[i];
if (i > period - 1)
{
MAs.Add(interval.Average());
}
}
return MAs;
}
/// <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;
}
}
}
}
public class MovingAverage
{
private Queue<Decimal> samples = new Queue<Decimal>();
private int windowSize = 16;
private Decimal sampleAccumulator;
public Decimal Average { get; private set; }
/// <summary>
/// Computes a new windowed average each time a new sample arrives
/// </summary>
/// <param name="newSample"></param>
public void ComputeAverage(Decimal newSample)
{
sampleAccumulator += newSample;
samples.Enqueue(newSample);
if (samples.Count > windowSize)
{
sampleAccumulator -= samples.Dequeue();
}
Average = sampleAccumulator / samples.Count;
}
}
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();
}
}
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));
}
// 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();
}
public class Sample
{
private decimal sum = 0;
private uint count = 0;
public void Add(decimal value)
{
sum += value;
count++;
}
public decimal AverageMove => count > 0 ? sum / count : 0;
}
public class ThreadSafeSample
{
private decimal sum = 0;
private uint count = 0;
private static object locker = new object();
public void Add(decimal value)
{
lock (locker)
{
sum += value;
count++;
}
}
public decimal AverageMove => count > 0 ? sum / count : 0;
int period = 20;
for(int k=0;data.Count()-period;k++){
decimal summe = data.Skip(k).Take(period).Sum();
summe /= (decimal)period;
}