MQL5入門(第21回):ハーモニックパターン検出の自動化
はじめに
連載「MQL5入門」の第21回へようこそ。第20回では、ハーモニックパターンの概要や、フィボナッチのリトレースメントおよびエクステンションの数学的概念、そしてそれらをMQL5で実装する方法を紹介しました。今回の記事ではさらに一歩進め、ガートリーハーモニックパターンの検出を自動化する方法に焦点を当てます。
ここで学ぶことで、フィボナッチレベルや価格のスイング、チャートオブジェクトを活用し、チャート上で潜在的なガートリーパターンをプログラムで認識できるようになります。紹介するロジックや手法は、ガートリーパターンに限らず、バット、バタフライ、クラブ、ディープクラブなど、他のハーモニックパターンにも応用可能です。
この記事の特徴は、初心者でも理解しやすいスタイルで、複雑な概念を段階的に整理して説明している点です。理論だけで頭を悩ませるのではなく、プロジェクトベースの実践的アプローチを採用し、ガートリーパターンを自動で認識するエキスパートアドバイザー(EA)を一緒に作っていきます。
プロジェクトの設定
このプロジェクトの目標は、チャート上でガートリーハーモニックパターンを自動的に認識できるEAを作成することです。EAはあらかじめ設定された条件に基づき、価格のスイングを評価し、重要なフィボナッチのリトレースメントおよびエクステンションのレベルを計算し、可能性のあるガートリーパターンを特定します。
ガートリーパターンは、4つの主要なレッグ(XA、AB、BC、CD)と特定のフィボナッチ関係で定義されます。具体的には、BはXAのおよそ78.6%をリトレースし、CはABの38.2%~88.6%の範囲でリトレースし、DはXAのおよそ78.6%をリトレースする必要があります。EAは柔軟で初心者にも使いやすく、トレーダーが自分の取引スタイルに合わせてフィボナッチのリトレースメントおよびエクステンションの範囲を調整できるようになっています。
買いのロジック
強気のガートリーパターンを検出するために、EAは以下の手順を実行します。
- Xをスイングローとして特定する。
- Xの後の重要なスイングハイをAとして検出する。
- Bを特定する。BはXAの指定範囲内でリトレースする必要があります。
- Cを確認する。CはABの指定範囲内でリトレースします。
- 可能性のあるDを計算する。DはXAの指定リトレース範囲内にある必要があります。
- すべてのレッグが正しいフィボナッチの整合性に従っていることを確認する。
- Dが確認されると、EAは買いシグナルを生成し、このレベルから価格が上昇に反転することを期待する。
- さらに、EAでは入力設定を通じてすべてのフィボナッチのリトレースメントおよびエクステンションのパーセンテージを調整でき、異なる市場状況に柔軟に対応可能
有効な強気のガートリーパターンが確認された場合
- エントリ:パターン完成後すぐにDで買いポジションを開く
- ストップロス(SL):Dを形成したスイングローに設定する
- テイクプロフィット(TP):リスクリワード比1:3で設定する(つまりTP距離はSLの3倍)
売りのロジック
弱気のガートリーパターンを検出するために、EAは以下の手順を実行します。
- Xをスイングハイとして特定する。
- Xの後の重要なスイングローをAとして検出する。
- Bを特定する。BはXAの指定範囲内でリトレースする必要があります。
- Cを確認する。CはABの指定範囲内でリトレースします。
- 可能性のあるDを計算する。DはXAの範囲内にある必要があります。
- すべてのレッグが正しいフィボナッチの整合性に従っていることを確認する。
- Dが確認されると、EAは売りシグナルを生成し、このレベルから価格が下落に反転することを期待する。
- さらに、EAでは入力設定を通じてすべてのフィボナッチのリトレースメントおよびエクステンションのパーセンテージを調整でき、異なる市場状況に柔軟に対応可能
有効な弱気のガートリーパターンが確認された場合
- エントリ:パターン完成後すぐにDで売りポジションを開く
- ストップロス(SL):Dを形成したスイングハイに設定する
- テイクプロフィット(TP):リスクリワード比1:3で設定する(つまりTP距離はSLの3倍)
弱気ガートレーパターンの識別
前回の記事では、強気パターンと弱気パターンの違いについて詳しく説明しました。いつも言っているように、チャートパターンを扱う際にはローソク足データが必要であり、スイングハイとスイングローを正確に識別できることが重要です。 弱気のガートリーパターンの場合、開始点Xはスイングハイである必要があります。その後の構造は、XA、AB、BC、CDの各レッグに沿って、必要なフィボナッチのリトレースメントおよびエクステンションレベルを満たす形で形成されます。EAはこれらのスイングを正確に識別することで、XABCD構造をマッピングし、弱気のガートリーパターンのパラメータに合致するかどうかを判定できます。
例:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // TIMEFRAME double open[]; double close[]; double low[]; double high[]; datetime time[]; datetime time_bar; int bars_check = 500; int total_symbol_bars; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { total_symbol_bars = Bars(_Symbol, timeframe); time_bar = iTime(_Symbol, timeframe, 0); CopyOpen(_Symbol, timeframe, time_bar, bars_check, open); CopyClose(_Symbol, timeframe, time_bar, bars_check, close); CopyLow(_Symbol, timeframe, time_bar, bars_check, low); CopyHigh(_Symbol, timeframe, time_bar, bars_check, high); CopyTime(_Symbol, timeframe, time_bar, bars_check, time); } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; // If the current high is not the highest, return false. } return true; }
説明
最初の手順として、EAの時間足を、動作させるチャートの時間足に合わせて設定します。過去のローソク足データ(始値、終値、高値、安値、時間)を記録するための配列を準備します。また、最新バーのタイムスタンプ、利用可能なバーの総数、解析対象とするバーの数を追跡するための変数も用意します。後ほど、価格変動の詳細な分析をおこなうために、市場データがこれらの配列に格納されます。
プログラムは、まず最新バーの時刻を取得し、市場更新ごとに利用可能なバーの数を確認します。解析が常に市場の状態に基づくように、最新の価格および時間データのセットを取得します。これにより、さらなる計算や、価格データにおけるテクニカルなトレンドの検出のための確かな基礎が得られます。
各バーを隣接するバーと比較することで、カスタム関数が重要な高値と安値を特定します。ある関数は明確な安値を検証し、別の関数は明確な高値を検証します。強気および弱気のガートリーのセットアップは、コンピュータが新しいバーが形成されるたびにこれらの点だけを監視することで、常に検出できるハーモニックパターンの例です。
Xの識別
弱気のガートリーパターンを識別する最初のステップは、Xを見つけることです。Xは、このセットアップが弱気であるためにはチャート上でスイングハイである必要があります。つまり、価格が上昇した後に下落するピークとして明確に確認できる点である必要があります。
例:
double X; datetime X_time; string X_letter; int z = 4; long chart_id = ChartID();
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { total_symbol_bars = Bars(_Symbol, timeframe); time_bar = iTime(_Symbol, timeframe, 0); CopyOpen(_Symbol, timeframe, time_bar, bars_check, open); CopyClose(_Symbol, timeframe, time_bar, bars_check, close); CopyLow(_Symbol, timeframe, time_bar, bars_check, low); CopyHigh(_Symbol, timeframe, time_bar, bars_check, high); CopyTime(_Symbol, timeframe, time_bar, bars_check, time); if(total_symbol_bars >= bars_check) { for(int i = z ; i < bars_check - z; i++) { if(IsSwingHigh(high, i, z)) { X = high[i]; X_time = time[i]; X_letter = StringFormat("X %d",i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); } } } }
説明
解析に十分なローソク足があることを確認するため、プログラムはまず安全チェックをおこないます。EAは少なくとも500本のバーがチャート上に存在することを確認してから処理を進めます。この数が必要であるためです。 このように事前に確認することでエラーを防ぎ、EAが常に包括的な市場データを使用できるようにします。このチェックの後、ソフトウェアはループとルックバック期間(z)を用いてローソク足を順に確認し、各ローソク足をその前後のローソク足と比較します。たとえば、zを4に設定すると、各ローソク足は前後4本のローソク足と比較され、範囲外エラーを防ぎつつスイングポイントを正確に判定できます。
このループ内で、弱気ガートリーパターンの最初の基点であるXを、スイングハイを探すことで特定します。有効なスイングハイが見つかった場合、その価格、時間、インデックスを保存し、チャート上に「X」と記したテキストラベルを表示します。この視覚的マーカーは、重要なスイングポイントを強調するだけでなく、残りのガートリーパターンを決定する際の明確な参照にもなります。
Aの識別
次のステップは、Xが有効なスイングハイとしてマークされた後に、Aを探すことです。弱気のハーモニックパターンにおいて、Aは通常Xの後に形成されるスイングローです。EAはXが検出されたバーから順に進み、Aを特定します。Xで使用したIsSwingHigh関数と同様に、IsSwingLow関数を使って範囲内の各バーを確認し、スイングローを探します。
例:
double A; datetime A_time; string A_letter; string xa_line;
if(total_symbol_bars >= bars_check) { for(int i = z ; i < bars_check - z; i++) { if(IsSwingHigh(high, i, z)) { X = high[i]; X_time = time[i]; X_letter = StringFormat("X %d",i); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); for(int j = i; j < bars_check - z; j++) { if(IsSwingLow(low, j, z) && low[j] < X) { A = low[j]; A_time = time[j]; A_letter = StringFormat("A %d",j); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,X_time,X,A_time,A); break; } } } } }
出力

