
MQL5入門(第10回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド
はじめに
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では、インジケーターハンドルはインジケーターが作成または初期化されるときに割り当てられる一意の識別子です。これらのハンドルはインジケーターへの参照として機能し、インジケーターを操作してそのデータにアクセスできるようになります。インジケーターをチャートに追加する場合、期間、価格タイプ、インジケーターの動作を定義するその他の設定など、特定のプロパティを入力する必要があります。
コードでは、ハンドルは同様の役割を果たします。つまり、プログラムがどのインジケーターを操作しているかを「認識」し、そのプロパティにアクセスできるようにします。基本的に、インジケーターハンドルは、プログラム内でインジケーターの設定を入力してトリガーする手段として機能し、取引戦略でインジケーターを効果的に操作できるようにします。
iRSIやiBandsなどの関数を使用してインジケーターハンドルを作成すると、コードがその特定のインジケーターに「バインド」されるため、そのデータを簡単に取得して操作できるようになります。ハンドルがなければ、プログラムは異なるインジケーターを区別できず、インジケーターバッファの計算値にアクセスすることもできません。 たとえば、インジケーターの設定をコードに入力する場合は、iRSIなどの関数を使用して必要なパラメータ(期間、適用価格、シフトなど)を指定し、RSIインジケーターのハンドルを作成します。
構文
iRSI(symbol, period, rsi_period, applied_price);
説明
- symbol:RSIを計算する銘柄(通貨ペア、株式、または資産)を指定します。
- period:RSIが計算される期間(または時間枠)。RSIがデータポイントをどのくらい遡って考慮するかを定義します。
- rsi_period:RSIの計算に使用される期間の数。RSIは通常14期間を使用して計算されますが、これは戦略に合わせて調整できます。
- applied_price:RSIを計算するときに使用する価格タイプ。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つのバッファがあり、各ローソク足で計算された移動平均値が格納されます。
相対力指数(RSI)
同様に、RSIインジケーターにはRSI値を格納するためのバッファが1つあります。
ボリンジャーバンド
このインジケーターは、データを格納するために3つのバッファを使用します。
- 中間バンド(インデックス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は上昇を予想して買い取引を開始します。
2.1.2.売りのロジック
- RSI値が70を超えているかどうかを確認します。
- RSIが高値を形成したかどうかを判断します。
- EAは通常のチャート上の対応するローソク足の高値を識別します。
- 次に、価格の動きを監視し、価格が特定されたローソク足の高値を上回り、流動性を一掃するのを待ちます。
- 価格が高値を下回った後、EAは最初の弱気ローソク足が高値を下回って終値を付けると売り取引をおこない、下降を予測します。
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値にアクセスできるようになります。これは、買われ過ぎや売られ過ぎの状況を分析し、取引の決定を下すために重要です。
出力
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の安値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つの一致するローソク足の安値を使用して正確なレベルを決定します。
この最安値は、プログラムが潜在的な流動性スイープを待機する領域を確立するため重要です。
ロジック
- RSIが最安値を形成するのは、次の場合です。
- rsi_buffer[i+1] < 30:RSIは売られ過ぎレベルを下回っている
- rsi_buffer[i] > rsi_buffer[i+1]:RSIは最低点に達した後、上昇し始める
- RSIの安値が確認されると、プログラムはチャート上の対応する2つのローソク足の安値配列を使用して最安値を識別します。
- この最安値は、EAが潜在的な流動性スイープを待機するレベルを設定します。
- RSIの安値が特定されると、プログラムは安値配列から対応する2つのローソク足の安値を取得します。
- MathMin()関数を使用して、これら2つの安値間の最小値を計算し、流動性スイープを監視するためのレベルをマークします。
- この最安値は、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; } }
2.3.4.2.チャートの高値をマーク
RSIの高値を識別するロジックが満たされると、プログラムは対応するローソク足データを評価して、チャートの最大高値を決定します。高値を形成するには2つのRSI値が必要であるため、EAは2つの一致するローソク足の高値を使用して正確なレベルを特定します。
この最高値は、EAが潜在的な流動性スイープを待機する領域を設定するための基準になります。このアプローチは、安値の場合のロジックの逆になります。
ロジック
- RSIが高値を形成するのは、次の場合です。
- rsi_buffer[i+1] > 70:RSIは買われ過ぎレベルを超えて上昇します。
- rsi_buffer[i] < rsi_buffer[i+1]:RSIは最高値に達した後、下降し始めます。
- RSIの高値が確認されると、プログラムはチャート上の対応する2つのローソク足の高値配列を使用して最高値を識別します。
- この最高値は、EAが潜在的な流動性スイープを待機するレベルを設定します。
- RSIの高値が特定されると、プログラムは高値配列から対応する2つのローソク足の高値を取得します。
- 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; } }
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ロジックによって、高値または安値が形成されているかどうかを評価するために使われます。
- この場合、プログラムは関連するローソク足から最高値または最安値を決定します。
- Bars()を使用して、プログラムは最後の高値または安値以降に形成されたローソク足の数を計算します。
- 高値または安値は、少なくとも12本のローソク足が経過した場合にのみ更新され、頻繁な変化を防ぎます。
- 13本を超えるローソク足が形成され、前回の高値または安値のRSI条件が有効でなくなった場合、保存された値はクリアされます。
- これにより、EAが取引の決定に古い情報に依存しないことが保証されます。
このセクションでは、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]);:エントリーポイントからストップロスレベル(ローソク足の高値)までの売り取引のリスク距離を計算します。
- 残りのロジック(リスク量とロットサイズの計算)は買い取引の場合と同じであり、両方の取引タイプで一貫したリスク管理が保証されます。
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





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