
MQL5入門(第15回):初心者のためのカスタムインジケーター作成ガイド(IV)
はじめに
「MQL5入門」の連載へようこそ。 この記事は、これまでの回で学んできたアイデアや技術を土台にして構成されており、まったく新しいスタートというよりも、自然な続きとして感じられるでしょう。ここまででMQL5の基礎をしっかりと理解できているはずです。今回はその知識をさらに発展させ、より実践的で興味深いカスタムインジケーターを開発していきます。
MQL5のスキルは、実際に取り組んだプロジェクトの質によって決まります。そのため、本連載では常にプロジェクトベースのアプローチを採用しています。それは自ら学び、成長するための最も効果的な方法です。今回は、トレンドを認識し、構造のブレイクを検出し、売買シグナルを生成するインジケーターを作成します。シグナルにはエントリーポイント、ストップロス、複数のテイクプロフィット水準も含まれており、テストと発展が可能な包括的な戦略を構築できます。この記事では、プライスアクションの概念を用いて、MQL5でカスタムインジケーターを設計する方法を学びます。トレンドフォロー戦略を作成するために、高値更新(HH)、安値切り上げ(HL)、高値切り下げ(LH)、安値更新(LL)といった、重要な市場構造をどのように認識するかを理解していきます。
本記事では、以下の内容について学びます。
- プライスアクションインジケーターの作成方法
- 安値(L)、高値(H)、安値切り上げ(HL)、高値更新(HH)、安値更新(LL)、高値切り下げ(LH)といった重要ポイントを認識し、強気/弱気トレンドの構造を理解する
- トレンドの重要ポイントに基づいてプレミアムゾーンとディスカウントゾーンを描画し、50%リトレースメントラインを表示する
- 強気トレンドのセットアップにおいて、リスクリワード比を使って潜在的な利益目標を計算する方法
- トレンド構造に基づいて、エントリーポイント、ストップロス(SL)、複数のテイクプロフィット(TP)レベルを計算・表示する方法
1. プロジェクトの準備
1.1. インジケーターの仕組み
このインジケーターは、安値(L)、高値(H)、安値切り上げ(HL)、高値更新(HH)を識別して上昇トレンドを示し、買いシグナルを生成します。HLとHHの間の50%リトレースメントレベルを特定し、HHを上抜ける構造がブレイクされた際にエントリーします。50%リトレースメントはストップロスとして使用され、テイクプロフィット1はリスクリワード比1:1、テイクプロフィット2は1:2を目標とします。
売りシグナルとして下降トレンドを識別するために、インジケーターはまずH、L、LH、LLを特定します。次に、LHとLLの間の50%リトレースメントを計算します。エントリーはLLを下抜けたブレイクでおこない、ストップロスは50%の位置に設定します。テイクプロフィット1はリスクリワード比1:1、テイクプロフィット2は1:2を目標とします。
2. プライスアクションインジケーターの構築
すべての取引戦略はインジケーターに変換できます。まだ可視化されていないだけです。需要と供給、価格変動、サポートとレジスタンスなど、一定のルールに従うものであれば何でもコードにしてチャート上に表示することが可能です。ここで登場するのがMQL5です。アルゴリズムトレーダーにとって、MQL5は最も優れた、かつ扱いやすいプログラミング言語のひとつであり、どんな取引ロジックも有用かつ視覚的にわかりやすいツールに変換することを可能にします。この章では、価格の動きを分析するインジケーターの開発を開始します。高値(H)、安値(L)、高値更新(HH)、安値更新(LL)といった市場構造を認識し、それに基づいてエントリー、ストップロス、テイクプロフィットを含む有益な売買シグナルを生成します。
第1章では、このプロジェクトの目的と、インジケーターがトレンドをどのように検出し、構造のブレイクを認識し、取引シグナル(エントリー、SL、TP)を生成するかを概説しました。ここからは、MQL5でそのロジックを実際にコードへと落とし込んでいく段階です。前章で説明したロジックを元に、一歩ずつ実装を進めていきます。
2.1.高値と安値の識別
プライスアクションインジケーターを作成するうえで最初のステップは、スイングハイとスイングローを見つけることです。これらの重要な転換点は、トレンド構造を識別する助けになります。MQL5では、現在のローソク足の高値または安値を、直前と直後のローソク足と比較することでそれを特定できます。この手法を使えば、HH、HL、LL、LHといった構造上のパターンを識別できるようになります。これらの構造変化は、トレンドの継続や転換を判断するための基礎になります。
例//+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOWS | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGHS | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; }
これら2つの関数は前回の記事で使用されており、スイングハイとスイングローの両方を識別するのに役立ちます。
2.2. 強気トレンド
市場構造を用いて、このインジケーターはまず上昇トレンドが存在するかを確認し、その後に買いシグナルを示します。これを達成するために識別すべき主な価格ポイントは、L、H、HL、HHです。このパターンは強気トレンドを示しており、買い手が優勢であり、市場が上昇を続ける可能性が高いことを意味します。このパターンの確認後、インジケーターは有効な買いシグナルを発する準備を整えます。
例
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
出力
説明
コードの冒頭で設定された入力パラメータは、市場構造を解析するために使われます。スイングハイやスイングローを特定し、現在の市場における妥当性を保証するために、LookbackBarsは現在のバーの前後で評価するバーの数を指定します。一方で、bars_checkは解析対象となるバーの総数を制御し、最大1000本のバーを対象に強気パターンの探索を可能にします。
bars_checkの値が大きいほどより多くのデータを考慮するため、より多くの計算リソースを要しますが、広範囲のデータを用いて場所を検出できます。bool型の入力パラメータ「show_bullish」は、買いシグナル(強気シグナル)を表示するかどうかを制御します。show_bullishがtrueに設定されている場合のみ、価格の動きを解析し、スイングポイントに基づく強気の市場構造を判定します。falseの場合は、条件を満たしても強気構造はチャートに表示されません。
インジケーターのロジックで最初におこなわれる処理は、「show_bullish == true」のチェックです。これにより、買いシグナルの表示を希望した場合のみ強気構造の判定がおこなわれます。続いて、十分な価格データがあるかを確認します。条件式「if (rates_total >= bars_check)」により、必要なバー数が揃っているかが検証され、不足している場合は解析をスキップしてエラーを防ぎます。条件を満たした場合、価格データをループで走査しスイングポイントを探します。
外側のループ「i」は最新のバーから過去に向かって価格データを調べ、有効なスイングローを検出します。IsSwingLow関数は現在のバーがLookbackBarsの範囲内で最も安いかを判定し、スイングローを特定します。スイングローが見つかった場合、その価格と時刻が変数LとL_timeに記録され、強気パターン探索の次の段階の基礎となります。 スイング安値の後、2つ目の「j」ループでスイングハイを探します。IsSwingHigh関数を用い、スイングローの後に続くスイングハイが見つかった場合、その値と時刻がHとH_timeに保存されます。これで強気市場構造の最初の部分、安値と高値が揃います。
次に、3つ目の「k」ループが安値切り上げ(HL)を探索します。再びIsSwingLow関数を使い、最初の安値Lよりも高い安値が検出されると、その価格と時刻がHLとHL_timeに更新されます。このHLの発見後、4つ目の「l」ループでさらに高値更新(HH)を探します。これが見つかると、その値と時刻はHHとHH_timeに記録されます。4つの重要なポイント(L、H、HL、HH)が揃った後、次の条件で正当な強気パターンかを判定します。最初の安値は最初の高値より低く、それより高い安値は最初の高値より低く、それより高い安値は最初の安値より高く、それより高い高値は最初の高値より高くなければなりません。つまり「L < HかつHL < HかつHL > LかつHH > H」です。これにより、順番通りに高値更新・安値切り上げのパターンが成立していることを確認し、強気トレンドであることが保証されます。
条件を満たすと、プログラムはチャート上にテキストオブジェクトを作成して、特定した各ポイントを目立つように表示します。L、H、HL、HHの各ポイントが対応する時間と価格にラベル付けされます。ObjectCreate関数でテキストオブジェクトを生成し、ObjectSetInteger関数とObjectSetString関数でフォントサイズや色などの設定をおこないます。これにより、ユーザーはチャート上で強気の市場構造を視覚的に容易に認識できます。まとめると、このプログラムの目的は、価格データから高値更新・安値切り上げのパターンを見つけ出し、強気市場構造かどうかを判定することにあります。これは、あらかじめ定められたバーの範囲内でスイングポイントを検出し、関連する情報(価格と時間)を記録し、その構造が期待されるパターンになっているかどうかを確認することで実現されます。そしてそのパターンが正当であると判断された場合、チャート上に視覚的に表示されるようになっています。すべての処理は、ユーザーが調整可能な入力パラメータによって制御されています。
2.2.1. HLからHHへのプレミアムおよびディスカウントレベルのマッピング
この分割により、「プレミアムゾーン」と「ディスカウントゾーン」という用語が定義されます。50%ラインより下は「ディスカウントゾーン」となり、購入に適した割安な価格帯と見なされます。一方、50%より上は「プレミアムゾーン」となり、相対的に「割高」な価格帯を意味します。多くの取引手法では、リスクとリワードの最適化のために、通常ディスカウントゾーンでの買いが選ばれます。しかし、本インジケーターにおいては、やや異なる戦略が採用されます。
この場合、私たちが関心を持つのは、市場がプレミアムゾーンを上回って取引するか、高値更新の構造をブレイクしたときのみです。このパターンは、強気の市場構造が依然として有効であり、価格がさらに上昇する可能性が高いことを示しています。トレンドに沿った取引を維持し、押し目や反転局面での誤ったエントリーを避けるためには、高値更新またはプレミアムゾーンの上抜けを待つことが推奨されます。
例
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") // Variables for Premium and Discount string pre_dis_box; // Name/ID for the premium-discount zone box (rectangle object on chart) double lvl_50; // The price level representing the 50% retracement between Higher Low and Higher High string lvl_50_line; // Name/ID for the horizontal line marking the 50% level //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
出力
説明
このコードは、50%リトレースメントレベル(安値引き上げ(HL)と高値更新(HH)の中間点)を算出します。この50%ラインを基準に、プレミアムゾーン(上側)とディスカウントゾーン(下側)が分かれます。HLとHHの中間点は、式「lvl_50 = HL + ((HH - HL)/2);」を使用して決定されます。次に、ループインデックス「i」が、文字列変数pre_dis_boxとlvl_50_lineの名前に含まれます。これらの文字列は、チャート上に描画する視覚要素の固有の識別子として機能します。インデックス「i」を名前に含めることで、各描画が一意になり、ループ内で以前の描画が置き換えられないようになります。
次の行で、「ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH);」によって、チャート上に矩形が作成されます。これは、安値引き上げから高値更新までの移動を視覚的に示すもので、トレーダーが直近の強気スイングの範囲を一目で確認できるようにします。矩形の終点は、HHの価格を使い、将来のバー(l + LookbackBars)に固定されます。始点はHLの時間と価格に固定されています。これにより、ボックスが将来方向に延長され、チャート上に明確に表示されるようになります。
続いて、「ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50);」によって、チャート全体に50%レベルの水平線が描画されます。この水準は、インジケーターのロジックにおいて非常に重要で、市場がプレミアムゾーン上、つまり直近の上昇スイングの50%以上で取引されているかを判断する基準となります。ObjectSetInteger関数を使って、ボックスとラインの色をclrDarkGreenに設定し、太さ(幅)を2に指定することで、視認性が高まるようにしています。最後に、買いシグナルが有効と見なされるには、価格が HH(高値更新)を完全に上抜ける必要があります。つまり、プレミアムゾーン全体を超えてクローズしなければなりません。言い換えれば、明確に強気の市場構造が継続しており、前回のスイングハイ(HH)を超えている状況でのみ、買いを検討するということです。
2.2.2. エントリーポイント、ストップロス、テイクプロフィットの表示
HLとHH間のプレミアムおよびディスカウントゾーンが正しくマークされたら、次のステップは、買いシグナルの可能性を見つけることです。正当な買いエントリーの鍵は、強気のブレイクアウトバーがHHを上抜けるのを待つことです。これは、市場が依然として力強く上昇していることを示します。
しかし、HHを一時的に上回るだけでは不十分です。その強気のバーがHHより上でクローズすることによってのみ、ブレイクアウトが本物であることが確認されます。価格がHHの上で終値を付けたという事実は、買いの需要が継続しており、価格がさらに上昇する可能性が高いことを意味します。エントリーポイントは、HHを上抜けてクローズしたその強気バーの終値に設定されます。これにより、「市場は十分な強さを示した」と判断し、安心して取引に参加することができます。
ストップロス(SL)は、HLとHHの中間点である50%ライン(lvl_50)に設定されます。これは、市場が反転する可能性に対する保険として機能します。また、価格がディスカウントゾーン(50%より下)へ戻るような動きを見せた場合、市場の勢いが弱まった、あるいは心理が変化したサインと見なされるため、この位置にSLを置くことでリスクを制御しています。 テイクプロフィット(TP)レベルは、リスクリワード比(R:R)に基づいて設定されます。最初のテイクプロフィット(TP1)の利益目標は、エントリーポイントとストップロスとの距離と同じであり、1:1のリスクリワード比となります。2つ目のテイクプロフィット(TP2)は、エントリーからSLまでの距離の2倍の位置に設定され、1:2のリスクリワード比となります。 これらの2つのTP設定により、トレーダーはTP1で部分的に利益を確定しつつ、市場がさらに上昇する可能性がある場合にはポジションの一部を保有し続けるといった柔軟な対応が可能になります。
例
// Variables for Entry, Stop Loss, and Take Profit string entry_line; // Line object to represent the entry point on the chart string entry_txt; // Text object for displaying "BUY" at the entry point double lvl_SL; // Stop Loss level (set at the 50% retracement level) string lvl_sl_line; // Line object for representing the Stop Loss level string lvl_sl_txt; // Text object for labeling the Stop Loss level double TP1; // Take Profit 1 level (1:1 risk-reward ratio) double TP2; // Take Profit 2 level (1:2 risk-reward ratio) string lvl_tp_line; // Line object for representing the Take Profit 1 level string lvl_tp2_line; // Line object for representing the Take Profit 2 level string lvl_tp_txt; // Text object for labeling the Take Profit 1 level string lvl_tp2_txt; // Text object for labeling the Take Profit 2 level string buy_object; // Arrow object to indicate the Buy signal on the chart
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); for(int m = l; m < rates_total-1; m++) { if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { TP1 = close[m] + (close[m] - lvl_50); TP2 = TP1 + (close[m] - lvl_50); entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); ObjectCreate(chart_id,entry_line,OBJ_TREND,0,HL_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line,OBJ_TREND,0,HL_time,lvl_50,time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line,OBJ_TREND,0,HL_time,TP1,time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line,OBJ_TREND,0,HL_time,TP2,time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_COLOR,clrDarkGreen); entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id,buy_object,OBJ_ARROW_BUY,0,time[m],close[m]); break; } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }出力
上の画像では、プレミアムゾーンやディスカウントゾーン、そして買いシグナルのマーカーが正確に描画されていないことが確認できます。これは、チャート上にオブジェクトを描く前に、いくつかの条件が見落とされていたことが原因です。このアプローチを改善するには、買いシグナルが正当なものであることを確認するための追加の検証処理を導入する必要があります。まず、チャートにどのようなオブジェクトを描画する前にも、ローソク足が本当に高値更新(HH)をブレイクしているかどうかを確認する必要があります。HHのブレイクは、強気トレンドの継続を意味しており、買いシグナルが有効であると見なすための重要な条件です。この条件が満たされる前に、エントリーやリスク管理の計算を始めるべきではありません。
次に、HLからプレミアム/ディスカウントボックスの終点までのバーの本数をカウントする必要があります。この工程により、価格変動が許容範囲内に収まっているかを確認でき、市場がどれだけ動いたかを把握する助けとなります。このカウントが完了した後は、HHをブレイクした強気バーの終値が、プレミアム/ディスカウントボックスの近くにあることを確認する必要があります。これにより、買いシグナルが想定された市場構造からかけ離れておらず、妥当な価格帯の中で発生していることが保証されます。
例
// Declare variables to count bars int n_bars; // Number of bars from Higher Low to the end of the Premium/Discount box int n_bars_2; // Number of bars from the end of the Premium/Discount box to the bullish bar that broke HH
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Loop through the bars to check for the conditions for entry for(int m = l; m < rates_total-1; m++) { // Check if the current bar is a bullish bar and if the price has broken the higher high (HH) if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { // Count the bars between HL_time and the end of the Premium/Discount box n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]); // Count the bars between the end of the Premium/Discount box and the candle that broke HH n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H && open[l+LookbackBars] <= HH && n_bars_2 < n_bars) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); // Calculate Take Profit levels based on the 50% retracement TP1 = close[m] + (close[m] - lvl_50); // TP1 at 1:1 risk-reward ratio TP2 = TP1 + (close[m] - lvl_50); // TP2 at 1:2 risk-reward ratio // Create unique object names for Entry, Stop Loss, and Take Profit lines and text entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); // Create the lines on the chart for Entry, Stop Loss, and Take Profit levels ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, HL_time, close[m], time[m], close[m]); ObjectCreate(chart_id, lvl_sl_line, OBJ_TREND, 0, HL_time, lvl_50, time[m], lvl_50); ObjectCreate(chart_id, lvl_tp_line, OBJ_TREND, 0, HL_time, TP1, time[m], TP1); ObjectCreate(chart_id, lvl_tp2_line, OBJ_TREND, 0, HL_time, TP2, time[m], TP2); // Set the properties for the lines (width, color, etc.) ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_COLOR, clrDarkGreen); // Create the text labels for Entry, Stop Loss, and Take Profit levels entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); // Create the text objects for the Entry, Stop Loss, and Take Profit labels ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0, time[m], lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0, time[m], close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, entry_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0, time[m], TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0, time[m], TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_FONTSIZE, 15); // Create a Buy arrow object to indicate the Buy signal on the chart buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id, buy_object, OBJ_ARROW_BUY, 0, time[m], close[m]); break; // Exit the loop once a Buy signal is found } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }
出力
説明
コードのグローバル領域では、n_barsとn_bars_2という2つの整数型変数が宣言されています。これらの変数は、強気の市場構造パターンにおける重要なポイント間に存在するローソク足(バー)の本数を判定するために使用されます。具体的には、n_barsはプレミアム/ディスカウントボックスの終点(time[l + LookbackBars])からHLまでのバー数を示します。一方で、n_bars_2はHHをブレイクした強気のローソク足と、プレミアム/ディスカウントボックスの終点との間のバー数をカウントします。このカウントにより、価格の動きが理想的なトレードゾーンからどれだけ離れているか、または買いシグナルがまだ有効かどうかを判断するための基準が得られます。
これらの変数は、コードの後半で強気の市場構造をより厳密に評価する追加の検証条件として使用されます。L、H、HL、HHを特定したあと(それぞれが以下の構造に従っていることを確認:L < H, HL < H, HL > L, HH > H)、さらに条件「プレミアム/ディスカウントボックスを終えたローソク足の始値がHHを超えていないこと」、そして「n_bars_2 < n_barsであること」が確認されます。この条件により、HHをブレイクする強気のローソク足が、パターンの形成からあまり時間をおかずに出現していることが保証されます。もし時間的に離れすぎている場合、そのブレイクアウトは信頼性の低い、あるいは弱いシグナルである可能性があるため、排除されます。
以前に使用していたObjectCreateやObjectSet*といったチャート描画用の関数群(チャートのL、H、HL、HH、プレミアム/ディスカウントボックス、50%ライン、およびエントリー/SL/TPマーカーを描画する処理)は、このより厳密な検証を実施するために、このif文の内部に移動されました。つまり、すべての構造的条件と時間的条件が満たされたときにのみ、これらの視覚要素がチャート上に表示されるようになっています。この仕組みによって、チャートが誤ったシグナルや時期尚早な描画で乱雑になるのを防ぎ、明確で信頼性の高い視覚表示を維持できるようになっています。
2.3. 弱気トレンド
このインジケーターが売りシグナルを示す前に、市場構造を使って下降トレンドを確認する必要があります。これは、H、L、LH、LLという一連の重要な価格ポイントを見つけることでおこなわれます。このパターンは、売り手が市場を支配しており、価格がさらに下落する可能性が高いことを示しており、弱気のモメンタムを裏付けるものです。インジケーターはこの構造が確認され次第、有効な売りシグナルの検出を開始します。
例
// Variables for Bearish Market Structure double LH; // Lower High: the high formed after the initial low in a downtrend datetime LH_time; // Time of the Lower High string LH_letter; // Label used to display the Lower High on the chart (e.g., "LH") double LL; // Lower Low: the new low formed after the Lower High in a downtrend datetime LL_time; // Time of the Lower Low string LL_letter; // Label used to display the Lower Low on the chart (e.g., "LL") string sell_object; // Arrow object to indicate the Sell signal on the chart
// BEARISH TREND if(show_bearish == true) // Check if the user enabled the bearish trend display { if(rates_total >= bars_check) // Ensure enough candles are available for processing { // Loop through historical bars to find a swing high (potential start of bearish structure) for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingHigh(high, i, LookbackBars)) // Detect first swing high { H = high[i]; H_time = time[i]; H_letter = StringFormat("High B%d", i); // Label for the high // From the swing high, look for the next swing low for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingLow(low, j, LookbackBars) && time[j] > H_time) // Confirm next swing low { L = low[j]; L_time = time[j]; L_letter = StringFormat("Low B%d", j); // Label for the low // From the swing low, look for the Lower High for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingHigh(high, k, LookbackBars) && time[k] > L_time) { LH = high[k]; LH_time = time[k]; LH_letter = StringFormat("Lower High%d", k); // Label for the Lower High // From the LH, find a Lower Low for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingLow(low, l, LookbackBars) && time[l] > LH_time) { LL = low[l]; LL_time = time[l]; LL_letter = StringFormat("Lower Low%d", l); // Label for Lower Low // Calculate 50% retracement level from LH to LL lvl_50 = LL + ((LH - LL)/2); // Prepare object names pre_dis_box = StringFormat("Gan Box B%d", i); lvl_50_line = StringFormat("Level 50 Line B%d", i); // Search for a bearish entry condition for(int m = l; m < rates_total-1; m++) { // Confirm bearish candle breaking below the LL if(close[m] < open[m] && close[m] < LL && time[m] >= time[l+LookbackBars]) { // Count bars for pattern distance validation n_bars = Bars(_Symbol,PERIOD_CURRENT,LH_time, time[l+LookbackBars]); // From LH to box end n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[l+LookbackBars], time[m]); // From box end to break candle // Confirm valid bearish structure and proximity of break candle if(H > L && LH > L && LH < H && LL < L && open[l+LookbackBars] >= LL && n_bars_2 < n_bars) { // Draw the Premium/Discount box ObjectCreate(chart_id,pre_dis_box, OBJ_RECTANGLE,0,LH_time,LH, time[l+LookbackBars],LL); ObjectCreate(chart_id,lvl_50_line, OBJ_TREND,0,LH_time,lvl_50, time[l+LookbackBars],lvl_50); ObjectSetInteger(chart_id,pre_dis_box,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_50_line,OBJPROP_WIDTH,2); // Label the structure points ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id,H_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id,L_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LH_letter, OBJ_TEXT, 0, LH_time, LH); ObjectSetString(chart_id, LH_letter, OBJPROP_TEXT, "LH"); ObjectSetInteger(chart_id,LH_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LL_letter, OBJ_TEXT, 0, LL_time, LL); ObjectSetString(chart_id, LL_letter, OBJPROP_TEXT, "LL"); ObjectSetInteger(chart_id,LL_letter,OBJPROP_FONTSIZE,15); ObjectSetInteger(chart_id,H_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,L_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LL_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LH_letter,OBJPROP_WIDTH,2); // Calculate Take Profits based on 1:1 and 1:2 RR TP1 = close[m] - (lvl_50 - close[m]); TP2 = TP1 - (lvl_50 - close[m]); // Generate entry, SL and TP object names entry_line = StringFormat("Entry B%d", m); lvl_sl_line = StringFormat("SL B%d", m); lvl_tp_line = StringFormat("TP B%d", m); lvl_tp2_line = StringFormat("TP 2 B%d", m); // Draw entry, SL, TP1, TP2 levels ObjectCreate(chart_id,entry_line,OBJ_TREND,0,LH_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,0,LH_time,lvl_50, time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,0,LH_time,TP1, time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,0,LH_time,TP2, time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); // Generate text labels entry_txt = StringFormat("Entry Text B%d", m); lvl_sl_txt = StringFormat("SL Text B%d", m); lvl_tp_txt = StringFormat("TP Text B%d", m); lvl_tp2_txt = StringFormat("TP 2 Text B%d", m); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "SELL"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); // Draw sell arrow sell_object = StringFormat("Sell Object%d", m); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[m],close[m]); } break; // Exit loop after valid setup } } break; // Exit LL search } } break; // Exit LH search } } break; // Exit L search } } } } } }
出力
説明
コードはまず、「if(show_bearish == true)」を使って、ユーザーが下降トレンドロジックを有効にしているかどうかを確認することから始まります。このオプションが有効で、かつバーの本数が十分ある(rates_total >= bars_check)場合、インジケーターは過去のローソク足をループ処理しながら、正当な下降の市場構造を探します。このプロセスの最初のステップは、スイングハイ(H)を特定することです。スイングハイが特定された後、その次に現れるスイングロー(L)を探します。それが見つかった場合、コードはさらに、LLを探し、それによって下降トレンド構造を確定します。続いて、最初の高値Hよりも低い位置にあるスイングハイ(LH)を探します。こうして見つけたH、L、LH、LLの値とそのタイムスタンプを用いて、チャート上に「H」、「L」、「LH」、「LL」といったラベルが表示されます。
次に、プレミアム/ディスカウントゾーンのボックスがLHからLLに向けて描画され、50%リトレースメントレベルが次の式「lvl_50 = LL + ((LH - LL)/2」により算出されます。その後、インジケーターは、LLを下抜けてクローズする陰線(終値が始値より低い)を探します。これは、取引関連のオブジェクト(エントリーポイント、ストップロス、TP1、TP2)を配置する前の確認条件です。また、n_barsおよびn_bars_2の2つの変数を使用して、構造のブレイクが適切な本数のバーの範囲内で起きているかも検証します。n_barsはLHからボックス終点までのバー数をカウントし、n_bars_2はボックス終点からLLをブレイクした陰線までのバー数をカウントします。構造が正しく、ブレイクが有効であり、距離も適切であるというすべての条件が満たされた場合にのみ、ローソク足の終値にエントリーラインが描画され、ストップロス(SL)は50%レベルに設定されます。さらに、テイクプロフィット1(TP1)とテイクプロフィット2(TP2)は、それぞれ1:1および1:2のリスクリワード比に基づいて配置されます。
加えて、下降を示す矢印と「SELL」というテキストが、該当する陰線の上に表示されます。この下降トレンドに関するロジックは、基本的には上昇トレンドパターンのロジックと同じ構造を持ちますが、その内容が反転しているだけです。すでに上昇トレンドについては詳細に説明されているため、ここでの解説は簡潔にとどめられています。市場構造や判断の根拠は同じですが、価格が上昇するのではなく下降する方向に向かっている点が異なります。
結論
本記事では、安値(L)、安値更新(LL)、高値引き下げ(HL)、高値(H)、高値更新(HH)といった主要なポイントを検出することで市場構造を識別する、MQL5のカスタムインジケーターを構築しました。これらのポイントをもとに、インジケーターは上昇トレンドまたは下降トレンドを判断し、構造に基づいたパターンに従って、エントリーポイント、50%レベルに設定されたストップロス、テイクプロフィット(TP1およびTP2)を自動的に描画します。また、プレミアムゾーンとディスカウントゾーンをチャート上に可視化することで、価格が反応しやすい領域を視覚的に強調します。すべてのチャートオブジェクトは、特定の条件が満たされたときにのみ描画されるため、シグナルは明確で信頼性の高いものに保たれています。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/17689




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