English Deutsch
preview
MQL5入門(第10回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド

MQL5入門(第10回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド

MetaTrader 5トレーディング | 11 4月 2025, 08:28
80 0
Israel Pelumi Abioye
Israel Pelumi Abioye

はじめに

MQL5の連載にようこそ。第10回では、アルゴリズム取引におけるもう一つの重要な側面である組み込みインジケーターの利用方法について学びます。いつものように、このパートは実践的で魅力的な内容になることをお約束します。プロジェクトベースのアプローチを引き続き採用し、学んだことを実際の取引戦略に直接応用できるように進めていきます。

この記事では、相対力指数(RSI)を活用したEAを開発します。RSIは取引で最も広く使用されるテクニカル指標の一つです。このインジケーターをEAに組み込むことで、市場の状況を追跡し、自動で取引を行うツールを作成します。相対力指数 (RSI)が本記事の主題ですが、ここで紹介する考え方は、他の組み込みインジケーターにも応用できます。なぜなら、組み込みインジケーターはどれも同じような原理に基づいて動作するからです。本連載は主に初心者向けに構成しているため、説明やコードはできるだけ分かりやすくしており、特に初心者にとって重要な「なぜそのコードが必要か」「各コンポーネントがどんな役割を果たしているか」「それらがどのように連携しているか」を十分に理解できるように心がけています。

プロのMQL5開発では、簡潔でメモリ効率の高いコードが高く評価されることが多いです。このアプローチは効率的ですが、プログラミングに不慣れな方にとってはコードが理解しにくくなる場合があります。そこで、私はこの連載では、より包括的で体系的なアプローチを採用し、読者が安心して理解できるようにしています。

この記事では、次の内容を学びます。

  • 組み込みインジケーターの使い方
  • インジケーターハンドルを使ってインジケーターを操作する方法
  • インジケーターバッファを利用して計算されたインジケーター値を取得する方法
  • チャートからRSI値およびそれに対応するローソク足データを取得する方法
  • 流動性スイープの概念を実装するためにRSIの高値と安値を識別する方法
  • RSI値およびローソク足データに基づくEAの段階的な作成方法
  • 分析向上のために、重要なRSIの高値と安値をチャートに直接表示するオブジェクトを作成する方法
  • 一貫性のないローソク足のサイズに対応するため、取引ごとにパーセンテージベースのリスクを設定する方法

この記事を読み終える頃には、リスク管理、リスク修正、RSIベースのEAの作成と改善に関する実践的な洞察を得ながら、組み込みインジケーターを取引戦略に統合する方法を包括的に理解できるようになります。



1.MQL5の組み込みインジケーターについて

1.1.組み込みインジケーターとは

MetaTrader 5に組み込まれているインジケーターは、市場分析に役立つ便利なツールです。これらは、市場の状況、モメンタム、価格の動きに関する即時の情報を提供します。たとえば、ボリンジャーバンドは市場の変動幅を示し、移動平均はトレンドの識別に役立ち、RSIは市場が買われ過ぎまたは売られ過ぎであるかを示します。これらのツールは取引を大幅に簡素化し、時間を節約するのに役立ちます。

1.2.インジケーターハンドル

MQL5では、インジケーターハンドルはインジケーターが作成または初期化されるときに割り当てられる一意の識別子です。これらのハンドルはインジケーターへの参照として機能し、インジケーターを操作してそのデータにアクセスできるようになります。インジケーターをチャートに追加する場合、期間、価格タイプ、インジケーターの動作を定義するその他の設定など、特定のプロパティを入力する必要があります。

図1:インジケーターのプロパティ

コードでは、ハンドルは同様の役割を果たします。つまり、プログラムがどのインジケーターを操作しているかを「認識」し、そのプロパティにアクセスできるようにします。基本的に、インジケーターハンドルは、プログラム内でインジケーターの設定を入力してトリガーする手段として機能し、取引戦略でインジケーターを効果的に操作できるようにします。

iRSIやiBandsなどの関数を使用してインジケーターハンドルを作成すると、コードがその特定のインジケーターに「バインド」されるため、そのデータを簡単に取得して操作できるようになります。ハンドルがなければ、プログラムは異なるインジケーターを区別できず、インジケーターバッファの計算値にアクセスすることもできません。 たとえば、インジケーターの設定をコードに入力する場合は、iRSIなどの関数を使用して必要なパラメータ(期間、適用価格、シフトなど)を指定し、RSIインジケーターのハンドルを作成します。

構文

iRSI(symbol, period, rsi_period, applied_price);

説明

  • symbol:RSIを計算する銘柄(通貨ペア、株式、または資産)を指定します。

  • period:RSIが計算される期間(または時間枠)。RSIがデータポイントをどのくらい遡って考慮するかを定義します。

  • rsi_period:RSIの計算に使用される期間の数。RSIは通常14期間を使用して計算されますが、これは戦略に合わせて調整できます。

  • applied_priceRSIを計算するときに使用する価格タイプ。RSIは、終値、始値、高値、安値など、さまざまな価格値に基づくことができます。

int rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);

説明

int rsi_handle

  • RSIインジケーターハンドル(インジケーターの一意のID)を格納するための整数変数。

iRSI(...)

  • 指定された銘柄、時間枠、設定の相対力指数(RSI)を計算するために使用される関数。

 _Symbol

  • チャート上の現在の取引銘柄(例:EUR/USD)を参照します。作業中の銘柄が自動的に使用されます。

PERIOD_CURRENT

  • チャートの時間枠(例:1時間、1日)を参照します。これにより、RSIはチャートのアクティブな時間枠に基づいて計算されます。

14

  • RSI期間は、計算に使用するバー/ローソク足の数(通常は14期間)を指定します。

PRICE_CLOSE

  • 各バーまたはローソク足の終値を使用してRSIを計算することを指定します。

これは、MetaTrader 5のチャートでインジケーターを設定する方法と同様に、インジケーターの詳細をコードに直接入力する方法です。iRSIなどの関数を使用することで、チャートにインジケーターを適用する際と同様に、銘柄、時間枠、期間、価格タイプを定義できます。これにより、コードがインジケーターのデータにアクセスして操作でき、指定された設定に基づいて取引戦略が期待通りに機能することが保証されます。

同じ原則は、MetaTrader 5の他の組み込みインジケーターにも適用できます。たとえば、それぞれに特定のパラメータが必要なさまざまなインジケーターを処理するために、異なる関数を使用することができます。以下に、よく使用される5つの関数を示します。

  • iBands(ボリンジャーバンド):銘柄、時間枠、期間、偏差、適用価格を指定できます。
  • iMA(移動平均):銘柄、時間枠、期間、シフト、方法、適用価格を指定できます。
  • iMACD(MACD):銘柄、時間枠、高速EMAと低速EMA、シグナル期間、適用価格を指定できます。
  • iADX(平均方向指数):銘柄、時間枠、期間を指定できます。

MQL5は、取引手法を向上させるための多くのテクニカル分析ツールを提供し、さらに豊富な組み込みインジケーター機能を備えています。1つのインジケーターの使い方を理解していれば、同じアプローチを他のインジケーターにも簡単に適用できます。MQL5のドキュメントをさらに掘り下げて調べることで、取引ニーズに最適なインジケーターを見つけることができます。

1.2.インジケーターバッファ

インジケーターをハンドルで定義した後、次に行うべきステップはそのデータを取得することです。これは、チャート上の各価格ポイントに対するインジケーターの計算結果を保存するインジケーターバッファを通じておこなわれます。各インジケーターには、生成するデータの種類に応じて特定の数のバッファがあります。

移動平均(MA)

このインジケーターには1つのバッファがあり、各ローソク足で計算された移動平均値が格納されます。

図2:移動平均インジケーター

相対力指数(RSI)

同様に、RSIインジケーターにはRSI値を格納するためのバッファが1つあります。

図3:RSIインジケーター

ボリンジャーバンド

このインジケーターは、データを格納するために3つのバッファを使用します。

図4:ボリンジャーバンドインジケーター

  • 中間バンド(インデックス0)は、主なトレンドラインです。
  • 上部バンド(インデックス1)は、潜在的な買われ過ぎレベルを表します。
  • 下部バンド(インデックス2)は、潜在的な売られ過ぎレベルを表します。

これらのバッファには、CopyBuffer()関数を使用してプログラムでアクセスできます。

MQL5のCopyBuffer()関数は、インジケーターのバッファから配列にデータを取得し、さらに分析したり意思決定をおこなうために使用されます。iRSIやiBandsなどの関数を使用してインジケーターハンドルを作成したら、CopyBuffer()を使用して計算されたインジケーター値にアクセスします。

構文

int CopyBuffer(indicator_handle, buffer_number, start_position, count, buffer);

パラメータ

  • indicator_handle:iRSIやiBandsなど、以前に作成されたインジケーターの一意の識別子(ハンドル)。
  • buffer_number:データを取得するバッファのインデックス。たとえば、ボリンジャーバンドの場合、中間バンドは0、上部バンドは1、下部バンドは2です。RSIまたは移動平均の場合、バッファが1つしかないため、0のみになります。
  • start_pos:チャートの開始位置(0=最新のローソク足)。
  • count:バッファから取得する値の数。
  • buffer[]:インジケーターのバッファからのデータが格納される配列。

int band_handle;               // Bollinger Bands handle
double upper_band[], mid_band[], lower_band[]; // Buffers for the bands
void OnStart()
  {
// Create the Bollinger Bands indicator
   band_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
// Ensure arrays are series for correct indexing
   ArraySetAsSeries(upper_band, true);
   ArraySetAsSeries(mid_band, true);
   ArraySetAsSeries(lower_band, true);
// Copy data from buffers (Index 0 = Middle, 1 = Upper, 2 = Lower)
   CopyBuffer(band_handle, 0, 0, 10, mid_band);   // Middle band data
   CopyBuffer(band_handle, 1, 0, 10, upper_band); // Upper band data
   CopyBuffer(band_handle, 2, 0, 10, lower_band); // Lower band data
// Print the most recent values
   Print("Middle Band: ", mid_band[0]);
   Print("Upper Band: ", upper_band[0]);
   Print("Lower Band: ", lower_band[0]);
  }

説明

この例では、CopyBuffer()関数を使用してボリンジャーバンドインジケーターからデータを取得する方法を示します。まず、iBands関数は、銘柄(_Symbol)、時間枠(PERIOD_CURRENT)、期間(20)、シフト(0)、偏差(2.0)、適用価格(PRICE_CLOSE)などの特定のパラメータを使用してインジケーターハンドルを作成します。このハンドルはband_handleに保存され、アプリケーションとボリンジャーバンドインジケーターの間の橋渡しをし、計算された数値にアクセスできるようにします。上部バンド、中間バンド、下部バンドのデータは、宣言した3つの配列(upper_band、mid_band、lower_band)に格納されます。これらの配列がArraySetAsSeriesで系列として設定されていれば、最新の値はインデックス0に配置されます。ArraySetAsSeriesを使用して配列を系列として設定することで、最新の値がインデックス0に配置されるようになります。

ボリンジャーバンドインジケーターの3つのバッファ(中間バンドのバッファ0、上ボリンジャーバンドインジケーターの3つのバッファ(中間バンドのバッファ0、上部バンドのバッファ1、下部バンドのバッファ2)には、それぞれCopyBuffer()関数が呼び出されます。各呼び出しでは、関連するバッファから最後の11個の値を取得し、それらを対応する配列に保存します。最後に、プログラムはPrint()を使用して、中間バンド、上部バンド、下部バンドの最新の値を出力します。価格とボリンジャーバンドの関係は、ブレイクアウトやトレンドの反転の可能性を特定するために活用でき、このデータは取引システム内での追加分析や意思決定にも利用できます。



2.RSIベースEAの開発

2.1.EAの仕組み

EAは、買われ過ぎと売られ過ぎのレベルを、反転の可能性を示す重要な指標と見なします。

2.1.1. 買いのロジック

  • RSI値が30未満かどうかを確認します。
  • RSIが安値を形成したかどうかを判断します。
  • EAは通常のチャート上の対応するローソク足の安値を識別します。
  • 指定されたローソク足の安値を下回って価格が流動性を一掃するのを待ちます。
  • 価格が安値を下回った後、最初の強気ローソク足が安値を上回って終値すると、EAは上昇を予想して買い取引を開始します。

図5:購入のロジック

2.1.2.売りのロジック

  • RSI値が70を超えているかどうかを確認します。
  • RSIが高値を形成したかどうかを判断します。
  • EAは通常のチャート上の対応するローソク足の高値を識別します。
  • 次に、価格の動きを監視し、価格が特定されたローソク足の高値を上回り、流動性を一掃するのを待ちます。
  • 価格が高値を下回った後、EAは最初の弱気ローソク足が高値を下回って終値を付けると売り取引をおこない、下降を予測します。

図6:売りのロジック

2.2.  取引ライブラリの追加

ポジションを開いたり、閉じたり、変更したりするEAを構築する際の最初の手順は、取引ライブラリを組み込むことです。このライブラリは、プログラムで取引を実行および管理するための重要な機能を提供します。

#include <Trade/Trade.mqh> // Include the trade library for trading functions

// Create an instance of the CTrade class for trading operations
CTrade trade;
//magic number
input int MagicNumber = 1111;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Set the magic number for the EA's trades
   trade.SetExpertMagicNumber(MagicNumber);

// Return initialization success
   return(INIT_SUCCEEDED);
  }

説明

取引ライブラリをインクルードする

  • CTradeクラスを使用するには、「#include <Trade/Trade.mqh>」を宣言して取引ライブラリをインポートする必要があります。このクラスは、テイクプロフィット、ストップロス、成行注文、予約注文、その他の取引関連の義務を管理する機能を提供することで、取引プロセスを合理化します。

CTradeのインスタンスを作成する

  • 行「CTrade trade;」は、取引を実行および管理するためにEA全体で使用されるCTradeクラスのインスタンスを作成します。
マジックナンバー

  • MagicNumberは、Inputキーワードを使用して入力変数として宣言されます。これにより、EAのユーザーは、EAによっておこなわれた取引の一意の識別子を定義できます。

マジックナンバーが重要な理由

  • マジックナンバーにより、EAによっておこなわれた取引を他の取引とは別に識別および管理できるようになります。
  • これにより、EAは自身の取引を手動の取引や他のEAの取引と区別できるようになります。
  • マジックナンバーを変更することで、ユーザーはさまざまな市場状況や時間枠で、異なる資産、あるいは同じ資産に対してEAを実行できます。
int OnInit()
  {
// Set the magic number for the EA's trades
   trade.SetExpertMagicNumber(MagicNumber);

// Return initialization success
   return(INIT_SUCCEEDED);
  }

初期化関数

EAの起動時にOnInit()関数が実行されます。ここで、SetExpertMagicNumber()メソッドは指定されたマジックナンバーをEAに割り当て、EAのこのインスタンスによって開かれたすべての取引にこの識別子が付与されるようにします。 このアプローチは柔軟性を提供し、ユーザーはさまざまな取引シナリオに合わせてマジックナンバーを調整できるため、さまざまな商品や戦略にわたってEAの複数のインスタンスを管理しやすくなります。

2.3.RSI値とローソク足データの取得

正確な市場データを取得することは、EAが効果的な取引操作を実行するための基盤です。このEAのロジックは、ローソク足パターンとRSIインジケーターを理解することを中心に展開されており、これらの値を確実に収集することが非常に重要です。このデータにより、EAはRSIの高値と安値が特定のローソク足に対応しているかどうかを分析でき、これは潜在的なエントリーポイントおよびエグジットポイントを決定するための鍵となります。 RSI値とローソク足データを組み合わせることで、EAは市場の状況を総合的に評価し、価格変動と関連づけて買われ過ぎや売られ過ぎの状態を識別します。このアプローチによって、EAは論理的かつ正確な取引決定を下す能力が強化されます。

2.3.1.RSI値の取得

前述したように、RSI値を取得することは、EAが市場の状況を評価するための重要なステップです。これは、iRSIハンドルを使用してRSIインジケーターのプロパティを設定し、CopyBuffer関数を介して実際のRSI値を取得するという2つの部分で実行されます。

#include <Trade/Trade.mqh>
CTrade trade;

// Magic number
input int MagicNumber = 1111;
//RSI handle
int       rsi_handle;
double    rsi_buffer[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);
// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
// Set the magic number (for trading, not relevant to RSI retrieval here)
   trade.SetExpertMagicNumber(MagicNumber);
   return (INIT_SUCCEEDED); // Indicate successful initialization
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+

void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);
// Display the most recent RSI value for verification
   Comment("rsi_buffer[0]: ", rsi_buffer[0]);
  }

