在下面,您可以看到我的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"]; } } }
答案是可以的。在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。由于无论如何您都将结果除以周期,因此您只需添加新的均方值即可,而无需额外的循环。
k^2*n
period
最后,在第二项(SUM(-2*v[i]) * k)中,因为SUM(v[i]) = total = k*n您可以将其更改为:
SUM(-2*v[i]) * k
SUM(v[i]) = total = k*n
-2 * k * k * n
或,只要-2*k^2*n再次将句点(n)除掉,它就是平均值平方的-2倍。因此,最终的组合公式为:
-2*k^2*n
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); } } }