
Expert Advisorの限界と検証
はじめに
自動トレーディングのアルゴリズムを作成する時、トレードシグナルを作るために価格を処理するだけでなく、Expert Advisorの操作の限界について補助情報をたくさん入手できるべきです。本記事でその方法を紹介します。
- トレーディングセッション情報の入手;
- ポジションをオープンするのに十分な資産があるか確認;
- シンボルによる総トレーディングボリュームに限界をもうける;
- 総オーダー数をに限界をもうける;
- エントリー価格とストップロスの間の潜在的ロスを計算;
- 新しいバーがあるか確認。
トレーディングセッションと気配値セッション
トレーディング セッションについて情報を得るには、SymbolInfoSessionTrade()関数、気配値セッションでは対応する SymbolInfoSessionQuote() 関数を使うべきです。両方の関数は同じように働きます。指定の曜日の指定インデックスによるセッションがある場合 (セッションのインデックスはゼロからスタート)、関数は trueに戻ります。セッション開始と終了時間はリンクによって渡される4番目と5番目パラメータに書かれます。
//--- check if there is a quotation session with the number session_index bool session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);指定日のすべてのセッションを見つけるには、この関数がfalseに戻るまでループで呼び出します。
//+------------------------------------------------------------------+ //| Display information about quotation sessions | //+------------------------------------------------------------------+ void PrintInfoForQuoteSessions(string symbol,ENUM_DAY_OF_WEEK day) { //--- start and end of session datetime start,finish; uint session_index=0; bool session_exist=true; //--- go over all sessions of this day while(session_exist) { //--- check if there is a quotation session with the number session_index session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish); //--- if there is such session if(session_exist) { //--- display the day of week, the session number and the time of start and end Print(DayToString(day),": session index=",session_index," start=", TimeToString(start,TIME_MINUTES)," finish=",TimeToString(finish-1,TIME_MINUTES|TIME_SECONDS)); } //--- increase the counter of sessions session_index++; } }
ENUM_DAY_OF_WEEK列挙値をパラメータとして受け取るカスタム関数 DayToString()を使って曜日が文字列形式で表示されています。
//+------------------------------------------------------------------+ //| Receive the string representation of a day of week | //+------------------------------------------------------------------+ string DayToString(ENUM_DAY_OF_WEEK day) { switch(day) { case SUNDAY: return "Sunday"; case MONDAY: return "Monday"; case TUESDAY: return "Tuesday"; case WEDNESDAY: return "Wednesday"; case THURSDAY: return "Thursday"; case FRIDAY: return "Friday"; case SATURDAY: return "Saturday"; default: return "Unknown day of week"; } return ""; }
SymbolInfoSession.mq5 スクリプトの最終コードは本記事の下に添付されています。ここでは主要部分だけ表示しましょう。
void OnStart() { //--- the array where the days of week are stored ENUM_DAY_OF_WEEK days[]={SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY}; int size=ArraySize(days); //--- Print("Quotation sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForQuoteSessions(Symbol(),days[d]); } //--- Print("Trading sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForTradeSessions(Symbol(),days[d]); } }
マージンの確認
ポジションのオープンまたは増加に必要なマージン量を確認するには、OrderCalcMargin() 関数を使うことができます。それに渡される最初のパラメータは ENUM_ORDER_TYPE 列挙からの値です。買いでは ORDER_TYPE_BUY パラメータ、売りには ORDER_TYPE_SELL パラメータを使って呼び出すべきです。この関数は、ロットの数と始値に応じてマージン量を戻します。
void OnStart() { //--- the variable to receive the value of margin double margin; //--- to receive information about the last tick MqlTick last_tick; //--- try to receive the value from the last tick if(SymbolInfoTick(Symbol(),last_tick)) { //--- reset the last error code ResetLastError(); //--- calculate margin value bool check=OrderCalcMargin(type,Symbol(),lots,last_tick.ask,margin); if(check) { PrintFormat("For the operation %s %s %.2f lot at %G required margin is %.2f %s",OrderTypeToString(type), Symbol(),lots,last_tick.ask,margin,AccountInfoString(ACCOUNT_CURRENCY)); } } else { Print("Unsuccessful executio the SymbolInfoTick() function, error ",GetLastError()); } }
OrderCalcMargin() 関数はマーケットオーダーだけでなくペンディングオーダのマージン値も計算できることに注意します。Check_Money.mq5 スクリプトを使って、全てのオーダータイプにおいて戻された値を確認できます。
トレーディングシステムによってはペンディングオーダーにもマネーバックが求められるかもしれないのでOrderCalcMargin() 関数はペンディングオーダーのマージンサイズを計算することを目的としています。 通常、 ペンディングオーダーのマージンサイズは長期・短期ポジションのマージンサイズの係数を通して計算されます。
識別子 |
説明 |
プロパティのタイプ |
SYMBOL_MARGIN_LONG |
長期ポジションにかかるマージンレート |
ダブル |
SYMBOL_MARGIN_SHORT |
短期ポジションにかかるマージンレート |
ダブル |
SYMBOL_MARGIN_LIMIT |
リミットオーダーにかかるマージンレート |
ダブル |
SYMBOL_MARGIN_STOP |
ストップオーダーで変わるマージンレート |
ダブル |
SYMBOL_MARGIN_STOPLIMIT |
ストップリミットオーダーにかかるマージンレート |
ダブル |
これらの係数値はシンプルなコードを使って得られます。
//--- Calculate the rates of margin charging for different types of orders PrintFormat("Rate of margin charging on long ポジション is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LONG)); PrintFormat("Rate of margin charging on short ポジション is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_SHORT)); PrintFormat("Rate of margin charging on Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LIMIT)); PrintFormat("Rate of margin charging on Stop orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOP)); PrintFormat("Rate of margin charging on Stop Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOPLIMIT));
Forexシンボルでは、 ペンディングオーダーにかかるマージンレートは0に等しいです。つまり、それらにはマージン要求がありません。
Check_Money.mq5スクリプト実行の結果
マージンのかかり方に応じ、マネー管理システムが変わるかもしれません。また、もしペンディングオーダーにマージンが要求されている場合、トレーディングシステム自身がある限界を経験するかもしれません。このためこれらのパラメータがExpert Advisor操作の自然の制限であるかもしれません。
潜在的利益と損失の管理
保護ストップレベルを置くとき、その起動に対して準備ができているべきです。マネーの観点から潜在的損失のリスクを考慮に入れるべきです。そしてOrderCalcProfit()はこれを目的としています。すでに考察したOrderCalcMargin() 関数にとても似ていますが、それは計算に始値と終値の両方を必要とします。
ENUM_ORDER_TYPE列挙の2つの値の内1つ値を最初のパラメータ ORDER_TYPE_BUYまたはORDER_TYPE_SELLとして指定します。 他のタイプのオーダーはエラーになります。最後のパラメータでは、レファレンスを使って、正常な実行の場合、OrderCalcProfit() 関数が利益・損失値を書くところへ変数を渡すべきです。
入るときと抜け るときの指定レベル付きの長期ポジションをクローズする時における、利益または損失を計算するCalculateProfitOneLot() 関数の使用例:
//+------------------------------------------------------------------+ //| Calculate potential profit/loss for buying 1 lot | //+------------------------------------------------------------------+ double CalculateProfitOneLot(double entry_price,double exit_price) { //--- receive the value of profit to this variable double profit=0; if(!OrderCalcProfit(ORDER_TYPE_BUY,Symbol(),1.0,entry_price,exit_price,profit)) { Print(__FUNCTION__," Failed to calculate OrderCalcProfit(). Error ",GetLastError()); } //--- return(profit); }
この関数の計算結果は図に表されています。
OrderCalcProfit() 関数を使った潜在的損失の計算とチャート表示例
全体のコードは添付Expert Advisor CalculateProfit_EA.mq5にあります。
新しいバーがあるか確認
多くのトレーディングシステムの開発は、新しいバー が現れた時トレード シグナルが計算され、全てのトレードアクションが一度だけ実行されることを想定しています。 MetaTrader 5 クライアントターミナルのストラテジーテスターの「Only open prices」 モードはこのような自動トレーディング システムを確認するの良いです。
「Open prices only」モードでは、全てのインディケーター計算とExpert Advisor のOnTick() 関数の呼び出しはテスト中各バーにおいて一度だけ実行されます。これは速めのトレーディングモードで、 概して、僅少価格振動に対して最も耐障害性あるトレーディング システム作成方法です。 同時に、もちろん, Expert Advisorで使用されているインディケーターは正しく書かれ、新しいバーが来た時、値をゆがめるべきではありません。
ストラテジーテスター の「Open prices only」モードは、Expert Advisorが各バーにつき一度だけ起動するか確認せずに済むようになっており、それはとても便利です。しかし、デモまたは正式アカウントにおいてリアルタイムモードで作業する間、トレーダーはそれらExpert Advisorの活動をコントロールし、トレード操作 が1つのシグナル受信毎に一回だけ実行するようにするべきです。その一番な簡単な方法は現在の未形成バーのオープニングを追跡することです。
最後のバーのオープニング時間を手に入れるには、シンボルの指定の名前・タイムフレーム・ SERIES_LASTBAR_DATE プロパティ付きでSeriesInfoInteger() 関数を使うべきです。一つのバーが変数に保存された状態で現在のバーのオープニング時間を常に比較していると、新しいバーが現れた瞬間を簡単に検知することができます。そのことで以下のように見えるカスタム関数 isNewBar() を作成することができます。
//+------------------------------------------------------------------+ //| Return true if a new bar appears for the symbol/period pair | //+------------------------------------------------------------------+ bool isNewBar() { //--- remember the time of opening of the last bar in the static variable static datetime last_time=0; //--- current time datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if it is the first call of the function if(last_time==0) { //--- set time and exit last_time=lastbar_time; return(false); } //--- if the time is different if(last_time!=lastbar_time) { //--- memorize time and return true last_time=lastbar_time; return(true); } //--- if we pass to this line then the bar is not new, return false return(false); }
この関数の使用例は添付Expert Advisor CheckLastBar.mq5にあります。
M1タイムフレームにおける新しいバー登場についてのCheckLastBar Expert Advisorのメッセージ
ペンディングオーダー数の制限
アカウントに同時にプレースすることができるアクティブペンディングオーダーの数を制限する必要がある場合、 自分のカスタム関数を書くことができます。別のペンディングオーダーをプレースできるか確認する IsNewOrderAllowed()を作りましょう。Automated Trading Championshipのルールに準拠して書きましょう。
//+------------------------------------------------------------------+ //| Checks if it is allowed to place another order | //+------------------------------------------------------------------+ bool IsNewOrderAllowed() { //--- get the allowed number of pending orders on an account int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); //--- if there is no limitations, return true; you can send an order if(max_allowed_orders==0) return(true); //--- if we pass to this line, then there are limitations; detect how many orders are already active int orders=OrdersTotal(); //--- return the result of comparing return(orders<max_allowed_orders); }
この関数はシンプルです。max_allowed_orders 変数に許容オーダー数を入手します。もし値がゼロに等しくない場合、 現在の オーダー数と比較します。しかし、 この関数は別の潜在的限界である指定シンボルによるオープンポジションとペンディングオーダーの総許容ボリュームにおける限界を考えません。
指定シンボルによるロット数の制限
指定シンボルによるオープンポジションのサイズを得るには、まずPositionSelect() 関数を使ってポジションを選択する必要があります。その後、やっとオープンポジションのボリュームをPositionGetDouble()を使って要求できます。 ダブルタイプの様々な選択ポジションのプロパティを戻します。シンボルによってポジションボリュームを得るためのPostionVolume() 関数を書きましょう。
//+------------------------------------------------------------------+ //| Returns the size of position by a specific symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a positions by a symbol bool selected=PositionSelect(symbol); //--- the position exists if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- report about the unsuccessful attempt to select the position Print(__FUNCTION__," Failed to execute PositionSelect() for the symbol ", symbol," Error ",GetLastError()); return(-1); } }
シンボルでペンディング オーダーをプレースするトレード要求を作成する前、SYMBOL_VOLUME_LIMITのシンボルによるにオープンポジションとペンディングオーダーの総ボリュームの限界を確認するべきです。 もし制限がない場合、ペンディングオーダーのボリュームは、 SymbolInfoDouble() ボリュームを使って受け取られる最大許容ボリュームを超えることができません。
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
しかし、このアプローチはシンボル規定による現在のペンディングオーダーのボリュームを考えません。この値を計算する関数を書きましょう。
//+------------------------------------------------------------------+ //| Returns the size of position by a specified symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a position by a symbol bool selected=PositionSelect(symbol); //--- the position exist if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- return zero if there is no position return(0); } }
オープンポジションのボリュームとペンディングオーダーのボリュームを考慮し、最終確認は以下のように見えます。
//+------------------------------------------------------------------+ //| Returns maximum allowed volume for an order by a symbol | //+------------------------------------------------------------------+ double NewOrderAllowedVolume(string symbol) { double allowed_volume=0; //--- get the limitation on the maximum volume of an order double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); //--- get the limitation of volume by a symbol double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); //--- get the volume of open position by a symbol double opened_volume=PositionVolume(symbol); if(opened_volume>=0) { //--- if we already used available volume if(max_volume-opened_volume<=0) return(0); //--- volume of the open position doen't exceed max_volume double orders_volume_on_symbol=PendingsVolume(symbol); allowed_volume=max_volume-opened_volume-orders_volume_on_symbol; if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume; } return(allowed_volume); }
このセクションで言及した関数を含むCheck_Order_And_Volume_Limits.mq5 Expert Advisorのコード全体は本記事に添付されています。
自動トレーディングチャンピオンシップ 2010参加者のアカウントにおけるCheck_Order_And_Volume_Limits Expert Advisorを使った確認例。
ボリューム正確性の確認
トレーディングロボットの重要な点はトレード操作を実行するための正しいボリュームを選択できる事です。ここではマネー管理やリスクマネジメントのシステムではなく、対応するシンボルのプロパティに応じた正しいボリュームについて話していきます。
識別子 |
説明 |
プロパティのタイプ |
SYMBOL_VOLUME_MIN |
ディールの最小ボリューム |
ダブル |
SYMBOL_VOLUME_MAX |
ディールの最大ボリューム |
ダブル |
SYMBOL_VOLUME_STEP |
ディール実行の最小ボリューム変更ステップ |
ダブル |
こうような検証を行うには、カスタム 関数 CheckVolumeValue()を書くことができます。
//+------------------------------------------------------------------+ //| Check the correctness of volume of an order | //+------------------------------------------------------------------+ bool CheckVolumeValue(double volume,string &description) { //--- Minimum allowed volume for trade operations double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); if(volume<min_volume) { description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f",min_volume); return(false); } //--- Maximum allowed volume for trade opertations double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); if(volume>max_volume) { description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f",max_volume); return(false); } //--- get the minimal volume change step double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP); int ratio=(int)MathRound(volume/volume_step); if(MathAbs(ratio*volume_step-volume)>0.0000001) { description=StringFormat("Volume is not a multiple of minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f", volume_step,ratio*volume_step); return(false); } description="Correct value of volume "; return(true); }
この関数の作動は、本記事に添付されたCheckVolumeValue.mq5 スクリプトを使って確認できます。
ボリュームの正しさをチェックするCheckVolumeValue.mq5 のメッセージ
結論
本記事は自分自身の自動トレーディング システムを書く際に発生するかもしれないExpert Advisor動作の潜在的限界における基本的検証を説明しました。これらの例はトレードアカウントでExpert Advisor操作時に確認するべき潜在的条件をすべて網羅していません。しかしこれらの例が初めての方にとってMQL5 言語で最もよく使われる検証をどのように実行するか理解する手助けになればいいと願っています。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/22





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