説明
EAは、Xが有効なスイングハイであることを確認した後、次の重要な点であるAを探し始めます。EAはXの位置からスタートし、その後に続く各バーを確認して、スイングローとして適切な低値を特定します。弱気のハーモニックパターンに適合する構造であることを保証するため、プログラムは新しいローが以前のハイよりも低いかどうかをこの探索中に確認します。条件を満たすバーが見つかると、ソフトウェアはこの重要なレベルをチャート上にラベルで表示し、価格と時間をAとして記録します。
EAは、選択されたAに「A」と書かれたテキストラベルをObjectCreateを使って描画し、チャート上で明確に識別できるようにします。次に、OBJ_TRENDオブジェクトを使用してXからAまでのトレンドラインを作成します。このラインは最初のスイングハイと次のスイングローを結び、ハーモニックパターンのXAを視覚的に表します。最初の有効なスイングローが見つかった時点で「break;」命令を使用して探索を終了することで、コンピュータがXの後で最も近く、かつ関連性の高いAを確実に捉え、それ以上の探索をおこなわないようにします。
X(スイングハイ)を探す際、外側のループ(for(int i = z; i < bars_check - z; i++))は、範囲内のすべてのバーを順にチェックするために設けられています。ハーモニックパターンの可能な開始点を特定するためには、データ内のすべてのスイングハイを確認する必要があります。このループでbreakを入れると、最初のスイングハイを見つけた時点でループが停止するため、後続の有効なXが見逃され、チャート上の後のパターンが検出されなくなります。
一方で、Xを見つけた後の対応するA(Xの後のスイングロー)を探す場合、プログラムはXAを定義するために、Xの後に出現する最初の有効なスイングローだけを必要とします。breakによって最初の有効なAを見つけた後に探索を終了することで、XAが重複したり混乱したりすることを防ぎます。 これにより、各Aが最も近い有効なXとペアになり、XAの構造が安定し、重複検出を回避できます。
例:if(total_symbol_bars >= bars_check) { for(int i = z ; i < bars_check - z; i++) { if(IsSwingHigh(high, i, z)) { for(int j = i; j < bars_check - z; j++) { if(IsSwingLow(low, j, z) && low[j] < high[i]) { A = low[j]; A_time = time[j]; A_letter = StringFormat("A %d",j); for(int a = j; a >= i; a--) { if(IsSwingHigh(high, a, z) && high[a] > A) { X = high[a]; X_time = time[a]; X_letter = StringFormat("X %d",a); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,X_time,X,A_time,A); break; } } break; } } } } } } } }
出力

