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

C# 如何有效地计算移动标准偏差,c#,algorithm,finance,moving-average,standard-deviation,C#,Algorithm,Finance,Moving Average,Standard Deviation,下面你可以看到我的C#方法来计算每个点的布林格带(移动平均、上升带、下降带) 如您所见,此方法使用2 for循环来使用移动平均值计算移动标准偏差。它曾经包含一个额外的循环来计算过去n个周期的移动平均值。我可以通过在循环开始时将新的点值添加到total_average中,然后在循环结束时删除I-n点值来删除这个值 我现在的问题基本上是:我能用和移动平均线类似的方法删除剩余的内环吗 public static void AddBollingerBands(SortedList<Date

下面你可以看到我的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"];
            }
        }
    }
public static void addbollingerband(分类列表数据、整数周期、整数系数)
{
两倍总平均值=0;
对于(int i=0;i=时段-1)
{
两倍总波林格=0;
双平均值=总平均值/周期;
对于(int x=i;x>(i-周期);x--)
{
total_bollinger+=数学功率(数据值[x][“关闭”]-平均值,2);
}
double stdev=数学Sqrt(总博林格数/周期);
数据.数值[i][“bollinger_average”]=平均值;
数据值[i][“bollinger_top”]=平均值+系数*stdev;
数据值[i][“bollinger_bottom”]=平均系数*stdev;
总平均值-=数据值[i-期间+1][“结束”];
}
}
}

我使用了commons math(并为该库做出了贡献!)来实现类似的功能。它是开源的,移植到C#应该很容易,就像商店买的馅饼一样(你试过从头开始做馅饼吗?)。请查看:。他们有一个标准偏差类。进城去

答案是肯定的,你可以。在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*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
)只是
周期的平均平方倍。由于您将结果除以周期,因此只需添加新的平均值平方,而无需额外循环

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

-2 * k * k * n
或者,只要将周期(
n
)再次分开,它就是平均平方的-2倍。因此,最终的组合公式是:

SUM(i=1..n){v[i]^2} - n*k^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);
        }
    }
}
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)
public static void addbollingerband(参考分类列表数据、整数周期、整数系数)
{
两倍总平均值=0;
两倍总平方=0;
对于(int i=0;i=时段-1)
{
两倍总波林格=0;
双平均值=总平均值/周期;
双标准偏差=数学Sqrt(总平方-数学功率(总平均值,2)/周期)/周期);
数据.数值[i][“bollinger_average”]=平均值;
数据值[i][“bollinger_top”]=平均值+系数*stdev;
数据值[i][“bollinger_bottom”]=平均系数*stdev;
总平均值-=数据值[i-期间+1][“结束”];
总平方-=数学功率(数据值[i-周期+1][“关闭”],2);
}
}
}

计算平方和的方法的问题是,它和平方和可能会变得相当大,计算它们的差值可能会引入一个新的方法,所以让我们考虑一些更好的方法。要了解为什么需要这样做,请参阅维基百科上的文章(和约翰·库克上的文章)

首先,让我们关注方差,而不是计算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)
之后:

因此,方差会受到不需要保持平方和的干扰,这对数值精度更有利

您可以在开始时使用适当的算法()计算一次均值和方差。之后,每次您必须用另一个
x[n]
替换窗口
x[0]
中的一个值时,您都会像下面这样更新平均值和方差:

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);
        }
    }
}
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)

上面已经给出了最重要的信息——但也许这是一个例子
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)