説明

RSIバッファの配置

  • OnInit関数では、ArraySetAsSeries関数を使用してrsi_bufferを系列配列として構成します。つまり、最新のデータはインデックス0になります。これにより、最新のRSI値へのアクセスが簡単になります。

RSIハンドルの作成

  • OnTick関数内では、iRSI関数が呼び出され、RSIハンドル(rsi_handle)が初期化されます。この関数は、銘柄(_Symbol)、時間枠(PERIOD_CURRENT)、期間(14)、適用価格(PRICE_CLOSE)などのRSIインジケーターのプロパティを設定します。

パラメータ

  • _Symbol:RSIが計算される取引商品。
  • PERIOD_CURRENT:チャートの現在の時間枠。
  • 14:RSI期間(一般的に使用されるデフォルト値)。
  • PRICE_CLOSE:RSIは終値に基づいて計算されます。

RSI値のコピー

  • CopyBuffer関数は、インジケーターのRSI値を取得し、rsi_buffer配列に入力します。最新の値(オフセット1)からコピーを開始し、最大100個の値を取得します。

パラメータ

  • 0:RSIインジケーターのバッファインデックスを指定します(メインラインの場合は0)。
  • 1:最新のRSIはまだ形成中で値が不安定なので、2番目に新しいRSI値からコピーを開始します。
  • 100:最大100個の値を取得します。