説明
スイングローを確認するために最初に条件「low[j] < high[i]」を使用する際、high[i]はあくまで一時的な参照として利用しています。どのスイングハイを正式にXとして選択するかはまだ決まっていません。high[i]を使うことで、少なくともその前に存在するスイングハイよりも低いA(スイングロー)が確認できるため、XAを開始する構造としては問題ありません。言い換えると、high[i]はAに適合しないローを除外するための仮のチェックとして機能しています。
ただし、そのスイングハイはAの直前にある最も強い、あるいは最も近い候補とは限らないため、有効なAが決まった段階で単純にhigh[i]をXとして選ぶことはできません。そこで、逆方向ループ「for(int a = j; a >= i; a--)」を導入します。このループはAの位置から最初の高値に向かって逆方向に進みます。Aに最も近く、かつAより高いスイングハイを特定するため、Xとして最も妥当な選択肢となります。これにより、任意の過去の高値に依存せず、XAが正確かつ適切に形成されます。
また、Xの有効性を確認するまでは、チャート上にオブジェクトを描画しないようにします。具体的には、Xのマーク、Aのマーク、XAラインの描画は逆方向ループ内でおこないます。オブジェクト作成をループ外に置くと、誤ったスイングハイでXやAを早期に描画してしまう可能性があるためです。このようにすることで、有効で最も近いスイングハイが確認された後にのみ、チャートにXAが表示されることが保証されます。
BCの識別
XAが完成した後は、次にBCを特定します。弱気ガートリーパターンにおけるこの部分は、BとCの2点で構成されます。CはBに続くスイングローとして形成され、BはAに続くスイングハイとして出現します。この2つの位置を確定することで、ハーモニックパターンの第2部を描き始めます。
現時点では、フィボナッチのリトレースメントやエクステンションの条件はまだ導入していません。焦点はあくまで、価格の自然なスイングポイントを特定することに置かれています。
例:double B; datetime B_time; string B_letter; double C; datetime C_time; string C_letter; string ab_line; string bc_line;
for(int k = j; k < bars_check - z; k++) { if(IsSwingHigh(high, k, z) && high[k] > A) { B = high[k]; B_time = time[k]; B_letter = StringFormat("B %d",k); for(int l = k; l < bars_check - z; l++) { if(IsSwingLow(low, l, z) && low[l] < B) { C = low[l]; C_time = time[l]; C_letter = StringFormat("C %d",l); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ab_line = StringFormat("AB Line %d",i); ObjectCreate(chart_id,ab_line,OBJ_TREND,0,A_time,A,B_time,B); bc_line = StringFormat("BC Line %d", i); ObjectCreate(chart_id,bc_line,OBJ_TREND,0,B_time,B,C_time,C); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,X_time,X,A_time,A); i = l+1; break; } } break; } }
出力

