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

二乗和を計算するアプローチの問題は、二乗和と和の二乗が非常に大きくなる可能性があり、それらの差の計算が非常に大きな誤差をもたらす可能性があることです。そこで、もっと良いことを考えてみましょう。これが必要な理由については、分散を計算するためのアルゴリズムに関するWikipediaの記事と、数値結果の理論的説明に関するJohn Cookを参照してください) 。

まず、標準偏差を計算する代わりに、分散に焦点を当てましょう。分散が得られると、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)

したがって、分散は、二乗和を維持する必要のないものによって摂動されます。これは、数値の精度に優れています。

適切なアルゴリズム(ウェルフォードの方法)を使用して、最初に平均と分散を1回計算できます。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 年以上前のことであり、正確な式は覚えていませんが、この手法は移動平均の拡張であり、線形計算ではなく 2 次計算を使用していました。


あなたのコードをいくつか見た後、当時のやり方を推測できると思います。内側のループが平方和を作成する方法に注目してください。

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

あなたの平均がもともと値の合計を持っていたに違いないのと同じように? 唯一の 2 つの違いは、次数 (1 ではなく 2 乗) と、2 乗する前に各値の平均を減算していることです。これは切り離せないように見えるかもしれませんが、実際には分離できます。

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) は、 の 2 乗平均periodです。とにかく結果を期間で割るので、余分なループなしで新しい平均二乗を追加できます。

最後に、第 2 項 ( 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);
        }
    }
}
于 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 に答える