Bibliotecas: TesterBenchmark - página 4

 

O motivo da principal lentidão do MT5 é a imitação do ambiente de negociação completo. Na grande maioria dos Expert Advisors, não é necessária uma simulação tão precisa. Uma excelente solução para esses Expert Advisors seria mudar para um modo de teste simplificado, com modelagem simples do ambiente de negociação. Entretanto, essa possibilidade não existe. Mas, se você escrever seu próprio testador de estratégia simplificado e executá-lo no modo de cálculos matemáticos, a velocidade de otimização será enorme! Isso é o que sugiro que você pense para todos aqueles que desejam aumentar drasticamente a velocidade de otimização dos Expert Advisors.

 
Vasiliy Sokolov:

No CStrategy, as operações de negociação são realizadas diretamente por meio do CTrade. Ou seja, o CStrategy não tem nenhuma lógica de negociação própria. No Expert Advisor de teste, não vi nenhuma outra lógica de negociação além da abertura/fechamento de uma posição após N segundos. O CStrategy também não armazena posições históricas, portanto, infelizmente, esse exemplo não pode ser realizado no CStrategy.

A CTrade também não armazena histórico, mas isso não nos impediu de implementar a variante por meio dela.

O fato de o código OOP ser mais lento do que o código procedural não me diz nada. Vamos direto ao ponto: quais métodos do CTrade são um gargalo? Por quê? O que o perfil desses métodos lhe diz? Como identificar seções de código lento no testador?

Não analisei os motivos dos freios do SB, mas não acho que seja OOP, porque o MT4Orders também é escrito com OOP, mesmo que minimamente. Após uma solicitação ao BOD, os desenvolvedores aceleraram o SB. Não sei se eles ajustaram o próprio SB ou o compilador.

Eu mesmo analiso primeiro os freios absolutos e depois os relativos

O MetaTrader 5 tem uma excelente função de criação de perfil de um Expert Advisor em dados históricos. Mas, além do fato de que ele funciona lentamente (no modo visual), o resultado final é fornecido em unidades relativas, ou seja, é impossível comparar o desempenho em termos absolutos.

 
Vasiliy Sokolov:

O motivo da principal lentidão do MT5 é a imitação do ambiente de negociação completo. Na grande maioria dos Expert Advisors, não é necessária uma simulação tão precisa. Uma excelente solução para esses Expert Advisors seria mudar para um modo de teste simplificado, com modelagem simples do ambiente de negociação. Entretanto, essa possibilidade não existe. Mas, se você escrever seu próprio testador de estratégia simplificado e executá-lo no modo de cálculos matemáticos, a velocidade de otimização será incrível! Sugiro que você pense nisso para todos aqueles que desejam aumentar drasticamente a velocidade de otimização do EA.

Por meio de símbolos personalizados, você pode obter ordens de magnitude de aumento de velocidade com a simulação completa. Na verdade, mesmo agora, no modo beta dos símbolos personalizados, é possível conseguir isso. Farei isso mais tarde.

 
MQL5:
i = 5 Pass = 5 OnTester = 8.521 s.: Count = 15925124, 1868926.7 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


MT4Orders:

i = 2 Pass = 2 OnTester = 8.001 s.: Count = 15925124, 1990391.7 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


MQL5 acabou sendo mais lento do que a biblioteca escrita na mesma MQL5! Comecei a investigar e descobri o motivo fazendo esta substituição

// se (!PositionSelect(_Symbol))
  if (!PositionGetTicket(0))

A variante MQL5 do Expert Advisor começou a apresentar esse desempenho.

i = 4 Pass = 4 OnTester = 7.931 s.: Count = 15925124, 2007959.1 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


A substituição estúpida de PositionSelect por PositionGetTicket aumenta a velocidade do backtest em 7%!

O MT4Orders fica atrás do MQL5 puro maximamente otimizado em menos de um por cento.

 
fxsaber:

As vagas foram distribuídas da seguinte forma

  1. Pure MQL5 - 100% de desempenho.

  2. SB Trade\Trade.mqh - ~84% de desempenho.
O SB ficou menos lento.

Fiz ajustes no próprio SB (partes do SB usadas pelo Expert Advisor) sem alterar sua funcionalidade e versatilidade. O desempenho não ficou em 84%, mas em 97%.

Aqueles que usam o SB devem observar a possibilidade potencial de aceleração.

 

Foi interessante comparar o desempenho do mesmo Expert Advisor MT4 multimoeda convertido de diferentes maneiras.

Conversão completa

#include <TesterBenchmark.mqh> // https://www.mql5.com/pt/code/18804
#include "Spreader 2.mq5"      // https://www.mql5.com/pt/code/19402


e conversão artesanal (rápida)

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação.

Expert Advisors: Spreader

fxsaber, 2016.09.03 11:18 AM.

// https://www.mql5.com/pt/code/16006
#include <MT4Orders.mqh>
#include <MQL4_to_MQL5.mqh>

#include "Spreader_v2.mq4" // https://www.mql5.com/pt/code/9495

void OnTick()
{
  start();
}

