インジケータ、スクリプトおよびライブラリー P295
インジケータ P295
第17章では、指標をEAプログラムに追加する方法を検討しました。MQL5では、独自のカスタム指標を作成することもできます。このセクションでは、最後のxバーの最高値と最低値を使用して価格チャンネルをプロットするカスタムインジケータを作成します。これは、どんちゃんチャンネルとも呼ばれます。
描画スタイル P295
MQLには、インジケータライン用に十数種類の異なる描画スタイルがあります。描画スタイルによって、チャートウィンドウでの線の表示方法が決まります。もっとも一般的な描画スタイルのいくつかを次に示します。
- DRAW_LINE -- これは最も一般的な描画スタイルで、指定された色の1本の線で構成されます。移動平均、RSI、
およびその他の多くのインジケータは、この描画スタイルを使用します。
- DRAW_HISTOGRAM -- ヒストグラムの描画スタイルは、通常、MACD、OsMA、ビルウィリアムズなどのオシレータで使
用されます。これは、ゼロ軸を中心に振動する垂直線で構成されています。
- DRAW_SECTION -- セクション描画スタイルは、連続していないバーの価格を連続線で結びます。MetaTraderに付属す
る。ZigZagカスタムインジケータは、この描画スタイルの典型的な例です。
- DRAW_ARROW -- 矢印描画スタイルは、チャート上に矢印オブジェクトを描画します。フラクタルインジケータは、
矢印オブジェクトを使用してスイングの高値と安値を示します。
- DRAW_NONE -- チャートに描画されないインジケータバッファに使用されます。
各描画スタイルにはカラーバリエーションもあります。DRAW_COLOR_描画スタイルにより、プログラマーはバーごとにインジケータラインの色を変えることができます。すべての描画スタイルと例については、MQL5リファレンスの「カスタムインジケータ」 > 「インジケータスタイルの例」ですべての描画スタイル表示できます。
OnCalculate()イベントハンドラ P295
全てのインジケータには、OnCalculate()イベントハンドラが必要です。これはEAのOnStart()イベントハンドラと同等であり、着信ティックごとに実行されます。OnCalculate()イベントハンドラには2つのバリアントがあります。1つ目は、単一の価格シリーズを使用するインジケータ用です。例えば、移動平均インジケータを使用すると、ユーザーはデータを計算する価格シリーズ(終値、高値、安値など)を選択できます。選択した価格シリーズは、OnCalculate()イベントハンドラに渡されます。OnCalculate()の最初のバリアントは次のとおりです。
int OnCalculate(const int rates_total, // price[]配列のサイズ
const int prev_calculated, // 前の呼び出しで処理されたバー
const int begin, // 重要なデータの開始位置
const int double & price[] // 計算する配列
);
rates_totalパラメータには、現在のチャートシンボルの履歴のバー数が含まれます。prev_calculatedパラメータは、インディケータによって以前に計算された履歴内のバーの数です。OnCalculate()が初めて実行されるとき、prev_calculatedはゼロになります。以降の実行では、prev_calculatedはrates_totalと等しくなります。新しいバーが開き、rates_totalの値が1増加すると、rates_totalとprev_calculatedの差は1になり、最新のバーを計算する必要があることを示します。
rate_totalの値をprev_calculatedと比較することにより、すでに計算されたバーの再計算にプロセッサー時間を浪費することを避けます。通常、beginパラメータは使用されません。price[]配列には、指標が計算で使用する価格データが含まれています。インディケータが使用するシリーズは、インディケータプロパティウィンドウのパラメータタブでユーザーが選択します。[Apply to]ドロップダウンボックスを使用すると、ユーザーは使用する価格シリーズを選択できます。
図21.1-指標プロパティダイアログのパラメータタブの下にある適用先ドロップダウンボックス。
OnCalculate()イベントハンドラの2番目のバリアントは、インジケータの計算で使用できる価格と時系列の配列を追加します。インジケータに複数の価格シリーズ(各バーの高値と安値など)が必要な場合は、OnCalculate()の2番目のバリアントを使用します。
int OnCalculate(const int rates_total, // 入力時系列のサイズ
const int prev_calculated, // 前の呼び出しで処理されたバー
const int datetime & time[], // 時間
const double & open[], // 開ける
const double & high[], // 高い
const double & low[], // 低い
const double & close[], // 閉める
const long & tick_volume[], // ティックボリューム
const long & volume[], // リアルボリューム
const int & spread[] // 拡大
高値系列と安値系列の両方を使用する必要があるため、チャンネルインジケータはOnCalculate()イベントハンドラのこのバリアントを使用します。
MQL5ウィザード P297
MQL5ウィザードを使用して、カスタム指標の開始テンプレートを作成できます。イベントハンドラ、バッファ、および行を手動で追加するよりも、ウィザードを使用して追加する方がはるかに便利です。MetaEditorツールバーの新規ボタンをクリックして、MQL5ウィザードを開きます。【カスタムインジケータ】を選択し、【次へ】をクリックして続行します。
図21.2-MQL5ウィザードのカスタム指標プロパティダイアログ
カスタム指標は、デフォルトで\MQL5\Indicatorsフォルダに保存されます。必要に応じて、この画面で入力変数を追加できます。図21.2では、HighLowBarsという名前のint入力変数を追加しました。デフォルト値は8です。
次の画面では、インジケータに追加するイベントハンドラを選択できます。OnCalculate()イベントハンドラの最初のバリアントがデフォルトで選択されています。必要に応じて、イベントハンドラを追加できます。このインジケータでは、OnCalculate()イベントハンドラの2番目のバリアントを選択する必要があります。以下の図21.3は、【イベントハンドラ】ダイアログを示しています。
図21.3-カスタム指標のMQL5ウィザードのイベントハンドラダイアログ
最後の画面では、インジケータの描画プロパティを設定します。このインジケータが別のウィンドウ(オシレータなど)に表示される場合は、【別のウィンドウのインジケータ】をオンにして、関連する#propertyディレクティブをインジケーターファイルに追加します。プロットウィンドウでは、インジケータのプロパティを追加および設定できます。
図21.4-カスタム指標のMQL5ウィザードの描画プロパティダイアログ
Label列は、データウィンドウに表示されるインジケータラインの名前です。また、配列バッファ名の作成にも使用されます。UpperとLowerという2つのインジケータラインを追加しました。【タイプ】列をダブルクリックしてドロップダウンボックスを表示し、線の描画タイプを選択します。線をラインの描画タイプに設定したままにしています。最後に、【カラー】列では、インジケータラインの色を設定します。両方の行を赤に設定しました。
完了をクリックしてウィザードを閉じ、MetaEditorでインジケータテンプレートを開きます。以下は、インジケータテンプレートの外観です。ここでは、OnCalculate()イベントハンドラを省略し、わかりやすくするためにファイルをフォーマットしました。
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
//―――上をプロット
#property indicator_label1 “Upper”
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
//―――下をプロット
#property indicator_label2 “Lower”
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrRed
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//入力パラメータ
input int HighLowBars = 8;
//指標バッファ
double UpperBuffer[];
double LowerBuffer[];
//+-------------------------+
//カスタム指標初期化関数 |
//+-------------------------+
int OnInit()
{
//指標バッファのマッピング
SetIndexBuffer(0, UpperBuffer, INDICATOR_DATA);
SetIndexBuffer(1, LowerBuffer, INDICATOR_DATA);
return(0);
}
関連する#propertyディレクティブがインジケータラインに挿入されました。MQL5リファレンスのプログラムプロパティの下のLanguage Basics(基本言語)> プリプロセッサ>ですべてのインジケータ#propertyディレクティブを表示できます。HighLowBarsの入力変数が挿入され、バッファー用のUpperBuffer[]とLowerBuffer[]の2つの配列が作成されました。OnInit()イベントハンドラのSetindexBuffer()関数は、配列を関連する指標バッファに割り当てます。
指標の計算 P300
インジケータの計算は、OnCalculate()イベントハンドラで実行されます。インジケータに対するOnCalculate()イベントハンドラは以下の通りです。
(※ カスタムインジケータにおいて、価格データの配列を受け取りその配列を用いてインジケータの値を計算し、バッファに格納する関数のことです。この関数は通常『OnCalculate』と呼ばれ定義されます)
//+---------------------------------------+
// | カスタム指標の反復関数
//+---------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const int datetime & Time[],
const double & Open[],
const double & High[],
const double & Low[],
const double & Close[],
const long & TickVolume[],
const long & Volume[],
const int & Spread[])
{
//---
ArraySetAsSeries(UpperBuffer, true);
ArraySetAsSeries(LowerBuffer, true);
ArraySetAsSeries(High, true);
ArraySetAsSeries(Low, true);
int bars = rates_total – 1;
if(prev_calculated > 0) bars = rates_total – (prev_calculated - 1);
for(int i = bars; i >= 0; i-- )
{
UpperBuffer[i] = High[ArrayMaximum(High, i, HighLowBars)];
LowerBuffer[i] = Low[ArrayMaximum(Low, i, HighLowBars)]
}
//--- 次の呼び出しのためにprev_calculatedの値を返す
return(rates_total);
}
このプログラムはどの配列も、既定では系列配列として設定されていません。ArraySetAsSeries()関数を使用してシリーズ配列として設定する必要があります。これは通常オプションですが、この指標では、一連の価格データにアクセスする必要があります。OnCalculate()イベントハンドラによって渡されるHigh[]およびLow[]配列と同様に、インジケータバッファの両方がシリーズとして設定されます。
ArraySetAsSeries(UpperBuffer, true);
ArraySetAsSeries(LowerBuffer, true);
ArraySetAsSeries(High, true);
ArraySetAsSeries(Low, true);
forループを使用して、現在のチャートの各バーのインジケータ値を計算します。OnCalculate()イベントハンドラのprev_calculateおよびrates_totalパラメータを使用して、処理するバーの数を決定します。前述のように、rates_total変数にはチャートのバーの総数が含まれ、prev_calculatedにはOnCalculate()イベントハンドラの前回の実行によって計算されたバーの数が含まれます。
シリーズ配列の場合、配列の最大インデックスはrate_total – 1です。これは、チャートのもっとも古い棒を参照します。最新のバーのインデックスはゼロです。OnCalculate()が最初に実行されるとき、prev_calculatedの値はゼロになります。prev_calculatedが0の場合、最大配列インデックスをrates_total1設定します。その後の実行では、rates_totalからprev_calculatedを引いて最大配列インデックスを計算します。これより、最新のバーのみが計算されます。
int bars = rates_total – 1;
if(prev_calculated > 0) bars = rates_total -prev_calculated;
変数barsは、配列の開始インデックスを保持します。以下のforループでは、バーの値をインクリメント変数1に割り当てます。i = 0になるまでiの値を減らします。
for(int i = bars; i >= 0; i-- )
{
//指標の計算
}
価格とバッファ配列がシリーズとして設定されている限り、上記のforループはほとんどの指標を計算するために機能します。インジケータを計算してバッファを埋めるコードは、ループ内に入ります。
チャンネルインジケータの配列バッファを計算するには、単にArrayMaximum()およびArrayMinimum()関数を使用して、iインクリメント変数によって示される配列インデックスに関連して、HighLowBarsによって示されるバー数の最高値と最低値を見つけます。結果の配列インデックスは、High[]およびLow[]配列で使用され、最高値と最低値を返します。結果は、UpperBuffer[]およびLowerBuffer[]配列にそれぞれ保存されます。
for(int i = bars; i >= 0; i-- )
{
UpperBuffer[i] = High[ArrayMaximum(HighLowBars)];
LowerBuffer[i] = Low[ArrayMinimum(Low, i, HighLowBars)];
}
最後のステップは、rates_totalの値を返し、OnCalculate()イベントハンドラを終了することです。これはMQL5ウィザードによって自動的に挿入されます。
//---次の呼び出しのためにprev_calculatedの値を返します
return(rates_total);
}
OnCalculate()イベントハンドラから価格データを取得し、2つのインジケータラインを計算する単純なインジケータを作成しました。この指標は、取引シグナルの作成、またはトレンドポジションのストップロスとして使用できます。MQL5のカスタム指標でできることだけに触れました。カスタム指標関数の詳細については、MQL5リファレンスのカスタム指標を参照してください。
\MQL5\Indicators\High Low Channel.mq5でこのファイルのソースコードを表示できます。
図21.5HighLowチャネルのカスタムインジケータ
スクリプト P302
スクリプトは、チャートに添付されると1回実行されるシンプルなMQL5プログラムです。これは、1つのイベントハンドラであるOnStart()イベントハンドラで構成されます。スクリプトがチャートに添付されると、OnStart()イベントハンドラが実行されます。EAやインジケータとは異なり、スクリプトはOnStart()イベントハンドラが終了した後、その実行を繰り返しません。スクリプトは、実行後にチャートから自動的に切り離されます。
スクリプトを作成するには、MQL5ウィザードを使用します。すべてのスクリプトは\MQL5\Scriptsディレクトリに保存されます。新しいスクリプトファイルには、空のOnStart()イベントハンドラが含まれます。スクリプトの動作を制御する#propertyディレクティブがいくつかあります。スクリプトに入力変数がある場合は、script_show_inputsプロパティを使用して、ユーザーが入力を調整し、実行を確認できるようにします。スクリプトに入力変数が無い場合、script_show_confirmプロパティがスクリプトの実行を確認するための確認ボックスを表示します。これらの#propertyディレクティブの1つをスクリプトに追加する必要があります。
以下は、複数の#propertyディレクティブとOnStart()イベントハンドラを含む空のスクリプトファイルの例です。
#property copyright “Andrew Young”
#property link “http://www.expertadvisorbook.com”
#property script_show_confirm
void OnStart()
{
}
チャート上のすべてのオープン注文とポジションをクローズするために使用できる便利なスクリプトを作成します。スクリプトをチャートに添付すると、最初にスクリプトを実行するかどうかをユーザーに確認するメッセージが表示されます。その場合、スクリプトはまず現在のチャートシンボルのオープンポジションを閉じます。その後、未決注文をクローズします。
#property copyright “Andrew Young”
#property link “http://www.expertadvisorbook.com”
#property description “現在のポジションと現在のすべての注文を閉じる”
#property script_show_confirm
#include <Mql5Book\Trade.mqh>
CTrade Trade;
#include <Mql5Book\Pending.mqh>
CPending Pending;
void OnStart()
{
//現在のポジションを閉じる
if(PositionType(_Symbol) != WRONG_VALUE)
{
bool closed = Trade.Close(_Symbol);
if(closed == true)
{
Comment(“Position closed on ” +_Symbol);
}
}
//未決注文を閉じる
if(Pending.TotalPending(_Symbol) > 0)
{
//未決注文チケットを取得する
ulong tickets[];
Pending.GetTickets(_Symbol, tickets);
int numTickets = ArraySize(_tickets);
//注文を閉じる
for(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}
if(Pending.TotalPending(_Symbol) == 0)
{
Comment(“All pending orders closed on ”+_Symbol);
}
}
}
#propertyディレクティブには、著作権、リンク、説明、およびscript_show_confirmプロパティが含まれます。これにより、スクリプトを実行する前に確認ダイアログが表示されます。また、\MQL5\Include\Mql5BookフォルダからTrade.mqhおよびPending.mqhファイルを含め、CTradeおよびCPendingクラスに基づいてオブジェクトを作成しました。
スクリプトが実行されると、OnStart()イベントハンドラが実行されます。まず、PositionType()関数の出力を確認します。オープンポジションを示している場合は、Trade.Close()関数を呼び出して現在のポジションを決済します。ポジションが適切にクローズされた場合、チャートにコメントが表示されます。
次に、Pendingを確認します。TotalPending()関数を使用して未決注文(保留中の注文)があるかどうかを確認します。保留中の注文があれば、Pending.GetTickets()関数を使用して現在の注文チケットを取得し、Trade.Delete()を使用して注文を閉じます。もし、保留中の注文が無ければ、コメントがチャートに出力されます。
このスクリプトは、現在のチャートシンボルのすべての注文とポジションを決済します。プログラムを修正して、ポジションと注文を決済するシンボルを指定できます。CloseSymbolという名前の入力変数を追加します。CloseSymbolに値が指定されている場合、スクリプトは指定されたシンボルのすべての注文を閉じます。それ以外の場合は、スクリプトは現在のチャートシンボルを使用します。
#property script_show_inputs
#include <Mql5Book\Trade.mqh>
CTrade Trade;
#include <Mql5Book\Pending.mqh>
CPending Pending;
input string CloseSymbol = “”;
void OnStart()
{
//シンボル名をチェック
string useSymbol = CloseSymbol;
if(CloseSymbol == “”) useSymbol = _Symbol;
//現在のポジションを閉じる
if(PositionType(useSymbol) != WRONG_VALUE)
{
bool closed = Trade.Close(useSymbol);
if(closed == true)
{
Comment(“ポジションクローズ ” +useSymbol);
}
}
//未決注文を閉じる
if(Pending.TotalPending(useSymbol) > 0)
{
//未決注文チケットを取得
ulong tickets[];
Pending.GetTickets(useSymbol, tickets);
int numTickets = ArraySize(_tickets);
//注文を閉じる
for(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}
if(Pending.TotalPending(useSymbol) == 0)
{
Comment(“All pending orders closed on ”+useSymbol);
}
}
}
script_show_inputsプロパティディレクティブを使用して、スクリプトの実行前に入力ウィンドウをユーザーに表示します。ユーザーは、CloseSymbolのシンボル名を入力し、スクリプトを実行します。シンボル名が指定されていない場合、プログラムは現在のチャートシンボルをuseSymbol変数に割り当てます。useSymbol変数は、使用するシンボルを示すためにスクリプト全体で使用されます。
\MQL5\ScriptsフォルダーでCloseAllOrders.mq5スクリプトのソースコードを表示できます。
ライブラリー P306
ライブラリは、ほかのプログラムで使用するための再利用可能な関数を含む実行可能ファイルです。これはインクルードファイルに似ていますが、重要な違いがいくつかあります。インクルードファイルとは異なり、ライブラリには、他のプログラムで使用できるクラスや変数がありません。ライブラリでクラス、構造体、列挙などを定義できますが、それらはライブラリの外では使用できません。
MQL5プログラムでは、ネイティブのWindowsDLLsまたはC++で作成されたそのほかのDLLsを使用できます。DLLs関数をインポートするプロセスは、MQL5ライブラリから関数をインポートするのと似ています。ライブラリに含まれる関数には、渡すことができるパラメータの型に関して制限があります。動的配列を含むポインターとオブジェクトは、ライブラリ関数に渡すことができません。DLLから関数をインポートする場合、それらの関数に文字列または動的配列を渡すことはできません。
ライブラリの利点は、ソースコードを公開せずに配布できることです。多数のEAでライブラリを使用する場合、ライブラリに依存するすべてのプログラムを再コンパイルする必要なく、ライブラリに小さな変更を加えることができます。(関数パラメータを変更しない限り)。
MQL5ウィザードを使用して空のライブラリファイルを作成できます。ライブラリは\MQL5\Librariesフォルダに保存されます。ライブラリには、ファイルの先頭にライブラリプロパティディレクティブが必要です。存在しない場合、ファイルはコンパイルされません。2つのエクスポート可能な関数を含むサンプルライブラリを作成してみましょう。エクスポートしたい関数は関数パラメータの後にexport修飾子があります。
#property library
#include <Mql5Book\Indicators.mqh>
CiRSI RSI;
bool BuySignal(string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrice) export
{
RSI.Init(pSymbol, pTimeframe, pPeriod, pPrice);
if( RSI.Main() < 30) return(true);
else return(false);
}
bool SellSignal(string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrice) export
{
RSI.Init(pSymbol, pTimeframe, pPeriod, pPrice);
if( RSI.Main() < 70) return(true);
else return(false);
}
このファイルはSignalLibrary.mq5という名前で、\MQL5\Librariesフォルダにあります。#propertyライブラリディレクティブは、これがライブラリファイルであることを示します。このライブラリには、取引シグナルを呼び出しプログラムに返すために使用される2つの関数が含まれています。これらの関数は単純なRSIの買われすぎおよび売られすぎのトレードシグナルを使用しますが、それらを使用するEAやプログラマーから隠しておくことができる、より精巧なトレードシグナルを含むライブラリを作成することもできます。
\MQL5\Include\Mql5Book\Indicators.mqhファイルをインクルードし、CiRSIクラスに基づいてオブジェクトを宣言します。このオブジェクトはライブラリの外では見えないことに注意してください。BuySignal()およびSellSignal()関数は、RSIインジケータのパラメータを設定するパラメータを取ります。閉じ括弧の後のexport修飾子に注意してください。別のプログラムにインポートされる関数には、エクスポート修飾子が必要です!
このファイルは、他のMQL5プログラムと同様にコンパイルされます。ライブラリ関数を別のプログラムで使用するには、それらをそのプログラムにインポートする必要があります。#importディレクティブを使用してこれを行います。これらの関数をEAプログラムにインポートする方法は次のとおりです。
#import “SignalLibrary.ex5”
bool BuySignal(string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrice)
bool SellSignal(string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrice)
#import
ライブラリ名は、開始#importディレクティブの後に二重引用符で囲みます。これがコンパイル済みの実行可能ファイルであることを示すライブラリ名の.ex5に注意してください。ソースコードファイルをインポートすることはできません。すべてのライブラリは、\MQL5\Librariesフォルダーに配置する必要があります。開始#importディレクティブに続いて、ライブラリからインポートする関数があります。最後の#importディレクティブは、最後にインポートされた関数の後に存在する必要があります。
インポートされた関数は、他の関数と同じように使用されます。EAでBuySignal()関数を使用する方法の例を次に示します。
if(PositionType(_Symbol) == WRONG_VALUE && BuySignal(_Symbol, _Period, 14, PRICE_CLOSE))
{
//買いポジションを開く
}
上記の例はBuySignal()関数を呼び出し、現在のチャートシンボルと期間の終値を使用して14期間のRSIのRSI値を計算します。RSIが現在売られすぎている場合、関数はtrueを返します。
プログラム内の関数または事前定義されたMQL5関数と同じ名前のライブラリからインポートされた関数がある場合、正しい関数を識別するためにスコープ解決演算(::)を使用する必要があります。例えば、プログラムに既にBuySignal()という名前の関数があるとします。ライブラリファイルからBuySignal()関数をインポートした場合、呼び出すときにライブラリ名を先頭に付ける必要があります。
SignalLibrary : : BuySignal(_Symbol, _Period, 10, PRICE_CLOSE)