
知っておくべきMQL5ウィザードのテクニック(第40回):Parabolic SAR(パラボリックSAR)
はじめに
MQL5ウィザードを活用してさまざまな取引設定やアイデアを検証する連載を続けます。本記事は、前回の2つの記事で構築した基盤をもとに進めていきます。これらの過去記事では、IDEのウィザードクラスに含まれる基本的な指標やオシレーターに焦点を当て、それらが提供する多様なパターンを詳しく調査しました。そして、各パターンを個別にテストした後、複数のパターンを組み合わせた設定を最適化し、独立したパターンの結果と集合的または最適化された設定の結果を比較しました。
今回の記事では、この形式を踏襲し、パラボリックSARのパターンを1つずつ分析していきます。さらに、前回の記事と同様に、複数のパターンを組み合わせたテストの実行で締めくくります。パラボリックSARは、新しいバーごとにほぼ独立して再計算される特性を持っています。ただし、その計算式に含まれるいくつかのパラメータを調整する必要があるため、この独立性は完全ではありません。この特性により、価格変動や全体的なトレンドに非常に敏感であり、カスタムシグナルクラス内で使用するのに適した指標と言えます。本記事では、パラボリックSARの10個の異なるパターンを個別にテストします。そして最後に、これらのパターンのいくつかを組み合わせたテストを実行し、そのパフォーマンスを評価します。
この記事の最後には、MQL5ウィザードで使用可能なソースコードが添付されています。このコードを利用して、エキスパートアドバイザー(EA)を組み立てることができます。初心者の方は、組み立て方法についてこちらとこちらをご参照ください。
パラボリックSARの定義
パラボリックSARとは、現在のトレンドの極値に基づきオフセットされる値のバッファであり、その値は設定された閾値に到達するまで一定の増加量(または段階)でオフセットされます。一見すると理解が難しいように聞こえるかもしれませんが、これは現在のトレンドを視覚的に示し、トレンドが反転する可能性のある点を動的にマッピングする非常に有用な手法です。パラボリックSARの計算式は非常に柔軟であり、強気トレンドと弱気トレンドの場合で異なる形を取ります。具体的に、強気トレンドの場合、以下の式が適用されます。
ここで
- SAR n+1 は次の期間のSAR値
- SAR n は現在のSAR値
- EP(Extreme Point)は現在のトレンドにおける最高値
- αはアクセラレーションファクター(AF)で、通常0.02から始まり、新しいEPに到達するたびに0.02ずつ増加し、最大0.20まで増加する(ユーザー設定によって異なる)
上昇トレンドでは次のようになることも注目に値します。
- EPはトレンドが始まって以来の最高値
- SARの値は、トレンドが継続するにつれて上昇し、価格の動きに追随するように調整される
弱気の場合は次のようになります。
ここで
- EPは現在の下降トレンドにおける最安値
同様に注目すべきは、下降トレンドの場合です。
- EPはトレンドが始まって以来の安値
- SAR値は下降トレンドに従い、時間とともに減少する
したがって、トレンドが進むにつれて、SARの値は増加または減少(弱気トレンドの場合など)しながら価格に向かって圧縮される傾向があります。この特性により、トレンドの反転や変化がより差し迫った状況として視覚的に示されることになります。MQL5でのこの実装は、組み込みの指標と標準ライブラリクラスによって効率的に処理されます。そのため、本記事ではこれらの仕組みについて簡単に説明しつつ、SARが提供するさまざまなパターンに注目して詳しく見ていきます。
リバーサルギャップクロスオーバー
最初のパターン0はギャップのクロスオーバーです。ここでは、強気ギャップの場合はSAR指標のドットが価格の高値より上から安値より下、弱気ギャップの場合は安値より下から高値より上へと切り替わります。多くの場合、パラボリックSARドットと最も近い価格ポイント(強気ギャップの場合は安値、弱気ギャップの場合は高値)間のギャップのサイズは、シグナルの強さを示します。このギャップが広いほど、新しいトレンドが強くなります。
ただし、市場のマクロ状況も考慮する必要があります。これらのギャップクロスオーバーは、特に非常に不安定な市場では頻繁に発生する可能性があり、多くの誤ったシグナルにつながる可能性があるためです。したがって、ボラティリティが最小限の市場では、このシグナルにもっと依存するべきです。SARがストップロス調整に使用される場合、これらのクロスオーバーポイントでは、ポジションをクローズするのではなく、ストップロスがSARに近づくだけで、実際のポジションのクローズと反転は別のシグナルに依存します。
パターン0を実装するために、カスタムシグナルクラスでは以下の関数を使います。
//+------------------------------------------------------------------+ //| Check for Pattern 0. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_0(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && Base(StartIndex() + 1) > High(StartIndex() + 1) && Base(StartIndex()) < Low(StartIndex())) { return(true); } else if(T == POSITION_TYPE_SELL && Base(StartIndex() + 1) < Low(StartIndex() + 1) && Base(StartIndex()) > High(StartIndex())) { return(true); } return(false); }
また、パターン0のみを使用するウィザードで組み立てられたEAのテスト実行では、次の結果が得られます。
SAR圧縮ゾーン
次のパターンは「圧縮ゾーン」であり、パターン0の改良版と言えます。このパターンの主な違いは、名前の通り、SAR指標が反転する前に価格の圧縮(以前の低ボラティリティ傾向への変化)が発生していることを条件とする点です。すでに説明したように、SARは現在のトレンドが強気か弱気かを示します。そのため、以前のトレンドがごくわずかにしか推進力を持たなかった場合、これは圧縮と解釈できます。この「ごくわずか」という基準を定量化するには新たな入力パラメータを導入する必要があるかもしれませんが、この記事では別途圧縮関数を使用して、この条件を実装する方法を採用しました。
bool Compression(ENUM_POSITION_TYPE T, double &Out) { Out = 0.0; int _i = StartIndex() + 1, _c = 0; double _last = Base(StartIndex() + 1); double _first = 0.0; if ( T == POSITION_TYPE_BUY && Base(StartIndex()) < Low(StartIndex()) && Base(_i) < Close(StartIndex()) && Base(_i) > High(_i) ) { while(Base(_i) > High(_i) && _c < __COMPRESSION_LIMIT) { _first = Base(_i); _i++; _c++; } if(_c > 0) { Out = fabs(_first - _last)/_c; return(true); } } else if ( T == POSITION_TYPE_SELL && Base(StartIndex()) > High(StartIndex()) && Base(_i) > Close(StartIndex()) && Base(_i) < Low(_i) ) { while(Base(_i) < Low(_i) && _c < __COMPRESSION_LIMIT) { _first = Base(_i); _i++; _c++; } if(_c > 0) { Out = fabs(_first - _last)/_c; return(true); } } return(false); }
つまり、パターン0の関数は次のように処理されます。
//+------------------------------------------------------------------+ //| Check for Pattern 1. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_1(ENUM_POSITION_TYPE T) { double _compression = 0.0; if(Compression(T, _compression)) { if(T == POSITION_TYPE_BUY && _compression < 0.02*fabs(Base(StartIndex())-Low(StartIndex()))) { return(true); } else if(T == POSITION_TYPE_SELL && _compression < 0.02*fabs(Base(StartIndex())-High(StartIndex()))) { return(true); } } return(false); }
この関数は、以前のトレンド中にSARの値がどの程度調整され続けたかを定量化します。この評価に使用される閾値としては、すでにSARステップ入力として設定されているものを利用しています。具体的には、価格からの初期SARギャップをSARステップの分数として計算し、それが以前のトレンド中におけるSAR値の平均変化を上回る場合、圧縮が発生したと見なされます。このパターンにおける条件は、単純に「トレンドの圧縮」と「その後のトレンド反転」の2点のみです。そのため、ポジションのオープンは、すでに説明したパターン0のトレンド反転条件に従っておこないます。この条件を基に、ウィザードで組み立てたEAを用いてパターン1専用のテストを実施したところ、以下の結果が得られます。
2023年のEURJPY銘柄の日足データを使用してテストをおこなっています。このテストでは、SARのステップ入力を分数として使用することで全体的なトレンドを厳密に制限し、明確に定義された圧縮パターンを適用しています。ただし、この取引頻度を調整するために、別のパラメータを導入することが可能です。このテストで使用されるパターン使用の入力値は2に設定されています。
拡張トレンドSAR
このパターンは継続パターンであり、初期のトレンド反転が抑制されている場合、たとえば、SARと価格のギャップが当初は非常に小さかった場合に採用できます。これは非常に単純で、強気パターンはSARドット間のギャップが拡大し、SAR指標が安値より下に留まることで示され、弱気シグナルは逆のシナリオで示され、ドットギャップも拡大し、SARが高値より上に留まることで示されます。これを遅れと呼ぶ人もいるかもしれませんが、そのような結論を出す前にまずテストすることをお勧めします。MQL5では、これを次のように実装します。
//+------------------------------------------------------------------+ //| Check for Pattern 2. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_2(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && Base(StartIndex()) - Base(StartIndex() + 1) > Base(StartIndex() + 1) - Base(StartIndex() + 2) && Base(StartIndex() + 1) - Base(StartIndex() + 2) > Base(StartIndex() + 2) - Base(StartIndex() + 3) && Base(StartIndex() + 2) - Base(StartIndex() + 3) > Base(StartIndex() + 3) - Base(StartIndex() + 4) ) { return(true); } else if(T == POSITION_TYPE_SELL && Base(StartIndex() + 1) - Base(StartIndex()) > Base(StartIndex() + 2) - Base(StartIndex() + 1) && Base(StartIndex() + 2) - Base(StartIndex() + 1) > Base(StartIndex() + 3) - Base(StartIndex() + 2) && Base(StartIndex() + 3) - Base(StartIndex() + 2) > Base(StartIndex() + 4) - Base(StartIndex() + 3) ) { return(true); } return(false); }
このパターン(パターン2)だけをテストするには、パターンの入力マップを4として使用します。上記と同じ設定でこのパターンだけをテストすると、次の結果が得られます。
SAR反転フェイクアウト
このパターンは、名前が示すように、新しいトレンドへの変化がほぼ即座に前のトレンドへと反転することを指します。これは、SAR価格チャート上の1つまたは2つのドットのトレンドによって示されることが多く、これらのドットの反対側にあるトレンドは、トレーダーが注目すべきものを示しています。したがって、強気のシグナルの場合、通常の強気トレンドの後に、1~2つの価格バーのみ持続する反転が特徴で、その後、長期トレンドが再開され、シグナルはトレンドの再開です。 同様に、弱気のパターンは下降トレンドで始まり、1つまたは2つの価格バーで一時的に強気になり、その後下降を再開します。このパターンを次のようにコードします。
//+------------------------------------------------------------------+ //| Check for Pattern 3. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_3(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && Base(StartIndex()) < Low(StartIndex()) && Base(StartIndex() + 1) > High(StartIndex() + 1) && Base(StartIndex() + 2) < Low(StartIndex() + 2)) { return(true); } else if(T == POSITION_TYPE_SELL && Base(StartIndex()) > High(StartIndex()) && Base(StartIndex() + 1) < Low(StartIndex() + 1) && Base(StartIndex() + 2) > High(StartIndex() + 2)) { return(true); } return(false); }
これがパターン4番目であり、インデックスとしてはパターン3として分類されていることから、このシグナルにのみ依存して取引をおこなう場合、パターン使用の入力マップは8に設定する必要があります。このパターンについて、前述の設定と類似した条件でテスト期間中にテストを実施しましたが、取引は一切おこなわれず、結果を共有することはできませんでした。それでもなお、こうした「反転のフェイクアウト」が発生する一般的な原因は、レンジ相場や横ばい市場、低ボラティリティ、または市場ノイズといった典型的な要因に起因します。これが予期していないトレーダーに与える影響は重大である可能性があるため、補助的な指標(MACDなど)、支持・抵抗の価格アクション分析、あるいは(可能であれば)出来高分析を活用することでこれを回避できます。とはいえ、このシグナルは、単一の反転(たとえばパターン0)よりも信頼性が高いと考えられます。
ダブルSARフリップとトレンド継続
このパターンは、パターン3にさらに2回の反転を加えたものです。パターン3がパターン0よりも信頼性が高いと主張できるのと同様に、このパターン4はパターン3よりもさらに信頼性が高く、強力であると言えます。以下に示すように、このコード実装を提供しましたが、テストは実行しておらず、さらなる調査は読者に委ねられています。理想的には、対象としている1年間を超える長期間でのテストが必要です。2023年にEURJPYの日次取引においてパターン3での取引がおこなわれなかったため、パターン4のシグナルや取引も発生しないと予想されます。
//+------------------------------------------------------------------+ //| Check for Pattern 4. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_4(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && Base(StartIndex()) < Low(StartIndex()) && Base(StartIndex() + 1) > High(StartIndex() + 1) && Base(StartIndex() + 2) < Low(StartIndex() + 2) && Base(StartIndex() + 3) > High(StartIndex() + 4) && Base(StartIndex() + 4) < Low(StartIndex() + 5) ) { return(true); } else if(T == POSITION_TYPE_SELL && Base(StartIndex()) > High(StartIndex()) && Base(StartIndex() + 1) < Low(StartIndex() + 1) && Base(StartIndex() + 2) > High(StartIndex() + 2) && Base(StartIndex() + 3) < Low(StartIndex() + 4) && Base(StartIndex() + 4) > High(StartIndex() + 5) ) { return(true); } return(false); }
移動平均を用いたSARのダイバージェンス
パターン5は、ダイバージェンスに基づいています。価格とSARの間のダイバージェンスは非常に一般的なため、移動平均指標が確認として役立ちます。したがって、強気シグナルの場合、価格がSARに向かって下落する一方でSARが上昇しており、移動平均がSARの下または同等の位置にある必要があります。逆に、弱気パターンでは、価格が上昇している間にSARが下落しており、移動平均がSARおよび価格の両方の上または同等の位置にある必要があります。上昇または下降を測定するのに必要なステップ数は、任意で決定することができます(またはテストから導き出すことが可能)ですが、ここでは単純に3ステップとして採用しています。この条件を満たすパターンを検出するための関数は、以下のように実装されます。
//+------------------------------------------------------------------+ //| Check for Pattern 5. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_5(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && MA(StartIndex()) <= Base(StartIndex()) && Base(StartIndex()) > Base(StartIndex() + 1) && Base(StartIndex() + 1) > Base(StartIndex() + 2) && Close(StartIndex()) < Close(StartIndex() + 1) && Close(StartIndex() + 1) < Close(StartIndex() + 2) ) { return(true); } else if(T == POSITION_TYPE_SELL && MA(StartIndex()) >= Base(StartIndex()) && Base(StartIndex()) < Base(StartIndex() + 1) && Base(StartIndex() + 1) < Base(StartIndex() + 2) && Close(StartIndex()) > Close(StartIndex() + 1) && Close(StartIndex() + 1) > Close(StartIndex() + 2) ) { return(true); } return(false); }
このパターンのみでEAをテストすると、以下のような結果が得られます。
ここで、価格、移動平均、SARという3つのデータバッファを扱っている点は注目に値します。これらのデータバッファから選択された偏差は、価格とSARの間ですが、移動平均とSARの間など、他の偏差も考慮できます。ただし、この乖離は、移動平均の遅延効果により、この記事で実装した価格とSARの乖離と比較して多少の遅延が生じることになります。一方で、価格変動は短期的には多くの変動を生み出しますが、長期的にはそれほど重要ではないことが多いため、ノイズも少なくなります。そのため、ある程度の用途がある可能性があり、読者の皆さんもこの方法を検討してみてください。このパターンのパターン使用の入力は32です。
パラボリックSARチャネリング
パラボリックSARチャネリングパターンは、価格アクションと現在のSARトレンドを組み合わせてシグナルを生成します。価格チャネルは価格チャート上では比較的簡単に理解できますが、そのロジックをコードに組み込むと、最初に想定したよりも複雑になることがよくあります。そのため、まずは、履歴で確認する価格バーの数によって範囲が設定される価格チャネルの現在の上限と下限を定義する基本的な関数を定義することをお勧めします。この関数をChannelと名付けます。インターフェイスにあるロジックを以下に示します。
bool Channel(ENUM_POSITION_TYPE T) { vector _max,_max_i; vector _min,_min_i; _max.Init(2); _max.Fill(High(0)); _max_i.Init(2); _max_i.Fill(0.0); _min.Init(2); _min.Fill(Low(0)); _min_i.Init(2); _min_i.Fill(0.0); for(int i=0;i<m_ma_period;i++) { if(High(i) > _max[0]) { _max[0] = High(i); _max_i[0] = i; } if(Low(i) < _min[0]) { _min[0] = Low(i); _min_i[0] = i; } } double _slope = (Close(0) - Close(m_ma_period-1))/m_ma_period; double _upper_scale = fabs(_slope); double _lower_scale = fabs(_slope); for(int i=0;i<m_ma_period;i++) { if(i == _max_i[0]) { continue; } else { double _i_slope = (High(i) - _max[0])/(i - _max_i[0]); if((_i_slope > 0.0 && _slope > 0.0)||(_i_slope < 0.0 && _slope < 0.0)) { if(fabs(_i_slope-_slope) < _upper_scale) { _max[1] = High(i); _max_i[1] = i; } } } } for(int i=0;i<m_ma_period;i++) { if(i == _min_i[0]) { continue; } else { double _i_slope = (Low(i) - _min[0])/(i - _min_i[0]); if((_i_slope > 0.0 && _slope > 0.0)||(_i_slope < 0.0 && _slope < 0.0)) { if(fabs(_i_slope-_slope) < _lower_scale) { _min[1] = Low(i); _min_i[1] = i; } } } } vector _projections; _projections.Init(4); _projections[0] = _max[0] + (_max_i[0]*_slope); _projections[1] = _min[0] + (_min_i[0]*_slope); _projections[2] = _max[1] + (_max_i[1]*_slope); _projections[3] = _min[1] + (_min_i[1]*_slope); if(T == POSITION_TYPE_BUY && Close(0) < Close(m_ma_period) && Close(0) < _projections.Mean()) { return(true); } else if(T == POSITION_TYPE_SELL && Close(0) > Close(m_ma_period) && Close(0) > _projections.Mean()) { return(true); } return(false); }
このチャネルの主な出力は、ポジションの種類が与えられた場合、チャネルが反転の可能性を示しているかどうかです。これに答えるには、まずどの価格ポイントが上の線と下の線を定義するかを判断する必要があります。通常の価格チャートを視覚的に選択するのは簡単ですが、コードではフラクタルに頼ることに簡単に陥る可能性があります。使用しているフラクタル指標非常に優れている場合はこれを機能させることができますが、特定のルックバック期間の全体的な傾斜に焦点を当てると、より一般的な解決策が得られることがわかりました。
したがって、チャネルを定義するには、まず、ルックバック期間全体の傾斜を把握します。ルックバック期間は、上記のパターン5で強調表示された移動平均指標で使用される期間と同じになるように設定されています。読者は、これを定義するために独自のセカンダリパラメータを作成できますが、入力パラメータが少ないほど、モデルを一般化できると常に感じています。したがって、傾斜がわかったら、定義されたルックバック期間内でこの傾斜に最も一致する2つの高値を取得する必要があります。
ただし、通常、ルックバック期間の最高点は、常にチャネルのこの上側の線に沿っていると予想されるため、この最大値を取得することから始める場合は、他のすべての高値を調べて、最高点からの傾斜が全体的な傾向の傾斜と最も一致する2番目の高値を見つける必要があります。ここでの全体的なトレンドは、もちろん、ルックバック期間全体にわたる終値の変化です。
これら2つの点を取得したら、チャネルの下限に対して同じことをおこないます。つまり、最も低い安値と、最低点に接続したときにトレンドの傾斜と最も一致する別の安値を見つけます。2組のポイントによって2本の線が定義されるため、それらを使用して、ルックバック期間のみに基づいたチャネルが作成されます。価格チャートを見るときは、このような機械的なアプローチを取ることは絶対にありません。固定されたルックバック履歴に、これを定義するのに十分なデータポイントが常にあるとは考えにくいためです。そのため、これらのポイントを単純に接続して推定しようとすると、多くのワイルドなチャネルやランダムなチャネルが生成されます。固定されたルックバック履歴では、分析に必要なすべての主要な過去の価格ポイントがキャプチャされないことがよくあります。
そのため、チャネルには上限と下限の線が1つではなく2つあると定義します。これらの線はそれぞれ、上で定義した4つの点を通過します。パターン6の強気シグナルは、価格がチャネルの下半分にあり、放物線SARも長期トレンドを示します。逆に、弱気シグナルの場合、価格はチャネルの上半分にあり、SARは弱気トレンドを示します。現在の価格がチャネルのどの半分にあるかを判断するには、4つの投影価格ポイントすべての平均を取得します。これらの投影は、全体的なトレンドの同じ傾斜を維持しながら、2つの高ポイントと2つの低ポイントを通過して現在のインデックスまで伸びる線の単なる延長です。MQL5では、このパターンを以下のように実装します。
//+------------------------------------------------------------------+ //| Check for Pattern 6. | //+------------------------------------------------------------------+ bool CSignalSAR::IsPattern_6(ENUM_POSITION_TYPE T) { if(T == POSITION_TYPE_BUY && Base(StartIndex()) < Low(StartIndex())) { return(Channel(T)); } else if(T == POSITION_TYPE_SELL && Base(StartIndex()) > High(StartIndex())) { return(Channel(T)); } return(false); }
2023年のEURJPYの日次取引で上記と同じ設定を使用してテストすると、次の結果が得られます。
このパターンの使用パターンの入力は64です。
結論
パラボリックSARの10種類のパターンのうち、今回は7つを取り上げましたが、記事が長くなりすぎないように、ここで終了とします。後続の記事では、パラボリックSARとボリュームダイバージェンス、長期的な反転SAR、さらにSARとRSIの重なりについて検討する予定です。この記事で紹介した各パターンは、さらに活用して、さまざまな方法や形式で実装することができます。この記事と、前回の2つの記事では、カスタムシグナルクラスが継承する組み込みパターンメソッドに依存しています。前回の2つの記事で親クラスにすでにm_patterns_usageパラメータがあるため、重複するm_patterns_usedパラメータを宣言して使用しました。これによりコーディングの手間を減らし、実際の入力マップが適切に使用されるため、より簡潔な結果が得られます。
読者の皆さんには、このアプローチをそのまま実行するのではなく、前回の記事に添付されたコードを適宜変更して使用していただきたいと思います。また、この記事の教訓として、EAにおける価格チャネルの実装は一般的ではないため、これをシグナルクラスとして独立した記事で取り上げる方法も考慮する価値があります。価格チャートにチャネルを視覚的に読み取るのは簡単ですが、コードでこれをおこなうのは別の話です。そのため、今後の記事でこれについても検討するかもしれません。
最後に、このパターンシリーズでは、標準ライブラリに付属する組み込みのパターンベースのシグナルに対して、閾値条件を最適化しています。これは、指標を使用する際にトレーダーが自身の経験と観察に基づいて閾値を事前に設定するという慣習に反するものです。複数のパターンを使用しているため、テスト結果は有望に見えますが、一般化や交差検証は難しいと言えます。したがって、複数のパターンを使用する場合は、トレーダーが事前に閾値に重みを割り当てることをお勧めします。前回の記事で紹介したように、1つのパターンのみを使用する場合は、その閾値だけを最適化することも可能です。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/15887





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索