Características da linguagem mql5, subtilezas e técnicas - página 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);
}
Aplicação
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);
}
Aplicação
Request.type_time = GetExpirationType(Request.symbol, (uint)Expiration); // Expiration может быть datetime-временем

if (Expiration > ORDER_TIME_DAY)
  Request.expiration = Expiration;
 
Conversão de estruturas MqlTrade em cadeia
#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
Aplicação
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);
}

Aplicação

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

As coisas são muito mais simples. Antes de enviar um pedido, lembrou-se da duração do histórico, e depois de enviá-lo, esperou que a duração do histórico aumentasse. Um timeout deve ser inserido, para que de repente não fique preso para sempre.

 
Dmitry Fedoseev:

As coisas são muito mais simples. Antes de enviar um pedido, lembrou-se da duração do histórico, e depois de enviá-lo, esperou que a duração do histórico aumentasse. Um timeout tem de ser introduzido, para que de repente não fique preso para sempre.

Há um desconto de tempo. Na sua variante, infelizmente, pode haver problemas se vários OrderSends trabalharem em conjunto (de diferentes Expert Advisors).

Além disso, não só o histórico não está sincronizado, mas também SL/TP de posições/ordens abertas, etc. Para os mercados, a história pode aumentar primeiro apenas por uma ordem, e depois um momento depois por uma negociação. Há outras situações também. É por isso que parece tão incómodo à primeira vista.

 
Dmitry Fedoseev:

Qual é o objectivo da sua mensagem? Então vem na OnTradeTransaction(), e daí? Significa que temos de esperar pelo evento. Ainda tens de esperar. A essência da mensagem do fxsaber é que após a execução do OrderSend(), a informação sobre a ação executada não aparecerá imediatamente no terminal. Algumas pessoas gostam de esperar pela OnTradeTransaction(), enquanto outras gostam de ver uma ordem ou um negócio na lista. Aqui está a diferença do MT4, a título de exemplo. Em M4, após OrderSend(), a ordem já está na lista de ordens, e após OrderClose(), ela está sempre no histórico.

E por que esperar pela OnTradeTransaction()? Faça uma fila de pedidos, se houver uma resposta, faça algo... pelo menos apague o pedido da fila. É uma pena que a SB não tenha Fila de espera, mas é uma aula simples.

 
fxsaber:

Há um desconto de tempo. Na sua versão, infelizmente, pode haver problemas se vários OrderSends trabalharem em conjunto (de diferentes EAs).

Além disso, não só o histórico não está sincronizado, mas também SL/TP de posições/ordens abertas, etc. Para os marcadores, a história pode aumentar primeiro apenas por uma ordem, e depois, um momento depois, por um comércio. Há outras situações também. É por isso que parece tão incómodo à primeira vista.

Sim, eu concordo. Mas eu só tive um desses EA, e se alguma coisa não vai sair mal disso.

Seria melhor escrever uma função separada para espera, para que a classe de negociação padrão pudesse ser usada também.

 
Alexey Volchanskiy:

Por que esperar pela OnTradeTransaction()? Faça uma fila de pedidos, se a resposta chegar, faça alguma coisa... pelo menos retire o pedido da fila. É uma pena que a SB não tenha Fila de espera, mas é uma aula simples.

A questão é que a OnTradeTransaction() não irá acionar imediatamente após o OrderSend.

Em resumo, há apenas uma multidão de fãs discutindo aqui sem entrar no assunto da discussão.

Existem duas variantes do algoritmo se algo tiver de ser feito imediatamente após OrderSend():

1. Podemos iniciar um loop à espera de actualizações das listas de encomendas e ofertas.

2. Termine OnTick() e espere a OnTradeTransaction() acionar.

3. Marque com um visto para ver se uma nova ordem ou negócio aparece na lista.