このアプローチにより、EAは最新のRSI値にアクセスできるようになります。これは、買われ過ぎや売られ過ぎの状況を分析し、取引の決定を下すために重要です。

RSI値の表示

  • このコードは、Comment関数を使用してチャートに最新のRSI値(rsi_buffer[0])を表示することで、データ取得手順を検証します。
  • このアプローチにより、EAは最新のRSI値にアクセスできるようになります。これは、買われ過ぎや売られ過ぎの状況を分析し、取引の決定を下すために重要です。

出力

図7:コード出力

2.3.2. ローソク足データの取得

価格の動きを分析し、それをRSIの読み取り値と照合するには、ローソク足データを取得することが不可欠です。始値、終値、高値、安値、およびそれらに関連するタイムスタンプなどの重要なローソク足情報を取得するために、EAは特定の関数を使用します。市場の状況を評価し、賢明な取引決定を下すためには、これらのデータポイントが不可欠です。

#include <Trade/Trade.mqh>
CTrade trade;

// Magic number
input int MagicNumber = 1111;

int       rsi_handle;
double    rsi_buffer[];

double open[];
double close[];
double high[];
double low[];
datetime time[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

// Display the most recent candlestick data for verification
   Comment("open[0]: ", open[0],
           "\nclose[0]: ", close[0],
           "\nhigh[0]: ", high[0],
           "\nlow[0]: ", low[0],
           "\ntime[0]: ", time[0]);
  }

説明

ローソク足配列の配置

  • OnInit関数では、ArraySetAsSeries関数を使用して、始値、終値、高値、安値、および時間配列を系列として構成します。
  • これにより、最新のデータがインデックス0に表示されるようになり、最新の完了したローソク足データの取得と分析が簡単になります。

ローソク足データのコピー

  • OnTick関数では、CopyOpen、CopyClose、CopyHigh、CopyLow、CopyTime関数を使用して、ローソク足のそれぞれのデータを取得します。

パラメータ

  •  _Symbol:現在の取引商品。
  • PERIOD_CURRENT:現在のチャートの時間枠(例:M1、H1)。
  • 1:最新のローソク足(インデックス0)はまだ形成中であり、その値は不安定であるため、2番目に新しいローソク足からコピーを開始します。
  • 100:分析用に最大100個のローソク足の値を取得します。

各関数はそれぞれの配列にデータを設定します。

  • open[]:指定されたローソク足の始値が含まれます。
  • close[]:終値が含まれます。
  • high[]:最高価格が含まれています。
  • low[]:最低価格が記載されています。
  • time[]:プライスアクションとの同期のために開始タイムスタンプを保存します。

ローソク足データの表示

Comment関数は、検証の目的でチャート上に最新の完了したローソク足データ(インデックス0)を表示するために使用されます。

  • open[0]:始値
  • close[0]:終値
  • high[0]:最高価格
  • low[0]:最低価格
  • time[0]:ローソク足が開いた時刻を示すタイムスタンプ

2.3.3.RSIの高値と安値の決定

