Librerías: TesterBenchmark - página 4

 

La razón de la principal ralentización de MT5 es la imitación del entorno completo de trading. En la gran mayoría de los Asesores Expertos, una simulación tan precisa no es necesaria. Una excelente solución para este tipo de Asesores Expertos sería cambiar a un modo de prueba simplificado, con un simple modelado del entorno de negociación. Sin embargo, no existe tal posibilidad. Pero si escribe su propio probador de estrategias simplificado y lo ejecuta en el modo de cálculos matemáticos, ¡la velocidad de optimización será de fuego! Esto es lo que sugiero que piensen todos los que quieren aumentar drásticamente la velocidad de optimización de EA.

 
Vasiliy Sokolov:

En CStrategy, las operaciones comerciales se realizan directamente a través de CTrade. Es decir, CStrategy no tiene lógica de negociación propia en absoluto. En el Asesor Experto de prueba no vi ninguna otra lógica de negociación que la apertura/cierre de una posición después de N segundos. CStrategy tampoco almacena posiciones históricas, por lo que este ejemplo no es realizable en CStrategy.

CTrade tampoco almacena el histórico, pero eso no nos impidió implementar la variante a través de él.

El hecho de que el código OOP sea más lento que el procedimental no me dice nada. Vayamos al grano: ¿qué métodos de CTrade son un cuello de botella? ¿Por qué? ¿Qué te dice el perfilado de estos métodos? ¿Cómo se identifican las secciones de código lento en el probador?

No he analizado las razones de los frenos de SB, pero no creo que sea OOP, porque MT4Orders también está escrito mediante OOP, aunque sea mínimamente. Tras una petición al BOD, los desarrolladores aceleraron la SB. No sé si han retocado la propia SB o el compilador.

Analizo primero los frenos absolutos y luego los relativos

MetaTrader 5 tiene una excelente función de perfilado de un Asesor Experto sobre datos históricos. Pero, además de que funciona lentamente (en modo visual), el resultado final se proporciona en unidades relativas, es decir, es imposible comparar el rendimiento en términos absolutos.

 
Vasiliy Sokolov:

La razón de la principal ralentización de MT5 es la imitación del entorno completo de trading. En la gran mayoría de los Asesores Expertos, una simulación tan precisa no es necesaria. Una excelente solución para este tipo de Asesores Expertos sería cambiar a un modo de prueba simplificado, con un simple modelado del entorno de negociación. Sin embargo, no existe tal posibilidad. Pero si escribe su propio probador de estrategias simplificado y lo ejecuta en el modo de cálculos matemáticos, ¡la velocidad de optimización será de fuego! Esto es lo que le sugiero que piense para todos aquellos que quieren aumentar drásticamente la velocidad de optimización de EA.

A través de símbolos personalizados puede lograr órdenes de magnitud speedup con simulación completa. En realidad, incluso ahora en el modo beta de símbolos personalizados es posible lograr esto. Lo haré más 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 resultó ser más lento que la librería escrita en el mismo MQL5! Empecé a investigar y encontré la razón haciendo esta sustitución

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

La variante MQL5 del Asesor Experto comenzó a mostrar tal rendimiento.

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


¡Estúpida sustitución de PositionSelect con PositionGetTicket aumenta la velocidad de backtest en un 7%!

MT4Orders va por detrás del MQL5 puro optimizado al máximo por menos de un porcentaje.

 
fxsaber:

Las plazas se distribuyeron del siguiente modo

  1. Pure MQL5 - 100% de rendimiento.

  2. SB Trade\Trade.mqh - ~84% de rendimiento.
El SB se ha convertido en menos lag.

He retocado la SB en sí (piezas de la SB utilizado por el Asesor de Expertos) sin cambiar su funcionalidad y versatilidad. Se convirtió no 84%, pero 97%.

Aquellos que utilizan SB, tomar nota de la posibilidad potencial de aceleración.

 

Resulta interesante comparar el rendimiento del mismo Asesor Experto multidivisa de MT4 convertido de diferentes maneras.

Conversión completa

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


y conversión artesanal (rápida)

Foro sobre trading, sistemas automatizados de trading y testeo de estrategias de trading.

Asesores Expertos: Spreader

fxsaber, 2016.09.03 11:18 AM.

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

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

void OnTick()
{
  start();
}

Resultados de la medición del rendimiento.


Versión 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


El artesanal pierde significativamente. La razón es simple - si quieres series de tiempo rápido, no se puede convertir MT4 series de tiempo de cabeza

Foro sobre trading, sistemas automatizados de trading y testeo de estrategias de trading.

Bibliotecas: CPrice

fxsaber, 2016.08.28 10:36 AM

Mira una opción de este tipo

#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)

Puedes, como en MT4, llamar a Open[bar], High[bar], Time[bar], Volume[bar], etc. Y también iHigh(...), iClose(...) y otros.

Es necesario utilizar (no sólo para el monte, sino también para las variantes de pleno derecho) soluciones como esta.

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

El artesanal pierde significativamente. La razón es simple - si quieres timeseries rápido, no se puede convertir MT4 timeseries de frente.

Conclusión errónea. La razón de los frenos es bastante diferente. Insertemos sólo una línea en cada una de las variantes de MT5

#define Comment(A)

Y medir de nuevo

  • De pleno derecho

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


El full-fledged es un 67% más rápido, ¡el artisanal es un 108% más rápido! En resumen, la diferencia es ahora pequeña - la full-fledged es ~11% más rápida que la artisanal. Ahí, parece, la razón está en la sobrecarga OOP.


Pero eso no es lo principal. ¡Con una línea hemos conseguido acelerar los EAs! Y esto es en el Optimizador, donde Comment no juega ningún papel.

 

Esto fue escrito en el verano junto con la aplicación SD. ¡Me sorprende que todo sea parejo! ¿O es lógico? ¡Como se puede hablar de super-velocidad del tester cuando casi todos los EAs (usando SB) fallan en velocidad, comen dinero en la Nube, etc.! Aparentemente esto es normal.


Simplifiqué el código fuente desechando el trabajo con History y MT4Orders. Dejé sólo MQL5 puro y SB

#include <TesterBenchmark.mqh>

#include <Trade\Trade.mqh> // Comentar para habilitar la variante en MQL5 puro

input int Interval = 60;

void OnTick()
{
#ifdef   ERR_USER_INVALID_HANDLE // comercio.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 de 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 de MQL5 puro

------
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

¡Todo lo mismo 1,5 veces! Pero a los tontos no les importa, sigamos usando super-SB.

 
fxsaber:

¡Igual 1,5 veces! Pero a los tontos no les importa, sigamos usando el super-SB.

Este es el tipo de ajustes SB

//+------------------------------------------------------------------+
//| Enviar orden|
//+------------------------------------------------------------------+
bool CTrade::OrderSend(const MqlTradeRequest &request,MqlTradeResult &result)
  {
   static const bool IsTester = (::MQLInfoInteger(MQL_TESTER) || ::MQLInfoInteger(MQL_OPTIMIZATION));
   
   bool res;
   string action="";
   string fmt   ="";
//--- acción
   if(m_async_mode)
      res=::OrderSendAsync(request,result);
   else
      res=::OrderSend(request,result);
//--- comprobar
   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));
     }
//--- devuelve el resultado
   return(res);
  }
//+------------------------------------------------------------------+
//| Seleccione una posición en el í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 a los asesores SB. ¿Es complicado?

Francamente hablando, la SB de trading, sin cambiar la lógica de la API, debería reescribirse casi por completo. Cualquier parte de ella es mala.