マイクさんという方が、FX-ONや自身のブログで素晴らしい記事をアップしています。ボリンジャーの都市伝説(1)
この中で、価格は移動平均に対して中心に滞在していない事が示されています。
この現象はなぜ発生するのでしょうか?
これは単純なグラフを描画すると直感的に理解できる現象です。
値変化に対して、5単純移動平均をとったグラフです。
■価格変化と移動平均の遅れについて
青色の値に対してオレンジの移動平均が重なるのは値が変化してクロスする地点のみで後は基本的に遅れて追従していることが確認できるかと思います。
このように、移動平均と同じ時間の価格を比較すると遅延分のずれが発生します。結果、統計を取ると、中心値に対して遅延分だけずれた位置にピークが発生します。さらに遅延分だけ分布が両側に広がる為、マイクさんが解析してくれたような症状が発生します。
では、実データを使って遅延の影響を検証したいと思います。
21単純移動平均ボリンジャーバンドに対して、その時のクローズ値がどのシグマ内にいたのかをカウントした結果です。中心値に対して両脇にピークが来ています。またそれによって分布が広がっており、確率通りに収まっていない事が分かります。
■EURUSD 15分足 バンドカウント結果
ボリンジャーバンドでよく使用される21単純移動平均の場合(21-1)/2=10の遅延が発生します。そのため統計値内にはいっているかどうかは10本先の移動平均に対して確認する必要があります。
次のグラフは、バンドを-10させた値と比較した場合のどのα内にいたのかをカウントした結果です。
■EURUSD 15分足 -10シフト バンドカウント結果
見比べると違いが分かるかと思います。
なお、確率的には10本先の移動平均対して、1σ以内の確率は78%、2σは98%、3σは99.9%と正規分布と比較してもかなり移動平均に沿って価格が動いている事が分かります。
動いた結果に基づいた移動平均に対して比較しているため当たり前と思われるかもしれませんが、ここではちょうど単純移動平均の遅延分だけを加えるときっちり正規分布の形になるという事が重要です。
このように移動平均は遅れているという事を念頭に入れると、できるだけ遅延なく対応できるのか?という事に考えが移ると思います。統計結果から移動平均は強力なツールであることが示されていますが、遅延したままではばらつきが大きい事も判ります。そのため移動平均だけではなく、移動平均に対して遅延を消す予測値を付加する必要性があり、ここがいろんなインジケータの見せ所でもあります。
以前紹介した移動平均にモメンタムを加えるというゼロラグEMAもその一つです。
(2015.07.03) 追記
そんな、移動平均の遅れに由来する誤差を消す!予測移動平均ボリンジャーバンドを発売開始!詳しくは「MT4インジケータ 予測移動平均ボリンジャーバンド」で!
カウントするプログラムもアップしておきます。
バンド内の値をカウントするスクリプト
#property copyright "Copyright 2015, Daisuke" #property link "http://mt4program.blogspot.jp/" #property version "1.00" #property strict void OnStart() { int counter[]; ArrayResize(counter, 40); ArrayFill(counter, 0, 40, 0); string symbol = "EURUSD"; int i = 10; int period = 21; int dilay = (period - 1) / 2; while( !IsStopped() ) { double close = iClose( symbol, PERIOD_M15, i); if( close <= 0 ) break ; double center = iMA(symbol, PERIOD_M15, period, -dilay, MODE_SMA, PRICE_CLOSE, i); double beforeHigh = 0; double beforeLow = 0; double currentHigh = center ; double currentLow = center; for( int index = 0 ; index < 20; index++) { double d = 0.2 * (index + 1); beforeHigh = currentHigh; beforeLow = currentLow; currentHigh = iBands( symbol, PERIOD_M15, period, d, -dilay, PRICE_CLOSE, MODE_UPPER, i); currentLow = iBands( symbol, PERIOD_M15, period, d, -dilay, PRICE_CLOSE, MODE_LOWER, i); if( beforeHigh <= close && close < currentHigh) { counter[ 20 + index ] ++; break; } if( currentLow <= close && close < beforeLow) { counter[ 19 - index ] ++; break; } } i++; } int handle = FileOpen("bandCounter.csv", FILE_CSV|FILE_WRITE, ',', CP_ACP); for( i = 0; i < ArraySize(counter); i++ ) { FileWrite(handle, counter[i]); } FileClose(handle); }