EAは、RSIが売られ過ぎレベル(30)を下回ったときにRSIの安値を認識し、買われ過ぎレベル(70)を超えたときにRSIの高値を認識する必要があります。関心のある可能性のある領域を特定するために、これらのRSIポイントはチャート上の特定のローソク足に接続されます。これらのマーカーは流動性スイープのロジックを構成するために不可欠であるため、この段階はEAの動作の基礎となります。 

RSIの高値と安値を判断するのは非常に簡単です。

RSIの安値 

図8:RSI低

RSIの高値 

図9:RSI高

// Magic number
input int MagicNumber = 1111;

// RSI handle and buffer
int       rsi_handle;
double    rsi_buffer[];

// Candlestick data arrays
double open[];
double close[];
double high[];
double low[];
datetime time[];

// Variables to store high and low levels
double max_high = 0;        // Maximum high for the candlesticks
datetime min_time1 = 0;     // Time of the maximum high candlestick
double min_low = 0;         // Minimum low for the candlesticks
datetime min_time2 = 0;     // Time of the minimum low candlestick

// Variables to store RSI highs and lows
datetime time_low = 0;      // Time of the RSI low
datetime times_high = 0;    // Time of the RSI high

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number for the EA
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |            |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data (open, close, high, low, time)
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

// Loop to find the maximum high from a bullish candlestick pattern
   for(int i = 0; i < 12; i++)
     {
      // Check for a bullish pattern: current close < open and previous close > open
      if(close[i] < open[i] && close[i+1] > open[i+1])
        {
         // Calculate the maximum high between the two candlesticks
         max_high = MathMax(high[i], high[i+1]);
         // Record the time of the corresponding candlestick
         min_time1 = MathMin(time[i], time[i+1]);
         break;
        }
     }

// Loop to find the minimum low from a bearish candlestick pattern
   for(int i = 0; i < 12; i++)
     {
      // Check for a bearish pattern: current close > open and previous close < open
      if(close[i] > open[i] && close[i+1] < open[i+1])
        {
         // Calculate the minimum low between the two candlesticks
         min_low = MathMin(low[i], low[i+1]);
         // Record the time of the corresponding candlestick
         min_time2 = MathMin(time[i], time[i+1]);
         break;
        }
     }

// Loop to find the RSI low point
   for(int i = 0; i < 12; i++)
     {
      // Check if the RSI is oversold and forms a low point
      if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
        {
         // Record the time of the RSI low
         time_low = time[i+1];
         break;
        }
     }

// Loop to find the RSI high point
   for(int i = 0; i < 12; i++)
     {
      // Check if the RSI is overbought and forms a high point
      if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
        {
         // Record the time of the RSI high
         times_high = time[i+1];
         break;
        }
     }
  }

説明

RSIの安値(売られ過ぎ状態)

  • ループは、RSIが30未満(rsi_buffer[i+1]<30)で上昇し始める(rsi_buffer[i]>rsi_buffer[i+1])ポイントを探します。
  • これらの状況は、RSIが最安値に達し、方向が反転していることを示しています。
  • 対応するローソク足のタイムスタンプはtime_lowに保存されます。

RSIの高値(買われ過ぎ状態)

  • ループは、RSIが70を超えていて(rsi_buffer[i+1] > 70)下降し始めるポイント(rsi_buffer[i] < rsi_buffer[i+1])を探します。
  • これらの状況は、RSIが最高値に達し、方向が反転していることを示しています。
  • 対応するローソク足のタイムスタンプはtimes_highに保存されます。

2.3.4.チャートに安値と高値をマークする
2.3.4.1.チャートに安値を示す

RSIの安値を識別するロジックが満たされると、プログラムはローソク足データを評価して、チャート上の対応する最安値を正確に特定します。安値を形成するには2つのRSI値が必要であるため、EAは2つの一致するローソク足の安値を使用して正確なレベルを決定します。

この最安値は、プログラムが潜在的な流動性スイープを待機する領域を確立するため重要です。

ロジック

  1. RSIが最安値を形成するのは、次の場合です。
    • rsi_buffer[i+1] < 30:RSIは売られ過ぎレベルを下回っている
    • rsi_buffer[i] > rsi_buffer[i+1]:RSIは最低点に達した後、上昇し始める
  2. RSIの安値が確認されると、プログラムはチャート上の対応する2つのローソク足の安値配列を使用して最安値を識別します。
  3. この最安値は、EAが潜在的な流動性スイープを待機するレベルを設定します。
  4. RSIの安値が特定されると、プログラムは安値配列から対応する2つのローソク足の安値を取得します。
  5. MathMin()関数を使用して、これら2つの安値間の最小値を計算し、流動性スイープを監視するためのレベルをマークします。
  6. この最安値は、EAが潜在的な反転または流動性スイープを予想するポイントとして機能し、取引の決定における重要な要素となります。
// Loop to find RSI and candlestick lows
for(int i = 0; i < 12; i++)
  {
// Check if the RSI is oversold and forms a low point
   if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
     {
      // Record the time of the RSI low
      time_low = time[i+1];

      // Find the minimum low from the two corresponding candlesticks
      min_low = (double)MathMin(low[i], low[i+1]);

      // Break the loop once the low is found
      break;
     }
  }

図10:対応するローソク足に印を付ける

2.3.4.2.チャートの高値をマーク

RSIの高値を識別するロジックが満たされると、プログラムは対応するローソク足データを評価して、チャートの最大高値を決定します。高値を形成するには2つのRSI値が必要であるため、EAは2つの一致するローソク足の高値を使用して正確なレベルを特定します。

この最高値は、EAが潜在的な流動性スイープを待機する領域を設定するための基準になります。このアプローチは、安値の場合のロジックの逆になります。

ロジック

  1. RSIが高値を形成するのは、次の場合です。
    • rsi_buffer[i+1] > 70:RSIは買われ過ぎレベルを超えて上昇します。
    • rsi_buffer[i] < rsi_buffer[i+1]:RSIは最高値に達した後、下降し始めます。
  2. RSIの高値が確認されると、プログラムはチャート上の対応する2つのローソク足の高値配列を使用して最高値を識別します。
  3. この最高値は、EAが潜在的な流動性スイープを待機するレベルを設定します。
  4. RSIの高値が特定されると、プログラムは高値配列から対応する2つのローソク足の高値を取得します。
  5. MathMax()関数を使用して、これら2つの安値間の最小値を計算し、流動性スイープを監視するためのレベルをマークします。
// Loop to find RSI and candlestick highs
for(int i = 0; i < 12; i++)
  {
// Check if the RSI is overbought and forms a high point
   if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
     {
      // Record the time of the RSI high
      times_high = time[i+1];

      // Find the maximum high from the two corresponding candlesticks
      max_high = (double)MathMax(high[i], high[i+1]);

      // Break the loop once the high is found
      break;
     }
  }

図11:対応するローソク足に印を付ける

2.3.5.12ローソク足遅延でRSIの高値と安値を制御する

RSIの高値や安値が短期間に複数回発生する可能性があることは、特にRSIが買われ過ぎや売られ過ぎのゾーンに長時間留まっている場合、RSIを使用して高値と安値を特定する際の難しさの一つです。そのため、流動性スイープには異なるレベルが設定されることがあります。このロジックでは、これを解決するために12本のローソク足の待機時間を設け、高値または安値が決定された後、12本のローソク足が形成されるまでその値は更新されないようにします。

