2013-01-31 89 views
18

以下は、各点(移動平均、アップバンド、ダウンバンド)のボリンジャーバンドを計算する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"]; 
      } 
     } 
    } 

答えて

20

答えははいです。 80年代半ばには、プロセスモニタリングと制御アプリケーションのためのFORTRANにこのようなアルゴリズム(おそらくオリジナルではない)を開発しました。残念ながら、これは25年以上前のことであり、私は正確な公式を覚えていませんが、テクニックは移動平均の拡張であり、直線的なものではなく2次の計算を使用しています。


あなたのコードを見た後、私はそれがどうやってそれをやったのか思い出してもいいと思います。 ?:あなたの内側のループが二乗和を作っているか

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

をあなたの平均は、元々の値の合計を持っている必要がありますほとんど同じように注意してください?唯一の2つの違いは、順序(1の代わりに2のべき乗)と、それを二乗する前に各値の平均を引いていることです。今では切っても切れない見えるかもしれませんが、実際には、それらを分離することができます。

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

もある

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 -2*v[i]*k} + k^2*n 

なり

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

です

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

ありがとう!私はこの盲目を見つめていた。 最後にtotal_squaresを減らすことを忘れただけです。 total_squares - = Math.Pow(data。値[i-period + 1] ["close"]、2); – ChrisW

+2

http://www.johndcook.com/blog/standard_deviation/ – odyth

+0

@odythいいね!私はこれがクヌスにあることに気がつかなかった。私は80年代にこれを書き上げる数年前に実際にTAoCPを読んでいましたが、今私は無意識にそれを盗んでしまったのだろうかと思います。 – RBarryYoung

1

私はこれに非常に類似したもののためにcommons-mathを使用しました(そしてそのライブラリに貢献しました!)。オープンソースなので、C#への移植は店舗で購入したパイのように簡単に行えるはずです(パイをゼロから作ってみましたか?)。それをチェックしてください:http://commons.apache.org/math/api-3.1.1/index.html。彼らはStandardDeviationクラスを持っています。町に行く!

+0

ありがとうございますが、私はこのような簡単な計算のために数学ライブラリが必要ではないと思います。 – ChrisW

+0

あなたは大歓迎です!申し訳ありませんが、あなたが探している答えはありませんでした。私は間違いなくライブラリ全体の移植を提案するつもりはありませんでした!必要最小限のコードだけで、数百行程度にする必要があります。私は、コードにどのような法的/著作権上の制限があるのか​​分かりませんので、チェックしてください。あなたがそれを追求する場合は、ここに[リンク](http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/descriptive)があります。 /moment/StandardDeviation.java?revision=1416643&view=markup)。だから+分散+ FastMath? –

25

合計の平方根aresはそれであり、合計の二乗はかなり大きくなる可能性があり、その差の計算はvery large errorを導入するかもしれないので、何か良いものを考えてみましょう。これが必要な理由については、Algorithms for computing varianceのWikipediaの記事とTheoretical explanation for numerical resultsのJohn Cookを参照してください。

まず、stddevを分散に焦点を当てるのではなく、一度分散を計算すると、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's method)で平均と分散を計算できます。

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) 
+1

ありがとうございます。私はCLRのC#の実装の基礎としてそれを使用しました。私は実際には、 'new_Var'が非常に小さな負の数であり、sqrtが失敗するように更新するかもしれないことを発見しました。この場合、値をゼロに制限するために 'if'を導入しました。アイデアではなく、安定しています。これは、ウィンドウ内のすべての値が同じ値(私は20のウィンドウサイズを使用し、問題の値は0.5だったので、誰かがこれを試して再現したい場合に備えて発生しました)。 –

0

最も重要な情報は、既に上で与えられている---多分これはまだです:その後、毎回あなたは、このような平均と分散を更新し、別のx[n]によってウィンドウx[0]で値を交換する必要が一般的に関心のある平均値と標準偏差を算出する移動

小さなJavaライブラリはこちら: https://github.com/tools4j/meanvar

実装は、上記ウェルフォードの方法の変形に基づいています。値ウィンドウの移動に使用できる値の削除および置換方法が導出されました。

関連する問題