説明
Aからスタートし、最初のループ「for(int k = j; k < bars_check - z; k++)」でBを探します。このループ内で、ソフトウェアはIsSwingHigh関数を使ってスイングハイを検出します。また、「high[k] > A」であることも確認し、Bが構造の一部として適切なスイングハイであることを保証します。この条件が満たされた場合、Bの価格、時間、ラベルを記録します。
次に、Bの後に形成されるスイングローであるCを特定するため、2つ目のループ「for(int l = k; l < bars_check - z; l++)」を使用します。条件「low[l] < B」によって、CがスイングハイBより低いことが保証され、弱気のガートリーパターンの正しい下向き構造が維持されます。Cが有効であると判断された場合、価格、時間、ラベルの情報を保存します。BとCを識別した後は、チャート上でXとAを再描画し、パターンの明確性を維持します。
外側のループを「i = l+1;」で進めると、同じ位置を繰り返し検出することを避けられます。しかし、Cを識別した後に「i = l+1;」を使うと、多くの有効なガートリーシグナルを見逃す可能性があります。スイングポイントの可能性を飛ばしてしまうためです。この戦略は避け、ループは通常通り機能させるか、フィルタリング技術を使ってシグナルを洗練し、チャンスを保持する方が望ましいです。
Dの識別
X、A、B、Cが正しく特定された後、次のステップはDを見つけることです。Dはエントリーポイントの可能性があるため、最後のレッグであり非常に重要です。Dを識別するために、Cの後に形成されるスイングハイを探します。このスイングハイは、CDを完成させるために、できるだけBCとは反対方向に動くことが望ましいです。
例:
double D; datetime D_time; string D_letter; string cd_line;
for(int m = l; m < bars_check - z; m++) { if(IsSwingHigh(high, m, z) && high[m] > B) { D = high[m]; D_time = time[m]; D_letter = StringFormat("D %d",m); ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); cd_line = StringFormat("CD Line %d",i); ObjectCreate(chart_id,cd_line,OBJ_TREND,0,C_time,C,D_time,D); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,B_time,B); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,C_time,C); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ab_line = StringFormat("AB Line %d",i); ObjectCreate(chart_id,ab_line,OBJ_TREND,0,A_time,A,B_time,B); bc_line = StringFormat("BC Line %d", i); ObjectCreate(chart_id,bc_line,OBJ_TREND,0,B_time,B,C_time,C); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,X_time,X); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,A_time,A); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,X_time,X,A_time,A); i = m+1; break; } }
出力