// Magic number
input int MagicNumber = 1111;

int       rsi_handle;
double    rsi_buffer[];

double open[];
double close[];
double high[];
double low[];
datetime time[];

double max_high = 0;
datetime min_time1 = 0;
double min_low = 0;
datetime min_time2 = 0;

datetime time_low = 0;
datetime times_high = 0;



//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

   static double max_high_static = max_high;
   static datetime min_time1_static = min_time1;

   static double min_low_static = min_low;
   static datetime min_time2_static = min_time2;


   int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent());

   for(int i = 0; i < 12; i++)
     {

      if(close[i] < open[i] && close[i+1] > open[i+1])
        {

         max_high = (double)MathMax(high[i],high[i+1]);
         min_time1 = (datetime)MathMin(time[i],time[i+1]);

         break;


        }
     }

   int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent());
   for(int i = 0; i < 12; i++)
     {

      if(close[i] > open[i] && close[i+1] < open[i+1])
        {

         min_low = (double)MathMin(low[i],low[i+1]);

         min_time2 = (datetime)MathMin(time[i],time[i+1]);
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
        {

         time_low = time[i+1];
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
        {

         times_high = time[i+1];
         break;

        }

     }

   if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high))
     {

      max_high_static = max_high;
      min_time1_static = min_time1;

     }
   else
      if(min_time1 != times_high && total_bar_high > 13)
        {
         max_high_static = 0;
         min_time1_static = 0;
        }

   if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low))
     {

      min_low_static = min_low;
      min_time2_static = min_time2;

     }

   else
      if(min_time2 != time_low && total_bar_low > 13)
        {
         min_low_static = 0;
         min_time2_static = 0;
        }
  }

説明

RSIとローソク足の高値/安値の識別

  • 買われ過ぎまたは売られ過ぎレベルに関連するインジケーターのアクティビティは、RSIロジックによって、高値または安値が形成されているかどうかを評価するために使われます。
  • この場合、プログラムは関連するローソク足から最高値または最安値を決定します。

12ローソク足ルールの施行

  • Bars()を使用して、プログラムは最後の高値または安値以降に形成されたローソク足の数を計算します。
  • 高値または安値は、少なくとも12本のローソク足が経過した場合にのみ更新され、頻繁な変化を防ぎます。
古い値のリセット

  • 13本を超えるローソク足が形成され、前回の高値または安値のRSI条件が有効でなくなった場合、保存された値はクリアされます。
  • これにより、EAが取引の決定に古い情報に依存しないことが保証されます。
2.3.6.チャートの高値と安値を監視するための線オブジェクトの追加

このセクションでは、EAを強化して、チャート上に直接線をプロットすることで、特定されたRSIの高値と安値を視覚的に表現します。これにより、EAはこれらのラインオブジェクトを使用して高値と安値をプログラムで識別できるだけでなく、トレーダーは重要なレベルを手動で監視して、より適切な意思決定をおこなうこともできます。

#include <Trade/Trade.mqh>
CTrade trade;

// Magic number
input int MagicNumber = 1111;

int rsi_handle;
double rsi_buffer[];

double open[];
double close[];
double high[];
double low[];
datetime time[];

double max_high = 0;
datetime min_time1 = 0;
double min_low = 0;
datetime min_time2 = 0;

datetime time_low = 0;
datetime times_high = 0;

string high_obj_name = "High_Line";
string low_obj_name = "Low_Line";

long chart_id;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

   static double max_high_static = max_high;
   static datetime min_time1_static = min_time1;

   static double min_low_static = min_low;
   static datetime min_time2_static = min_time2;

//CHART ID
   chart_id = ChartID();

   int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent());

   for(int i = 0; i < 12; i++)
     {

      if(close[i] < open[i] && close[i+1] > open[i+1])
        {

         max_high = (double)MathMax(high[i],high[i+1]);
         min_time1 = (datetime)MathMin(time[i],time[i+1]);

         break;

        }
     }

   int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent());
   for(int i = 0; i < 12; i++)
     {

      if(close[i] > open[i] && close[i+1] < open[i+1])
        {

         min_low = (double)MathMin(low[i],low[i+1]);

         min_time2 = (datetime)MathMin(time[i],time[i+1]);
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
        {

         time_low = time[i+1];
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
        {

         times_high = time[i+1];
         break;

        }

     }

   if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high))
     {

      max_high_static = max_high;
      min_time1_static = min_time1;

     }
   else
      if(min_time1 != times_high && total_bar_high > 13)
        {
         max_high_static = 0;
         min_time1_static = 0;
        }

   if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low))
     {

      min_low_static = min_low;
      min_time2_static = min_time2;

     }

   else
      if(min_time2 != time_low && total_bar_low > 13)
        {
         min_low_static = 0;
         min_time2_static = 0;
        }

   ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3);

   ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3);
  }

説明

このコードは、ObjectCreate()関数を使用して、高値を示すhigh_obj_nameと、安値を示すlow_obj_nameの2つのトレンドラインをチャート上に作成します。これらのトレンドラインは現在の時刻(TimeCurrent())まで延長され、対応する時刻(min_time1_staticとmin_time2_static)の計算された高値と安値のレベル(max_high_staticとmin_low_static)から導出されます。これにより、トレーダーはチャートの高値と安値を視覚的に監視できるようになります。

ObjectSetInteger()関数を使用すると、これらのトレンドラインの外観を変更できます。高いトレンドラインは緑色、低いトレンドラインは赤色に設定されます。両方の線がチャート上で読みやすくなるように、線の幅は3に設定されています。この視覚ツールを使用すると、トレーダーとEAの両方が主要な価格レベルをより簡単に監視し、流動性スイープなどの市場の動きの可能性を評価できるようになります。

2.3.7.流動性スイープの売買条件の指定

特定された高値と安値の流動性スイープの資格を得るには、取引条件が特定の市場シナリオの精度と遵守を保証する必要があります。このセクションでは、これらの重要なレベル付近で売買取引を実行するためのロジックを確立します。

// Magic number
input int MagicNumber = 1111;

int       rsi_handle;
double    rsi_buffer[];

double open[];
double close[];
double high[];
double low[];
datetime time[];

double max_high = 0;
datetime min_time1 = 0;
double min_low = 0;
datetime min_time2 = 0;

datetime time_low = 0;
datetime times_high = 0;

string high_obj_name = "High_Line";
string low_obj_name = "Low_Line";

long chart_id;
double take_profit;
double  ask_price = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

   static double max_high_static = max_high;
   static datetime min_time1_static = min_time1;

   static double min_low_static = min_low;
   static datetime min_time2_static = min_time2;


