一尘不染

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

algorithm

在下面,您可以看到我的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"];
            }
        }
    }

阅读 456

收藏
2020-07-28

共1个答案

一尘不染

答案是可以的。在80年代中期,我在FORTRAN中为过程监视和控制应用程序开发了这种算法(可能不是原始算法)。不幸的是,那是25年前的事了,我不记得确切的公式,但是该技术是移动平均数的扩展,具有二阶计算,而不仅仅是线性计算。


在看完您的代码后,我认为我可以暂缓当时的工作方式。请注意您的内部循环如何使平方和?:

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

与您的平均值最初必须具有“值总和”的方式大致相同?唯一的两个区别是阶数(阶数为2而不是1),并且您要在对每个值求平方之前减去平均值。现在这看起来分不开,但实际上它们可以分开:

SUM(i=1; n){ (v[i] - k)^2 }

SUM(i=1..n){v[i]^2 -2*v[i]*k + k^2}

变成

SUM(i=1..n){v[i]^2 -2*v[i]*k} + k^2*n

这是

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]*k} + k^2*n

这也是

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]}*k + k^2*n

现在,第一个项只是平方和,您可以用对平均值求和的方式来处理。最后一项(k^2*n)只是平均值的平方乘以period。由于无论如何您都将结果除以周期,因此您只需添加新的均方值即可,而无需额外的循环。

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

-2 * k * k * n

或,只要-2*k^2*n再次将句点(n)除掉,它就是平均值平方的-2倍。因此,最终的组合公式为:

SUM(i=1..n){v[i]^2} - n*k^2

要么

SUM(i=1..n){values[i]^2} - period*(average^2)

(请务必检查此方法的有效性,因为我是从头顶上衍生出来的)

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

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);
        }
    }
}
2020-07-28