説明
ループはC(l)のインデックスから前方に向かってチャートを反復処理し、Dの可能性があるスイングハイを探します。条件「IsSwingHigh(high, m, z) && high[m] > B」によって、選択されたDがBよりも高く、単なるスイングハイではないことが保証されます。弱気のガートリーパターンでは、DがBより高くなる必要があり、構造を正しく保つためにこのルールは非常に重要です。
有効なスイングハイが見つかると、コードはCと接続するラインCDを作成し、チャート上に「D」とラベルを付け、Dの価格と時間をD変数に記録します。同時に、他の重要な点に対応するラインも再描画されます。
スイングが正確に特定された後は、各点が各レッグ内で支配的なスイングを正しく示しているかを確認する必要があります。単に高値や安値にラベルを付けるだけでは不十分で、わずかな調整でもパターン全体が混乱する可能性があります。騙しを防ぐために、各スイングポイントの正当性を検証する厳格なルールを実装する必要があります。
まずCとDの間ではCが最低値であることが重要で、これによりCDは一時的な下落ではなく、実際のスイングローから開始されることが保証されます。BとCの間ではBが最高値である必要があり、BCが支配的なピークからリトレースされることを確認できます。
同様に、AとBの間ではAが最低値である必要があり、ABがランダムな変動ではなく適切なスイングローから始まることを保証します。最後に、XとAの間ではXが最高値であることが求められ、このチェックによってXAはそのエリア内で最も強いスイングハイから開始されます。
例:
int x_a_bars; int x_highest_index; double x_a_hh; datetime x_a_hh_t; int a_b_bars; int a_lowest_index; double a_b_ll; datetime a_b_ll_t; int b_c_bars; int b_highest_index; double b_c_hh; datetime b_c_hh_t; int c_d_bars; int c_lowest_index; double c_d_ll; datetime c_d_ll_t;
for(int m = l; m < bars_check - z; m++) { if(IsSwingHigh(high, m, z) && high[m] > B) { D = high[m]; D_time = time[m]; D_letter = StringFormat("D %d",m); c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,D_time); c_lowest_index = ArrayMinimum(low,l,c_d_bars); c_d_ll = low[c_lowest_index]; c_d_ll_t = time[c_lowest_index]; b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_ll_t); b_highest_index = ArrayMaximum(high, k, b_c_bars); b_c_hh = high[b_highest_index]; b_c_hh_t = time[b_highest_index]; a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_hh_t); a_lowest_index = ArrayMinimum(low,j,a_b_bars); a_b_ll = low[a_lowest_index]; a_b_ll_t = time[a_lowest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_ll_t); x_highest_index = ArrayMaximum(high, a, x_a_bars); x_a_hh = high[x_highest_index]; x_a_hh_t = time[x_highest_index]; ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); cd_line = StringFormat("CD Line %d",i); ObjectCreate(chart_id,cd_line,OBJ_TREND,0,c_d_ll_t,c_d_ll,D_time,D); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_hh_t,b_c_hh); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_ll_t,c_d_ll); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ab_line = StringFormat("AB Line %d",i); ObjectCreate(chart_id,ab_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); bc_line = StringFormat("BC Line %d", i); ObjectCreate(chart_id,bc_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_hh_t,x_a_hh); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_ll_t,a_b_ll); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); i = m+1; break; } }
説明
このコードのセクションの主な目的は、特定されたスイングポイントが正確であることを確認し、各レッグにおける最も重要な高値と安値からガートリーパターンの構造が導かれていることを保証することです。処理は、Cの後にBより高い新しいスイングハイが見つかった時点で開始されます。このスイングハイをDに割り当てますが、パターンを確定する前にまずそれ以前の点すべてを検証します
検証はまずC~Dの範囲から始まります。この部分のコードは、CがC~D間の実際の底であり、一時的な下落ではないことを確認するために、最も低い安値を探します。次にB~Cの範囲を調べて最高値を特定します。これにより、パターンを歪める可能性のある小さな突起ではなく、Bがその区間内で最も強いピークであることが保証されます。
さらに、A~Bの範囲を確認することで、Aがそのレッグ内で最も顕著なスイングローであることを検証します。最後にX~Aの範囲を確認して、XAの開始点としてXが最も強いスイングハイであることを確認します。これらのチェックを実行することで、X、A、B、Cの各点がハーモニックパターンを構築するために適切な構造的スイングであることが保証されます。
フィボナッチリトレースメント
スイングポイントの識別と検証が終わった後は、その構造が本当にガートリーパターンの条件を満たしているかどうかを判定します。すべてのスイングが正しいハーモニックパターンを作るわけではないため、単にチャート上にX、A、B、C、Dの点が示されているだけでは不十分です。各レッグに対してフィボナッチのリトレースメントおよびエクステンションの基準を適用し、パターンとして成立するかを確認する必要があります。
価格変動がガートリーパターンに必要な比率に合致しているかを確認する「フィルター」が、このフィボナッチ測定です。例えば、Dの拡張が他のスイングに対して特定の範囲に収まっていること、BのXAに対するリトレースメント、CのABに対するリトレースメントなど、特定のフィボナッチ比率が満たされる必要があります。これらの条件がすべて満たされると、その構造は正当なガートリーパターンとして分類されます。
例:
input double b_xa_max = 78.6; // MAX B RETRACEMENT LEVEL FOR XA input double b_xa_min = 61.8; // MIN B RETRACEMENT LEVEL FOR XA input double c_ab_max = 88.6; // MAX C RETRACEMENT LEVEL FOR AB input double c_ab_min = 38.2; // MIN C RETRACEMENT LEVEL FOR AB input double d_xa_max = 76.0; // MAX D RETRACEMENT LEVEL FOR XA input double d_xa_min = 80.0; // MIN D RETRACEMENT LEVEL FOR XA double lvl_max_b; double lvl_min_b; double lvl_max_c; double lvl_min_c; double lvl_max_d; double lvl_min_d;
if(IsSwingHigh(high, m, z) && high[m] > B) { D = high[m]; D_time = time[m]; D_letter = StringFormat("D %d",m); c_d_bars = Bars(_Symbol,PERIOD_CURRENT,C_time,D_time); c_lowest_index = ArrayMinimum(low,l,c_d_bars); c_d_ll = low[c_lowest_index]; c_d_ll_t = time[c_lowest_index]; b_c_bars = Bars(_Symbol, PERIOD_CURRENT, B_time, c_d_ll_t); b_highest_index = ArrayMaximum(high, k, b_c_bars); b_c_hh = high[b_highest_index]; b_c_hh_t = time[b_highest_index]; a_b_bars = Bars(_Symbol,PERIOD_CURRENT,A_time,b_c_hh_t); a_lowest_index = ArrayMinimum(low,j,a_b_bars); a_b_ll = low[a_lowest_index]; a_b_ll_t = time[a_lowest_index]; x_a_bars = Bars(_Symbol, PERIOD_CURRENT, X_time, a_b_ll_t); x_highest_index = ArrayMaximum(high, a, x_a_bars); x_a_hh = high[x_highest_index]; x_a_hh_t = time[x_highest_index]; lvl_min_b = a_b_ll + ((b_xa_min / 100) * (x_a_hh - a_b_ll)); lvl_max_b = a_b_ll + ((b_xa_max / 100) * (x_a_hh - a_b_ll)); lvl_min_c = b_c_hh - ((c_ab_min / 100) * (b_c_hh - a_b_ll)); lvl_max_c = b_c_hh - ((c_ab_max / 100) * (b_c_hh - a_b_ll)); lvl_min_d = a_b_ll + ((d_xa_min / 100) * (x_a_hh - a_b_ll)); lvl_max_d = a_b_ll + ((d_xa_max / 100) * (x_a_hh - a_b_ll)); if(b_c_hh >= lvl_min_b && b_c_hh <= lvl_max_b && c_d_ll <= lvl_min_c && c_d_ll >= lvl_max_c && D >= lvl_min_d && D <= lvl_max_d) { ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); cd_line = StringFormat("CD Line %d",i); ObjectCreate(chart_id,cd_line,OBJ_TREND,0,c_d_ll_t,c_d_ll,D_time,D); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_hh_t,b_c_hh); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_ll_t,c_d_ll); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ab_line = StringFormat("AB Line %d",i); ObjectCreate(chart_id,ab_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); bc_line = StringFormat("BC Line %d", i); ObjectCreate(chart_id,bc_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_hh_t,x_a_hh); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_ll_t,a_b_ll); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); } // i = m+1; break; } }
説明
まず、入力パラメータから始めます。X、A、B、C、Dの構造が有効なガートリーパターンであるかを判定するために、EAはユーザーが設定したフィボナッチリトレースメントレベルを使用します。たとえば、入力に応じて、BはXAの指定リトレースメントの範囲内、CはABの指定リトレースメント上、DはXAの指定リトレースメント上に位置する必要があります。
各スイングポイントが形成される実際の価格レベルは、計算された変数によって表されます。これは、入力で設定されたフィボナッチの原則に基づき、XAおよびABの高値と安値から計算されます。たとえば、XAのリトレースメントはBの合法的な範囲を決定し、同じ方法でABおよびXAに対するCとDの範囲も算出されます。
最後の条件チェックで、パターンの各点が必要なフィボナッチ範囲内にあるか確認します。これにより、B、C、Dの各点がリトレースメント条件を満たしていることが保証されます。すべての条件が満たされると、プログラムはチャート上にオブジェクトを生成し、認識されたガートリーパターンを視覚的に表現します。具体的には、X、A、B、C、Dの各点を示し、それらを結ぶラインを描画します。
取引の執行
有効なガートリーパターンを正しく認識した後、取引の実行に移ります。ここまでEAは、価格データの解析、スイングハイとスイングローの特定、フィボナッチの整合性確認に注力してきました。しかし、パターンを特定するだけでは、トレーダーに取引の機会は与えられても利益は生じません。EAがこの情報を使って市場で実際に取引をおこなうことで、初めて実動が始まります。
具体的には、EAはどこでエントリーするか、どこにテイクプロフィット(TP)を置くか、どこにストップロス(SL)を設定するかを決定します。例えば、弱気のガートリーパターンでは、EAは通常Dで売り注文を開始します。パターンの無効化を防ぐため、SLはDで形成されたスイングハイの少し上に設定されます。TPは、フィボナッチエクステンション目標やリスクリワードレシオ(たとえば1:3)を用いて決定できます。このプロセスを自動化することで、EAは躊躇や感情に左右されることなく、一貫したパフォーマンスを保証します。
例:
#include <Trade/Trade.mqh> CTrade trade; input int MagicNumber = 61626; input double lot_size = 0.1; double ask_price; datetime time_price[]; double take_p; string XAB; string BCD; datetime lastTradeBarTime = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); ArraySetAsSeries(time_price,true); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- ObjectsDeleteAll(chart_id); }
CopyTime(_Symbol, timeframe, 0, 2, time_price); ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); datetime currentBarTime = iTime(_Symbol,timeframe,0); if(b_c_hh >= lvl_min_b && b_c_hh <= lvl_max_b && c_d_ll <= lvl_min_c && c_d_ll >= lvl_max_c && D >= lvl_min_d && D <= lvl_max_d) { ObjectCreate(chart_id,D_letter,OBJ_TEXT,0,D_time,D); ObjectSetString(chart_id,D_letter,OBJPROP_TEXT,"D"); cd_line = StringFormat("CD Line %d",i); ObjectCreate(chart_id,cd_line,OBJ_TREND,0,c_d_ll_t,c_d_ll,D_time,D); ObjectCreate(chart_id,B_letter,OBJ_TEXT,0,b_c_hh_t,b_c_hh); ObjectSetString(chart_id,B_letter,OBJPROP_TEXT,"B"); ObjectCreate(chart_id,C_letter,OBJ_TEXT,0,c_d_ll_t,c_d_ll); ObjectSetString(chart_id,C_letter,OBJPROP_TEXT,"C"); ab_line = StringFormat("AB Line %d",i); ObjectCreate(chart_id,ab_line,OBJ_TREND,0,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); bc_line = StringFormat("BC Line %d", i); ObjectCreate(chart_id,bc_line,OBJ_TREND,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll); ObjectCreate(chart_id,X_letter,OBJ_TEXT,0,x_a_hh_t,x_a_hh); ObjectSetString(chart_id,X_letter,OBJPROP_TEXT,"X"); ObjectCreate(chart_id,A_letter,OBJ_TEXT,0,a_b_ll_t,a_b_ll); ObjectSetString(chart_id,A_letter,OBJPROP_TEXT,"A"); xa_line = StringFormat("XA Line %d",i); ObjectCreate(chart_id,xa_line,OBJ_TREND,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll); XAB = StringFormat("XAB TRIANGLE %d", i); BCD = StringFormat("BCD TRIANGLE %d", i); ObjectCreate(chart_id,XAB,OBJ_TRIANGLE,0,x_a_hh_t,x_a_hh,a_b_ll_t,a_b_ll,b_c_hh_t,b_c_hh); ObjectSetInteger(chart_id,XAB,OBJPROP_FILL,true); ObjectSetInteger(chart_id,XAB,OBJPROP_COLOR,clrPink); ObjectSetInteger(chart_id,XAB,OBJPROP_BACK,true); ObjectCreate(chart_id,BCD,OBJ_TRIANGLE,0,b_c_hh_t,b_c_hh,c_d_ll_t,c_d_ll,D_time,D); ObjectSetInteger(chart_id,BCD,OBJPROP_FILL,true); ObjectSetInteger(chart_id,BCD,OBJPROP_COLOR,clrPink); ObjectSetInteger(chart_id,BCD,OBJPROP_BACK,true); if(time[m+z] == time_price[1] && currentBarTime != lastTradeBarTime) { take_p = ask_price - (MathAbs(D - ask_price) * 3); trade.Sell(lot_size,_Symbol,ask_price,D,take_p); lastTradeBarTime = currentBarTime; } }
出力