//GETTING TOTAL POSITIONS

   int totalPositions = 0;

   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);

      if(PositionSelectByTicket(ticket))
        {

         if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
           {

            totalPositions++;

           }
        }
     }

   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);

   if(totalPositions < 1)
     {

      if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static
            && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static
                                       && close[0] > open[0] && close[1] < open[1])))
        {

         take_profit = (close[0] - low[0]) * 3 + close[0];

         trade.Buy(0.5,_Symbol,ask_price, low[0], take_profit);

        }

      else
         if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static
               && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static
                                          && close[0] < open[0] && close[1] > open[1])))
           {

            take_profit = MathAbs((high[0] - close[0]) * 3 - close[0]); // Adjusted take-profit calculation

            trade.Sell(0.5,_Symbol,ask_price, high[0], take_profit);
           }
     }

//CHART ID
   chart_id = ChartID();

   int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent());

   for(int i = 0; i < 12; i++)
     {

      if(close[i] < open[i] && close[i+1] > open[i+1])
        {

         max_high = (double)MathMax(high[i],high[i+1]);
         min_time1 = (datetime)MathMin(time[i],time[i+1]);

         break;

        }
     }

   int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent());
   for(int i = 0; i < 12; i++)
     {

      if(close[i] > open[i] && close[i+1] < open[i+1])
        {

         min_low = (double)MathMin(low[i],low[i+1]);

         min_time2 = (datetime)MathMin(time[i],time[i+1]);
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
        {

         time_low = time[i+1];
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
        {

         times_high = time[i+1];
         break;

        }

     }

   if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high))
     {

      max_high_static = max_high;
      min_time1_static = min_time1;

     }
   else
      if(min_time1 != times_high && total_bar_high > 13)
        {
         max_high_static = 0;
         min_time1_static = 0;
        }

   if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low))
     {

      min_low_static = min_low;
      min_time2_static = min_time2;

     }

   else
      if(min_time2 != time_low && total_bar_low > 13)
        {
         min_low_static = 0;
         min_time2_static = 0;
        }

   ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3);

   ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3);
  }

説明

totalPositionsによる複数ポジションの防止

totalPositions変数は、EAが一度に1つのアクティブポジションのみを維持することを保証します。このメカニズムは、すべてのポジションをチェックし、マジックナンバー(EAの取引の識別子)と銘柄が現在のチャートと一致するかどうかを確認することで、過剰露出を防止します。一致するポジションが存在しない場合(totalPositions<1)、EAは新しい取引をおこなうための条件を評価します。

この安全メカニズムにより、規律ある取引実行が保証され、ポジションの重複が回避されるため、戦略の管理が容易になり、リスクが増大する可能性が低くなります。

買い取引の条件(低流動性スイープ)

買い取引ロジックは、以前に特定された低レベル(min_low_static)での流動性スイープを検出し、強気の確認を確実にすることに重点が置かれています。

条件

安値割れ

  • 最近の3つのローソク足(low[0]、low[1]、low[2])のいずれかが、特定された最小安値を下回る必要があり、これは流動性がキーレベルを下回ったことを示唆しています。

強気な回復

  • 現在の終値(close[0])は最低安値(min_low_static)を超えて回復する必要があり、スイープ後の強気の意図を示します。
  • さらに、ローソク足は強気(終値[0]>始値[0])で、上昇の勢いを反映している必要があります。
追加確認(オプション)

  • 現在のローソク足の2本前(終値[1]<始値[1])が弱気だった場合、反転要素が追加され、スイープの正当性が強化されます。

取引執行

条件が満たされると、次の買い注文が実行されます。

  • TakeProfit:終値と安値の間の範囲の3倍(take_profit=(close[0]-low[0])*3+close[0])
  • StopLoss:識別された最小低レベル(low[0])

売り取引の条件(高流動性スイープ)

売り取引ロジックは買い取引ロジックを反映していますが、弱気の確認とともに以前に確立された高レベル(max_high_static)での流動性スイープを識別することに重点を置いています。

条件

上放れ

  • 最近の3つのローソク足(高値[0]、高値[1]、高値[2])のいずれかが、特定された最大高値を超えている必要があり、これはこの重要なレベルを超える流動性のスイープを示しています。

弱気反転

  • 現在の終値(close[0])は最大高値(max_high_static)を下回る必要があります。これはブレイクアウトを維持できなかったことを示します。
  • さらに、ローソク足は弱気(終値[0]<始値[0])で、下降の勢いを示唆している必要があります。
追加確認(オプション)

  • 現在のローソク足の2本前(終値[1]>始値[1])が強気だった場合、反転の可能性が示唆されます。

取引執行


条件が満たされると、次の売り注文が出されます。

  • TakeProfit:高値と終値の範囲の3倍(take_profit=MathAbs((high[0]-close[0])*3-close[0]))
  • StopLoss:識別された最大高レベル(high[0])

まとめ

この戦略は、totalPositionsメカニズムと明確に定義された売買条件を組み合わせることで、取引決定における精度を確保し、リスクを制限します。

  • 買い取引は、強気な回復とともに重要な安値水準を下回った後にトリガーされます。
  • 売り取引は、弱気反転で重要な高水準を上回った後に開始されます。

この構造化されたアプローチは、流動性スイープを中核概念として活用しながら、取引が有利な条件下でのみ実行されることを保証します。 この方法の制限の1つは、ストップロスがローソク足のlow[0]またはhigh[0]に基づいて動的に設定されることです。つまり、取引ごとのリスクはローソク足のサイズに応じて変化し、一貫性のないエクスポージャーにつながります。 これに対処するには、戦略で、取引ごとにリスクに対して口座残高の固定割合(例:2%)を指定できるようにする必要があります。これにより、指定されたリスク率に合わせて、エントリー価格とストップロスの間の距離に基づいてポジションサイズを計算することで、一貫したリスク管理が保証されます。

2.3.8.リスク管理と損益分岐点の変更

組み込みインジケーターを使用する場合、一貫したパフォーマンスを確保するために効果的なリスク管理が不可欠です。ローソク足のサイズは大きく異なる可能性があるため、取引ごとにリスクを負う口座残高の割合を指定することが重要になります。この一貫性により、リスク報酬比率(RRR)が、取引に勝利した際の損失を補うことができます。さらに、損益分岐点の修正により、取引が戦略に有利に動いたときに利益が確保されます。損益分岐点の修正を組み込むと、取引が有利に動いたときに利益が確保され、全体的な戦略の堅牢性が強化されます。

//+------------------------------------------------------------------+
//|                                      MQL5INDICATORS_PROJECT4.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "ForexYMN"
#property link      "[email protected]"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade trade;

// Magic number
input int    MagicNumber = 1111;
input double account_balance          = 1000;  // Account Balance
input double percentage_risk = 2.0;   //     How many percent of the account do you want to risk per trade?
input bool   allow_modify  = false; // Do you allow break even modifications?
input int    rrr           = 3;      // Choose Risk Reward Ratio

int       rsi_handle;
double    rsi_buffer[];

double open[];
double close[];
double high[];
double low[];
datetime time[];

double max_high = 0;
datetime min_time1 = 0;
double min_low = 0;
datetime min_time2 = 0;

datetime time_low = 0;
datetime times_high = 0;

string high_obj_name = "High_Line";
string low_obj_name = "Low_Line";

long chart_id;
double take_profit;
double  ask_price = 0;
double lot_size;
double risk_Amount;
double points_risk;

