マーケットでの公開前にトレードロボットに行うべき検査

MetaQuotes | 27 7月, 2016

マーケットの公開前検査は何故必要か

マーケットの全ての製品は、公開前に必ず事前検査を通過することになっています。これは、エキスパートアドバイザーやインディケータの小さな論理エラーが、取引口座の損失に繋がる恐れがあるためです。この理由から、マーケットの製品品質に要求されるレベルを確保する為に必要な、一連の基本検査を私達は開発しました。

マーケットのモデレーターが貴方の製品の検査をする過程でエラーが検出された場合、貴方はそれを必ず修正する必要があります。この記事では、開発者が自分のテクニカルインディケータやトレードロボットで犯しがちなミスについてお話しします。また、以下の記事についても、ご一読いただく事をお勧めします。


トレードロボットのエラーを迅速に検出し修正する方法

プラットフォームに組み込まれたストラテジーテスターは、取引システムを履歴上でチェックするだけでなく、トレードロボット作成時に見過ごした論理/アルゴリズムエラーを検出することができます。テスト時の取引操作やエラー検出に関するメッセージは、全てテスターの操作ログに表示されます。これらのメッセージは、コンテキストメニューのコマンドで呼び出される、特別なログビューアで分析するのに便利です。


図のように、エクスパートアドバイザのテスト後にビューアを開いて、『エラーのみ』モードを有効にしてください。貴方のトレードロボットにエラーがある場合、すぐにそれが表示されます。初回にエラーが検出されなかった場合、銘柄/時間軸/入力パラメータを変え、様々な初期証拠金の額による一連のテストを実行してください。これらの簡単な適用で99%のエラーが検出されますが、この事についてこの記事でお話しをしたいと思います。

検出されたエラーの詳細な分析には、MetaEditorのヒストリーデータによるデバッグを使用してください。ビジュアルテストモードで、チャート価格だけでなく、使用するインディケータの値、そしてプログラムの各変数値をティックごとに見ることができます。これによって、週単位の時間をかけることなく、リアルタイムに貴方の取引戦略を微調整することができます。

取引操作を実行する資金が足りない

毎回取引指示を送信する前に、貴方の口座上の資金が十分であるかどうかを確認する必要があります。今後の注文またはポジションの保有への資金の不足は、イージーミスとなります。

以下の事にご留意ください指値(逆指値)注文の発注であっても証拠金を必要とする場合があります。


1米ドルや1ユーロなど、意図的に初期証拠金サイズを小さくして、自分のトレードロボットをテストする事をお勧めします。

検査の際に取引操作に対して資金が不足していると表示された場合には、OrderSend()関数を呼び出す代わりに操作ログにエラーに関するメッセージを出力する必要があります。テスト例:

MQL5

bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type)
  {
//--- オープン価格を取得
   MqlTick mqltick;
   SymbolInfoTick(symb,mqltick);
   double price=mqltick.ask;
   if(type==ORDER_TYPE_SELL)
      price=mqltick.bid;
//--- 自由/必要証拠金の値
   double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   //--- チェック関数を呼び出します
   if(!OrderCalcMargin(type,symb,lots,price,margin))
     {
      //--- 何か間違っていたので、報告をしてfalseを返します
      Print("Error in ",__FUNCTION__," code=",GetLastError());
      return(false);
     }
   //--- 操作チェックに資金が不足している場合
   if(margin>free_margin)
     {
      //--- エラーについての報告し、falseを返します
      Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
      return(false);
     }
//--- チェックが正常に行われました
   return(true);
  }

MQL4

bool CheckMoneyForTrade(string symb, double lots,int type)
  {
   double free_margin=AccountFreeMarginCheck(symb,type,lots);
   //-- 資金が不足している場合
   if(free_margin<0)
     {
      string oper=(type==OP_BUY)? "Buy":"Sell";
      Print("Not enough money for ", oper," ",lots, " ", symb, " Error code=",GetLastError());
      return(false);
     }
   //-- チェックが正常に行われました
   return(true);
  }

取引操作内の不正数量

取引指示の送信前に、注文で指示した数量が正しいかどうかもチェックする必要があります。エキスパートアドバイザが注文で指示しようとしているロット数を、OrderSend()関数の呼び出し前にチェックする必要があります。銘柄仕様ウィンドウに金融商品の為に取引へ許可している最小/最大数量や数量のステップを指定します。MQL5ではこれらの値は、SymbolInfoDouble()関数を使ったENUM_SYMBOL_INFO_DOUBLE
列挙から取得できます。

SYMBOL_VOLUME_MIN

取引決済の為の最小数量

SYMBOL_VOLUME_MAX

取引決済の為の最大数量

SYMBOL_VOLUME_STEP

取引決済の為の数量変更の最小ステップ

数量の正確さをチェックする関数の例

//+--------------------------------------------------------+
//|  注文数量の正確さをチェックします                          |
//+--------------------------------------------------------+
bool CheckVolumeValue(double volume,string &description)
  {
//--- 取引操作の為の最小許容数量
   double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("SYMBOL_VOLUME_MIN=%.2fの最小許容以下の数量",min_volume);
      return(false);
     }

//--- 取引操作の為の最大許容数量
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("SYMBOL_VOLUME_MAX=%.2fの最大許容以下の数量",max_volume);
      return(false);
     }

//--- 数量の最小ステップを取得します
   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("数量はSYMBOL_VOLUME_STEP=%.2fのステップの最小倍数ではありません。直近の正確な数量は%.2f",
                               volume_step,ratio*volume_step);
      return(false);
     }
   description="数量の正確な値";
   return(true);
  }


指値注文数の制限