説明
このプログラムのセクションでは、チャート上でのガートリーパターンの視覚表示と取引ロジックの管理をおこないます。まず、標準のMQL5 Tradeライブラリを「#include <Trade/Trade.mqh>」を用いてインクルードします。このライブラリからCTradeオブジェクト「trade」を作成し、エントリーやエグジットなど、すべての取引関連のタスクを管理します。さらに、入力パラメータとしてMagicNumberを宣言し、このEAによる取引を他の取引と区別するために使用します(MagicNumber; SetExpertMagicNumber;)。
取引数量はlot_sizeなどの重要なパラメータで決定されます。市場価格、時間データ、計算されたTPレベルを格納するために、ask_price、time_price[]、take_pなどの補助変数を宣言します。また、新しい取引が同じバーで複数回開かれないようにするため、lastTradeBarTimeという変数を使用しています。
最新のデータをインデックス0に保持するため、「ArraySetAsSeries(time_price, true);」を適用し、価格データが適切に処理されるようにします。その後、「CopyTime(_Symbol, timeframe, 0, 2, time_price)」で最後の2本のバーの開始時刻をtime_price配列にコピーします。「ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);」で現在のAsk価格を取得し、「currentBarTime = iTime(_Symbol, timeframe, 0);」で最新バーのタイムスタンプを格納します。
これらの情報をもとに、EAは取引条件が満たされた正確なタイミングを特定できます。バーの時刻を比較する条件チェックにより、プログラムは各バーで一度だけ反応することが保証されます。有効な場合、tradeを使って売り注文を実行し、TPは「take_p = ask_price - (MathAbs(D - ask_price) * 3);」によって決定されます。「Sell(lot_size, D, take_p, ask_price, _Symbol);」で取引が実行され、同じバーで再度取引が発生しないようにlastTradeBarTimeが更新されます。
プログラムは取引を実行するだけでなく、チャート上にガートリーパターンを表示します。たとえば、「XAB = StringFormat("XAB TRIANGLE %d", i);」および「BCD = StringFormat("BCD TRIANGLE %d", i);」で、グラフィカルオブジェクトの名前を一意に作成します。次にObjectCreateを使用して2つの三角形を描画します。パターンのXAB部分は最初の三角形、BCD部分は2つ目の三角形で表され、ソフトウェアで特定されたスイングポイントに基づき座標が設定されます。
三角形のプロパティはObjectSetIntegerで設定され、塗りつぶされ、ピンクに着色され、背景に送られてパターンが目立つようにします。これにより、プログラムは認識したガートリーパターンに反応するだけでなく、トレーダーが視覚的に確認できるようになります。
結論
本記事では、MQL5でガートリーパターンを構築し、取引する方法を解説しました。まずスイングポイント(X、A、B、C、D)を特定し、フィボナッチのリトレースメントおよびエクステンションを用いてパターン構造を確認しました。その後、パターンが有効である場合に自動で取引を実行し、さらにチャートオブジェクトを追加して、パターンの形成を視覚的に確認できるようにしました。このプロジェクトは、複雑なハーモニックパターンをコーディングし、自動で取引する手法を示しています。この基礎をもとに、ルールをさらに精緻化したり、確認条件を追加したり、他のハーモニックパターンへのロジックを拡張したりすることも可能です。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19331
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5でのAI搭載取引システムの構築(第1回):AI API向けJSON処理の実装
Parafrac V2オシレーター:パラボリックSARとATRの統合
初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(XI) - ニュース取引における相関
カスタム口座パフォーマンス行列インジケーターの開発
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索