
MQL5入門(第11回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド(II)
はじめに
MQL5の連載にようこそ。本章では、非常に有用かつ興味深いテーマを取り扱います。前章では、MQL5の組み込みインジケーターの使用方法に焦点を当て、特にRSI(相対力指数)について詳しく解説しました。また、RSIを取引戦略に組み込む方法を示す実践的なプロジェクトにも取り組みました。今回はさらに一歩踏み込み、RSIだけでなく、ストキャスティクス、移動平均(MA)という3つの有力なインジケーターを本プロジェクトに組み込みます。加えて、本稿では隠れたダイバージェンスの検出という興味深い概念にも取り組みます。具体的には、隠れた強気および弱気のダイバージェンスをどのように検出するかについて解説します。
このプロジェクトの目的は、あくまで教育的なものであり、読者がMQL5の使用に自信を持ち、こうしたパターンをプログラム上で識別する方法を学ぶことにあります。それでは、MQL5の理解を深め、取引スキルを一段階高めるための実践的なプロジェクトに取り組んでいきましょう。
この記事では、次の内容を学びます。
- RSIの安値および高値を価格変動と関連付けることで、隠れた強気および弱気のダイバージェンスを検出する方法
- 複数のインジケーター(RSI、移動平均、ストキャスティクス)を活用したEAの開発方法
- チャート上における隠れた強気・弱気ダイバージェンスの識別および可視化
- 一貫性のないローソク足のサイズに対応するために、ドルリスクとストップロスの距離に基づいてロットサイズを計算するリスク管理手法の実装
- ダイバージェンスシグナルに基いた、事前定義されたリスクリワードレシオ(RRR)を用いた取引の自動化
1. プロジェクトの設定
1.1. EAの仕組み
本プロジェクトでは、移動平均(MA)およびRSIインジケーターを使用して、隠れた強気および弱気のダイバージェンスを検出します。RSIは、RSIレベルと対応する価格との間の乖離を特定する役割を担い、一方でMAはトレンドの識別に使用されます。ストキャスティクスは、最終的に売買シグナルの検証にのみ用いられ、ダイバージェンスの特定には使用されません。
1.1.1.隠れた強気のダイバージェンス
この局面では、RSIがより低い安値(LL)を形成しつつ、価格はより高い安値(HL)を形成します。このダイバージェンスは、RSIが弱さを示している一方で、対応する価格(C-Price)がより高い下値をつけていることから、将来的な上昇圧力の可能性を示唆しています。
1.1.2. 隠れた弱気のダイバージェンス
この局面では、RSIがより高い高値(HH)を形成し、価格はより低い高値(LH)を形成します。これは、将来的な下落圧力の可能性を示唆しています。
1.1.3. 売りエントリーのロジック
トレンドフィルター:
- 現在の価格が200EMAの下にあることを確認し、下降トレンドを判断します。
直近の有意なRSI高値の特定:
- 陽線の後に形成され、その後3本連続で陰線が続くような直近のRSI高値を特定します。
- その水準に、RSIチャート上に水平線を引きます。
対応する価格の特定:
- 価格チャート(ラインチャートに切り替え)上で、先ほどのRSI高値に対応する価格(C-PRICE)水準に水平線を引きます。
隠れた弱気のダイバージェンス:
- RSIが先に引いた水平線よりも高い高値を形成しているかを確認します。
- その際、対応する高値が価格の水平線より下にあることを確認します。
ストキャスティクスによる確認:
- すべての条件が満たされた後、ストキャスティクスが買われすぎ水準(例:80)を上抜けた後、再び下抜けるのを待ちます。
- このクロスが11本以内に発生しない場合、エントリーはおこないません。
SLとTPの設定:
- SL:最後のスイングハイ
- TP:あらかじめ入力パラメータとして定義されたリスクリワードレシオ(RRR)に基づいて設定
1.1.4. 買いエントリーのロジック
トレンドフィルター:
- 現在の価格が200EMAの上にあることを確認し、上昇トレンドであることを確認します。
直近の有意なRSI安値の特定:
- 陰線の後に形成され、その後3本連続で陽線が続くような直近のRSI安値を特定します。
- その水準に、RSIチャート上に水平線を引きます。
対応する価格の特定:
- 価格チャート(ラインチャートに切り替え)上で、先ほどのRSI安値に対応する価格(C-PRICE)水準に水平線を引きます。
隠れた強気のダイバージェンス:
- RSIが先に引いた水平線よりも低い安値を形成しているかを確認します。
- このとき、対応する安値が価格の水平線より上にあることを確認します。
ストキャスティクスによる確認:
- すべての条件が満たされた後、ストキャスティクスが売られすぎ水準(例:20)を下抜けた後、再び上抜けるのを待ちます。
- このクロスが11本以内に発生しない場合、エントリーはおこないません。
SLとTPの設定:
- SL:最後のスイングロー
- TP:あらかじめ入力パラメータとして定義されたリスクリワードレシオ(RRR)に基づいて設定
2. インジケーターのプロパティ設定とデータ取得
2.1. インジケーターのプロパティ設定
プロジェクトで使用するインジケーター(ストキャスティクス、RSI、移動平均)のプロパティを設定します。これらのプロパティは、EAを設計する際に柔軟性を持たせるためのものであり、各インジケーターがどのように動作するかを定義する役割を果たします。
比喩的な説明
特定の作業のためにツールキットを組み立てていると想像してみてください。移動平均は巻き尺のようなもので、作業全体の大きさ(=トレンドの方向)を把握する手助けをします。RSIは虫眼鏡のような役割を果たし、微細な変化や隠れたダイバージェンスを見つけるのに役立ちます。ストキャスティクスは、最後の段階(=エントリーの確認)において全体のバランスを調整する水平器のような役割を担います。
この作業において、コンピュータはあなたのアシスタントです。ですが、これらのツールを使用させる前に、それが何であり、どのように機能すべきかを正確に説明しなければなりません。汎用的なツールをただ渡すだけでは、コンピュータは何をすべきか理解できません。そうではなく、巻き尺の種類、虫眼鏡の拡大倍率、水平器の感度といったように、それぞれのツールの特性を明確にS設定する必要があります。これらのパラメータを設定することで、コンピュータはどのインジケーターを使用するべきか、そしてどのように使えばよいかを正しく理解し、効果的に動作できるようになります。
例//DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); return(INIT_SUCCEEDED); }
解説
あなたが作業場にいて、プロジェクトのために3つの専門的なツールを使おうとしているところを想像してみてください。それらは、巻き尺(移動平均)、虫眼鏡(RSI)、そして水平器(ストキャスティクス)です。作業を始める前に、それぞれのツールが正しくセットアップされ、使用可能な状態であることを確認する必要があります。ただツールボックスから取り出して、すぐに使い始めるわけにはいきません。
インジケーターハンドルの宣言(ツールの準備)
int ma_handle; int rsi_handle; int stoch_handle;
作業場のキャビネットに各ツール専用の保管場所に名前を付けるようなものです。それぞれのツールには専用の場所が必要で、必要なときにすぐに見つけられるようにしておかなければなりません。
- ma_handleは巻き尺(移動平均)のためのものです。
- rsi_handleは虫眼鏡(RSI)のためのものです。
- stoch_handleは水平器(ストキャスティクス)のためのものです。
ツールの設定(インジケーターの初期化)
iMA(移動平均)の設定ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE);
これはアシスタントに、このプロジェクトのために巻き尺(移動平均)をセットアップしてくれと伝えるようなものです。つまり、ツールの使い方を正確に指定しているわけです。
- _Symbol:これは「何を測定するのか(通貨ペアや株式、資産など)」をアシスタントに伝えるものです。ちょうど巻き尺で「何を測るか」を最初に決めるのと同じです(例:EUR/USD)。
- PERIOD_CURRENT:これは、使用する時間枠を意味します。巻き尺の「目盛りの長さ」を決めるようなもので、過去1日分を見るのか、1週間なのか、1ヶ月なのかを指定します。PERIOD_CURRENTは現在開いているチャートの時間枠を使用するという意味です。
- 200:これは巻き尺の長さ、つまり「過去何本のローソク足を対象にするか」です。ここでは過去200本の足を見て、全体のトレンドを測定するようアシスタントに指示しています。
- 0:これはシフト量を示します。測定の位置を右または左にどれだけずらすかを指定します。ここでは「0」、つまりずらさずに現在の価格と過去200本分をそのまま測定することを意味します。
- MODE_EMA:これは使用する「巻き尺の種類」を指定しています。ここでは指数平滑移動平均(EMA)です。EMAは単純移動平均よりも価格変動に素早く反応するため、より敏感な測定が可能です。つまり、新しいデータにすぐに適応できる巻尺を選択することになります。
- PRICE_CLOSE:これは巻き尺が「どこを基準にして測るか」を示します。ここでは各ローソク足の終値を使って計算するように指示しています。ちょうど、巻き尺を特定の位置(たとえば端点)から測るように指定するのと同じです。
iRSI(相対力指数)の設定
rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
次に、拡大鏡(RSI)を設定します。あなたはアシスタントに、これをどのように使うべきかを正確に指示します。
- _Symbol:前と同様に、分析対象となる銘柄(資産や通貨ペア)を指定します。
- PERIOD_CURRENT:移動平均のときと同じく、現在開いているチャートの時間足を使って分析をおこないます。
- 14:これはRSIの計算期間です。虫眼鏡の拡大倍率を設定するようなもので、過去14本のローソク足を対象に相対的な強さを評価します。この倍率で、小さなダイバージェンスの兆候を見逃さずに捉えることができます。
- PRICE_CLOSE:移動平均と同様に、各ローソク足の終値を使用してRSIを算出します。これにより、各期間の終点に基づいた正確な分析が可能になります。
iStochastic(ストキャスティクス)の設定
stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH);
- _Symbol:他のインジケーターと同様に、分析対象となる銘柄(資産や通貨ペア)を指定します。
- PERIOD_CURRENT:現在開いているチャートの時間足を使用して分析をおこないます。
- 5、3、3:これらはストキャスティクス・オシレーターのパラメータです。
- 5(%K期間):これは、ストキャスティクス(=水平器)の価格変動に対する感度を設定するもので、過去5期間(ローソク足)にわたって価格がどの位置にあるかを測定します。
- 3(%D期間):これは%Kの平滑化バージョンであり、より明確なシグナルを得るために使用されます。
- 3(減速期):これはさらに平滑化をかけることで、インジケーターが小さな変動に過剰反応しないようにし、誤ったシグナルを回避する役割を果たします。
- MODE_SMA:これは、%Kおよび%Dの平滑化において単純移動平均(SMA)を使用することを指定しています。SMAは最も基本的で信頼性の高い平均化手法です。
- STO_LOWHIGH:これは、ストキャスティクスの計算において「安値/高値」方式を用いることを示します。つまり、終値ではなく、各期間における最安値と最高値を基準にして計算をおこなうようアシスタントに指示しています。
これらのパラメータをコンピュータに指定することで、各インジケーター(ツール)が正しく調整され、プロジェクト内でその役割を正確かつ効果的に果たせるようにしているのです。
3. インジケーターおよびローソク足データの取得
3.1.インジケーターデータの取得
このセクションでは、前のセクションで設定したインジケーターからデータを抽出することに重点を置きます。取得したインジケーターデータを利用することで、EAの取引判断に役立てることが可能になります。対象となるのは、ストキャスティクス、RSI、および移動平均(MA)です。これらの要素は、EAにとって極めて重要な判断材料であり、EAが取引をおこなうべきかどうかを決定するためのシグナルを提供します。ちょうど、作業を進める建築者が正確な測定値を必要とするように、EAもこれらのインジケーターから正確な情報を取得しなければなりません。そうすることで、プロジェクトが順調に進んでいるかどうかを判断できます。このあとのステップでは、これらの変数をどのように呼び出し、保存するかを具体的に解説します。EAがリアルタイムの市場データに基づいて適切な判断を下せるようにするための方法です。
例
//DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA double ma_buffer[]; double rsi_buffer[]; double stoch_buffer_k[], stoch_buffer_d[]; //DECLEAR VARIABLES TO STORE CANDLE DATA double open[]; double close[]; double high[]; double low[]; datetime time[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(ma_buffer,true); ArraySetAsSeries(rsi_buffer,true); ArraySetAsSeries(stoch_buffer_k,true); ArraySetAsSeries(stoch_buffer_d,true); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyBuffer(ma_handle,0,1,30,ma_buffer); CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k); CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d); CopyBuffer(rsi_handle,0,1,30,rsi_buffer); //COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time); }
解説
インジケーターデータを格納するdouble型配列の宣言
まず、インジケーターから得られる数値データを格納するために配列を宣言する必要があります。これらの配列はdouble型であり、ストキャスティクス、RSI、移動平均といったインジケーターからの数値が保存されます。ma_buffer[]は移動平均の値を格納します。rsi_buffer[]はRSIの値を格納します。stoch_buffer_k[]とstoch_buffer_d[]は、それぞれストキャスティクスの%Kラインと%Dラインの値を格納します。これらの配列は最初は空ですが、グラフィカルに取得されるインジケーターデータで即座に埋められていきます。
チャート上の最新ローソク足から始める
このステップでは、ArraySetAsSeries()関数を使って、配列の順序を最新のデータが配列のインデックス0に来るように設定します。引数にtrueを渡すことで、プログラムに、もっとも新しい値から順に配列を埋めるように指示しています。これは非常に重要な設定です。なぜなら、デフォルトでは配列は最も古いデータから順に埋まりますが、今回のように取引判断において最新データを優先する場合には、逆順にする必要があるからです。このようにデータの順序を設定しておくことで、取引判断を下す際に最新情報にすばやくアクセスできるようになります。
インジケータデータを配列にコピーする
配列の準備が整ったら、CopyBuffer()関数を使用して実際のインジケーターデータをチャートから取得し、先ほど宣言した配列へコピーします。この関数は、各インジケーターの内部バッファからデータを取得して、配列へ転送します。CopyBuffer()への各呼び出しは1つのインジケータに固有です。
- 移動平均の場合、ma_handleからma_buffer[]にデータをコピーします。
- ストキャスティクスの場合、%Kと%Dの両方の値をそれぞれstoch_buffer_k[]とstoch_buffer_d[]にコピーします。
- RSIのデータはrsi_buffer[]にコピーします。
CopyBuffer()関数の第3引数はコピーを開始する位置で、ここでは「直近から2本目」を指定しています。第4引数には取得するバーの本数(ここでは30本)を指定しています。これにより、EAは必要な過去のインジケーターデータを取得し、分析と判断に活用できるようになります。
これらの手順により、インジケーターからのデータを収集・保存・アクセスする体制が整います。配列の宣言、データ順序の設定、そしてCopyBuffer()による取得処理を通じて、インジケーターの値をもとに取引判断を下すためのすべての準備が整うのです。
3.2. ローソク足データ
インジケーターからデータを正常に取得した後は、インジケーターの値に対応するローソク足データを取得する必要があります。ローソク足データは、インジケーターを適用する文脈を提供するものであり、取引判断において極めて重要です。インジケーターがトレンドやモメンタムを示す一方で、ローソク足は実際の市場価格の動きを可視化します。インジケーターとローソク足データを組み合わせることで、強気または弱気の隠れたダイバージェンスの形成や、市場心理の変化を示唆するその他のパターンなど、有望な取引シグナルを見つけることが可能になります。
例
double open[]; double close[]; double high[]; double low[]; datetime time[];
ローソク足の基本情報を格納するために、open[]、close[]、high[]、low[]、time[]といった配列を宣言します。これらの配列は、それぞれローソク足の始値、終値、高値、安値、そして対応するタイムスタンプを保持します。これらの変数は、価格の動きを分析し、先に取得したインジケーターのデータと照らし合わせるために重要な役割を果たします。
ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true);最新のローソク足が各配列の先頭に配置されるようにするために、ArraySetAsSeries関数を使用します。この関数によって配列のデータ順が並べ替えられ、最新のローソク足がインデックス0に格納され、それ以降に過去のローソク足が続く構造になります。この並び方により、分析が簡略化され、インジケーターのデータ構造との整合性も確保されます。
CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time);
CopyOpen、CopyClose、CopyHigh、CopyLow、CopyTimeといった関数を使用して、配列に実際の市場データを格納します。これらの関数により、チャート上の2本目のローソク足から直近30本分のデータを取得します。このようにして取得した価格データを、対応するインジケーターの値と正確に対応させることで、より正確な取引判断が可能になります。
4. 最新のRSI高値および安値を対応するデータと共に特定する
インジケーターの挙動とプライスアクションを比較するための基準として、最新のRSI高値および安値を特定することは極めて重要です。たとえば、価格がより高い安値を形成し、RSIがより低い安値を形成している場合、これは隠れた強気のダイバージェンスを示しており、市場が上昇を継続する可能性を示唆します。一方で、価格がより低い高値を形成し、RSIがより高い高値を形成している場合には、隠れた弱気のダイバージェンスが発生しており、下降トレンドが継続する可能性を示します。
また、その時点における高値が移動平均(MA)を下回っていたか、あるいは安値がMAを上回っていたかを確認するために、該当するMAの数値も取得します。この追加の検証により、想定される価格方向とトレンドとの整合性が確認され、ダイバージェンスシグナルの信頼性が高まります。
4.1. 隠れた強気のダイバージェンスを検出する
最新のRSI安値を特定することは、隠れた強気のダイバージェンスを検出する上での重要なステップです。RSIの安値は、価格が調整局面に入った際にモメンタムが一時的に弱まるポイントを示しており、対応する価格および移動平均(MA)の値と合わせて分析することで、重要な洞察が得られます。隠れた強気のダイバージェンスは、RSIがより低い安値を形成しつつも、価格がより高い安値を形成しているときに発生します。これは、調整が発生しているにもかかわらず、上昇方向のモメンタムが維持されている可能性を示唆しています。
このRSI安値を検出するために、「少なくとも1本の陰線の後に、3本連続で陽線が続いたタイミングで形成された安値であること」という条件を満たすロジックを定義します。この条件により、その安値がモメンタムの明確な転換点を反映していることを保証します。RSIの安値が特定されたら、そのインデックスを記録し、その時点における価格チャート上の対応価格を取得し、移動平均(MA)の値も取り出して、価格安値がMAを上回っているかを確認します。この検証により、ダイバージェンスシグナルが現在のトレンドと整合していることが確認され、その信頼性が高まります。
さらに、RSIの安値が特定された後は、その安値以降に新たに出現する、より低いRSI安値を探すロジックを定義します。この新たな安値は、「RSIの値が先に特定したRSI安値を下回っている」という条件を満たす必要があります。これにより、ダイバージェンスの条件が維持されているかを確認でき、隠れた強気のダイバージェンスの基準に合致していることが確認できます。これらのステップを統合することで、隠れた強気のダイバージェンスを確実に検出するための、構造化された一貫性のあるアプローチが構築されます。
例//DECLEAR INDICATOR HANDLES int ma_handle; int rsi_handle; int stoch_handle; //DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA double ma_buffer[]; double rsi_buffer[]; double stoch_buffer_k[], stoch_buffer_d[]; //DECLEAR VARIABLES TO STORE CANDLE DATA double open[]; double close[]; double high[]; double low[]; datetime time[]; //DECLEAR VARIBLE ROR RSI LOW AND CORESPONDING CANDLE double rsi_low_value; // Stores the RSI value at the identified low point. double corresponding_low_value; // Stores the price (closing value) corresponding to the RSI low. double corresponding_low_ma; // Stores the Moving Average value at the same point. datetime rsi_low_time; // Stores the timestamp of the RSI low. //DECLEAR VARIABLE FOR THE MINIMUM CORRESPONDING PRICE VALUE int minimum_value_low; //DECLEAR VARIBLE ROR NEW RSI LOW AND CORESPONDING CANDLE double new_rsi_low_value; datetime new_rsi_low_time; double new_corresponding_low_value; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,200,0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,5,3,3,MODE_SMA,STO_LOWHIGH); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(ma_buffer,true); ArraySetAsSeries(rsi_buffer,true); ArraySetAsSeries(stoch_buffer_k,true); ArraySetAsSeries(stoch_buffer_d,true); //START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //COPY INDICATOR'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyBuffer(ma_handle,0,1,30,ma_buffer); CopyBuffer(stoch_handle,0,1,30,stoch_buffer_k); CopyBuffer(stoch_handle,1,1,30,stoch_buffer_d); CopyBuffer(rsi_handle,0,1,30,rsi_buffer); //COPY CANDLE'S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyOpen(_Symbol,PERIOD_CURRENT,1,30,open); CopyClose(_Symbol,PERIOD_CURRENT,1,30,close); CopyHigh(_Symbol,PERIOD_CURRENT,1,30,high); CopyLow(_Symbol,PERIOD_CURRENT,1,30,low); CopyTime(_Symbol,PERIOD_CURRENT,1,30,time); //LOOP THROUGH THE LAST 30 BARS ON THE CHART for(int i = 0; i < 30; i++) { //PREVENT ARRAY OUT OF RANGE ERROR if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30)) { //LOGIC TO IDENTIFY THE LATEST RSI LOW if(rsi_buffer[i+4] > rsi_buffer[i+3] && rsi_buffer[i+2] > rsi_buffer[i+3] && rsi_buffer[i+1] > rsi_buffer[i+2] && rsi_buffer[i] > rsi_buffer[i+1]) { //GETTING LATEST RSI LOW, CORRESPONDING CANDLE LOW, CORRESPONDING MA VALUE, and RSI TIME rsi_low_value = rsi_buffer[i+3]; corresponding_low_value = close[i+3]; corresponding_low_ma = ma_buffer[i+3]; rsi_low_time = time[i+3]; break; } } } //TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI LOW int total_bars_2 = 0; total_bars_2 = Bars(_Symbol,PERIOD_CURRENT,rsi_low_time, time[0]); //MINIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW minimum_value_low = ArrayMinimum(close,0,total_bars_2); if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value) { //CREATE LINES TO MARK RSI AND CORRESPONDING CANDLE LOW ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value); ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0); ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value); ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value); ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack); //19 LOGIC TO GET THE NEW RSI LOW if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value) { new_rsi_low_value = rsi_buffer[1]; new_rsi_low_time = time[1]; new_corresponding_low_value = close[1]; } //CONDITION FOR HIDDEN BULLISH DIVERGENCE if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value) { ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value); ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value); ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value); ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL"); ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack); //for candle ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value); ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value); ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value); ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL"); ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack); } } //LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT else if((close[0] < ma_buffer[0]) || (close[minimum_value_low] < corresponding_low_value)) { ObjectDelete(ChartID(),"RSI LOW VALUE"); ObjectDelete(ChartID(),"C-CANDLE"); ObjectDelete(ChartID(),"C-CANDLE LINE"); ObjectDelete(ChartID(),"RSI LOW LINE"); ObjectDelete(ChartID(),"C-CANDLE TEXT"); ObjectDelete(ChartID(),"RSI LOW TEXT"); ObjectDelete(ChartID(),"RSI LOW TREND LINE"); ObjectDelete(ChartID(),"L"); ObjectDelete(ChartID(),"LL"); ObjectDelete(ChartID(),"C-CANDLE TREND LINE"); ObjectDelete(ChartID(),"CL"); ObjectDelete(ChartID(),"RSI LOW TEXT"); ObjectDelete(ChartID(),"CLL"); } }
解説
rsi_low_valueは、指定された範囲内で最も低いRSI値を記録し、潜在的なダイバージェンスの検出に役立ちます。corresponding_low_valueはRSIの安値が出現したローソク足の終値であり、ラインチャートが終値に基づいて描画されることから、これにより互換性が確保されます。この整合性により、ラインチャートに変換した際にもRSI安値と対応する価格が論理的に一致します。corresponding_low_maは、RSIの安値と同じ箇所における移動平均(MA)の値を保持し、価格がMAを上回っているか下回っているかを判断するのに用います。これは、トレンドの検証によく使われます。最後に、rsi_low_timeはRSI安値の正確な時間を記録し、特定のローソク足に関連付けてトレンドラインやラベルなどのオブジェクトを描画する際に利用されます。
次のステップは、最後の有効なRSIの安値を特定することです。これは、RSIの値に特定のパターンが現れているかどうかを確認することでおこないます。具体的には、1本以上の弱気の動きの後に3本連続の強気の動きがある場合です。
- コードは、直近30本のバーを対象に、有効なRSI安値をスキャンします。
- 主要な条件
- i+3のRSI値がi+4よりも低くなる(つまり、i+4 → i+3でRSIが下落)。
- その後、RSIがi+3 → i+2 → i+1 → iと3回連続で上昇する。
- このパターンが検出されると、そのRSI安値、対応する終値、移動平均の値、タイムスタンプが保存されます。
このロジックにより、単なる一時的な変動ではなく、RSIの下落から回復という意味のある動きの中で形成された安値が特定されるため、重要な転換点としての妥当性が高まります。
close[i+3]を使用する理由
ラインチャートを使用する場合、チャート上の線は終値を基に描画されます。そのため、RSIの安値と終値を結びつけることで、作成するマーカー(例:トレンドライン)がラインチャート上で正しく整列します。これにより、視覚的な明確さと一貫性が保たれます。
if(corresponding_low_value > corresponding_low_ma && close[0] > ma_buffer[0] && close[minimum_value_low] >= corresponding_low_value)
この条件文は、隠れた強気のダイバージェンスを検出するための状況が整っているかどうかを確認するために、複数の要素をチェックしています。
- corresponding_low_value > corresponding_low_ma:特定されたRSIの安値が出現した時点での価格が移動平均(MA)を上回っているかを確認します。これは、価格がまだMAの上に位置しており、トレンドが比較的強く、強気である可能性を示します。
- close[0] > ma_buffer[0]:最新のローソク足の終値(現在の足)が移動平均よりも上にあるかを確認します。これにより、現在の価格が依然としてMAより上にあることが裏付けられ、上昇トレンドが続いている可能性が強まります。
- close[minimum_value_low] >= corresponding_low_value:直近で検出された重要な安値の価格が、前回の安値と同じかそれ以上であることを確認します。これにより、価格が以前の安値を下回っていないことが保証され、強気のシナリオをサポートします。
これらすべての条件が満たされている場合、価格が移動平均線の上にあり、かつより高い安値を形成していることが示唆されます。これは、RSIがより強い弱気のモメンタムを示唆しているにもかかわらず、価格の動きがそれを裏付けていないという、潜在的な隠れた強気のダイバージェンスの重要な特徴です。
//CREATE LINES TO MARK RSI AND CORRESPONDING PRICE ObjectCreate(ChartID(),"RSI LOW VALUE",OBJ_TREND,1,rsi_low_time,rsi_low_value,TimeCurrent(),rsi_low_value); ObjectCreate(ChartID(),"C-CANDLE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,TimeCurrent(),corresponding_low_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI LOW VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI LOW AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,rsi_low_time,0); ObjectCreate(ChartID(),"RSI LOW LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,rsi_low_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI LOW AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT",OBJ_TEXT,0,TimeCurrent(),corresponding_low_value); ObjectSetString(ChartID(),"C-CANDLE TEXT",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI LOW TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_low_value); ObjectSetString(ChartID(),"RSI LOW TEXT",OBJPROP_TEXT,"RSI LOW"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI LOW TEXT",OBJPROP_COLOR,clrBlack);
このコードは、チャート上に視覚的な要素を作成し、潜在的なダイバージェンスパターンを識別するうえで重要となるRSIの安値と、それに対応する価格を強調表示します。まず、RSIの安値と対応する価格(C-PRICE)を示すトレンドラインが描画されます。これらの線は「RSI LOW VALUE」および「C-CANDLE」というラベル付きで表示され、視認性を高めるために黒で描かれます。これにより、RSIの動きとプライスアクションとの関係性を視覚的に結び付けて確認しやすくなります。
次に、RSIの安値とその対応価格を現在のチャート上に結ぶ追加のトレンドラインが作成されます。「C-CANDLE LINE」は、RSI安値時点の価格から現在の時刻までを結び、「RSI LOW LINE」はチャート上でRSIの安値を縦方向に延長します。これらの線は、RSIと価格の動きの関係性を明確にする視覚的なマーカーとして機能し、特に隠れた強気または弱気のダイバージェンスを分析する際に役立ちます。すべての線は一貫性を保つために黒で描画され、チャートの背景に対して明確に見えるよう配慮されています。
最後に、RSIの安値とその対応価格(ローソク足の安値)を注釈として表示するテキストラベルが作成されます。「RSI LOW」および「C-PRICE」というラベルが、それぞれの位置に配置され、チャート上でこれらの重要なポイントを一目で特定できるようになります。これらのテキストも黒で表示され、背景とのコントラストを保ち、読みやすさが確保されています。このようなオブジェクトをチャート上に作成することで、トレーダーはRSIと価格の重要なポイントを迅速に視覚化でき、テクニカル分析や隠れたダイバージェンスの識別において大きな助けとなります。
出力:
最初の重要なRSIの安値が特定されたら、次のステップは、それよりも低い新たなRSIの安値を見つけることです。このときのロジックは、最初の安値を見つける場合よりも単純です。
新しいRSI安値のロジック
新しいRSIの安値については、以下の条件を満たす必要があります。
- RSIが1本のバーで下落する、1回だけの弱気の動きがあること。
- その直後にRSIが上昇を開始する、1回の強気の動きが続くこと。
if(rsi_buffer[0] > rsi_buffer[1] && rsi_buffer[1] < rsi_buffer[2] && rsi_buffer[1] < rsi_low_value && close[1] > corresponding_low_value) { new_rsi_low_value = rsi_buffer[1]; new_rsi_low_time = time[1]; new_corresponding_low_value = close[1]; }
コードの最初の部分では、新しいRSIの安値と、それに対応する価格レベルを格納するための変数が宣言されています。new_rsi_low_value変数には、新しいRSIの安値が格納され、これはダイバージェンスの比較や分析に必要です。new_rsi_low_timeは、そのRSI安値が発生した時刻を記録するもので、チャート上でこのポイントをマークするために重要です。最後に、new_corresponding_low_valueには、そのRSI安値に対応する価格(通常は終値)が格納され、RSIの動きと実際の市場価格の変動とを結び付ける役割を果たします。
新しいRSI安値を決定するための条件は、コードの2番目のセクションで定義されています。このロジックでは、RSIが下落する「弱気の動き」と、その後にRSIが上昇し始める「強気の動き」の2つを満たす必要があります。また、対応する価格がより高い安値を形成し、新しいRSI安値が直前の主要なRSI安値よりも低いことも条件となります。後ほどチャート上での乖離を強調するために、アルゴリズムは以下の条件を満たした場合に、RSIの安値、新しいタイムスタンプ、および関連する価格の値を更新します。
if(rsi_low_value > new_rsi_low_value && corresponding_low_value < new_corresponding_low_value)
この条件は、価格チャートとRSI間の隠れた強気の乖離を探します。価格がより高い安値を形成し、RSIがより低い安値を形成する場合、それは隠れた強気のダイバージェンスとして知られています。
//FOR RSI ObjectCreate(ChartID(),"RSI LOW TREND LINE",OBJ_TREND,1,rsi_low_time,rsi_low_value,new_rsi_low_time,new_rsi_low_value); ObjectCreate(ChartID(),"L",OBJ_TEXT,1,rsi_low_time,rsi_low_value); ObjectSetString(ChartID(),"L",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"L",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI LOW TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"L",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"LL",OBJ_TEXT,1,new_rsi_low_time,new_rsi_low_value); ObjectSetString(ChartID(),"LL",OBJPROP_TEXT,"LL"); ObjectSetInteger(ChartID(),"LL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"LL",OBJPROP_COLOR,clrBlack); //FOR CORRESPONDING PRICE ObjectCreate(ChartID(),"C-CANDLE TREND LINE",OBJ_TREND,0,rsi_low_time,corresponding_low_value,new_rsi_low_time,new_corresponding_low_value); ObjectCreate(ChartID(),"CL",OBJ_TEXT,0,rsi_low_time,corresponding_low_value); ObjectSetString(ChartID(),"CL",OBJPROP_TEXT,"L"); ObjectSetInteger(ChartID(),"CL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CL",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLL",OBJ_TEXT,0,new_rsi_low_time,new_corresponding_low_value); ObjectSetString(ChartID(),"CLL",OBJPROP_TEXT,"HL"); ObjectSetInteger(ChartID(),"CLL",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLL",OBJPROP_COLOR,clrBlack);
隠れた強気のダイバージェンスを検知すると、このコードブロックはチャート上の重要なポイントを視覚的に表示します。最初の一連の処理では、RSIが主な対象となります。最初の重要なRSI安値と、それに続くさらに低いRSI安値は、トレンドラインとテキストラベルで表現されます。コードはまず、新しいRSIの安値(new_rsi_low_value)と元のRSIの安値(rsi_low_value)を結ぶトレンドラインを描画し、RSIの下降トレンドを視覚的に捉えやすくします。加えて、「L」と「LL」というテキストラベルが付けられ、これらのポイントを重要な安値として識別します。「L」は最初の安値、「LL」はそれよりも低い安値を示しています。
次の処理は対応する価格レベルに焦点を当てています。RSIと同様に、最初の価格の安値(corresponding_low_value)と新しい価格の安値(new_corresponding_low_value)を結ぶトレンドラインとテキストラベルが作成されます。対応する価格は、隠れた強気ダイバージェンスの条件として重要な「より高い安値」を形成することが期待されます。コードはこの2つの価格ポイント間にトレンドラインを引き、「L」と「HL」というラベルを付けます。「L」は最初の重要な価格安値、「HL」はそれよりも高い安値を示します。これらの視覚要素によって、RSIが低い安値を更新する一方で価格は高い安値を形成しているという典型的な隠れた強気ダイバージェンスの状態を確認しやすくなります。
ObjectDelete()関数は、隠れた強気ダイバージェンスの条件が成立しなくなった際に、チャート上の関連する視覚オブジェクトを削除して整理する役割を果たします。具体的には、現在の終値が移動平均線(ma_buffer[0])を下回っているか、または最後の重要なRSI安値以降の最小終値が対応する価格の安値(corresponding_low_value)を下回っているかどうかを判定します。これらのいずれかの条件が満たされると、これまでに描画されたトレンドラインやテキストラベル、RSIや価格の安値を示すその他のオブジェクトは削除されます。これにより、チャートが古い情報で煩雑にならず、トレーダーは最新かつ有効な市場状況に集中できるようになります。
出力:
4.2. 隠れた弱気のダイバージェンスを検出する
隠れた弱気ダイバージェンスは、RSIがより高い高値を形成する一方で、価格はより低い高値を形成し、一時的な価格回復にもかかわらず潜在的な下降モメンタムを示す場合に発生します。このパターンを特定するために、特定のロジックにより最新のRSI高値を検出します。これは、少なくとも1回の上昇(強気)の動きの後に3回連続の下降(弱気)の動きが続いたことを条件とし、モメンタムの有意なピークであることを示します。RSI高値が特定されると、それに対応する価格と移動平均(MA)値が取得され、現在の下落トレンドとの整合性が確認されます。有効な隠れた弱気ダイバージェンスとしては、価格の高値がMAを下回っていることが必要であり、これにより下降バイアスが強調されます。
隠れた強気ダイバージェンスがRSIのより低い安値と価格のより高い安値(潜在的な上昇モメンタムを示す)を特徴とするのに対し、隠れた弱気ダイバージェンスはこれと逆の動きをします。RSI高値を特定した後、ロジックは最初のRSI高値を超える新たなRSI高値の有無を確認し、その際対応する価格がより低い高値を形成しているかどうかをチェックします。この乖離は、隠れた強気ダイバージェンスが示す弱気モメンタムの弱まりとは対照的に、強気モメンタムの弱まりを示しています。こうした違いを踏まえることで、両者のダイバージェンスを効果的に検出・識別するための体系的なアプローチが確立されます。
例
//DECLEAR VARIBLE ROR RSI HIGH AND CORESPONDING PRICE double rsi_high_value; double corresponding_high_value; double corresponding_high_ma; datetime rsi_high_time; //DECLEAR VARIABLE FOR THE MAXIMUM CORRESPONDING PRICE VALUE int maximum_value_high; // DECLEAR VARIBLE ROR NEW RSI HIGH AND CORESPONDING PRICE double new_rsi_high_value; datetime new_rsi_high_time; double new_corresponding_high_value;
//LOOP THROUGH THE LAST 30 BARS ON THE CHART for(int i = 0; i < 30; i++) { //PREVENT ARRAY OUT OF RANGE ERROR if((i+1 < 30) && (i+2 < 30) && (i < 30) && (i+3 < 30) && (i+4 < 30)) { //LOGIC TO IDENTIFY THE LATEST RSI HIGH if(rsi_buffer[i+4] < rsi_buffer[i+3] && rsi_buffer[i+2] < rsi_buffer[i+3] && rsi_buffer[i+1] < rsi_buffer[i+2] && rsi_buffer[i] < rsi_buffer[i+1]) { //GETTING LATEST RSI HIGH, CORRESPONDING PRICE, CORRESPONDING MA VALUE, and RSI TIME rsi_high_value = rsi_buffer[i+3]; corresponding_high_value = close[i+3]; corresponding_high_ma = ma_buffer[i+3]; rsi_high_time = time[i+3]; break; } } } //TOTAL NUMBERS OF BARS FROM THE LAST SIGNIFICANT RSI HIGH int total_bars_3 = 0; total_bars_3 = Bars(_Symbol,PERIOD_CURRENT,time[0],rsi_high_time); //MAXIMUM CLOSE PRICE FROM THE LAST SIGNIFICANT RSI LOW maximum_value_high = ArrayMaximum(close,0,total_bars_3); if(corresponding_high_value < corresponding_high_ma && close[0] < ma_buffer[0] && close[maximum_value_high] <= corresponding_high_value) { //CREATE LINES TO MARK RSI AND CORRESPONDING PRICE ObjectCreate(ChartID(),"RSI HIGH VALUE",OBJ_TREND,1,rsi_high_time,rsi_high_value,TimeCurrent(),rsi_high_value); ObjectCreate(ChartID(),"C-CANDLE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,TimeCurrent(),corresponding_high_value); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"RSI HIGH VALUE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"C-CANDLE HIGH",OBJPROP_COLOR,clrBlack); //CREATE TWO LINES TO CONNECT RSI HIGH AND THE CORRESPONDING PRICE ON THE CHART ObjectCreate(ChartID(),"C-CANDLE LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,rsi_high_time,0); ObjectCreate(ChartID(),"RSI HIGH LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,rsi_high_time,100); //SETTING OBJECTS COLOUR ObjectSetInteger(ChartID(),"C-CANDLE LINE HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI HIGH LINE",OBJPROP_COLOR,clrBlack); //CREATE TEXTS TO MART RSI HIGH AND CORRESPONDING PRICE (C-PRICE) ObjectCreate(ChartID(),"C-CANDLE TEXT HIGH",OBJ_TEXT,0,TimeCurrent(),corresponding_high_value); ObjectSetString(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_TEXT,"C-PRICE"); ObjectCreate(ChartID(),"RSI HIGH TEXT",OBJ_TEXT,1,TimeCurrent(),rsi_high_value); ObjectSetString(ChartID(),"RSI HIGH TEXT",OBJPROP_TEXT,"RSI HIGH"); //SETTING TEXT COLOUR ObjectSetInteger(ChartID(),"C-CANDLE TEXT HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"RSI HIGH TEXT",OBJPROP_COLOR,clrBlack); //LOGIC TO GET THE NEW RSI HIGH if(rsi_buffer[0] < rsi_buffer[1] && rsi_buffer[1] > rsi_buffer[2] && rsi_buffer[1] > rsi_high_value && close[1] < corresponding_high_value) { new_rsi_high_value = rsi_buffer[1]; new_rsi_high_time = time[1]; new_corresponding_high_value = close[1]; } if(rsi_high_value < new_rsi_high_value && corresponding_high_value > new_corresponding_high_value) { //for rsi ObjectCreate(ChartID(),"RSI HIGH TREND LINE",OBJ_TREND,1,rsi_high_time,rsi_high_value,new_rsi_high_time,new_rsi_high_value); ObjectCreate(ChartID(),"H",OBJ_TEXT,1,rsi_high_time,rsi_high_value); ObjectSetString(ChartID(),"H",OBJPROP_TEXT,"H"); ObjectSetInteger(ChartID(),"H",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"RSI HIGH TREND LINE",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"H",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"HH",OBJ_TEXT,1,new_rsi_high_time,new_rsi_high_value); ObjectSetString(ChartID(),"HH",OBJPROP_TEXT,"HH"); ObjectSetInteger(ChartID(),"HH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"HH",OBJPROP_COLOR,clrBlack); //for candle ObjectCreate(ChartID(),"C-CANDLE TREND LINE HIGH",OBJ_TREND,0,rsi_high_time,corresponding_high_value,new_rsi_high_time,new_corresponding_high_value); ObjectCreate(ChartID(),"CH",OBJ_TEXT,0,rsi_high_time,corresponding_high_value); ObjectSetString(ChartID(),"CH",OBJPROP_TEXT,"H"); ObjectSetInteger(ChartID(),"CH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"C-CANDLE TREND LINE HIGH",OBJPROP_COLOR,clrBlack); ObjectSetInteger(ChartID(),"CH",OBJPROP_COLOR,clrBlack); ObjectCreate(ChartID(),"CLH",OBJ_TEXT,0,new_rsi_high_time,new_corresponding_high_value); ObjectSetString(ChartID(),"CLH",OBJPROP_TEXT,"LH"); ObjectSetInteger(ChartID(),"CLH",OBJPROP_FONTSIZE,15); ObjectSetInteger(ChartID(),"CLH",OBJPROP_COLOR,clrBlack); } } //LOGIC TO DELETE THE OBJECTS WHEN ITS NO LONGER RELEVEANT else if((close[0] > ma_buffer[0]) || (close[maximum_value_high] > corresponding_high_value)) { ObjectDelete(ChartID(),"RSI HIGH VALUE"); ObjectDelete(ChartID(),"C-CANDLE HIGH"); ObjectDelete(ChartID(),"C-CANDLE LINE HIGH"); ObjectDelete(ChartID(),"RSI HIGH LINE"); ObjectDelete(ChartID(),"C-CANDLE TEXT HIGH"); ObjectDelete(ChartID(),"RSI HIGH TEXT"); ObjectDelete(ChartID(),"RSI HIGH TREND LINE"); ObjectDelete(ChartID(),"H"); ObjectDelete(ChartID(),"HH"); ObjectDelete(ChartID(),"C-CANDLE TREND LINE HIGH"); ObjectDelete(ChartID(),"CH"); ObjectDelete(ChartID(),"CLH"); }
解説
このコードセグメントは、4.1「隠れた強気ダイバージェンスの発見」と同様のロジックに従い、RSIの高値とそれに対応する価格の高値を検出することで、隠れた弱気ダイバージェンスを識別するよう設計されています。ロジックの大部分は4.1で既に説明されているため、ここでは詳述しません。唯一の違いは、今回は逆のシナリオに焦点を当てている点です。基本的な手法は同じですが、本実装ではRSIの安値ではなく高値に注目し、弱気のモメンタムを検出します。
コードの冒頭で、rsi_high_value、corresponding_high_value、corresponding_high_maといった変数が宣言されます。これらはそれぞれ、RSIの高値、対応する価格、移動平均(MA)値を格納するためのものです。最新のRSI高値を特定するために、直近30本のバーをループし、1回の強気の動きに続いて3回連続の弱気の動きがあるという条件を適用します。RSI高値が見つかると、それに対応する価格とMA値が取得され、タイムスタンプ(rsi_high_time)も記録されます。さらに検証の段階で、対応する価格の高値がMAを下回っていることが確認され、これにより弱気トレンドが強調されます。
次に、RSIがより高い高値(HH)を形成し、価格がより低い高値(LH)を形成する新しいRSI高値を検出します。これは隠れた弱気ダイバージェンスの条件を満たしており、価格の上昇モメンタムが弱まっていることを示しています。この条件が成立すると、RSIの高値(HとHH)およびそれに対応する価格の高値(HとLH)を視覚的に示すためのトレンドラインとラベルがチャート上に描画されます。これらの視覚的マーカーは、トレーダーが潜在的な隠れた弱気ダイバージェンスのポイントを素早く把握するのに役立ちます。
また、現在の価格の終値が移動平均(MA)を上回った場合や、識別されたRSI高値に対応する価格の最高値を超えた場合など、隠れた弱気ダイバージェンスの条件が無効となった際に、これらのオブジェクトを削除するロジックも含まれています。これにより、チャートは常に見やすい状態が保たれ、有効なダイバージェンスシグナルのみが反映されるようになります。
5.ストキャスティクスの導入と取引の実行
移動平均とRSIを用いて隠れた強気・弱気のダイバージェンスを検出する方法を理解した上で、ここではストキャスティクスを最終的な確認ツールとして導入します。ダイバージェンスが市場のモメンタムの変化と一致していることを確認することで、エントリーの精度を高めるのが目的です。厳格なタイミングとリスク管理のガイドラインに基づき、強気・弱気どちらのダイバージェンスにおいても、確認の根拠はストキャスティクスの「買われすぎ」および「売られすぎ」レベルに対する動きに依存します。
5.1. ストキャスティクスの活用
RSIと価格の動きによって隠れた強気ダイバージェンスが確認された後は、ストキャスティクスが売られすぎ水準(20)を下回り、その後再び上回るかどうかを監視します。このクロスオーバーがダイバージェンス検出から11本以内のバーで発生した場合、シグナルが有効と判断されます。シグナルが確認されたら買いポジションをエントリーし、ストップロス(SL)は直近のスイングローに設定します。テイクプロフィット(TP)は、あらかじめ定義されたリスクリワード比率(RRR)に基づいて設定され、規律ある取引管理が実現されます。
例
if(total_bars < 11) { if(stoch_buffer_k[0] < 20 && stoch_buffer_k[1] > 20 && currentBarTime != lastTradeBarTime && totalPositions < 1) { // } }
解説
このコードは、ストキャスティクスによる確認を通じて、隠れた強気ダイバージェンスの成立を検証します。まず、Bars関数を使用してRSIの安値(new_rsi_low_time)からの経過バー数を計算し、取引のセットアップが11バー以内で有効であることを確認します(if(total_bars < 11))。この期間内に、ストキャスティクスの%Kラインが売られすぎ水準の20を下から上にクロスしたかをチェックします (stoch_buffer_k[1] < 20 and stoch_buffer_k[0] > 20)。このクロスオーバーは上昇モメンタムの兆候とされ、強気シグナルの裏付けとなります。これら両方の条件が満たされた場合、コードは取引ロジックを実行し、隠れた強気ダイバージェンスの全要件が成立していることを確認します。
同様に、隠れた弱気ダイバージェンスを確認するには、ストキャスティクスが買われすぎ水準(80)を上抜けた後、再び下抜けるクロスオーバーが発生するのを待ちます。この弱気クロスは、ダイバージェンスが検出されてから11バー以内に起こる必要があります。条件が確認されれば、売りポジションがエントリーされ、TP(テイクプロフィット)は設定されたリスクリワード比率(RRR)に基づいて計算され、SL(ストップロス)は直近のスイングハイに設定されます。こうした手順を元のダイバージェンス検出ロジックと組み合わせることで、モメンタムとリスク管理が明確に整った場面でのみ取引が実行される仕組みとなっています。
例
int total_bars = Bars(_Symbol,PERIOD_CURRENT,new_rsi_high_time,TimeCurrent()); if(total_bars < 11) { if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80) { // } }
解説
現在の時刻(TimeCurrent())と新しいRSI高値が検出された時刻(new_rsi_high_time)との間のバー数は、変数total_barsによって算出されます。total_barsが11未満であれば、その取引設定がまだ有効で、失効していないことが確認されます。続いて、コードはストキャスティクスの%Kライン(stoch_buffer_k)が、直近のバーで買われすぎ水準の80を上から下にクロスしたかどうかを判定します。具体的には、前のバーで80を上回っており(stoch_buffer_k[1] > 80)、現在のバーで80を下回っている(stoch_buffer_k[0] < 80)必要があります。これらの条件がすべて満たされた場合、取引の設定が検証され、コードは実際にポジションをエントリーするためのロジックを実行します。
5.2. 取引の実行
5.2.1. スイングローとスイングハイ
このセクションでは、有効なダイバージェンスとストキャスティクスシグナルの確認後に取引を実行するために必要な、重要な要素すべてに焦点を当てます。取引が体系的かつ適切なリスク管理のもとでおこなわれることを保証します。
ストップロス(SL)を設定するには、まず直近のスイングハイまたはスイングローを特定する必要があります。隠れた強気ダイバージェンスの場合は直近のスイングローがSLとなり、隠れた弱気ダイバージェンスの場合は直近のスイングハイがSLとして機能します。これにより、過去の価格動向に基づいて、各取引におけるリスクの上限が明確に定義されます。
例
double last_high; double last_low; //LOGIC FOR LAST HIGH for(int i = 0; i < 30; i++) { if(i < 30 && i+1 < 30) { if(close[i] < open[i] && close[i+1] > open[i+1]) { last_high = MathMax(high[i],high[i+1]); break; } } } //LOGIC FOR LAST LOW for(int i = 0; i < 30; i++) { if(i < 30 && i+1 < 30) { if(close[i] > open[i] && close[i+1] < open[i+1]) { last_low = MathMin(low[i],low[i+1]); break; } } } }
解説
直近のスイングハイおよびスイングローを特定するために、このコードは過去30本のローソク足を調べます。スイングハイ(last_high)は、陰線の後に陽線が出現するパターンから最高値を検出し、スイングロー(last_low)は、陽線の後に陰線が出現するパターンから最安値を検出して決定されます。これらの数値はストップロス(SL)水準を定める際に重要な役割を果たします。
5.2.2.取引の実行管理と過剰取引の防止
過剰な取引を抑制し、不要なリスクを低減するために、システムでは同時に保有できるポジションを1つに制限します。コードは、新たな取引を実行する前に、既存のポジションが存在しないことを確認します。これにより、すべてのシグナルで自動的に取引が実行されるのを防ぎ、より統制された取引行動が保証されます。また、取引の実行にはTradeライブラリの導入が不可欠です(#include <Trade/Trade.mqh>)。これにより、取引管理のためのCTradeクラスへのアクセスが可能となります。
例
#include <Trade/Trade.mqh> CTrade trade; input int MagicNumber = 9097; //MAGIC NUMBER //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //SET MAGIC NUMBER trade.SetExpertMagicNumber(MagicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Getting total positions int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(ChartID())) { totalPositions++; } } }
解説
コードはまず、MetaTraderにおける取引機能へのアクセスを提供するTrade.mqhライブラリをインクルードすることから始まります。次に、取引操作を管理するためにCTradeオブジェクトであるtradeがインスタンス化されます。MagicNumberは入力パラメータとして定義されており、この例では「9097」に設定されています。これは、このEAが発行した取引を一意に識別し、同一口座内の他のEAや手動取引と区別するために使用されます。OnInit()関数内では、SetExpertMagicNumber(MagicNumber)を使用して、このマジックナンバーをtradeオブジェクトに適用します。これにより、このEAが取引の発注・変更・決済を行う際に、自身のポジションを正確に認識できるようになります。
OnTick()関数では、現在のチャートに関連付けられた銘柄に対して、開かれているポジションの数を確認する処理がおこなわれます。まず、PositionsTotal()を使用してすべてのポジションを走査し、各ポジションのチケット番号をPositionGetTicket(i)で取得します。次に、PositionGetInteger(POSITION_MAGIC)によって、ポジションのマジックナンバーがEAのMagicNumberと一致するかを確認し、さらにPositionGetString(POSITION_SYMBOL)を使ってポジションの銘柄が現在のチャートと一致しているかを検証します。これら2つの条件がともに満たされた場合、totalPositions変数がインクリメントされます。これにより、現在のEAと銘柄に関連するポジションの数が正確に追跡され、同時に開かれるポジション数が1つに制限されるようになります。
5.2.3.リスク管理
この取引システムは、リスク管理と利益最大化という2つの重要な要素を統合することで、体系的なアプローチを保証します。この戦略は、長期的な市場での成功を支援し、取引口座を保護するのに役立ちます。このセクションの主な目標は、トレーダーが各取引でリスクを負いたい金額を正確に指定できるようにすることで、強力なリスク管理システムを実装することです。また、起こりうる利益が起こりうるリスクを上回るようにするために、すべての取引におけるリスクリワードレシオ(RRR)を計算する方法についても説明します。
例
//INPUTS input double risk_amount = 20; // $ PER TRADE input double rrr = 4; //RRR input int MagicNumber = 9097; //MAGIC NUMBER double take_profit; double points_risk; double lot_size; //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //BULLISH HIDDEN DIVERGENCE if(stoch_buffer_k[0] > 20 && stoch_buffer_k[1] < 20 && currentBarTime != lastTradeBarTime && totalPositions < 1) { take_profit = ((ask_price - last_low) * rrr) + ask_price; points_risk = ask_price - low[0]; lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk); trade.Buy(lot_size,_Symbol,ask_price,last_low,take_profit); lastTradeBarTime = currentBarTime; } //BEARISH HIDDEN DIVERGENCE if(stoch_buffer_k[0] < 80 && stoch_buffer_k[1] > 80 && currentBarTime != lastTradeBarTime && totalPositions < 1) { take_profit = MathAbs(((last_high - ask_price) * rrr) - ask_price); points_risk = high[0] - ask_price; lot_size = CalculateLotSize(_Symbol, risk_amount, points_risk); trade.Sell(lot_size,_Symbol,ask_price,last_high,take_profit); lastTradeBarTime = currentBarTime; } } //+-----------------------------------------------------------------------+ //| 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); return lotSize; }
解説
CalculateLotSize関数は、指定されたリスク金額(ドル単位)とストップロス距離(pips単位)に基づいて、取引に適切なロットサイズを算出します。ロットサイズは、リスク金額をストップロス値で割り、ストップロス距離を金額に換算し、銘柄特有のデータを用いて1ロットあたりのpipsバリューを計算することで導き出されます。得られたロットサイズは、ブローカーの最小ロット、最大ロット、およびロットステップの条件を満たすように調整されます。この関数はコード全体で再利用できるようにグローバル領域に宣言し、一貫性のある効果的なリスク管理を実現する必要があります。
買い取引の場合、ストキャスティクスの%Kラインが20を下回った後に再び上抜ける必要があります。さらに、取引はポジションが開かれておらず(totalPositions < 1)、現在のバーが前回取引を行ったバーと異なる場合にのみ実行されます。ストップロス(SL)は直近のスイングローに設定され、テイクプロフィット(TP)は、指定されたリスクリワードレシオ(RRR)と、現在の買値とスイングローの距離をもとに計算されます。ロットサイズは、リスク金額とストップロス距離をもとにCalculateLotSize関数を使用して決定されます。取引が実行された後は、lastTradeBarTimeが更新され、同じバー内での重複エントリーを防止します。
オシレーターの%Kラインがまず80(買われすぎ)を上回り、その後それを下回る必要があります。これにより、売り取引のシグナルが示されます。同様に、ポジションが未保有であり、バーが新しい場合にのみ、取引は実行されます。テイクプロフィット(take_profit)は、リスクリワードレシオと、現在のアスク価格と直近のスイングハイ(last_high)との差を基にして算出されます。ストップロスは、最後のスイングハイに設定されます。ロットサイズは、再度CalculateLotSize関数を使用して決定されます。重複取引を回避するために、sell関数ではlastTradeBarTimeを更新し、売り注文が発注されます。
このコードは、取引制限、適切なリスク管理、確認シグナルに従うことで、規律ある取引を保証し、取引戦略の信頼性を高めています。
結論
この記事では、移動平均、RSI、ストキャスティクスといった指標を統合し、MQL5を使用して隠れた強気および弱気のダイバージェンスを識別して取引する方法を、ステップバイステップで解説しました。市場でこうしたパターンを効果的に見つけるために、リスク管理、取引の実行、自動化の重要性が強調されました。この教育コンテンツの目的は、ダイバージェンスの検出と体系的な取引の基礎を学ぶことにあります。記事内で解説された概念を実装するための実用的な参考資料として、詳細なコメント付きのソースコードも添付されます。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16956





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