// Risk modification
double positionProfit = 0;
double positionopen = 0;
double positionTP = 0;
double positionSL = 0;
double modifyLevel = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Configure RSI buffer as a series for easier indexing
   ArraySetAsSeries(rsi_buffer, true);

// Initialize RSI handle for the current symbol, timeframe, and parameters
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

// Configure candlestick arrays as series
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(time, true);

// Set the magic number
   trade.SetExpertMagicNumber(MagicNumber);

   return (INIT_SUCCEEDED); // Indicate successful initialization
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   int currBars = iBars(_Symbol,_Period);
   static int prevBars = currBars;
   if(prevBars == currBars)
      return;
   prevBars = currBars;
// Copy RSI values from the indicator into the buffer
   CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer);

// Copy candlestick data
   CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open);
   CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close);
   CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high);
   CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low);
   CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time);

   static double max_high_static = max_high;
   static datetime min_time1_static = min_time1;

   static double min_low_static = min_low;
   static datetime min_time2_static = min_time2;

//GETTING TOTAL POSITIONS

   int totalPositions = 0;

   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);

      if(PositionSelectByTicket(ticket))
        {

         if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
           {


            totalPositions++;

           }
        }
     }

   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);

   if(totalPositions < 1)
     {

      if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static
            && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static
                                       && close[0] > open[0] && close[1] < open[1])))
        {

         take_profit = (close[0] - low[0]) * rrr + close[0];
         points_risk = close[0] - low[0];

         double riskAmount = account_balance * (percentage_risk / 100.0);
         double minus = NormalizeDouble(close[0] - low[0],5);
         lot_size = CalculateLotSize(_Symbol, riskAmount, minus);

         trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit);

        }

      else
         if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static
               && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static
                                          && close[0] < open[0] && close[1] > open[1])))
           {

            take_profit = MathAbs((high[0] - close[0]) * rrr - close[0]); // Adjusted take-profit calculation
            points_risk = MathAbs(high[0] - close[0]);

            double riskAmount = account_balance * (percentage_risk / 100.0);
            double minus = NormalizeDouble(high[0] - close[0],5);
            lot_size = CalculateLotSize(_Symbol, riskAmount, minus);

            trade.Sell(lot_size,_Symbol,ask_price, high[0], take_profit);
           }

     }

//CHART ID
   chart_id = ChartID();

   int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent());

   for(int i = 0; i < 12; i++)
     {

      if(close[i] < open[i] && close[i+1] > open[i+1])
        {

         max_high = (double)MathMax(high[i],high[i+1]);
         min_time1 = (datetime)MathMin(time[i],time[i+1]);

         break;

        }
     }

   int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent());
   for(int i = 0; i < 12; i++)
     {

      if(close[i] > open[i] && close[i+1] < open[i+1])
        {

         min_low = (double)MathMin(low[i],low[i+1]);

         min_time2 = (datetime)MathMin(time[i],time[i+1]);
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1])
        {

         time_low = time[i+1];
         break;

        }

     }

   for(int i = 0; i < 12; i++)
     {

      if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1])
        {

         times_high = time[i+1];
         break;

        }

     }

   if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high))
     {
      max_high_static = max_high;
      min_time1_static = min_time1;
     }
   else
      if(min_time1 != times_high && total_bar_high > 13)
        {
         max_high_static = 0;
         min_time1_static = 0;
        }
   if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low))
     {
      min_low_static = min_low;
      min_time2_static = min_time2;
     }
   else
      if(min_time2 != time_low && total_bar_low > 13)
        {
         min_low_static = 0;
         min_time2_static = 0;
        }

   ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen);
   ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3);


   ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed);
   ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3);

   if(allow_modify)
     {
      for(int i = 0; i < PositionsTotal(); i++)
        {
         ulong ticket = PositionGetTicket(i);

         if(PositionSelectByTicket(ticket))
           {
            positionopen = PositionGetDouble(POSITION_PRICE_OPEN);
            positionTP = PositionGetDouble(POSITION_TP);
            positionSL = PositionGetDouble(POSITION_SL);
            positionProfit = PositionGetDouble(POSITION_PROFIT);


            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
              {

               modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4));

               if(ask_price <= modifyLevel)
                 {

                  trade.PositionModify(ticket, positionopen, positionTP);
                 }
              }

            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
              {

               modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4));

               if(ask_price >= modifyLevel)
                 {
                  trade.PositionModify(ticket, positionopen, positionTP);
                 }
              }

           }
        }

     }

  }
             
//+------------------------------------------------------------------+
//| Function to calculate the lot size based on risk amount and stop loss
//+------------------------------------------------------------------+
double CalculateLotSize(string symbol, double riskAmount, double stopLossPips)
  {
// Get symbol information
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);

// Calculate pip value per lot
   double pipValuePerLot = tickValue / point;

// Calculate the stop loss value in currency
   double stopLossValue = stopLossPips * pipValuePerLot;

// Calculate the lot size
   double lotSize = riskAmount / stopLossValue;

// Round the lot size to the nearest acceptable lot step
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   lotSize = MathFloor(lotSize / lotStep) * lotStep;

// Ensure the lot size is within the allowed range
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   if(lotSize < minLot)
      lotSize = minLot;
   if(lotSize > maxLot)
      lotSize = maxLot;

   return lotSize;
  }

説明

input double account_balance   = 1000;  // Account Balance
input double percentage_risk   = 2.0;   // How many percent of the account do you want to risk per trade?
input bool   allow_modify      = false; // Do you allow break even modifications?
input int    rrr               = 3;     // Choose Risk Reward Ratio

入力は、取引戦略におけるリスク管理の主要なパラメータを定義します。account_balanceは固定の口座残高を指定します。これは、取引ごとにリスクを負う金額を計算するために使用され、一貫性が確保されます。percentage_riskは、各取引のリスクに対する口座残高の割合を決定し、エクスポージャーを制御し、安定したリスクレベルを維持するのに役立ちます。rrr(リスク報酬比率)は、リスクに対する望ましい報酬を設定し、戦略が損失よりも大きな利益を目指すようにします。 allow_modify入力は、取引が戦略に有利な方向に動いたときに、ストップロスを損益分岐点に移動するかどうかを制御します。有効にすると、利益が確保され、取引の勝利リスクが軽減されます。これらの入力を組み合わせることで、取引ごとに一貫したリスクを確保し、取引が成功したときに利益を保護することで、規律ある取引アプローチが作成されます。

take_profit = (close[0] - low[0]) * rrr + close[0];
points_risk = close[0] - low[0];

double riskAmount = account_balance * (percentage_risk / 100.0);
double minus = NormalizeDouble(close[0] - low[0],5);
lot_size = CalculateLotSize(_Symbol, riskAmount, minus);

trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit);

  • take_profit=(close[0]-low[0])*rrr+close[0];:現在の終値とローソク足の安値の間の距離にリスク報酬比率(rrr)を掛けて、テイクプロフィットレベルを計算します。結果は終値に追加され、エントリーポイントより上の利益目標が設定されます。
  • points_risk=close[0]-low[0];:エントリー価格(終値)からローソク足のストップロス(安値)までのリスク距離を計算します。
  • RiskAmount=account_balance*(percentage_risk/100.0);:口座残高とリスク率に基づいて、取引ごとのリスク額を計算します。
  • lot_size=CalculateLotSize(_Symbol,RiskAmount,minus);:リスク量とストップロス距離(マイナス)に基づいてロットサイズを計算し、取引が定義されたリスクと一致するようにします。

売り取引の場合

  • take_profit=MathAbs((high[0]-close[0])*rrr-close[0]);:リスクと報酬の比率を考慮しながら、現在の終値とローソク足の高値の間の距離を使用してテイクプロフィットの計算を調整します。
  • points_risk=MathAbs(high[0]-close[0]);:エントリーポイントからストップロスレベル(ローソク足の高値)までの売り取引のリスク距離を計算します。
  • 残りのロジック(リスク量とロットサイズの計算)は買い取引の場合と同じであり、両方の取引タイプで一貫したリスク管理が保証されます。
どちらの場合も、計算されたlot_sizeは、適切なリスクと報酬の設定で買いまたは売りの取引をおこなうために使用されます。
if(allow_modify)
  {
   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);


      if(PositionSelectByTicket(ticket))
        {
         positionopen = PositionGetDouble(POSITION_PRICE_OPEN);
         positionTP = PositionGetDouble(POSITION_TP);
         positionSL = PositionGetDouble(POSITION_SL);
         positionProfit = PositionGetDouble(POSITION_PROFIT);

         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
           {

            modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4));

            if(ask_price <= modifyLevel)
              {

               trade.PositionModify(ticket, positionopen, positionTP);
              }
           }

         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
           {

            modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4));

            if(ask_price >= modifyLevel)
              {
               trade.PositionModify(ticket, positionopen, positionTP);
              }
           }
        }
     }
  }

提供されたコードは、特定の条件に基づいてアクティブなポジションを変更するロジックを処理し、特に買いポジションと売りポジションの両方の損益分岐点調整の実装に重点を置いています。コードの機能の詳細は次のとおりです。

変更権限(allow_modify)の確認

  • 「if(allow_modify)」ブロックは、ユーザーが定義した損益分岐点の変更が許可されている場合にのみ変更ロジックが実行されるようにします。
すべてのポジションをループする

  • forループは、現在開いているすべてのポジション(PositionsTotal())を反復処理します。各位置が変更条件を満たしているかどうかがチェックされます。

ポジションの選択

  • コードは各ポジションのチケットを取得し(PositionGetTicket(i))、それを使用してポジションを選択します(PositionSelectByTicket(ticket))。

ポジションの詳細を取得する

選択されたポジションごとに、次の詳細が取得されます。

  • positionopen:ポジションが開かれた価格
  • positionTP:ポジションのテイクプロフィットレベル
  • positionSL:ポジションのストップロスレベル
  • positionProfit:ポジションの現在の利益

売りポジションの変更

  • コードは、ポジションが売りポジション(POSITION_TYPE_SELL)であるかどうかを確認します。
  • ストップロス(positionSL)と始値(positionopen)の絶対差として、modifyLevelを計算し、現在の売り値(ask_price)がこのレベルに到達したか、またはこのレベルを超えたかどうかを確認します。
  • 条件が満たされた場合、ストップロスは始値(損益分岐点)に変更され、テイクプロフィットは変更されません(trade.PositionModify(ticket,positionopen,positionTP))。
買いポジションの変更

  • 同様に、買いポジション(POSITION_TYPE_BUY)の場合、modifyLevelを始値とストップロスの絶対差として計算し、売り値がこのレベルを超えて有利に動いたかどうかを確認します。
  • 条件が満たされた場合、ストップロスを始値(損益分岐点)に変更し、テイクプロフィットは変更しません。



結論

この記事では、実践的なプロジェクトベースのアプローチを通じて、MQL5における組み込みインジケーターの活用方法を探りました。RSIを例として、自動売買用のEAを開発し、買われ過ぎや売られ過ぎのシグナルによって引き起こされる取引エントリーおよびエグジットに焦点を当てました。プロセス全体を通じて、利益目標と計算されたリスクを一致させるためのリスクリワード比率(RRR)の活用や、ローソク足のサイズが異なる状況にも対応できるように、取引ごとに一定のパーセンテージでリスクを設定することの重要性を強調しました。また、戦略の信頼性と効果を高めるために、流動性スイープへの対応や、取引が進行する中で利益を確保するためのブレイクイーブン調整も導入しました。これにより、EAは市場の急激な変動にも柔軟に対応できるようになりました。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16514

MQL5取引ツールキット(第4回):履歴管理EX5ライブラリの開発 MQL5取引ツールキット(第4回):履歴管理EX5ライブラリの開発
詳細なステップバイステップのアプローチで拡張履歴管理EX5ライブラリを作成し、MQL5を使用してクローズされたポジション、注文、取引履歴を取得、処理、分類、並べ替え、分析、管理する方法を学びます。
ケリー基準とモンテカルロシミュレーションを使用したポートフォリオリスクモデル ケリー基準とモンテカルロシミュレーションを使用したポートフォリオリスクモデル
数十年にわたり、トレーダーは破産リスクを最小限に抑えつつ長期的な資産成長を最大化する手法として、ケリー基準の公式を活用してきました。しかし、単一のバックテスト結果に基づいてケリー基準を盲目的に適用することは、個人トレーダーにとって非常に危険です。というのも、実際の取引では時間の経過とともに取引優位性が薄れ、過去の実績は将来の結果を保証するものではないからです。本記事では、Pythonによるモンテカルロシミュレーションの結果を取り入れ、MetaTrader 5上で1つ以上のエキスパートアドバイザー(EA)にケリー基準を現実的に適用するためのリスク配分アプローチを紹介します。
Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(III) Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(III)
連載第3回へようこそ。今回は、日足のトレンドに沿った最適なエントリーポイントを特定する戦略として、ダイバージェンスの活用について詳しく解説します。また、トレーリングストップロスに似た、しかし独自の機能を備えたカスタム利益ロック機構もご紹介します。さらに、Trend Constraint EAを高度化し、既存の取引条件を補完する形で新たなエントリー条件を追加します。今後も、MQL5を活用したアルゴリズム開発の実践的な応用方法を深掘りし、実際に使えるテクニックや洞察を継続的にお届けしていきます。
MQL5経済指標カレンダーを使った取引(第5回):レスポンシブコントロールとフィルターボタンでダッシュボードを強化する MQL5経済指標カレンダーを使った取引(第5回):レスポンシブコントロールとフィルターボタンでダッシュボードを強化する
この記事では、ダッシュボードの制御を改善するために、通貨ペアフィルター、重要度レベル、時間フィルター、キャンセルオプションのボタンを作成します。これらのボタンは、ユーザーのアクションに動的に応答するようにプログラムされており、シームレスな操作を可能にします。また、ダッシュボードにリアルタイムの変更を反映するために、ユーザーの行動を自動化します。これにより、パネルの全体的な機能性、モビリティ、応答性が向上します。
OSZAR »