その口座に 一度に置ける指値注文の数に制限がある場合があります。更にもう一つ指値注文を発注できるかのチェックを行うIsNewOrderAllowed()関数の例です。

//+------------------------------------------------------------+
//| さらに発注をすることができるかをチェックします                   |
//+------------------------------------------------------------+
bool IsNewOrderAllowed()
  {
//--- 口座上で許可された指値注文の数を取得します
   int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);

//--- 制限がない場合はtrueを返し、注文を発注できます
   if(max_allowed_orders==0) return(true);

//--- この地点までも来たということは、制限があるということなので、すでにいくつの注文があるかを調べます
   int orders=OrdersTotal();

//--- 比較結果を返します
   return(orders<max_allowed_orders);
  }

関数はシンプルなものです。max_allowed_orders変数に指値注文の許可数を取得し、値がゼロの場合、現在の注文数と比較します。ただし、この関数ではもう一つあり得る制限、特定の銘柄の保有ポジションと指値注文の最大許容総量が考慮されていません。


一つの銘柄に対するロット数の制限

指定した銘柄の保有ポジションの数量サイズを取得するには、PositionSelect()関数を使って事前にポジションを選択する必要があります。これを行った後に、double型を持つ様々な選択されたポジションのプロパティを返すPositionGetDouble()関数を使って、選択したポジションの数量をリクエストします。

//+------------------------------------------------------+
//| 指定した銘柄のポジションサイズを返します                  |
//+------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- 銘柄ごとにポジションを選択してみましょう
   bool selected=PositionSelect(symbol);
//--- ポジションが存在します
   if(selected)
      //--- ポジションボリュームを返します
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- ポジション選択に失敗したことを報告します
      Print(__FUNCTION__,"銘柄に対するPositionSelect()の実行に失敗しました",
            symbol,"エラー",GetLastError());
      return(-1);
     }
  }

両建てサポートを持つ口座には、その銘柄で全てのポジションを通過する必要があります。

銘柄の指値注文取引リクエストを行う前に、1つの銘柄での指値注文や保有ポジションの総量(SYMBOL_VOLUME_LIMIT)に対する制限をチェックする必要があります。制限がない場合、指値注文の数量はSymbolInfoDouble()関数を使って取得できる設定した最大値を超えることはありません。

double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);

しかしこのアプローチは、指定した銘柄の現在の指値注文の数量は考慮していません。この値を計算する関数の例を見てみましょう。

//+---------------------------------------------+
//| 銘柄によって現在の未決注文の取引量を返す         |
//+---------------------------------------------+
double   PendingsVolume(string symbol)
  {
   double volume_on_symbol=0;
   ulong ticket;
//---  すべての銘柄で、現在出されているすべての注文の数を取得する
   int all_orders=OrdersTotal();

//--- すべての注文をループ処理する
   for(int i=0;i<all_orders;i++)
     {
      //--- リスト内の位置によって注文のチケットを取得する
      if(ticket=OrderGetTicket(i))
        {
         //--- 私たちの銘柄が注文で指定されている場合、この注文のボリュームを追加する
         if(symbol==OrderGetString(ORDER_SYMBOL))
            volume_on_symbol+=OrderGetDouble(ORDER_VOLUME_INITIAL);
        }
     }
//--- 指定された銘柄の現在出されている未決注文の総量を返す
   return(volume_on_symbol);
  }

指値注文の数量や保有ポジションの数量を考慮した最終チェックは以下のようになります。

//+--------------------------------------+
//|  銘柄の注文の為の最大許容数量を返します   |
//+--------------------------------------+
double NewOrderAllowedVolume(string symbol)
  {
   double allowed_volume=0;
//--- 注文内の最大数量の制限を取得します
   double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
//--- 銘柄ごとの数量に対する制限を取得します
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);

//--- 銘柄ごとの保有ポジションの数量を取得します
   double opened_volume=PositionVolume(symbol);
   if(opened_volume>=0)
     {
      //--- すでに数量がなくなってしまった場合
      if(max_volume-opened_volume<=0)
         return(0);

      //--- 保有ポジションの数量は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);
  }


最小レベルSYMBOL_TRADE_STOPS_LEVEL内でのテイクプロフィットとストップロスレベルの設定

多くのエキスパートアドバイザが、売買実行時点でレベルを動的に計算するテイクプロフィットとストップロス注文を使用して取引を行っています。テイクプロフィット注文は有利な方向に価格の変動が起こった時にポジションを決済し、ストップロス注文は不利な方向に価格の変動が起こった時に損失を制限する為に使用されます。

したがって、テイクプロフィットとストップロスレベルは反対方向の取引操作を実行する現在価格と比較する必要があります。

  • 買いはAsk価格で実行され、テイクプロフィットとストップロスのレベルは現在のBid価格と比較する必要があります。
  • 売りはBid価格で実行され、テイクプロフィットとストップロスのレベルはげんざいのAsk価格と比較する必要があります。
買いはAsk価格で行われます
売りはBid価格で行われます
TakeProfit >= Bid
StopLoss <= Bid
TakeProfit <= Ask
StopLoss >= Ask



金融商品の銘柄の設定でSYMBOL_TRADE_STOPS_LEVELパラメータを指定することができます。これは保有ポジションの現在の決済価格からのストップロスやテイクプロフィットのレベルの最小距離(ポイントで)を指定します。このプロパティの値がゼロの場合、売買時の注文のストップロス/テイクプロフィットの最小距離が設定されていないということになります。

一般的に、最小距離を考慮したテイクプロフィットやストップロスのレベルSYMBOL_TRADE_STOPS_LEVELは以下のようになります。

  • 買いはAsk価格で実行され、テイクプロフィットとストップロスのレベルは現在のBid価格からSYMBOL_TRADE_STOPS_LEVELポイント以上の距離にある必要があります。
  • 売りは、Bid価格で実行され、テイクプロフィットとストップロスのレベルは、現在のAsk価格からSYMBOL_TRADE_STOPS_LEVELポイント以上の距離にある必要があります。
