23

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

4 回答 4

32

计算平方和的方法的问题是它和平方和可能会变得非常大,计算它们的差可能会引入非常大的误差,所以让我们想想更好的办法。为什么需要这样做,请参阅关于计算方差的算法的维基百科文章和关于数值结果的理论解释的约翰库克

首先,我们不计算标准差,而是关注方差。一旦我们有了方差,stddev 就是方差的平方根。

x假设数据在一个名为;的数组中。将 n 大小的窗口滚动 1 可以认为是移除 的值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)

因此,方差会受到不需要您保持平方和的东西的干扰,这对数值准确性更好。

您可以在开始时使用适当的算法(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)
于 2013-02-01T01:13:59.870 回答
25

答案是肯定的,你可以。在 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,它是平均平方的 -2 倍,一旦周期 ( n) 再次被划分。所以最终的组合公式是:

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);
        }
    }
}
于 2013-02-01T00:23:50.027 回答
1

我已经使用了 commons-math(并为该库做出了贡献!)与此非常相似。它是开源的,移植到 C# 应该像商店购买的馅饼一样容易(您是否尝试过从头开始制作馅饼!?)。看看:http ://commons.apache.org/math/api-3.1.1/index.html 。他们有一个 StandardDeviation 类。去镇上!

于 2013-01-31T21:48:47.533 回答
1

上面已经给出了最重要的信息——但也许这仍然是普遍感兴趣的。

此处提供了一个用于计算移动平均值和标准差的小型 Java 库: https ://github.com/tools4j/meanvar

该实现基于上述 Welford 方法的变体。已衍生出可用于移动值窗口的移除和替换值的方法。

免责声明:我是上述图书馆的作者。

于 2015-10-07T11:51:06.280 回答