
MQL5でExpert Advisorを書くための初心者向けステップバイステップガイド
はじめに
本記事は新しいMQL5 言語でシンプルなExpert Advisorを書く方法を学ぼうとする初心者を対象にしています。 EA (Expert advisor)がしてほしいことを最初に定義し、 次にEAにそうさせる方法に進みます。
1. トレーディングストラテジー
EAの働き:
- 特定のインディケーターをモニターし、 ある条件が満たされた時、満たされた現在の条件に応じてトレード(Short/SellまたはLong/Buyのいずれか)をプレースします。
上記はトレーディング ストラテジーと呼ばれます。EAを書く前に、まずEAで自動化したいストラテジーを策定しなければなりません。そのため、この場合EAの中に策定したいストラテジーが反映するように上記を修正しましょう。
- 期間8の移動平均と呼ばれるインディケータを使用します。 ( どんな期間も選ぶことができますが、このストラテジーの目的に合わせて8を使用します。)
- 移動平均-8 (ここではMA-8と呼びます) が増加し価格がそれ以上でクローズの時にEAがLong (Buy) トレードをプレースし、MA-8が減少し価格がそれ以下でクローズな時EAがShort (Sell) をプレースするようにしたいです。
- また、マーケットがトレンドかどうか判定することを助ける平均方向性移動 (ADX) と呼ばれる別のインディケーターを期間8で使います。マーケットにトレンドがある時にトレードに参加し、そうでないときに緩和するためにこうしています。 これを行うには上記条件が満たされ、ADX値が22以上である時だけトレード (BuyまたはSell) をプレースします。もし、ADXが22以上だが減少している、もしくはADXが22以下の場合、条件Bが満たされていたとしてもトレードしません。
- また30 pipsのストップロスを設定して自己保護をはかりましょう。そして利益ターゲットついては100 pipsをターゲットにします。
- 新しいバーが形成された時だけのBuy/Sellの機会をEAに見張っていてほしいです。またBuy条件が満たされた場合 Buy ポジションをオープンにし、すでにオープンなものがないこと、またSell 条件が満たされた場合 Sell ポジションをオープンにし、すでにオープンなものがないことを確実にします。
ストラテジーを策定したので、コードを書きましょう。
2. Expert Advisorの作成
2.1MQL5 ウィザード
MetaQuotes言語 Editor 5を立ち上げます。そしてメニューバーのNew ボタンをクリックするかCtrl+Nを押します。
図 1. 新MQL5 ドキュメントの開始
MQL5 ウィザード ウインドウで、図 2のようにExpert Advisorを選択し、「Next」をクリックします。
図 2. プログラムタイプの選択
次のウインドウで、 名前ボックスに希望するEAの名前をタイプします。この場合、 私はMy_First_EAとタイプしました。Authorボックスにあなたの名前をタイプし、あればウェブアドレスまたはEメールアドレスを Link ボックスにタイプします。
図 3. Expert Advisorの全般プロパティー
どの値がベストな結果を得られるか見るために我々のEAのパラメーターを変更したいため、「Add」ボタンをクリックしてそれらを追加します。
図 4. EA 入力パラメーターの設定
EAで、我々のストップロス、テイクプロフィット、ADX 期間、そして移動平均期間設定の実験したいので、それらをこの時点で定義します。
名前 セクションでダブルクリックし、パラメーターの名前をタイプします。それから、 Type をダブルクリックしてパラメーターのデータタイプを選択し、 初期値 セクションでダブルクリックしてパラメーターの初期データをタイプします。
完了するとこのように見えるはずです。:
図 5. EA入力パラメーターのデータタイプ
上でご覧のとおり、全てのパラメーターに整数 (int) データタイプを選択しました。 データタイプについて少しお話ししましょう。
- char: char タイプはメモリの1バイト(8 ビット) を取り、二進表記法 2^8=256 値による表現を可能にします。char タイプは正と負、両方の値を含みます。値の範囲は -128 から 127までです。
- uchar : uchar 整数タイプもメモリ1バイトを取り、char タイプと同様、しかしそれと違って、uchar は正の値だけに使われます。最小値はゼロで、最大値は255です。 uchar タイプ名の最初の文字は符号無しの略語です。
- short: shortタイプのサイズは 2 バイト (16 ビット) で、2の16乗: 2^16 = 65 536に等しい値の範囲で表現が可能です。short タイプは符号のもので、正と負の両方の値を含むので、値の範囲は -32 768 と 32 767の間です。
- ushort: 符号無し short タイプは、 タイプ ushortで、また2バイトのサイズを持ちます。最小値は0で、最大値は65 535です。
- int : int タイプののサイズは4バイト (32 ビット)です。最小値は -2 147 483 648で、最大値は2 147 483 647です。
- uint : 符号無し整数タイプはuint。メモリ4バイトを取り、0 から 4 294 967 295までの整数の表現を可能にします。
- long : long タイプのサイズは8 バイト (64 ビット)。最小値は -9 223 372 036 854 775 808で、最大値は 9 223 372 036 854 775 807。
- ulong : ulong タイプも 8 バイトを取り、 0 から 18 446 744 073 709 551 615までの値を保管できます。
上記の様々なデータタイプの説明から、符号無し整数タイプは負の値の保管にはデザインされていなく、負の値を設定しようとすると予測不明の事態になりかねます。例えば、もし負の値を保存したい場合、uchar, uint, ushort, ulongなどの符号無しタイプの中に保管することはできません。
我々のEAに戻りましょう。データタイプを見ると、これらのパラメーターを保存するデータはそれぞれ127 または 255 未満なので、charまたはuchar データタイプを使うべきであることに私と同感するでしょう。 メモリ管理のためにこれをするのがベストです。しかし、我々の議論のために、 int タイプにとどまります。
すべての必要なパラメーターの設定が済んだら、 Finished ボタンをクリックすると、MetaQuotes Editorが次の図に表示されるコードの骨子を作成します。
理解しやすいようにコードを様々なセクションに分けましょう。
コード の上の部分 (Header) でEAのプロパティが定義されます。図 3でMQL5 ウィザードに記入する値 が見れます。
このコードのセクションでは、 description (EAの簡略テキスト説明)のような追加的パラメーターの定義、定数の宣言、追加ファイルの含有、また関数のインポートが可能です。
文が # シンボルで始まる場合、 プリプロセッサ・ディレクティブと呼ばれ、 セミコロン ‘;’ で終わりません。プリプロセッサ・ディレクティブの他の例は以下。:
#define :
#define ディレクティブは定数宣言に使用されます。フォームで書かれます。
#define identifier token_string
これが何をするかというと、コードで識別子が発生するたびに token_string値で置き換えます。
例 :
#define ABC 100
#define COMPANY_NAME "MetaQuotes Software Corp."
それは COMPANY_NAMEが発生するたびに文字列 "MetaQuotes Software Corp."で置き換えます。または我々コードで、ABCの発生毎に char (または整数) 100 で置き換えます。
プリプロセッサ・ディレクティブについてはMQL5 マニュアルで詳しく書かれています。我々の議論を進めましょう。
コードのヘッダー第二の部分は 入力 パラメータ セクションです。:
このセクションで我々のEAに使用される全てのパラメーターを指定しましょう。これらは、EAに書く関数に使用されるすべての変数を含みます。
このレベルで宣言される変数は、我々のEAで、必要な場合、全ての関数によってアクセスできるので Global 変数 と呼ばれます。 入力 パラメーターは、我々のEA外でのみ変更することができます。EAで操作することになる他の変数も宣言できますが、このセクションのEA外では利用できません。
次はEA初期化関数です。EA立ち上げ時またはチャート添付時に呼ばれる最初の関数で 、一回だけ呼ばれます。
このセクションは、EAがよく作動するように重要な確認を行うベストな場所です。
EAが作動するのに十分なバーがチャートにあるか等を知ることができます。
我々のインディケーター (ADXと移動平均インディケーター)に使用するハンドルを入手するベストな場所でもあります。
EAがチャートから削除された時OnDeinit functioが呼ばれます。
我々のEAには、 インディケーターに作成されたハンドルをこのセクションの初期化中にリリースします。
この関数はシンボルの新しい気配値を受信した時に作られる新しいTickイベントを処理します。
もしクライアントターミナルのExpert Advisorの使用が許可されていない場合 (「自動トレーディング」ボタン)、Expert Advisorはトレード操作を実行できません。
図 6. 自動トレーディング有効化
前に策定したトレーディング ストラテジーを実装するコードのほとんどはこのセクション内に書かれます。
EAのコードの様々なセクションを見てきましたので、骨子に肉を足し始めましょう。
2.2入力パラメーターセクション
//--- input parameters input int StopLoss=30; // ストップロス input int TakeProfit=100; // テイクプロフィット input int ADX_Period=8; // ADX Period input int MA_Period=8; // Moving Average Period input int EA_Magic=12345; // EA Magic Number input double Adx_Min=22.0; // Minimum ADX Value input double Lot=0.1; // Lots to Trade //--- Other parameters int adxHandle; // handle for our ADX indicator int maHandle; // handle for our Moving Average indicator double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars double maVal[]; // Dynamic array to hold the values of Moving Average for each bars double p_close; // Variable to store the close value of a bar int STP, TKP; // To be used for ストップロス & テイクプロフィット values
ご覧のとおり、パラメーターをもっと追加しました。新しい パラメーターの考察に進む前に、今見れるものを考察しましょう。2つの斜線スラッシュ ‘//’ でコードにコメントを書くことができます。コメントで、変数が何を示すか、またはその時点でコードで何をしているか知ることができます。また我々コードをよりよく理解できます。コメントを書く基本的な方法が2つあります。:
// Other Parameters …
これはシングルラインのコメントを表します。
/*
Tこれはマルチラインのコメントを表します。
*/
これはマルチラインのコメント。マルチラインのコメントはシンボルのペア /* で始まり1つの */ で終わります。
コンパイラーはコードをコンパイルする時、全てのコメントを無視します。
入力パラメーターにシングルラインコメントを使うことは、 EAユーザーにとってパラメーターが何を表すか理解できるようにするいい方法です。 EA入力プロパティーで、我々のユーザーはパラメーター自身を見ませんが、以下の表示されるコメントを見ます。:
図 7. Expert Advisorの入力パラメーター
それでは我々のコードに戻りましょう…
EAに追加パラメーターを追加することを決めました。EA_MagicはEAによる全てのオーダーのマジックナンバーです。 最小ADX値 (Adx_Min) はdouble データタイプとして宣言されます。doubleは、整数部分・小数点・少数を含む浮動小数点定数を保存するのに使用されます。
例:
double mysum = 123.5678;
double b7 = 0.09876;
Lot to trade (Lot) はトレードしたい金融商品のボリュームを表します。そして以下を使って他のパラメーターを宣言します。:
adxHandle は ADX インディケーターハンドルを保管するのに使われ、一方 maHandle は移動平均インディケーターのハンドルを保管します。 plsDI[], minDI[], adxVal[] はチャートの各バーの+DI, ・DI ・メインADX値(ADXインディケーターの) を保持する動的配列です。maVal[] は、チャートの各バーの移動平均インディケーター値を保持する動的配列です。
ところで、動的配列とは何でしょう?動的配列とは次元のない宣言された配列です。つまり、 鉤括弧のペア内に指定された値がありません。一方、静的配列は宣言のポイントで定義された次元を持ちます。
例:
double allbars[20]; // this will take 20 elements
p_close は、Buy/Sellトレードを確認するために監視するバ ーのClose priceを保存するために使用する変数です。
STP と TKP はEAのストップロス とテイクプロフィット値を保存するために使用されます。
2.3. EA 初期化セクション
int OnInit() { //--- Get handle for ADX indicator adxHandle=iADX(NULL,0,ADX_Period); //--- Get the handle for Moving Average indicator maHandle=iMA(_Symbol,_Period,MA_Period,0,MODE_EMA,PRICE_CLOSE); //--- What if handle returns Invalid Handle if(adxHandle<0 || maHandle<0) { Alert("Error Creating Handles for indicators - error: ",GetLastError(),"!!"); }
ここでそれぞれのインディケーター関数を使って、インディケーターのハンドルを入手します。
ADXインディケーターハンドル はiADX 関数を使って入手されます。それは、 パラメーターまたは引数として、チャートシンボル (NULL はまた現チャートの現シンボルを意味。)、チャート期間/タイムフレーム (0はまた現チャートの現在のタイムフレームを意味。)、インデックス計算のためのADX平均期間 (前に入力 パラメーターセクションで定義済) をとります。
int iADX(
string symbol, // symbol name
ENUM_TIMEFRAMES period, // period
int adx_period // averaging period
);
移動平均インディケーターハンドルは、iMA 関数を使って入手されます。それは以下の引数を持ちます。:
- チャートsymbol (それは現チャートの現シンボルの_symbol, symbol()またはNULLを使って入手できます。)
- チャート period/timeframe (現チャートの現タイムフレームの _period, period(),または0 を使って入手できます。 )
- 移動平均 平均期間 (前に入力パラメーターセクションで定義済の)、
- 価格チャートに関連するこのインディケーターのshift (ここでのシフトは 0)、
- 移動平均平滑 タイプ (以下のいずれか: 単純平均モード Simple Averaging-MODE_SMA、指数平均モード Exponential Averaging-MODE_EMA、平滑平均モード Smoothed Averaging-MODE_SMMAまたは線形加重平均モード Linear-Weighted Averaging-MODE_LWMA)、そして
- 平均化に使われる価格 (ここでは終値を使います。)
int iMA( |
これらのインディケーター関数の詳細に関してはMQL5を読んでください。各インディケーターの使い方をよりよく理解できます。
関数がうまくハンドルを戻さなかった時のエラーをもう一度確認してみます。INVALID_HANDLE エラーを入手します。 GetlastError 関数を使い、アラート関数を使ってエラーを表示します。
//--- Let us handle currency pairs with 5 or 3 digit prices instead of 4 STP = StopLoss; TKP = TakeProfit; if(_Digits==5 || _Digits==3) { STP = STP*10; TKP = TKP*10; }
前に宣言した STP と TKP変数内のストップロス とテイクプロフィット値を保存することにします。 なぜこれをするのでしょう?
それは、入力 パラメーターに保管された値はread-onlyで修正できないからです。そのため、我々のEAが全てのブローカーとうまく働くようにしたいです。DigitsまたはDigits()は十進法の数を戻し、現チャートシンボルの価格の正確性を判定します。5桁または3桁の価格チャートでは、ストップロスとテイクプロフィットの両方を10倍にします。
2.4. EA非初期化セクション
EAが無効にされた時またはチャートから外された時、常にこの関数が呼ばれるので、ここの初期化プロセス中に作成された全てのインディケーターハンドルをリリースします。ADXインディケーター用のハンドルと移動平均インディケーター用のハンドルの2つのハンドルを作成します。
そのためにIndicatorRelease() 関数を使用します。引数を一つだけ取ります。 (インディケーターハンドル)
bool IndicatorRelease(
int indicator_handle, // indicator handle
);
この関数 はインディケーターハンドルを削除し、使用中でなければ、インディケーターの計算ブロックをリリースします。
2.5EA ONTICK セクション
ここで最初にやらなければならない事は、現チャートに十分なバーがあるか確認することです。Bars 関数を使って、チャートのヒストリーでバー総数を入手します。 それは2つのパラメーター、symbolを取ります。 (_SymbolまたはSymbol()を使って入手可能。 これらの2つは我々のEAが添付されている現チャートの現シンボル、そして現チャートの期間または タイムフレーム (PeriodまたはPeriod()を使って入手可能)を戻します。この2つはEAが添付されている現チャートのタイムフレームを戻します。
もし利用可能な総バー数が 60未満の場合、チャートが十分なバーを持つまで我々のEAには待機していてほしいです。 アラート 関数は別のウインドウにメッセージを表示します。パラメーター/引数としてカンマで区切られたどんな値も取ります。この場合、 文字列値を一つしか持ちません。戻しがEAの初期化を防ぎます。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Do we have enough bars to work with if(Bars(_Symbol,_Period)<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } // We will use the static Old_Time variable to serve the bar time. // At each OnTick execution we will check the current bar time with the saved one. // If the bar time isn't equal to the saved time, it indicates that we have a new tick. static datetime Old_Time; datetime New_Time[1]; bool IsNewBar=false; // copying the last bar time to the element New_Time[0] int copied=CopyTime(_Symbol,_Period,0,1,New_Time); if(copied>0) // ok, the data has been copied successfully { if(Old_Time!=New_Time[0]) // if old time isn't equal to new bar time { IsNewBar=true; // if it isn't a first call, the new bar has appeared if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time); Old_Time=New_Time[0]; // saving bar time } } else { Alert("Error in copying historical times data, error =",GetLastError()); ResetLastError(); return; } //--- EA should only check for new trade if we have a new bar if(IsNewBar==false) { return; } //--- Do we have enough bars to work with int Mybars=Bars(_Symbol,_Period); if(Mybars<60) // if total bars is less than 60 bars { Alert("We have less than 60 bars, EA will now exit!!"); return; } //--- Define some MQL5 Structures we will use for our trade MqlTick latest_price; // To be used for getting recent/latest price quotes MqlTradeRequest mrequest; // To be used for sending our trade requests MqlResult mresult; // To be used to get our trade results MqlRates mrate[]; // To be used to store the prices, volumes and spread of each bar ZeroMemory(mrequest); // Initialization of mrequest structure
Expert Advisor は新しいバーの始めでトレード操作を実行するので、新しいバー識別の問題を解決する必要があります。EAがLong/Short セットアップを各ティックで確認しないようにしたいです。新しいバーがある時だけ、EAがLong/Short ポジションを確認してほしいです。
バータイムを保管する静的データタイム変数 Old_Timeを宣言することから始めます。OnTick 関数の次の呼び出しまで値がメモリに保持されていてほしいので、それらを静的として宣言します。 これで、新しい(現在の)バータイムを保持するエレメント配列であるNew_Time 変数 (datetime データタイプ)と一緒に、その値を比較することができます。 また、 bool データ タイプ 変数 IsNewBarを宣言し、 その値をfalseに設定します。新しいバーがある時だけ、その値がTRUEとなってほしいからです。
CopyTime 関数を使って、 現在のバーを入手します。それは、1つのエレメントと一緒にバータイムを配列 New_Timeにコピーします。; もしうまくいったら、新しいバーの時間と前バーの時間を比較します。もし時間が同じでなければ、 新しいバーがあることを意味します。 変数 IsNewBar をTRUEに設定し、 現在のバーの値を変数 Old_Timeに保存します。
IsNewBar 変数は新しいバーがあることを示します。もしFALSEの場合、 OnTick 関数の実行を終了します。
コードを見てみましょう。
if(MQL5InfoInteger(MQL5_DEBUGGING)) Print("We have new bar here ",New_Time[0]," old time was ",Old_Time);
それはデバッグモード実行のために確認し、デバッグモード時にバータイムについてのメッセージを印刷します。それについてさらに考察します。
ここで次にしたいことは、使用できる十分なバーがあるか確認することです。なぜ繰り返すのか? 我々のEAが正常に働くことを確実にしたいだけです。 EAがチャートに添付された時OnInit 関数が一回だけ呼び出される一方、OnTick 関数は新しいティック(気配値段))がある度に呼ばれることに留意すべきです。
ここではそれを違った方法で行いました。式から入手したヒストリー内の総バーを保存することにします。
int Mybars=Bars(_Symbol,_Period);
OnTick 関数内で宣言される新しい変数 Mybarsの中 コードの入力パラメータセクションで宣言した変数と違って、このタイプの変数はローカル変数です。 コードの入力パラメーターセクションで宣言された変数がコード内全ての関数で利用できる一方、シングル関数で宣言された変数には制限があり、その関数でのみ利用できます。その関数外ではそれを使用できません。
次に、 我々のEAのこのセクションで使用されるMQL5の変数構造 タイプ を宣言します。MQL5はEA開発者の仕事をかなり楽にさせるような内蔵構造を多く持ちます。構造を一つ一つ見ていきましょう。
最新のシンボル価格を保管するために使用される構造です。
struct MqlTick
{
datetime time; // Time of the last prices update
double bid; // Current Bid price
double ask; // Current Ask price
double last; // Price of the last deal (Last)
ulong volume; // Volume for the current Last price
};
SymbolInfoTick() 関数を一旦呼ぶと、Ask, Bid, Last そして Volume の現在値を入手するためにMqlTickタイプとして宣言されたどんな変数も簡単に使用できます。
買値と売値を入手できるように latest_price をMqlTick タイプとして宣言します。
この構造はトレード操作のためのトレードリクエストを実行するために使用されます。それは構造の中にトレード ディールを実行するのに必要な全てのフィールドを含みます。
struct MqlTradeRequest
{
ENUM_TRADE_REQUEST_ACTIONS action; // Trade operation type
ulong magic; // Expert Advisor ID (magic number)
ulong order; // Order ticket
string symbol; // Trade symbol
double volume; // Requested volume for a deal in lots
double price; // Price
double stoplimit; // StopLimit level of the order
double sl; // Stop Loss level of the order
double tp; // Take Profit level of the order
ulong deviation; // Maximal possible deviation from the requested price
ENUM_ORDER_TYPE type; // Order type
ENUM_ORDER_TYPE_FILLING type_filling; // Order execution type
ENUM_ORDER_TYPE_TIME type_time; // Order execution time
datetime expiration; // Order expiration time (for the orders of ORDER_TIME_SPECIFIED type)
string comment; // Order comment
};
MqlTradeRequestタイプに宣言された変数はどんな変数であれ、トレード操作のためのオーダー送付に使用することができます。ここでは mrequest をMqlTradeRequest タイプとして宣言しました。
どんなトレード操作結果もMqlTradeResultタイプの特別事前定義構造として戻されます。MqlTradeResult タイプとして宣言されたいかなる変数はトレードリクエスト結果にアクセスすることができます。
struct MqlTradeResult
{
uint retcode; // Operation return code
ulong deal; // Deal ticket, if it is performed
ulong order; // Order ticket, if it is placed
double volume; // Deal volume, confirmed by broker
double price; // Deal price, confirmed by broker
double bid; // Current Bid price
double ask; // Current Ask price
string comment; // Broker comment to operation (by default it is filled by the operation description)
};
mresult をMqlTradeResult タイプとして宣言しました。
各バーの価格 (始値、終値、高値、低値)、タイム、ボリュームとシンボルのスプレッドはこの構造に保存されます。 MqlRates タイプに宣言されたいかなる配列は、シンボルの価格、ボリューム、スプレッドヒストリーを保存するのに使われます。
struct MqlRates
{
datetime time; // Period start time
double open; // Open price
double high; // The highest price of the period
double low; // The lowest price of the period
double close; // Close price
long tick_volume; // Tick volume
int spread; // Spread
long real_volume; // Trade volume
};
ここでは、これらの情報を保管するのに使用される配列 mrate[] を宣言します。
/* Let's make sure our arrays values for the Rates, ADX Values and MA values is store serially similar to the timeseries array */ // the rates arrays ArraySetAsSeries(mrate,true); // the ADX DI+values array ArraySetAsSeries(plsDI,true); // the ADX DI-values array ArraySetAsSeries(minDI,true); // the ADX values arrays ArraySetAsSeries(adxVal,true); // the MA-8 values arrays ArraySetAsSeries(maVal,true);
次に、バー詳細をシリーズとして保管するのに使用する全ての配列を設定することにします。配列にコピーされる値が時系列(つまり 0, 1, 2, 3, (バーインデックスに対応))のようにインデックス化されることを確実にします。 そのため ArraySetAsSeries() 関数を使います。
bool ArraySetAsSeries(
void array[], // array by reference
bool set // true denotes reverse order of indexing
);
コードの初期化セクションの時もこれが一回できることに留意するべきです。 しかし、説明のためにこの時点でそれをお見せすることにしました。
//--- Get the last price quote using the MQL5 MqlTick Structure if(!SymbolInfoTick(_Symbol,latest_price)) { Alert("Error getting the latest price quote - error:",GetLastError(),"!!"); return; }
ここで最新の気配値段を入手するために SymbolInfoTick 関数を使います。この関数は、チャート symbol と MqlTick 構造変数 (latest_price))の2つの引数を取ります。ここでも、もしエラーがあれば私たちは報告します。
//--- Get the details of the latest 3 bars if(CopyRates(_Symbol,_Period,0,3,mrate)<0) { Alert("Error copying rates/history data - error:",GetLastError(),"!!"); return; }
次に、CopyRates 関数を使って最新の3つのバーについての情報を我々の Mqlrates タイプ配列の中にコピーします。指定Symbol-PeriodのMqlRates 構造ヒストリー データ指定量をMqlRates タイプ配列に入手するためにCopyRates 関数を使用します。
int CopyRates(
string symbol_name, // symbol name
ENUM_TIMEFRAMES timeframe, // period
int start_pos, // start position
int count, // data count to copy
MqlRates rates_array[] // target array to copy
);
シンボル名は‘_symbol’を使って入手できます。現在の期間/タイムフレームは ‘_period’ を使って入手できます。スタートポジションには、現在のバー Bar 0 から開始し、3つの バー、バー 0・1・ ・ 2だけカウントします。結果は我々の配列mrate[]に保管されます。
mrate[] 配列は今や、 バー 0、1、2の全ての価格、時間、ボリューム、スプレッド情報を含みます。 そのためバーの詳細を得るには以下を使用します。
mrate[bar_number].bar_property
例えば、各バーについて以下の情報を持てます。:
mrate[1].time // Bar 1 Start time
mrate[1].open // Bar 1 Open price
mrate[0].high // Bar 0 (current bar) high price, etc
次に、 全てのインディケーター値をCopyBuffer 関数を使って宣言した動的配列にコピーします。
int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer number
int start_pos, // start position
int count, // amount to copy
double buffer[] // target array to copy
);
インディケーターハンドルは、OnInitセクションに作成したハンドルです。バッファーナンバーに関しては、ADXインディケーターは3つのバッファーを持っています。:
- 0 - MAIN_LINE,
- 1 - PLUSDI_LINE,
- 2 - MINUSDI_LINE.
移動平均インディケーターはバッファーを一つだけ持ちます。:
- 0 – MAIN_LINE.
バー (0) から過去2つのバーにコピーします。ですから、コピーする量は3つ (バー 0、1 、2)です。バッファー[] は以前に宣言したターゲット動的配列、adxVal, plsDI, minDI と maVal.です。
ここでもう一度見れるように、コピープロセス内で起こりうるエラーをとらえようとします。エラーがある場合、 これ以上進む必要はありません。
CopyBuffer() と CopyRates() 関数は正常の場合コピーされたレコード総数を戻す一方、エラーの場合-1を戻すことに留意すべきです。これが、エラー確認関数で0 (ゼロ)未満の値を確認する理由です。
//--- Copy the new values of our indicators to buffers (arrays) using the handle if(CopyBuffer(adxHandle,0,0,3,adxVal)<0 || CopyBuffer(adxHandle,1,0,3,plsDI)<0 || CopyBuffer(adxHandle,2,0,3,minDI)<0) { Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!"); return; } if(CopyBuffer(maHandle,0,0,3,maVal)<0) { Alert("Error copying Moving Average indicator buffer - error:",GetLastError()); return; }
このポイントで、BuyまたはSellポジションオープンがあるか確認したいです。 つまりSellまたはBuyトレードが1つしかないことを確実にしたいです。すでにある場合、新しいBuyをオープンしたくないです。 すでにオープンしているものがあれば、新しい Sell をオープンしたくないです。
これを達成するために、まず、BuyまたはSellオープンポジションがあればTRUE 値を持つ2つの bool データタイプ変数 (Buy_opened and Sell_opened) を宣言します。
//--- we have no errors, so continue //--- Do we have positions opened already? bool Buy_opened=false; // variable to hold the result of Buy opened position bool Sell_opened=false; // variable to hold the result of Sell opened position if (PositionSelect(_Symbol) ==true) // we have an opened position { if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { Buy_opened = true; //It is a Buy } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { Sell_opened = true; // It is a Sell } }
オープンポジションがあるか知るためにトレード関数 PositionSelect を使います。この関数はポジションオープンがある場合TRUEを戻し、ない場合FALSEを戻します。
bool PositionSelect( string symbol // Symbol name );
引数/パラメータとして、確認したいシンボル(通貨ペア)を取ります。 現在シンボル(通貨-ペア)を確認しているので、ここで _シンボルを使います。
もしこの式がTRUEを戻す場合、 ポジションオープンがBuyまたはSellか確認したいです。これにはPositionGetInteger 関数を使います。ポジション_TYPE モディファイアと共に使うときポジションオープンのタイプを知らせます。それは、ポジション_TYPE_BUY または ポジション_TYPE_SELLのいずれかである ポジション タイプ 識別子を戻します。
long PositionGetInteger( ENUM_POSITION_PROPERTY property_id // Property identifier );
我々のケースでは、 すでに持っているポジションでそれがオープンしたのか決めるためにそれを使います。もしSellの場合、TRUE 値を Sell_openedに保管し、もし Buyの場合、TRUE 値を Buy_openedに保管します。後で我々コードでSellまたはBuy 条件を確認する時、これらの2つの変数を使えます。
我々の Buy/Sell セットアップを使ってバーの終値を保管する時が来ました。その変数を前に宣言していることを思い出してください。
// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1 p_close=mrate[1].close; // bar 1 close price
これを完了させたら次のステップに進みます。
/* 1. Check for a long/Buy Setup : MA-8 increasing upwards, previous price close above it, ADX > 22, +DI > -DI */ //--- Declare bool type variables to hold our Buy Conditions bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]); // MA-8 Increasing upwards bool Buy_Condition_2 = (p_close > maVal[1]); // previuos price closed above MA-8 bool Buy_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum value (22) bool Buy_Condition_4 = (plsDI[0]>minDI[0]); // +DI greater than -DI //--- Putting all together if(Buy_Condition_1 && Buy_Condition_2) { if(Buy_Condition_3 && Buy_Condition_4) { // any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.ask,_Digits); // latest ask price mrequest.sl = NormalizeDouble(latest_price.ask - STP*_Point,_Digits); // ストップロス mrequest.tp = NormalizeDouble(latest_price.ask + TKP*_Point,_Digits); // テイクプロフィット mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type = ORDER_TYPE_BUY; // Buy Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
Buy機会を確認し始める時が来ました。
前にデザインしたストラテジーを表すので上記の式を分析しましょう。 オーダーが注文される前に我々の条件が満たされなければならない各条件のブール タイプ変数を宣言します。 ブールタイプ変数はTRUEまたはFALSEしか含みません。 そのため、 我々の Buyストラテジー は4つの条件に分けられます。 もしいずれかの条件が満たされる場合、 TRUE の値が我々のbool タイプ 変数に保存されます。そうでなければFALSE の値が保管されます。一つ一つ見ていきましょう。
bool Buy_Condition_1 = (maVal[0]>maVal[1]) && (maVal[1]>maVal[2]);
ここではバー 0・1・2の MA-8 値を見ています。もし現在のバーのMA-8の値が以前の Bar 1のその値より大きく、またBar 1のMA-8 値がBar 2のその値より大きい場合、それは、MA-8が上に増えていることを意味します。これはBuyセットアップの条件を満たします。
bool Buy_Condition_2 = (p_close > maVal[1]);
この式は Bar 1 終値が同じ期間(Bar 1 期間)でMA-8 の値より高いか確認しています。もし価格が高い場合、 我々の第二の条件も満たされ、 他の条件を確認できます。しかし、 今考察した2つの条件が満たされない場合、他の条件を確認する必要はありません。そのため、次の式をこれらの2つの初期条件 (式)の中に含むことにします。
bool Buy_Condition_3 = (adxVal[0]>Adx_Min);
ここで、ADXの現在の値 (Bar 0のADX値) が、入力パラメーターで宣言した最小ADX値より大きいか確認したいです。もしこの式がtrue、つまり現在値 ADX が最小要求値よりも大きい場合; plusDI 値もminusDI 値より大きいことを確実にしたいです。これが次の式で成し遂げたことです。
bool Buy_Condition_4 = (plsDI[0]>minDI[0]);
もしこれらの条件の全てが満たされたら、つまりもしtrueを戻したら、新しいBuyポジションをオープンにしないことを確実にしたいです。(もしそれをすでに持っている場合) 我々のコードで前に宣言したBuy_opened 変数の値を確認する時が来ました。
// any opened Buy position? if (Buy_opened) { Alert("We already have a Buy Position!!!"); return; // Don't open a new Buy Position }
もし Buy_opened がtrueなら、さらにもう一つの Buy ポジションをオープンにしたくありあません。 そのため、我々のEAが次のティックを待つように、通知アラートを表示してから戻します。しかし、 もし Buy_opened がFALSEの場合、前にオーダーを送るために宣言した MqlTradeRequest タイプ変数 (mrequest) を使ってレコードを準備します。
- 即時実行のためにトレード オーダーを注文するので、ここでの アクション はトレード操作タイプ TRADE_ACTION_DEAL です。もしオーダーを修正する場合、TRADE_ACTION_MODIFYを使用します。オーダーを宣言するために TRADE_ACTION_REMOVEを使用します。 MqlTick タイプ latest_price を使って最新のAsk価格を入手します。 オーダー ストップロス 価格は StopLossを買値から引くことで入手でき、 一方オーダー テイクプロフィット 価格は TakeProfit を買値に足すことで入手できます。買値、StopLossとTakeProfit 値にNormalizeDouble 関数を使ったことに気づくと思います。トレードサーバーに送る前に、常にこれらの価格を通貨ペアの桁数に正規化するのは良い行いです。
- シンボル は現シンボル (_SymbolまたはSymbol())です。オーダー タイプ は注文するオーダー のタイプで、ここでbuy オーダー ORDER_TYPE_BUYを注文します。Sell オーダーにはORDER_TYPE_SELLです。
- オーダー type_fillingはオーダー 実行タイプです。; ORDER_FILLING_FOK とは ディールがオーダー指定値と同等またはそれよりも良い値の時に排他的に指定量で実行されることを意味します。 もしオーダーシンボルに十分なボリュームオファーがない時、オーダーは成立しません。
OrderSend() 関数はMqlTradeRequest タイプ 変数と MqlTradeResult タイプ変数の2つの引数をとります。
bool OrderSend( MqlTradeRequest& request // query structure MqlTradeResult& result // structure of the answer );
ご覧のとおり、 OrderSendを使いオーダー注文する際、MqlTradeRequest タイプ変数とMqlTradeResult タイプ変数を使いました。
// get the result code if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Buy order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Buy order request could not be completed -error:",GetLastError()); ResetLastError(); return; }
オーダーを送ったので、ここでオーダーの結果を確認するためにMqlTradeResult タイプ変数を使います。もしオーダー が正常に成立した場合、通知を受けたく、そうでない場合も知りたいです。MqlTradeResult タイプ変数 ‘mresult’で、操作リターンコード にアクセスにでき、そしてもし オーダーが注文される場合オーダーチケットナンバーにもアクセスできます。
リターンコード10009はOrderSend リクエストの正常完了、一方 10008 はオーダーがプレースされたことを示します。このため、これらの2つのリターンコードのどちらかを確認しました。もし、これらのどれかがあれば、我々のオーダーが完了もしくはプレースされたこを確認できます。
Sell 機会を確認するために、指定最小値よりも大きくなければならないADXを除き、Buy 機会の時に行った事と反対の事を確認します。
/* 2. Check for a Short/Sell Setup : MA-8 decreasing downwards, previous price close below it, ADX > 22, -DI > +DI */ //--- Declare bool type variables to hold our Sell Conditions bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]); // MA-8 decreasing downwards bool Sell_Condition_2 = (p_close <maVal[1]); // Previous price closed below MA-8 bool Sell_Condition_3 = (adxVal[0]>Adx_Min); // Current ADX value greater than minimum (22) bool Sell_Condition_4 = (plsDI[0]<minDI[0]); // -DI greater than +DI //--- Putting all together if(Sell_Condition_1 && Sell_Condition_2) { if(Sell_Condition_3 && Sell_Condition_4) { // any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position } mrequest.action = TRADE_ACTION_DEAL; // immediate order execution mrequest.price = NormalizeDouble(latest_price.bid,_Digits); // latest Bid price mrequest.sl = NormalizeDouble(latest_price.bid + STP*_Point,_Digits); // ストップロス mrequest.tp = NormalizeDouble(latest_price.bid - TKP*_Point,_Digits); // テイクプロフィット mrequest.symbol = _Symbol; // currency pair mrequest.volume = Lot; // number of lots to trade mrequest.magic = EA_Magic; // Order Magic Number mrequest.type= ORDER_TYPE_SELL; // Sell Order mrequest.type_filling = ORDER_FILLING_FOK; // Order execution type mrequest.deviation=100; // Deviation from current price //--- send order OrderSend(mrequest,mresult);
buy セクションでやったように、オーダーがプレースする前に満たされなければならない各条件のbool タイプ変数を宣言します。bool タイプ変数はTRUEまたはFALSEしか含めません。 そのため、 我々の Sell ストラテジーは4つの条件に分けられます。 もしいずれかの条件が満たされる場合、 TRUE の値が我々のbool タイプ 変数に保存されます。そうでなければFALSE の値が保管されます。Buy セクションでやったように一つ一つ見ていきましょう。
bool Sell_Condition_1 = (maVal[0]<maVal[1]) && (maVal[1]<maVal[2]);
ここでは バー 0・1・2の MA-8 値を見ています。もし現在のバーの MA-8 の値が以前の Bar 1 のその値より小さく、 Bar 1のMA-8 値もBar 2のその値よりも小さい場合、それは MA-8 が 下に減少していることを意味します。これはSell セットアップ条件の1つを満たします。
bool Sell_Condition_2 = (p_close <maVal[1]);
この式は、Bar 1 終値が同じ期間 (Bar 1 期間)でMA-8値よりも低いか確認しています。 もし価格が低い場合、我々の第二の条件も満たされ、他の条件を確認することができます。しかし、 今考察した2つの条件が満たされない場合、他の条件を確認する必要はありません。そのため、次の式をこれらの2つの初期条件 (式)の中に含むことにします。
bool Sell_Condition_3 = (adxVal[0]>Adx_Min);
ここで、ADXの現在の値 (Bar 0のADX値) が、入力パラメーターで宣言した最小ADX値より大きいか確認したいです。もしこの式がtrueの場合、ADX の現在の値は最小要求値よりも大きいです。; MinusDI値がplusDI 値よりも大きいことも確証したいです。これが次の式で成し遂げたことです。
bool Sell_Condition_4 = (plsDI[0]<minDI[0]);
もしこれらの条件が満たされた、つまり、trueが戻された場合、すでにあるなら新しいBuy ポジションをオープンしないようにしたいです。 我々コードで以前に宣言した Buy_opened 変数の値を確認する時が来ました。
// any opened Sell position? if (Sell_opened) { Alert("We already have a Sell position!!!"); return; // Don't open a new Sell Position }
もし Sell_opened がtrueなら、もう一つの Sell ポジションをオープンしたくないです。そのため我々のEAが次のティックを待つように通知アラートを表示してから戻します。しかし、 もし Sell_opened がFALSEの場合、Buying オーダーでやったように我々の Sell トレード リクエストをセットアップします。
ここでの大きな違いはストップロス価格とテイクプロフィット価格の計算の仕方です。また売っているので、売値で売ります。; このため、最新の買値を入手するために我々のMqlTick タイプ変数 latest_price を使いました。前に説明した他のタイプはORDER_TYPE_SELLです。
ここでも買値、StopLossとTakeProfit 値に NormalizeDouble 関数を使用しました。 トレードサーバーに送る前に、常にこれらの価格を通貨ペアの桁数に正規化するのはよい行いです。
我々のBuy オーダーで行ったように、我々の Sell オーダーも正常か否か確認しなければなりません。 我々のBuy オーダーのように同じ式を使いました。
if(mresult.retcode==10009 || mresult.retcode==10008) //Request is completed or order placed { Alert("A Sell order has been successfully placed with Ticket#:",mresult.order,"!!"); } else { Alert("The Sell order request could not be completed -error:",GetLastError()); ResetLastError(); return; } }
3. Expert Advisorのデバッグとテスト
この時点で、 我々のストラテジー がうまくいくか知るためにEAをテストすることが必要です。また、EA コードに1つや2つのエラーがあるかもしれません。次のステップで発見します。
3.1デバッグ
コードのデバッグによって、コードがライン毎にどう動作するか見ることができます (ブレークポイント設定の場合)。 そしてしばしば、コードのエラーやバグに気づき、コードをリアルトレードで使う前に、迅速に修正できます。
ここではExpert Advisorのデバッグプロセスをステップバイステップに見ていきます。まず、ブレークポイントの設定、そして第二にブレークポイントなしです。これを行うために、Editorを閉じていないことを確認してください。まずEAのテストに使いたいチャートを選択します。Editor メニューバーで、Toolsをクリックし、以下に表されるオプションをクリックします。:
図 8. デバッグオプション設定
オプション ウインドウが現れたら、使う通貨ペア、期間/タイムフレームを選択し、OKボタンをクリックします。:
デバッガーをスタートする前に、ブレークポイントを設定しましょう。ブレークポイントによってある選択した場所またはラインでコードの動作/パフォーマンスをモニターできます。 すべてのコードを一旦実行するというより、デバッガーはブレークポイントが見えると止まり、あなたのネットアクションを待ちます。各設定ブレイクポイントに到達するので、これによってコードを分析し、その動作をモニターできます。 また、我々の変数値を評価して計画通りが確認することができます。
ブレークポイントを挿入するには、ブレークポイントを設定したいコードのラインに行きます。左サイドで、コードライン境界線の近くの灰色フィールドをダブルクリックすると、白い四角が中に入っている小さい青ボタンが見えます。他の方法では、マウスのカーソルをブレークポイントが現れて欲しい所のコードラインに置き、F9を押します。ブレークポイントを外すには F9 をもう一度押し、それをかダブルクリックします。
図 10. ブレークポイントの設定
我々コードでは、5つの異なったラインでブレークポイントを設定します。
また説明のためそれらを1 から 5 までラベルします。
次に、ブレークポイントを7つのコードラインに下の図のように設定します。ブレークポイント 1 は上で作成したものです。
図 11. 追加ブレークポイントの設定
ブレークポイントの設定が済んだら、コードのデバッグを開始します。
デバッガーを開始するにはF5またはMetaEditorのツールバーのグリーンのボタンを押します。:
図 12. デバッガーの開始
エディターが最初にする事はコードのコンパイルです。もしエラーがどこかの箇所であればそれを表示し、エラーがなければコードが正常にコンパイルされたことを知らせます。
図 13. コンパイルレポート
コードの正常コンパイルはエラーがコードにない事を意味しないことに注意して下さい。コードの書かれ方によって、ランタイムエラーがあるかもしれません。例えば、もし式が少しの不注意のため正確に評価しない場合、コードは正確にコンパイルしても、正確に戻さないかもしれません。話が長くなったので実行に移しましょう…
デバッガーがコードをコンパイルし終わると、トレーディング ターミナルに移ります。EAをMetaEditorオプション設定で指定したチャートに添付します。それと同時に、EAの入力パラメーターセクションを表示します。まだ何も調整しないので OK ボタンをクリックします。
図 14. デバッグのためのExpert Advisor入力パラメーター
今、チャートの右上コーナーにはっきりEAが見えるようになっています。
OnTick()をスタートすると、ブレークポイント 1に到達次第すぐに止まります。
図 15. デバッガーが最初のブレークポイントで止まる
コードラインでグリーンの矢印に気が付くでしょう。それは以前のコードラインが実行され、現在のラインを実行する準備ができていることを知らせます。
次に進む前に説明をしましょう。エディターのツールバーを見ると、曲線矢印付きの3つのボタンが以前は灰色で消されていたのに今はアクティブになっていることに気づくでしょう。それは今デバッガーを実行しているからです。これらのボタン/コマンドはコードをステップスルーするのに使われます。(Step into, Step overまたはStep out)
図 16. コマンドにStep into
Step Intoは1つのプログラム実行のステップから次のステップに行くのに使用され、コードライン内で呼ばれた関数に入ります。ボタンをクリックまたはF11を押してコマンドを実行します。(コードのステップバイステップ・デバッグにこのコマンドをを使用します。)
図 17. Step over コマンド
Step overは、一方、コードラインで呼ばれた関数に入りません。ボタンをクリックまたはF10を押してコマンドを実行します。
図 18. Step out コマンド
1つ上のプログラムステップを実行するにはこれをクリックするかまたは Shift+F11を押します。
また、 Editorの下の部分で Toolbox ウインドウが見えます。このウインドウのデバッグ タブは以下のヘディングを持ちます。:
- File : 呼ばれたファイル名を表示。
- Function : 呼ばれたファイルから現在の関数を表示。
- Line : 関数が呼ばれたファイルのコードラインの数を表示。
- Expression : コードから監視に興味がある式/変数の名前をタイプする場所。
- Value : Expressionエリアでタイプした式/変数の値を表示。
- Type : モニターしている式/変数のデータを表示。
我々のデバッグプロセスに戻ります…
次にしたいことはモニターしたいコードの変数/式内にタイプすることです。コードで本当に重要な変数/式だけをモニターするようにします。我々の例では以下をモニターします。:
- Old_Time (古いバータイム)
- New_Time[0] (現バータイム)
- IsNewBar (新しいバーを示すフラグ)
- Mybars (ヒストリー内の総バー) – 我々の EAはそれに左右される
他のADX 値、MA-8 値なども追加できます。
式/変数を追加するには、上の図に示されるようにExpressionエリア下でダブルクリックまたは右クリックし、Add を選択します。
モニターのため式/変数をタイプ
図 19. Expression監視ウインドウ
必要な変数/式をすべてタイプ…
図 20. 監視のために式または変数の追加
もし変数がまだ宣言されていない場合、そのタイプは「Unknown identifier」 (静的変数を除く)です。
では次に進みましょう…図 21. アクション中のコマンドにStep into
Step into ボタンをクリックまたはF11を押して何が起こるか見ます。ブレークポイント no 2を得るまでこのボタンかF11を押し続けます。 これを下の図のようにブレークポイント no 4が得られるまで続け、 Expression監視ウインドウを観察します。
図 22. 式または変数の監視
図 23. 式または変数の監視
図 24. 式または変数の監視
新しい tickがあると、 OnTick() 関数の最初のコードラインに戻ります。 静的変数に宣言されているものを除きこれは新しい ティックなので、変数/式 の全ての値がリセットされます。 我々のケースでは静的変数 Old_Timeが1つありました。
図 25. NewTickイベントの変数値
プロセスをもう一度調べるために F11 キー を押し続け、expressions監視ウインドウで変数をモニターし続けます。デバッガーを停止し、 すべてのブレークポイントを削除できます。
ご覧のとおり、デバッグモード内で 「新しいバーがここにあります...」のメッセージが印刷されます。
図 26. デバッグモードでExpert Advisorがメッセージを印刷
今回はブレークポイントなしでデバッグプロセスをもう一度スタート。オーダーが正常にプレースされたか知らせ、そうでない場合アラート表示するコードを書いたので各ティックを監視し続け、もし我々のBuy/Sell 条件が合えば、トレードをプレースします。
図 27. デバッグ中Expert Advisorがトレードをプレース
EAにはさらに数分作動させておいてコーヒーブレークをとれると思います。戻ったら、すでにお金を稼いでいます (冗談です)。それからMetaEditorのSTOP (Red) ボタンをクリックしてデバッグを停止します。
図 28. デバッガーの停止
ここで実際行ったことは我々のEAは新しいバーのオープニング時のみトレードを確認し、EAは実際作動します。 EAコードを調整する余地がまだたくさんあります。
ここではっきりさせて下さい。トレーディング ターミナルはインターネットに接続されていなければなりません。 そうでないとターミナルがトレードできないのでデバッグが動作しません。
3.2EAストラテジーをテスト
この時点でトレーディングターミナル内蔵のストラテジーテスターを使ってEAをテストしたいです。 ストラテジーテスターを開始するには、下の図のようにCONTROL+R を押すかターミナルメニューバーのView メニューをクリックし、 ストラテジーテスターをクリックします。
図 26. ストラテジーテストの開始
テスター (ストラテジーテスター) がターミナルの下の部分に表されています。 全てのテスター設定を見るには拡大/サイズ変更します。これをするには、下に示されたように、マウスポインタを赤い矢印で示されたポインに移動します。
図 27. ストラテジーテスター ウインドウ
マウスポインターが2重矢印に変わり、マウスを押し下げ、ラインを上にドラッグします。設定タブで全て見れるようになったら停止します。
図 28. ストラテジーテスター 設定タブ
- テストしたいEAを選択
- テストに使用する通貨ペアを選択
- テストに使用する期間/タイムフレームを選択
- カスタム期間を選択しデータを5に設定
- テストに使うカスタム期間のデータを設定
- 実行はノーマル
- テストに使用するデポジット額をUSDで選択
- 最適化を無効に設定 (今は最適化をせず、ただテストします)
- テストを開始する準備ができたらこのボタンをクリック
スタートボタンを押す前に、 テスターの他のタブを見てみましょう。
エージェント・タブ
テストのテスターに使われるプロセッサ。お使いのコンピューターのプロセッサタイプによります。私のはコアプロセッサが1つだけです。
図 29. ストラテジーテスター エージェント・タブ
エージェントでは以下の図に似たものを見るでしょう。
図 30. テスト中のストラテジーテスター エージェント・タブ
ジャーナルタブ
ここがテスト期間に継続するすべてのイベントが表示されている場所です。
図 31. トレード活動を表示する ストラテジーテスター ジャーナル タブ
入力タブ
ここがEAの入力 パラメーターを指定できる場所です。
図 32. ストラテジーテスター 入力タブ
もしEAを最適化するなら、丸く囲まれた場所の値を設定する必要があります。
- Start はテスターに開始してほしい値です。
- Step は選択した値の増加率です。
- Stop はテスターがパラメーターの値を増加するのを停止する値です。
しかし、 我々のケースではEAを最適化せず、今はこれに触る必要はありません。
全ての設定が終わったら、Settings タブに戻り、スタートボタンをクリックします。そうしてテスターが仕事を開始します。今やらなければならないのはもう一杯のコーヒーを淹れるか、もしあなたが私みたいなら全てのイベントをモニターするかもしれません。そして ジャーナル タブに行きます。
グラフタブ
ジャーナルタブにオーダーが送付されたメッセージを見たら、作成されたばかりの グラフと名付けられた新しいタブに注意を向けたいでしょう。グラフタブにスイッチすると、トレードの結果によってグラフが増え続けたり、減り続けたりするのを見るでしょう。
図 33. Expert Advisor テストのグラフ結果
結果タブ
テストが完了すると、 結果と呼ばれるもう一つのタブを見ます。結果 タブにスイッチすると実行したばかりのテストの要約が見れます。
図 34. 結果要約を表示する ストラテジーテスター結果タブ
総粗利益、純利益、総トレード、総損失額、トレードなど多くを見れます。我々のテストのために選択した期間内に USD 1,450.0 程があるのは本当に興味深いです。少なくともいくらかの利益を得ました。
ここではっきりさせて下さい。ストラテジーテスターで見たEAパラメーター設定が、EAの入力パラメーターの初期設定と違うことを発見します。EAを最大限に活用するために、これらの入力パラメーターを変更することが可能であることを実演しました。移動平均と ADXに期間8を使うかわりに、 移動平均を10、ADXを14に変更しました。ストップロス も 30から35に変えました。最後に大事なことですが、 2時間タイムフレームを使うことにしました。これはストラテジーテスターであることを覚えておいてください。
もしテストの完全なレポートを見たければ 結果 タブのどこかで右クリックをしてメニューを表示します。このメニューから ‘Save as Report’を選択します。
図 35. テスト結果の保存
保存ダイアログのウインドウが現れるのでレポートの名前をタイプし (デフォルト名のままも可能) 、保存ボタンをクリックします。レポート全体がHTMLフォーマットで保存されます。
実行されたテストのチャートを見るには、 Open Chart をクリックすると表示されたチャートが見れます。
図 36. テストを表示するチャート
これだけです。うまくEAを書きテストできたので、今は結果に取り組みます。 これで、ストラテジーテスター設定タブに戻り、 他のタイムフレーム/期間のテストを作成できます。
宿題
別の通貨ペア、別のタイムフレーム、別のストップロス、別のテイクプロフィットを使ってテストを実行し、EAがどのように作動するか確認して下さい。新しい移動平均とADX値さえも試すことができます。前述したように、それがストラテジーテスターのエッセンスです。また、ぜひ結果を私と共有して下さい。
結論
このステップバイステップガイドでは、策定されたトレーディング ストラテジーに基づいたシンプルExpert Advisorを書くための基本ステップを見ることができました。デバッガーを使って我々の EAのエラーを確認する方法も見てきました。 我々のEAのパフォーマンスを ストラテジーテスターを使ってテストする方法も考察しました。これで、新しい MQL5 言語の力強さと頑健性を見ることができました。リアルトレードに使用するためにはさらに多くの調整が必要ですので我々のEA はまだ完璧または完了はありません。
まだ学ぶことがたくさんあるので、ぜひ本記事をMQL5 マニュアルと共に読み直し、本記事で学んだ事を全て試してください。優れたEA開発者 にすぐになれることを保証します。
ハッピーコーディング。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/100





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