2020年4月18日土曜日

[MT4]オーダーを触るときはチケット指定が安全

EAにてポジションをクローズする処理を書く場合、素直に書くとこのようなコードになるかと思います。
これでもたぶん問題になりません。

   for( int i = OrdersTotal() - 1; i >= 0; i-- )
   {
      if( !OrderSelect(i, SELECT_BY_POS) ) continue;
      if( OrderMagicNumber() != MagicNumber ) continue;
      if( OrderSymbol() != Symbol() ) continue;
      
      int order = OrderType();
      
      if( order == OP_BUY )
      {
         //クローズ条件の算出
         if( 買い側クローズ条件なら )
         {
            OrderClose(OrderTicket(), OrderLots(), Ask, 1);
         }
      }
      else if ( order == OP_SELL )
      {
         //クローズ条件の算出
         if( 売り側クローズ条件なら )
         {
            OrderClose(OrderTicket(), OrderLots(), Bid, 1);
         }
      }
   }

ただ、OrderCloseの処理がそれなりに時間がかかる処理なので、その間に他の条件でポジション数が変更になってしまうと、インデックスずれなどで想定しないポジションに対しての処理や、ポジションが処理されないなどの不安があります。

そこで、このように一回チケットIDの一覧を取得して、その取得したチケットIDに対してOrderSelectをかけてやることで、その不安をある程度低減可能です。OrderCloseが100ms台の処理なのに対してOrderTicketなどの情報取得系は数ns台で処理されるためです。

#include <Arrays/ArrayInt.mqh>
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   CArrayInt positions;
   int orderCount = OrdersTotal();
   
   for( int i = 0; i < orderCount ; i++)
   {
      if( OrderSelect(i, SELECT_BY_POS) )
      {
         if( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
         {
            positions.Add(OrderTicket());
         }
      }
   }
   
   int targetCount = positions.Total();
   for( int i = 0; i < targetCount; i++ )
   {
      int ticket = positions.At(i);
      if( OrderSelect(ticket, SELECT_BY_TICKET) )
      {
         int order = OrderType();
         if(order  == OP_BUY)
         {
             //クローズ条件の算出
             if( 買い側クローズ条件なら )
             {
                OrderClose(ticket, OrderLots(), Ask, 1);
             }
         }
         else if(order  == OP_SELL)
         {
             //クローズ条件の算出
             if( 売り側クローズ条件なら )
             {
                OrderClose(ticket, OrderLots(), Bid, 1);
             }
         }
      }   
   }
}

なお、このような形で、forの中をcontinueで次の条件に飛ばすような書き方にするとインデントが深くなるのが抑えられて見やすくなりますが、処理には影響ありません。

   for( int i = 0; i < orderCount ; i++)
   {
      if( !OrderSelect(i, SELECT_BY_POS) ) continue;
      if( OrderMagicNumber() != MagicNumber) continue;
      if( OrderSymbol() != Symbol() ) continue;

      positions.Add(OrderTicket());
   }

ちょっとTwitterで話題になったので私なりの書き方をご紹介してみました。もし問題があればぜひご指摘お願いします。

本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。