Features of the mql5 language, subtleties and tricks - page 4

 
// Возвращает тип исполнения ордера, равный Type, если он доступен на символе Symb, иначе - корректный вариант.
ENUM_ORDER_TYPE_FILLING GetFilling( const string Symb, const uint Type = ORDER_FILLING_FOK )
{
  const ENUM_SYMBOL_TRADE_EXECUTION ExeMode = (ENUM_SYMBOL_TRADE_EXECUTION)::SymbolInfoInteger(Symb, SYMBOL_TRADE_EXEMODE);
  const int FillingMode = (int)::SymbolInfoInteger(Symb, SYMBOL_FILLING_MODE);

  return((FillingMode == 0 || (Type >= ORDER_FILLING_RETURN) || ((FillingMode & (Type + 1)) != Type + 1)) ?
         (((ExeMode == SYMBOL_TRADE_EXECUTION_EXCHANGE) || (ExeMode == SYMBOL_TRADE_EXECUTION_INSTANT)) ?
           ORDER_FILLING_RETURN : ((FillingMode == SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) :
          (ENUM_ORDER_TYPE_FILLING)Type);
}
Applications
Request.type_filling = GetFilling(Request.symbol);
 
// Возвращает тип истечения ордера, равный Expiration, если он доступен на символе Symb, иначе - корректный вариант.
ENUM_ORDER_TYPE_TIME GetExpirationType( const string Symb, uint Expiration = ORDER_TIME_GTC )
{
  const int ExpirationMode = (int)::SymbolInfoInteger(Symb, SYMBOL_EXPIRATION_MODE);

  if ((Expiration > ORDER_TIME_SPECIFIED_DAY) || (((ExpirationMode >> Expiration) & 1) == 0))
  {
    if ((Expiration < ORDER_TIME_SPECIFIED) || (ExpirationMode < SYMBOL_EXPIRATION_SPECIFIED))
      Expiration = ORDER_TIME_GTC;
    else if (Expiration > ORDER_TIME_DAY)
      Expiration = ORDER_TIME_SPECIFIED;

    uint i = 1 << Expiration;

    while ((Expiration <= ORDER_TIME_SPECIFIED_DAY) && ((ExpirationMode & i) != i))
    {
      i <<= 1;
      Expiration++;
    }
  }

  return((ENUM_ORDER_TYPE_TIME)Expiration);
}
Application
Request.type_time = GetExpirationType(Request.symbol, (uint)Expiration); // Expiration может быть datetime-временем

if (Expiration > ORDER_TIME_DAY)
  Request.expiration = Expiration;
 
Translation of MqlTrade structures into string
#define TOSTRING(A)  #A + " = " + (string)(A) + "\n"
#define TOSTRING2(A) #A + " = " + EnumToString(A) + " (" + (string)(A) + ")\n"

string ToString( const MqlTradeTransaction &Trans )
{
  return(TOSTRING(Trans.deal) + TOSTRING(Trans.order) + TOSTRING(Trans.symbol) +
         TOSTRING2(Trans.type) + TOSTRING2(Trans.order_type) + TOSTRING2(Trans.order_state) +
         TOSTRING2(Trans.deal_type) + TOSTRING2(Trans.time_type) +
         TOSTRING(Trans.time_expiration) + TOSTRING(Trans.price) + TOSTRING(Trans.price_trigger) +
         TOSTRING(Trans.price_sl) + TOSTRING(Trans.price_tp) + TOSTRING(Trans.volume) +
         TOSTRING(Trans.position) + TOSTRING(Trans.position_by));
}

string ToString( const MqlTradeRequest &Request )
{
  return(TOSTRING2(Request.action) + TOSTRING(Request.magic) + TOSTRING(Request.order) +
         TOSTRING(Request.symbol) + TOSTRING(Request.volume) + TOSTRING(Request.price) +
         TOSTRING(Request.stoplimit) + TOSTRING(Request.sl) +  TOSTRING(Request.tp) +
         TOSTRING(Request.deviation) + TOSTRING2(Request.type) + TOSTRING2(Request.type_filling) +
         TOSTRING2(Request.type_time) + TOSTRING(Request.expiration) + TOSTRING(Request.comment) +
         TOSTRING(Request.position) + TOSTRING(Request.position_by));
}

string ToString( const MqlTradeResult &Result )
{
  return(TOSTRING(Result.retcode) + TOSTRING(Result.deal) + TOSTRING(Result.order) +
         TOSTRING(Result.volume) + TOSTRING(Result.price) + TOSTRING(Result.bid) +  
         TOSTRING(Result.ask) + TOSTRING(Result.comment) + TOSTRING(Result.request_id) +  
         TOSTRING(Result.retcode_external));
}

#undef TOSTRING
#undef TOSTRING2
Using
void OnTradeTransaction ( const MqlTradeTransaction &Trans, const MqlTradeRequest &Request, const MqlTradeResult &Result )
{
  Print(ToString(Trans) + ToString(Request) + ToString(Result));
}
 
// Триггер срабатывания SL/TP - работает только на демо/реале (не в тестере).
void OnTradeTransaction ( const MqlTradeTransaction &Trans, const MqlTradeRequest &Request, const MqlTradeResult &Result )
{
  if ((Trans.type == TRADE_TRANSACTION_ORDER_ADD) &&
       PositionSelectByTicket(Trans.position) && OrderSelect(Trans.order) &&
       (PositionGetInteger(POSITION_TYPE) == 1 - OrderGetInteger(ORDER_TYPE)))
  {
    const double Price = OrderGetDouble(ORDER_PRICE_OPEN);
    
    if (Price == PositionGetDouble(POSITION_TP))
      Print("Position #" + (string)Trans.position + " - triggered TP.");    
    else if (Price == PositionGetDouble(POSITION_SL))
      Print("Position #" + (string)Trans.position + " - triggered SL.");    
  }
}
 
ENUM_DAY_OF_WEEK GetDayOfWeek( const datetime time )
{
  MqlDateTime sTime = {0};

  ::TimeToStruct(time, sTime);

  return((ENUM_DAY_OF_WEEK)sTime.day_of_week);
}

// true - находимся в торговой сессии
bool SessionTrade( const string Symb )
{
  datetime TimeNow = ::TimeTradeServer();

  const ENUM_DAY_OF_WEEK DayOfWeek = GetDayOfWeek(TimeNow);

  TimeNow %= 24 * 60 * 60;

  bool Res = false;
  datetime From, To;

  for (int i = 0; (!Res) && ::SymbolInfoSessionTrade(Symb, DayOfWeek, i, From, To); i++)
    Res = ((From <= TimeNow) && (TimeNow < To));

  return(Res);
}

// Возвращает true, если символ торгуемый. Иначе - false.
bool SymbolTrade( const string Symb )
{
  MqlTick Tick;

  return(::SymbolInfoTick(Symb, Tick) ? ((Tick.bid != 0) && (Tick.ask != 0) && SessionTrade(Symb) /* &&
         ((ENUM_SYMBOL_TRADE_MODE)::SymbolInfoInteger(Symb, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL) */
) : false);
}

Application

if (OrderCheck(Request, CheckResult) && SymbolTrade(Request.symbol))
  OrderSend(Request, Result);
 

Things are much easier. Before sending the order, I memorized the length of the history, and after sending it, I waited for the history length to increase. A timeout should be entered, so that suddenly it wouldn't get stuck forever.

 
Dmitry Fedoseev:

Things are much easier. Before sending the order, I memorized the length of the history, and after sending it, I waited for the history length to increase. A timeout should be entered, so that suddenly it would not be stuck forever.

There is a timeout. In your variant, unfortunately, there may be problems if several OrderSends work together (from different Expert Advisors).

Also, not only the history is not synchronized, but also SL/TP of open positions/orders, etc. For Markets, the history may increase first only by an order, and then a moment later by a trade. There are other situations as well. That's why it looks so cumbersome at first sight.

 
Dmitry Fedoseev:

What is the point of your message? Well it will come in OnTradeTransaction(), so what? It means that you have to wait for the event. You still have to wait. The point of fxsaber's message is that after OrderSend() is executed the information about the action appears in the terminal not immediately. Some people like to wait for OnTradeTransaction(), while others like to see an order or a deal in the list. For example, the difference from MT4. In M4, after OrderSend(), the order is already in the list of orders, and after OrderClose(), it is always in the history.

And why wait for OnTradeTransaction()? Make a queue of requests, if the answer came, do something... at least delete the request from the queue. It's a pity that SB doesn't have Queue, but it's a simple class.

 
fxsaber:

There is a timeout. In your version, unfortunately, there may be problems if several OrderSends work together (from different EAs).

Also, not only the history is not synchronized, but also SL/TP of open positions/orders, etc. For Markets, the history may increase first only by an order, and then a moment later by a trade. There are other situations as well. That's why it looks so cumbersome at first glance.

Yes, I agree. But I have only had one such EA, and if anything, nothing bad will happen.

It would be better to write a separate function for waiting, so that the standard trading class could be used as well.

 
Alexey Volchanskiy:

Why wait for OnTradeTransaction()? Make a queue of requests, if the answer came, do something... at least remove the request from the queue. It's a pity that SB doesn't have Queue, but it's a simple class.

The point is that OnTradeTransaction() won't trigger immediately after OrderSend.

In short, there's just a bunch of people who like to argue without getting into the subject of the argument.

There are two variants of the algorithm, if after OrderSend() something must be done immediately:

1. We can start a loop waiting for updates of the lists of orders and deals.

2. Finish OnTick() and wait for OnTradeTransaction() to take effect.

3. Check by tick to see if a new order or trade appears in the list.