如何在C#中更快地计算出简单的移动平均线?

zoz*_*zed 15 c# algorithm financial moving-average

用于计算简单移动平均值的最快库/算法是什么?我写了自己的,但是在33万项十进制数据集上花了太长时间.

  • 期间/时间(ms)
  • 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;
}
Run Code Online (Sandbox Code Playgroud)

Data.Close[]是一个固定大小(1 000 000)的十进制数组.

小智 12

您的主要问题是每次迭代都会丢失太多信息.如果要快速运行,则需要保留与帧长度相同的缓冲区.

此代码将运行整个数据集的移动平均值:

(不是真正的C#,但你应该明白)

decimal buffer[] = new decimal[period];
decimal output[] = new decimal[data.Length];
current_index = 0;
for (int i=0; i<data.Length; i++)
    {
        buffer[current_index] = data[i]/period;
        decimal ma = 0.0;
        for (int j=0;j<period;j++)
            {
                ma += buffer[j];
            }
        output[i] = ma;
        current_index = (current_index + 1) % period;
    }
return output;
Run Code Online (Sandbox Code Playgroud)

请注意,保持正在运行的cumsum可能很有吸引力,而不是保留整个缓冲区并计算每次迭代的值,但这对于很长的数据长度不起作用,因为累积的总和会增长得太大,以至于添加小的附加值将会导致舍入错误.

  • `decimal` 有一个 96 位尾数,但至关重要的是,浮点基数是 10 而不是 2。因此,如果您所做的只是使用小数点后有限位数的数字来操作值(对于大多数金融计算来说,10 位小数就足够了) ), `decimal` 没有错误。 (2认同)
  • 嗯,我承认我不知道C#decimal是浮点数.很高兴知道... (2认同)

J.L*_*nes 10

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


Ran*_*pho 6

现在,Math DotNet库有一个名为的类RunningStatistics,可以为您执行此操作。如果您只想在最后的“X”项上执行此操作,请MovingStatistics改用。

两者都将计算运行平均值、方差和标准偏差,仅通过一次运行,无需存储额外的数据副本。


nne*_*neo 5

如果数据是静态的,则可以对数组进行预处理,以使移动平均值查询变得非常快:

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;
}
Run Code Online (Sandbox Code Playgroud)

现在,移动平均线的计算既简单又快速:

decimal CSumMovingAverage(decimal[] csum, int period, int ii) {
    if(period == 0 || ii <= period)
        return -1;
    return csum[ii] - csum[ii - period];
}
Run Code Online (Sandbox Code Playgroud)