如何有效地计算移动标准差

Chr*_*ers 20 c# algorithm finance moving-average standard-deviation

下面你可以看到我的C#方法来计算每个点的布林带(移动平均线,上行带,下行带).

如您所见,此方法使用2 for循环来计算移动平均值的移动标准偏差.它曾经包含一个额外的循环来计算过去n个时期的移动平均值.我可以通过在循环开始时将新的点值添加到total_average并删除循环结束时的i-n点值来删除.

我现在的问题基本上是:我可以用移动平均线管理的类似方式删除剩余的内部循环吗?

    public static void AddBollingerBands(SortedList<DateTime, Dictionary<string, double>> data, int period, int factor)
    {
        double total_average = 0;

        for (int i = 0; i < data.Count(); i++)
        {
            total_average += data.Values[i]["close"];

            if (i >= period - 1)
            {
                double total_bollinger = 0;
                double average = total_average / period;

                for (int x = i; x > (i - period); x--)
                {
                    total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2);
                }

                double stdev = Math.Sqrt(total_bollinger / 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"];
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Jon*_*oni 29

计算平方和的方法的问题在于它和和的平方可以变得非常大,并且它们的差异的计算可能引入非常大的误差,所以让我们想一些更好的东西.为什么这是需要,请参见维基百科的文章算法计算方差上和约翰·库克对计算结果的理论解释)

首先,不是计算stddev而是关注方差.一旦我们得到方差,stddev就是方差的平方根.

假设数据在一个名为的数组中x; 将一个n大小的窗口滚动一个可以被认为是删除值x[0]并添加值x[n].让我们来表示的平均值x[0]..x[n-1],并x[1]..x[n]分别由μ,μ".在取消某些条款并适用之后,差异x[0]..x[n-1]和差异之间的差异:x[1]..x[n](a²-b²) = (a+b)(a-b)

Var[x[1],..,x[n]] - Var[x[0],..,x[n-1]] 
= (\sum_1^n x[i]² - n µ’²)/(n-1) - (\sum_0^{n-1} x[i]² - n µ²)/(n-1)
= (x[n]² - x[0]² - n(µ’² - µ²))/(n-1) 
= (x[n]-µ’ + x[0]-µ)(x[n]-x[0])/(n-1)
Run Code Online (Sandbox Code Playgroud)

因此,方差受到不需要你保持平方和的东西的干扰,这对数值精度更好.

您可以使用正确的算法(Welford方法)在开头计算一次均值和方差.之后,每次你必须x[0]用另一个值替换窗口中的值时,x[n]你更新平均值和方差,如下所示:

new_Avg = Avg + (x[n]-x[0])/n
new_Var = Var + (x[n]-new_Avg + x[0]-Avg)(x[n] - x[0])/(n-1)
new_StdDev = sqrt(new_Var)
Run Code Online (Sandbox Code Playgroud)


RBa*_*ung 21

答案是肯定的,你可以.在80年代中期,我在FORTRAN中开发了一种用于过程监控和控制应用的算法(可能不是原始算法).不幸的是,那是在25年前,我不记得确切的公式,但该技术是移动平均线的延伸,使用二阶计算而不仅仅是线性计算.


在看了一些你的代码后,我认为我可以说我当时是怎么做的.注意你的内循环是如何形成平方和的?:

            for (int x = i; x > (i - period); x--)
            {
                total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2);
            }
Run Code Online (Sandbox Code Playgroud)

与你的平均值最初必须具有一个值的总和的方式大致相同?唯一的两个区别是顺序(它的功率2而不是1),并且你在平方之前减去每个值的平均值.现在这可能看起来不可分割,但事实上它们可以分开:

SUM(i=1; n){ (v[i] - k)^2 }
Run Code Online (Sandbox Code Playgroud)

SUM(i=1..n){v[i]^2 -2*v[i]*k + k^2}
Run Code Online (Sandbox Code Playgroud)

变成了

SUM(i=1..n){v[i]^2 -2*v[i]*k} + k^2*n
Run Code Online (Sandbox Code Playgroud)

是的

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]*k} + k^2*n
Run Code Online (Sandbox Code Playgroud)

这也是

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]}*k + k^2*n
Run Code Online (Sandbox Code Playgroud)

现在第一个术语只是一个平方和,你可以像处理平均值的总和那样处理它.最后一个术语(k^2*n)只是平均时间的平均值period.由于您无论如何都将结果除以周期,您只需添加新的平均平方而无需额外的循环.

最后,在第二个术语(SUM(-2*v[i]) * k)中,因为SUM(v[i]) = total = k*n您可以将其更改为:

-2 * k * k * n
Run Code Online (Sandbox Code Playgroud)

或者只是-2*k^2*n,一旦句点(n)被再次划分,这是平均平方的-2倍.所以最终的组合公式是:

SUM(i=1..n){v[i]^2} - n*k^2
Run Code Online (Sandbox Code Playgroud)

要么

SUM(i=1..n){values[i]^2} - period*(average^2)
Run Code Online (Sandbox Code Playgroud)

(一定要检查一下它的有效性,因为我从头脑中得出它)

并入您的代码应该看起来像这样:

public static void AddBollingerBands(ref SortedList<DateTime, Dictionary<string, double>> data, int period, int factor)
{
    double total_average = 0;
    double total_squares = 0;

    for (int i = 0; i < data.Count(); i++)
    {
        total_average += data.Values[i]["close"];
        total_squares += Math.Pow(data.Values[i]["close"], 2);

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

  • 非常感谢!我一眼就盯着这个.你只是忘了在最后减少total_square:total_squares - = Math.Pow(data.Values [i - period + 1] ["close"],2); (4认同)
  • 谢谢你,一直在努力寻找一个简单的bollie乐队实现,没有2k行代码:).这是简短而重要的.赞赏 (2认同)
  • 似乎从未使用过“total_bollinger”? (2认同)