買いはAsk価格で行われます
売りはBid価格で行われます
TakeProfit - Bid >= SYMBOL_TRADE_STOPS_LEVEL
Bid - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Ask - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Ask >= SYMBOL_TRADE_STOPS_LEVEL

したがって、テイクプロフィットとストップロスから決済価格までSYMBOL_TRADE_STOPS_LEVELポイント以上の距離を要求するCheckStopLoss_Takeprofit()関数を作成することができます。

bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type,double SL,double TP)
  {
//--- SYMBOL_TRADE_STOPS_LEVELを取得します
   int stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   if(stops_level!=0)
     {
      PrintFormat("SYMBOL_TRADE_STOPS_LEVEL=%d:ストップロスとテイクプロフィットは"+
                  "決済価格から%dよりも近づかない",stops_level,stops_level);
     }
//---
   bool SL_check=false,TP_check=false;
//--- 二つの注文タイプのみチェックします
   switch(type)
     {
      //--- 買い操作
      case  ORDER_TYPE_BUY:
        {
         //--- ストップロスをチェックします
         SL_check=(Bid-SL>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be less than %.5f"+
                        " (Bid=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%dポイント)",
                        EnumToString(type),SL,Bid-stops_level*_Point,Bid,stops_level);
         //--- テイクプロフィットをチェックします
         TP_check=(TP-Bid>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be greater than %.5f"+
                        " (Bid=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%dポイント)",
                        EnumToString(type),TP,Bid+stops_level*_Point,Bid,stops_level);
         //--- チェック結果を返します
         return(SL_check&&TP_check);
        }
      //--- 売り操作
      case  ORDER_TYPE_SELL:
        {
         //--- ストップロスをチェックします
         SL_check=(SL-Ask>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be greater than %.5f "+
                        " (Ask=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%dポイント)",
                        EnumToString(type),SL,Ask+stops_level*_Point,Ask,stops_level);
         //--- テイクプロフィットをチェックします
         TP_check=(Ask-TP>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be less than %.5f "+
                        " (Ask=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%dポイント)",
                        EnumToString(type),TP,Ask-stops_level*_Point,Ask,stops_level);
         //--- チェック結果を返します
         return(TP_check&&SL_check);
        }
      break;
     }
//--- 指値注文には少し異なる関数が必要です
   return false;
  }

チェックは以下のようになります。

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- ランダムに操作タイプを取得します
   int oper=(int)(GetTickCount()%2); // 2による除算の余剰は常に0か1
   switch(oper)
     {
      //--- 買います
      case  0:
        {
         //--- 始値を取得し、意図的に間違ったTP/SLを設定します
         double price=Ask;
         double SL=NormalizeDouble(Bid+2*_Point,_Digits);
         double TP=NormalizeDouble(Bid-2*_Point,_Digits);
         //--- チェックをします
         PrintFormat("Buy at %.5f   SL=%.5f   TP=%.5f  Bid=%.5f",price,SL,TP,Bid);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_BUY,SL,TP))
            Print("ストップロスとテイクプロフィットのレベルが不正です!");
         //--- 実行結果を見る為に、それでも買いを試みます
         Buy(price,SL,TP);
        }
      break;
      //--- 売ります
      case  1:
        {
         //--- 始値を取得し、意図的に間違ったTP/SLを設定します
         double price=Bid;
         double SL=NormalizeDouble(Ask-2*_Point,_Digits);
         double TP=NormalizeDouble(Ask+2*_Point,_Digits);
         //--- チェックをします
         PrintFormat("Sell at %.5f   SL=%.5f   TP=%.5f  Ask=%.5f",price,SL,TP,Ask);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_SELL,SL,TP))
            Print("ストップロスとテイクプロフィットのレベルが不正です!");
         //--- 実行結果を見る為に、それでも売りを試みます
         Sell(price,SL,TP);
        }
      break;
      //---
     }
  }

関数の例は添付スクリプトのCheck_TP_and_SL.mq4Check_TP_and_SL.mq5で見ることができます。実行例:

MQL5
Check_TP_and_SL (EURUSD,H1) Buy at 1.11433   SL=1.11425   TP=1.11421  Bid=1.11423
Check_TP_and_SL (EURUSD,H1) SYMBOL_TRADE_STOPS_LEVEL=30: ストップロスとテイクプロフィットは、決済価格から30ポイント以内になってはいけない
Check_TP_and_SL (EURUSD,H1) ORDER_TYPE_BUY StopLoss=1.11425の注文には1.11393 (Bid=1.11423 - SYMBOL_TRADE_STOPS_LEVEL=30ポイント)以下である必要がある
Check_TP_and_SL (EURUSD,H1) ORDER_TYPE_BUY TakeProfit=1.11421の注文には1.11453 (Bid=1.11423 + SYMBOL_TRADE_STOPS_LEVEL=30ポイント)以上である必要がある
Check_TP_and_SL (EURUSD,H1) ストップロスまたはテイクプロフィットのレベルが不正です!
Check_TP_and_SL (EURUSD,H1) OrderSend error 4756
Check_TP_and_SL (EURUSD,H1) retcode=10016  deal=0  order=0
MQL4
Check_TP_and_SL EURUSD,H1:  Sell at 1.11430   SL=1.11445   TP=1.11449  Ask=1.11447
Check_TP_and_SL EURUSD,H1:  SYMBOL_TRADE_STOPS_LEVEL=1: ストップロスとテイクプロフィットは決済価格から1ポイントより近くなってはいけない
Check_TP_and_SL EURUSD,H1:  ORDER_TYPE_SELL StopLoss=1.11445 注文には1.11448  (Ask=1.11447 + SYMBOL_TRADE_STOPS_LEVEL=1ポイント)以上である必要がある
Check_TP_and_SL EURUSD,H1:  ORDER_TYPE_SELL TakeProfit=1.11449 注文には1.11446 (Ask=1.11447 - SYMBOL_TRADE_STOPS_LEVEL=1ポイント)以下である必要がある
Check_TP_and_SL EURUSD,H1:  ストップロスまたはテイクプロフィットのレベルが不正です!
Check_TP_and_SL EURUSD,H1:  OrderSend error 130 