Resultados da medição de desempenho.


Versão completa

i = 1 Pass = 1 OnTester = 3.465 s.: Count = 897532, 259028.0 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


Artesanal

i = 1 Pass = 1 OnTester = 4.815 s.: Count = 897532, 186403.3 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


A versão artesanal perde significativamente. O motivo é simples - se você quiser séries temporais rápidas, não poderá converter as séries temporais do MT4 de ponta a ponta

Fórum sobre negociação, sistemas de negociação automatizados e teste de estratégias de negociação.

Bibliotecas: CPrice

fxsaber, 2016.08.28 10:36 AM

Dê uma olhada nessa opção

#define  DEFINE_TIMESERIE(NAME,FUNC,T)                                                                         \
  class CLASS##NAME                                                                                           \
  {                                                                                                           \
  public:                                                                                                     \
    static T Get(const string Symb,const int TimeFrame,const int iShift)                                      \
    {                                                                                                         \
      T tValue[];                                                                                             \
                                                                                                              \
      return((Copy##FUNC((Symb == NULL) ? _Symbol : Symb, _Period, iShift, 1, tValue) > 0) ? tValue[0] : -1); \
    }                                                                                                         \
                                                                                                              \
    T operator[](const int iPos) const                                                                        \
    {                                                                                                         \
      return(CLASS##NAME::Get(_Symbol, _Period, iPos));                                                       \
    }                                                                                                         \
  };                                                                                                          \
                                                                                                              \
  CLASS##NAME  NAME;                                                                                           \
                                                                                                              \
  T i##NAME(const string Symb,const int TimeFrame,const int iShift)                                           \
  {                                                                                                           \
    return(CLASS##NAME::Get(Symb,  TimeFrame, iShift));                                                        \
  }

DEFINE_TIMESERIE(Volume,TickVolume,long)
DEFINE_TIMESERIE(Time,Time,datetime)
DEFINE_TIMESERIE(Open,Open,double)
DEFINE_TIMESERIE(High,High,double)
DEFINE_TIMESERIE(Low,Low,double)
DEFINE_TIMESERIE(Close,Close,double)

Você pode, como no MT4, chamar Open[bar], High[bar], Time[bar], Volume[bar], etc. E também iHigh(...), iClose(...) e outros.

Você precisa usar (não apenas para bush, mas também para variantes completas) soluções como esta.

Высокопроизводительная библиотека iTimeSeries
Высокопроизводительная библиотека iTimeSeries
  • votos: 23
  • 2017.05.25
  • nicholishen
  • www.mql5.com
Одной из основных проблем с MQL5 до сих пор было удаление встроенных функций для работы с таймсериями. Несмотря на то, что такой подход расширил для программистов возможности разработки, он также замедлил работу из-за обязательного шага по созданию и удалению новых ячеек памяти каждый раз, когда требуется доступ к данным таймсерии.  Рассмотрим...
 
fxsaber:

O artesanal perde significativamente. O motivo é simples: se você quiser séries temporais rápidas, não poderá converter as séries temporais do MT4 diretamente.

Conclusão errada. O motivo dos freios é bem diferente. Vamos inserir apenas uma linha em cada uma das variantes do MT5

#define Comment(A)

E medir novamente

  • Completa

i = 1 Pass = 1 OnTester = 2.074 s.: Count = 897532, 432754.1 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 165

  • Artesanal.

i = 1 Pass = 1 OnTester = 2.310 s.: Count = 897532, 388542.0 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1653


A versão completa é 67% mais rápida, enquanto a artesanal é 108% mais rápida! Em resumo, a diferença agora é pequena - a completa é ~11% mais rápida que a artesanal. Parece que o motivo está na sobrecarga de OOP.


Mas isso não é o principal. Com uma linha, conseguimos acelerar os EAs! E isso é no Otimizador, onde o Comentário não desempenha nenhum papel.

 

Isso foi escrito no verão junto com o aplicativo SD. Estou surpreso que tudo esteja igual! Ou isso é lógico? Como se pode falar sobre a supervelocidade do testador quando quase todos os EAs (usando SB) falham na velocidade, consomem dinheiro na nuvem, etc.! Aparentemente, isso é normal.


Simplifiquei o código-fonte, descartando o trabalho com History e MT4Orders. Deixei apenas a MQL5 pura e o SB

#include <TesterBenchmark.mqh>

#include <Trade\Trade.mqh> // Comentário para ativar a variante em MQL5 puro

input int Interval = 60;

void OnTick()
{
#ifdef   ERR_USER_INVALID_HANDLE // trade.mqh
  static CTrade Trade;
  static CDealInfo Deal;
  static CPositionInfo Position;

  if (!Position.SelectByIndex(0))
  {
    if (HistorySelect(0, LONG_MAX))
    {
      const int Total = HistoryDealsTotal() - 1;

      if ((Total >= 0) && Deal.SelectByIndex(Total) && (Deal.DealType() == DEAL_TYPE_SELL))
        Trade.Sell(1);
      else
        Trade.Buy(1);
    }
  }
  else if (TimeCurrent() - Position.Time() >= Interval)
    Trade.PositionClose(_Symbol);
#else  // ERR_USER_INVALID_HANDLE
  if (!PositionGetTicket(0))
  {
    if (HistorySelect(0, LONG_MAX))
    {
      const int Total = HistoryDealsTotal() - 1;
      MqlTradeRequest Request = {0};

      Request.action = TRADE_ACTION_DEAL;

      Request.symbol = _Symbol;
      Request.type = ((Total >= 0) && ((ENUM_DEAL_TYPE)HistoryDealGetInteger(HistoryDealGetTicket(Total), DEAL_TYPE) == DEAL_TYPE_SELL)) ?
                     ORDER_TYPE_SELL : ORDER_TYPE_BUY;;

      Request.volume = 1;
      Request.price = SymbolInfoDouble(Request.symbol, (Request.type == ORDER_TYPE_BUY) ? SYMBOL_ASK : SYMBOL_BID);

      MqlTradeCheckResult CheckResult;
      if (OrderCheck(Request, CheckResult))
      {
        MqlTradeResult Result;

        const bool AntiWarning = OrderSend(Request, Result);
      }
    }
  }
  else if (TimeCurrent() - PositionGetInteger(POSITION_TIME) >= Interval)
  {
    MqlTradeRequest Request = {0};
    MqlTradeResult Result;

    Request.action = TRADE_ACTION_DEAL;
    Request.position = PositionGetInteger(POSITION_TICKET);

    Request.symbol = PositionGetString(POSITION_SYMBOL);
    Request.type = (ENUM_ORDER_TYPE)(1 - PositionGetInteger(POSITION_TYPE));

    Request.volume = PositionGetDouble(POSITION_VOLUME);
    Request.price = PositionGetDouble(POSITION_PRICE_CURRENT);

    const bool AntiWarning = OrderSend(Request, Result);
  }
#endif // ERR_USER_INVALID_HANDLE
}


Resultado do SB

------
OnTesterInit
i = 0 Pass = 0 OnTester = 1.898 s.: Count = 2007057, 1057458.9 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1730
i = 1 Pass = 1 OnTester = 1.900 s.: Count = 2007057, 1056345.8 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1730
iMin = 0 Results[iMin] = 1.898 s.
iMax = 1 Results[iMax] = 1.900 s.
Amount = 2 Mean = 1.899 s. - 81.75%
OnTesterDeinit
------
Interval = 4.646 s., Count = 0, 0.0 unit/sec


Resultado da MQL5 pura

------
OnTesterInit
i = 0 Pass = 0 OnTester = 1.239 s.: Count = 2007057, 1619900.7 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1730
i = 1 Pass = 1 OnTester = 1.243 s.: Count = 2007057, 1614687.9 unit/sec, Agent = C:\Program Files\Alpari Limited MT5\Tester\Agent-127.0.0.1-3000 build = 1730
iMin = 0 Results[iMin] = 1.239 s.
iMax = 1 Results[iMax] = 1.243 s.
Amount = 2 Mean = 1.241 s. - 75.10%
OnTesterDeinit
------
Interval = 3.305 s., Count = 0, 0.0 unit/sec

Tudo a mesma coisa 1,5 vezes! Mas os otários não se importam, vamos continuar a usar o super-SB.

 
fxsaber:

O mesmo 1,5 vezes! Mas os otários não se importam, vamos continuar usando o super-SB.

Esse é o tipo de ajuste do SB

//+------------------------------------------------------------------+
//| Enviar pedido|
//+------------------------------------------------------------------+
bool CTrade::OrderSend(const MqlTradeRequest &request,MqlTradeResult &result)
  {
   static const bool IsTester = (::MQLInfoInteger(MQL_TESTER) || ::MQLInfoInteger(MQL_OPTIMIZATION));
   
   bool res;
   string action="";
   string fmt   ="";
//--- ação
   if(m_async_mode)
      res=::OrderSendAsync(request,result);
   else
      res=::OrderSend(request,result);
//--- verificação
   if(res)
     {
      if(!IsTester &&  m_log_level>LOG_LEVEL_ERRORS)
         PrintFormat(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result));
     }
   else
     {
      if(!IsTester &&  m_log_level>LOG_LEVEL_NO)
         PrintFormat(__FUNCTION__+": %s [%s]",FormatRequest(action,request),FormatRequestResult(fmt,request,result));
     }
//--- retornar o resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| Selecione uma posição no índice|
//+------------------------------------------------------------------+
bool CPositionInfo::SelectByIndex(const int index)
  {
   return(PositionGetTicket(index));
   
   ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
//---
   if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      ulong ticket=PositionGetTicket(index);
      if(ticket==0)
         return(false);
     }
   else
     {
      string name=PositionGetSymbol(index);
      if(name=="")
         return(false);
     }
//---
   return(true);
  }

que milagrosamente acelera os consultores do SB. É complicado?

Falando francamente, o SB de negociação, sem alterar a lógica da API, deveria ser reescrito quase que completamente. Qualquer parte dele é ruim.