テイクプロフィットやストップロスの値が間違っている状況のシュミレーションの為に、この記事にTest_Wrong_TakeProfit_LEVEL.mq5Test_Wrong_StopLoss_LEVEL.mq5のエキスパートアドバイザが添付されています。これらのエキスパートアドバイザはデモ口座でのみ起動することができます。どのような条件下で買い操作を正常に行うことができるかを自分で見る為に、以下の例をご覧ください。

エキスパートアドバイザTest_Wrong_StopLoss_LEVEL.mq5の実行例:

Test_Wrong_StopLoss_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: 作動価格まで残り20ポイントの場合、ポジションまたは注文の変更は禁止
SYMBOL_TRADE_STOPS_LEVEL=30: ストップロスとテイクプロフィットは決済価格から30ポイントより近くなってはいけない
1. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11430 ( StopLoss-Bid=-26 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
2. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11431 ( StopLoss-Bid=-27 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
3. Buy 1.0 EURUSD at 1.11442 SL=1.11402 Bid=1.11430 ( StopLoss-Bid=-28 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11402 [invalid stops]
4. Buy 1.0 EURUSD at 1.11440 SL=1.11399 Bid=1.11428 ( StopLoss-Bid=-29 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11440 sl: 1.11399 [invalid stops]
5. Buy 1.0 EURUSD at 1.11439 SL=1.11398 Bid=1.11428 ( StopLoss-Bid=-30 points ))
Buy 1.0 EURUSD done at 1.11439 with StopLoss=41 points (spread=12 + SYMBOL_TRADE_STOPS_LEVEL=30)

エキスパートアドバイザTest_Wrong_TakeProfit_LEVEL.mq5の実行例:

Test_Wrong_TakeProfit_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: 作動価格まで残り20ポイントの場合、ポジションまたは注文の変更は禁止
SYMBOL_TRADE_STOPS_LEVEL=30: ストップロスとテイクプロフィットは決済価格から30ポイントより近くなってはいけない
1. Buy 1.0 EURUSD at 1.11461 TP=1.11478 Bid=1.11452 (TakeProfit-Bid=26 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11478 [invalid stops]
2. Buy 1.0 EURUSD at 1.11461 TP=1.11479 Bid=1.11452 (TakeProfit-Bid=27 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11479 [invalid stops]
3. Buy 1.0 EURUSD at 1.11461 TP=1.11480 Bid=1.11452 (TakeProfit-Bid=28 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11480 [invalid stops]
4. Buy 1.0 EURUSD at 1.11461 TP=1.11481 Bid=1.11452 (TakeProfit-Bid=29 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11481 [invalid stops]
5. Buy 1.0 EURUSD at 1.11462 TP=1.11482 Bid=1.11452 (TakeProfit-Bid=30 points)
Buy 1.0 EURUSD done at 1.11462 with TakeProfit=20 points (SYMBOL_TRADE_STOPS_LEVEL=30 - spread=10)

指値注文のストップロスとテイクプロフィットレベルのチェックは遥かに簡単で、これらのレベルは注文の始値から離れている必要があります。つまり、最小距離(SYMBOL_TRADE_STOPS_LEVEL)を考慮したレベルのチェックは以下のようになります。テイクプロフィットやストップロスのレベルは注文の作動価格からSYMBOL_TRADE_STOPS_LEVELポイント以上の距離にある必要があります。

BuyLimit и BuyStop
SellLimit и SellStop
TakeProfit - Open >= SYMBOL_TRADE_STOPS_LEVEL
Open - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Open - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Open >= SYMBOL_TRADE_STOPS_LEVEL

エキスパートアドバイザTest_StopLoss_Level_in_PendingOrders.mq5で、操作が正常に完了するまでBuyStopとBuyLimtの注文設定の一連の試行が行われます。一回試行する度にストップロスまたはテイクプロフィットのレベルは正しい方向へ1ポイントシフトされます。このエキスパートアドバイザの実行例:

Test_StopLoss_Level_in_PendingOrders.mq5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: 作動価格まで残り20ポイントの場合、ポジションまたは注文の変更は禁止
SYMBOL_TRADE_STOPS_LEVEL=30: ストップロスとテイクプロフィットは決済価格から30ポイントより近くなってはいけない
1. BuyStop 1.0 EURUSD at 1.11019 SL=1.10993 (Open-StopLoss=26 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10993 [invalid stops]
2. BuyStop 1.0 EURUSD at 1.11019 SL=1.10992 (Open-StopLoss=27 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10992 [invalid stops]
3. BuyStop 1.0 EURUSD at 1.11020 SL=1.10992 (Open-StopLoss=28 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11020 sl: 1.10992 [invalid stops]
4. BuyStop 1.0 EURUSD at 1.11021 SL=1.10992 (Open-StopLoss=29 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11021 sl: 1.10992 [invalid stops]
5. BuyStop 1.0 EURUSD at 1.11021 SL=1.10991 (Open-StopLoss=30 points)
BuyStop 1.0 EURUSD done at 1.11021 with StopLoss=1.10991 (SYMBOL_TRADE_STOPS_LEVEL=30)
 --------- 
1. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10647 (TakeProfit-Open=26 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10647 [invalid stops]
2. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10648 (TakeProfit-Open=27 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10648 [invalid stops]
3. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10649 (TakeProfit-Open=28 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10649 [invalid stops]
4. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10648 (TakeProfit-Open=29 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10619 tp: 1.10648 [invalid stops]
5. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10649 (TakeProfit-Open=30 points)
BuyLimit 1.0 EURUSD done at 1.10619 with TakeProfit=1.10649 (SYMBOL_TRADE_STOPS_LEVEL=30)

指値注文のテイクプロフィットとストップロスレベルのチェックの例は添付ファイル(Check_TP_and_SL.mq4Check_TP_and_SL.mq5)で見ることができます。


凍結レベル(SYMBOL_TRADE_FREEZE_LEVEL)内のポジションまたは注文の変更の試行

保有ポジションや指値注文の為の取引操作凍結の距離をポイントで表すSYMBOL_TRADE_FREEZE_LEVELパラメータは銘柄設定で指定することができます。例えば、金融商品での取引が外部取引システムの為に転送される場合、買い指値注文はこの時点で現在のAsk価格から近すぎる場合があります。始値が十分にAsk価格に近くなった時に、この注文の変更指示を送信した場合、注文はすでに実行され変更はすることができません。

したがって、指値注文や保有ポジションの為に、銘柄設定でその範囲内では変更をすることができない凍結距離を指定することができます。一般的に、変更指示を送信する前に、SYMBOL_TRADE_FREEZE_LEVELを考慮したチェックをする必要があります。

注文/ポジションのタイプ
価格による有効化
チェック
Buy Limit注文
 Ask
Ask-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Buy Stop注文 AskOpenPrice-Ask >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Limit注文 BidOpenPrice-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Stop注文 BidBid-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
買いポジション
 BidTakeProfit-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Bid-StopLoss >= SYMBOL_TRADE_FREEZE_LEVEL
売りポジション
 AskAsk-TakeProfit >= SYMBOL_TRADE_FREEZE_LEVEL
StopLoss-Ask >= SYMBOL_TRADE_FREEZE_LEVEL

注文やポジションのSYMBOL_TRADE_FREEZE_LEVELレベルをチェックする為の関数例の完全版は添付スクリプト(Check_FreezeLevel.mq5Check_FreezeLevel.mq4)で見ることができます。

//--- 注文タイプをチェックします
   switch(type)
     {
      //--- 指値注文BuyLimit
      case  ORDER_TYPE_BUY_LIMIT:
        {
         //--- 始値から作動価格までの距離をチェックします
         check=((Ask-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("%s #%d注文は変更できません: Ask-Open=%dポイント < SYMBOL_TRADE_FREEZE_LEVEL=%dポイント",
                        EnumToString(type),ticket,(int)((Ask-price)/_Point),freeze_level);
         return(check);
        }
      //--- 指値注文BuyLimit
      case  ORDER_TYPE_SELL_LIMIT:
        {
         //--- 始値から作動価格までの距離をチェックします
         check=((price-Bid)>freeze_level*_Point);
         if(!check)
            PrintFormat("%s #%d注文は変更することができません: Open-Bid=%dポイント < SYMBOL_TRADE_FREEZE_LEVEL=%dポイント",
                        EnumToString(type),ticket,(int)((price-Bid)/_Point),freeze_level);
         return(check);
        }
      break;
      //--- 指値注文BuyStop
      case  ORDER_TYPE_BUY_STOP:
        {
         //--- 始値から作動価格までの距離をチェックします
         check=((price-Ask)>freeze_level*_Point);
         if(!check)
            PrintFormat("%s #%d注文は変更できません: Ask-Open=%dポイント < SYMBOL_TRADE_FREEZE_LEVEL=%dポイント",
                        EnumToString(type),ticket,(int)((price-Ask)/_Point),freeze_level);
         return(check);
        }
      //--- 指値注文SellStop
      case  ORDER_TYPE_SELL_STOP:
        {
         //--- 始値から作動価格までの距離をチェックします
         check=((Bid-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("%s #%d注文は変更することができません: Bid-Open=%dポイント < SYMBOL_TRADE_FREEZE_LEVEL=%dポイント",
                        EnumToString(type),ticket,(int)((Bid-price)/_Point),freeze_level);
         return(check);
        }
      break;
     }

凍結レベル内での指値注文の変更試行が起こる状況を自分でシュミレーションすることができます。この為にはゼロレベルのSYMBOL_TRADE_FREEZE_LEVELを持つ金融商品があるデモ口座を開いて、チャート上にエキスパートアドバイザTest_SYMBOL_TRADE_FREEZE_LEVEL.mq5(Test_SYMBOL_TRADE_FREEZE_LEVEL.mq4)を起動し、手動で任意の指値注文を設定してください。エキスパートアドバイザ自体が、注文を最大限に市場に近づけ、禁じられた変更を使用と試み始めます。この時、PlaySound()関数を使用したサウンドが鳴ります。


相場履歴が不足している銘柄を使用する時に起こるエラー

エキスパートアドバイザまたはインディケータが履歴が欠けているチャートで起動された場合、二つの可能性があります。

  1. プログラムは必要な深さで必要な履歴があるか全てチェックします。利用可能なバーが必要とされるものよりも少ない場合、プログラムは不足しているデータを要求し、次のティックの受信まで自分の作業を終了します。この方法は最も正しいもので、配列またはゼロ除算の範囲を超えるような多数のエラーを避ける為に役立ちます。
  2. プログラムはチェックを行わずに、全ての要求する銘柄と時間軸の必要な全履歴が最初の要求時にすぐに利用できるかのようにすぐに自分の作業を始めます。このような方法は多くの予測不能なエラーをはらんでいます。

このような状況で自分でシュミレーションすることができます。これを行うには、テストするインディケータまたはエキスパートアドバイザをチャート上に起動し、それからターミナルを閉じ全ての履歴を削除し、そして再びターミナルを起動させます。このような再起動の後に操作履歴にエラーが発生しなかったら、貴方のプログラムが動作しているチャート上の銘柄と時間軸を変え始めましょう。多くのインディケータは、バーの数が制限される為、週や月の時間軸での起動時にエラーを出します。また、EURUSDをCADJPYに変えるなどのチャート銘柄の急激な変化の折に、チャート上に起動しているインディケータまたはエキスパートアドバイザは計算の為に必要な履歴の欠如を引き起こすエラーに陥ることがあります。


配列の範囲の越出(array out of range)

配列を使用した作業時に、その要素へのアクセスは、マイナスではなく、且つ配列サイズよりも小さいインデックス番号で行われます。配列サイズはArraySize()関数を使って取得することができます。

このエラーは動的配列を使用した作業時(そのサイズがまだArrayResize()関数で分散されていない、または動的配列への送信でサイズを自分で設定する関数のこのような配列の使用時)に起こることがあります。例えば、CopyTicks() 関数は配列に要求するティック数を書きこもようとしますが、ティックが要求されたものより少ない場合、取得した配列のサイズは予想より少なくなります。

このエラーを出す可能性の高い他の方法は、まだサイズが初期化されていない時に、インディケータバッファのデータにアクセスしようとすることです。インディケータバッファは動的配列で、そのサイズはチャートの初期化後にターミナルの実行システムで設定されます。したがって、OnInit()関数でのこのようなバッファデータへのアクセスは、"array out of range"エラーをもたらします。


このエラーを出すインディケータの簡単な例は、Test_Out_of_range.mq5ファイルに添付されています。


ゼロ除算(zero divide)

もう一つの重要なエラーはゼロ除算の試行です。この場合、プログラムの実行は直ちに停止し、テスターはエラーが発生したソースコードの行番号と関数名を操作ログに表示します。


原則として、ゼロ除算は『悪い』データを持つ式の計算または何かしらのプロパティの取得など、プログラマーが予測できない状況によって起こります。

簡単なエキスパートアドバイザTestZeroDivide.mq5を使うことで、ゼロ除算を簡単にシュミレーションすることができます(ソースコードはスクリーンショットで表示されています)。その他の重大なエラーは不正なオブジェクトポインタの使用です。このようなエラーの原因を突き止めるのには履歴デバッガが役に立ちます。


実際の変更のないレベル変更リクエストの送信

取引システムのルールで指値注文または保有ポジションの変更が必要な場合、トランザクション実行へ取引リクエストを送信する前に、リクエストする操作が本当に注文またはポジションのパラメータを変更するものであることを確認する必要があります。実際には何の変化もない取引リクエストの送信はエラーとみなされます。取引サーバはこのようなアクションに対して応答コード( TRADE_RETCODE_NO_CHANGES=10025 (MQL5) )またはコード(ERR_NO_RESULT=1(MQL4)
)を返します。

Check_OrderLevels.mq5スクリプトに引用されたMQL5のチェック例:

//--- 取引操作実行の為のクラス
#include <Trade\Trade.mqh>
CTrade trade;
#include <Trade\Trade.mqh>
//--- 注文を使った作業のクラス
#include <Trade\OrderInfo.mqh>
COrderInfo orderinfo;
//--- ポジションを使った作業のクラス
#include <Trade\PositionInfo.mqh>
CPositionInfo positioninfo;
//+------------------------------------------------------------------+
//| 注文を変更する前のレベルの新しい値のチェック                      |
//+------------------------------------------------------------------+
bool OrderModifyCheck(ulong ticket,double price,double sl,double tp)
  {
//--- ティックごとに注文を選択します
   if(orderinfo.Select(ticket))
     {
      //--- 項目サイズと指値注文が発注された銘柄名
      string symbol=orderinfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      //--- 始値に変化があるかをチェックします
      bool PriceOpenChanged=(MathAbs(orderinfo.PriceOpen()-price)>point);
      //--- ストップロスレベルに変化があるかをチェックします
      bool StopLossChanged=(MathAbs(orderinfo.StopLoss()-sl)>point);
      //--- テイクプロフィットレベルに変化があるかどうかをチェックします
      bool TakeProfitChanged=(MathAbs(orderinfo.TakeProfit()-tp)>point);
      //--- レベルに何かしらの変化があった場合
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // 注文を変更することができます      
      //--- ストップロスとテイクプロフィットの開始レベルの変更はありません
      else
      //--- エラーを報告します
         PrintFormat("注文#%dはすでにOpen=%.5f SL=%.5f TP=%.5fのレベルを持っています",
                     ticket,orderinfo.PriceOpen(),orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- 最後まで行き、注文に対する変更はありません
   return(false);       // 変更をする意味がありません 
  }
//+----------------------------------------------------------+
//| 注文を変更する前のレベルの新しい値のチェック              |
//+----------------------------------------------------------+
bool PositionModifyCheck(ulong ticket,double sl,double tp)
  {
//--- ティックごとに注文を選択します
   if(positioninfo.SelectByTicket(ticket))
     {
      //--- 項目サイズと指値注文が発注された銘柄名
      string symbol=positioninfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- ストップロスレベルに変化があるかをチェックします
      bool StopLossChanged=(MathAbs(positioninfo.StopLoss()-sl)>point);
      //--- テイクプロフィットレベルに変化があるかどうかをチェックします
      bool TakeProfitChanged=(MathAbs(OrderTakeProfit()-tp)>point);
      //--- レベルに何かしらの変化があった場合
      if(StopLossChanged || TakeProfitChanged)
         return(true);  // ポジションを変更することができます      
      //--- ストップロスとテイクプロフィットレベルの変更はありません
      else
      //--- エラーを報告します
         PrintFormat("注文#%dはすでにOpen=%.5f SL=%.5f TP=%.5fのレベルを持っています",
                     ticket,orderinfo.PriceOpen(),orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- 最後まで行き、注文に対する変更はありません
   return(false);       // 変更をする意味がありません 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 注文とポジションの価格レベル
   double priceopen,stoploss,takeprofit;
//--- 現在の注文とポジションのチケット
   ulong orderticket,positionticket;
/*
   ... StopLoss/Takeprofit/PriceOpenの新しいレベルと注文チケットを取得します
*/
//--- 指値注文の変更前にレベルをチェックします   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- チェックが正常に行われました
      trade.OrderModify(orderticket,priceopen,stoploss,takeprofit,
                        orderinfo.TypeTime(),orderinfo.TimeExpiration());
     }
/*
   ... StopLoss/Takeprofitの新しいレベルとポジションチケットを取得します
*/
//--- ポジション変更前にレベルをチェックします
   if(PositionModifyCheck(positionticket,stoploss,takeprofit))
     {
      //--- チェックが正常に行われました
      trade.PositionModify(positionticket,stoploss,takeprofit);
     }
//---
  }

MQL4言語でのチェック例はCheck_OrderLevels.mq4スクリプトで見ることができます。

#property strict
//+----------------------------------------------------------+
//| 注文を変更する前のレベルの新しい値のチェック              |
//+----------------------------------------------------------+
bool OrderModifyCheck(int ticket,double price,double sl,double tp)
  {
//--- ティックごとに注文を選択します
   if(OrderSelect(ticket,SELECT_BY_TICKET))
     {
      //--- 項目サイズと指値注文が発注された銘柄名
      string symbol=OrderSymbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- 始値に変化があるかをチェックします
      bool PriceOpenChanged=true;
      int type=OrderType();
      if(!(type==OP_BUY || type==OP_SELL))
        {
         PriceOpenChanged=(MathAbs(OrderOpenPrice()-price)>point);
        }
      //--- ストップロスレベルに変化があるかをチェックします
      bool StopLossChanged=(MathAbs(OrderStopLoss()-sl)>point);
      //--- テイクプロフィットレベルに変化があるかどうかをチェックします
      bool TakeProfitChanged=(MathAbs(OrderTakeProfit()-tp)>point);
      //--- レベルに何かしらの変化があった場合
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // 注文を変更することができます      
      //--- ストップロスとテイクプロフィットの開始レベルの変更はありません
      else
      //--- エラーを報告します
         PrintFormat("注文#%dはすでにOpen=%.5f SL=%.5f TP=%.5fのレベルを持っています",
                     ticket,OrderOpenPrice(),OrderStopLoss(),OrderTakeProfit());
     }
//--- 最後まで行き、注文に対する変更はありません
   return(false);       // 変更をする意味がありません 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 注文とポジションの価格レベル
   double priceopen,stoploss,takeprofit;
//--- 現在の注文のチケット 
   int orderticket;
/*
   ... StopLoss/Takeprofit/PriceOpenの新しいレベルと注文チケットを取得します
*/
//--- 注文変更前にレベルをチェックします   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- チェックが正常に行われました
      OrderModify(orderticket,priceopen,stoploss,takeprofit,OrderExpiration());
     }
  }

以下の記事もお読みいただく事をお勧めします。

  1. エキスパートアドバイザのコード内のエラーの検出と修正を簡素化する方法
  2. MQL4言語で信頼性の高い安全なトレードロボットを開発する方法


コンパイルしたファイル(EX4/EX5)やDLLをインポートする

マーケットで配信されているプログラムは、ユーザにとって安全が保証されているものでなければなりません。したがって、DLLまたはEX4/EX5のコンパイルファイルの関数のあらゆる使用はエラーとみなされます。このような製品はマーケットに出品されません。

貴方のプログラムに供給されていない追加のインディケータを使用する必要がある場合は、 リソース使用してください。


iCustom()を介したカスタムインディケータへのアクセス

貴方のプログラムの動作がカスタムインディケータのデータにアクセスする必要がある場合、全ての必要なインディケータをリソースに配置する必要があります。マーケットの製品はどんな準備が整っていない環境でも動作するように準備する必要があるので、製品は自分のEX4/EX5ファイルに必要なものを全て備えている必要があります。推奨する関連記事:


無効なパラメータの関数への送信(ランタイムエラー)

このタイプのエラーは稀で、それらの多くの為に原因を突き止めるのに役立つ既製のコードがあります。

定数 意味 説明
ERR_INTERNAL_ERROR 4001 予期しない内部エラー
ERR_WRONG_INTERNAL_PARAMETER 4002 クライアントターミナル関数の内部呼び出し時の不正パラメータ
ERR_INVALID_PARAMETER 4003 システム関数呼び出し時の不正パラメータ
ERR_NOTIFICATION_WRONG_PARAMETER 4516 通知送信の為の不正パラメータ。SendNotification()関数に空の文字列またはNULLが引き渡されました
ERR_BUFFERS_WRONG_INDEX 4602 自分のインディケータバッファの不正インデックス
ERR_INDICATOR_WRONG_PARAMETERS 4808 インディケータ作成時の不正なパラメータ数
ERR_INDICATOR_PARAMETERS_MISSING 4809 インディケータ作成時のパラメータの欠如
ERR_INDICATOR_CUSTOM_NAME 4810 配列の最初のパラメータはカスタムインディケータの名前である必要があります
ERR_INDICATOR_PARAMETER_TYPE 4811 インディケータ作成時の配列内の不正なパラメータタイプ
ERR_NO_STRING_DATE 5030 文字列に日付がありません
ERR_WRONG_STRING_DATE 5031 文字列内の日付が不正です
ERR_TOO_MANY_FORMATTERS 5038 フォーマット指定子がパラメータよりも多いです
ERR_TOO_MANY_PARAMETERS 5039 パラメータがフォーマット指定子よりも多いです

表では起動したプログラムの動作時に起こる可能性がある全てのエラーを表示しているわけではありません。

 

Access violation

このエラーはアクセスが禁じられているメモリへアクセスしようとすると起こります。このような場合には、連絡先ページまたは自分のプロフィールのサービスデスクから開発者に問い合わせる必要があります。エラーをシュミレーションする詳細な説明や添付のソースコードは、このようなエラーの原因を突き止めるのが大幅に速くなり、ソースコードコンパイルの改善に役立ちます。




プロセッサとメモリの消費量

ターミナルの他のプログラムの動作に困難を生じたり動作不能に陥ることもある為、プログラムの作成時に実行時間が最適なアルゴリズムを使用することは重要です。

以下のことを覚えておいてください。各銘柄に対し気配値表示ウィンドウでは、ターミナルはそこで全てのチャートとインディケータが動いている一つの全体の組を表示します。

これはつまり、貴方の下にEURUSDの異なる時間軸のチャートが5つあり、これらのチャート上に15のインディケータが起動している場合、これらの全てのチャートとインディケータは、チャート上の情報の表示と計算の為に一つの組に置かれることになります。したがって、チャート上に起動した一つの非効率的で多くのリソースを消費するインディケータが、他のインディケータの動作をスローダウンさせたり、強いてはその銘柄の残りの全てのチャート上の価格描画が遅くなってしまうことがあります。

貴方のアルゴリズムにかかる時間はGetMicrosecondCount()関数を使うことで簡単にチェックすることができます。コードの二つの文字列間の時間の計測を行うことで、簡単に実行時間をマイクロ秒単位で取得することができます。ミリ秒(ms)へ変換するにはこの時間を1000で割る必要があります(1ミリ秒には1000マイクロ秒が入ります)。インディケータにとっての実行時間の重要な場所はOnCalculate()ハンドラです。一般的には、インディケータの最初の計算はウィンドウ内の最大バーのパラメータに依存するので、そこで"Unlimited"を指定し、M1の時間軸の10年以上の履歴を持つ銘柄で自分のインディケータを起動させてください。最初の起動に時間がかかる場合(例えば、100ms)、コードを最適化する必要があります。

ターミナルの供給ソースコードに入っている、インディケータROCのOnCalculate()ハンドラの実行時間の計測例は以下になります。挿入部分は黄色で表示されています:

//+------------------------------------------------------------------+
//| Rate of Change                                                   |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,const int begin,const double &price[])
  {
//--- check for rates count
   if(rates_total<ExtRocPeriod)
      return(0);
//--- 計算開始時間
   ulong start=GetMicrosecondCount();  
//--- preliminary calculations
   int pos=prev_calculated-1; // set calc position
   if(pos<ExtRocPeriod)
      pos=ExtRocPeriod;
//--- the main loop of calculations
   for(int i=pos;i<rates_total && !IsStopped();i++)
     {
      if(price[i]==0.0)
         ExtRocBuffer[i]=0.0;
      else
         ExtRocBuffer[i]=(price[i]-price[i-ExtRocPeriod])/price[i]*100;
     }
//--- 計算終了時間     
   ulong finish=GetMicrosecondCount();  
   PrintFormat("関数%sは%sで%.1f msかかりました",__FUNCTION__,__FILE__,(finish-start)/1000.);
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }

使用するメモリはMQLInfoInteger(MQL_MEMORY_USED)関数を使うことで計測することができます。そして勿論、貴方のプログラムの中の最も消費が多い関数を見つける為のコードの解析を使用してください。またインディケータの簡単な計算原理MQL5のプログラムのデバッグの記事もお読みいただく事をお勧めします。

エキスパートアドバイザは独自のスレッドで動作しますが、上述のことは全てエキスパートアドバイザに関連することです。エキスパートアドバイザ、インディケータ、ライブラリ、スクリプトなど、あらゆるプログラムタイプで最適なコードを記述する必要があります。


沢山のチェックはありません

インディケータやエキスパートアドバイザのチェックに関する全てのアドバイスは、マーケットに製品を公開するためだけのものではなく、貴方が自分の為に作成する際に役立つものでもあります。この記事で、実際口座での取引の際に起こる可能性のある全てのエラーを検証できたわけではありません。ここでは、取引システムの理想的なルールを乱す可能性がある、取引サーバやリクオート、取引実行拒否などに関連する損失時に起こる取引エラー処理のルールについては検証されていません。このようなケースの為に、ロボットの各開発者は経験によって培われた自分の処置方法を持っています。

初心者の方には、エラー処理に関する全ての記事をお読みいただいたり、フォーラムやこの記事のコメント欄に質問を書いていただければと思います。MQL5.communityのより多くの経験を持つ他のメンバーが、貴方の疑問を解くお手伝いをしてくれます。記事に集めた情報が、貴方がより短期間でより信頼性の高いトレードロボットを作成する為の一助となることを願っています。


推奨する関連記事: