程序库: TesterBenchmark - 页 4

 

MT5 运行速度慢的主要原因是模仿了完整的交易环境。在绝大多数智能交易系统中,没有必要进行如此精确的模拟。对于此类智能交易系统,一个极佳的解决方案是切换到简化测试模式,对交易环境进行简单模拟。不过,目前还没有这种可能性。但是,如果您自己编写简化策略测试器,并在数学计算 模式下运行,优化速度会非常快! 这就是我建议每个想大幅提高 EA 优化速度的人都应该考虑的问题。

 
Vasiliy Sokolov:

在 CStrategy 中,交易操作 直接通过 CTrade 执行。也就是说,CStrategy 完全没有自己的交易逻辑。在测试的智能交易系统中,除了在 N 秒后开仓/平仓外,我没有看到任何其他交易逻辑。CStrategy 也不存储历史仓位,因此很遗憾,这个例子无法在 CStrategy 中实现。

CTrade 也不存储历史仓位,但这并不妨碍我们通过它实现变体。

OOP 代码比程序代码慢这一事实并不能说明什么。让我们直奔主题:哪些 CTrade 方法是瓶颈?为什么?对这些方法的剖析能说明什么?如何在测试器中识别速度较慢的代码段?

我还没有分析 SB 刹车的原因,但我不认为这是 OOP 的问题,因为 MT4Orders 也是通过 OOP 编写的,甚至是最基本的 OOP。在向 BOD 提出要求后,开发人员加速了 SB。我不知道他们是调整了 SB 本身还是编译器。

我自己首先分析绝对刹车,然后分析相对刹车。

MetaTrader 5 具有根据历史数据分析智能交易系统的出色功能。但是,除了工作速度慢(在可视模式下)之外,最终结果是以相对单位提供的,即无法比较绝对性能。

 
Vasiliy Sokolov:

MT5 运行速度慢的主要原因是模仿了完整的交易环境。在绝大多数智能交易系统中,没有必要进行如此精确的模拟。对于此类智能交易系统,一个极佳的解决方案是切换到简化测试模式,对交易环境进行简单模拟。不过,目前还没有这种可能性。但是,如果您编写自己的简化策略测试器,并在数学计算 模式下运行,优化速度就会非常快! 这就是我建议所有想大幅提高 EA 优化速度的人考虑的问题。

通过自定义符号,你可以在完全模拟的情况下实现数量级的提速。事实上,即使是现在的自定义符号测试版模式也可以实现这一点。我稍后会做的。

 
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 结果比用相同的 MQL5 编写的库要慢!我开始调查,并通过以下替换找到了原因

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

Expert Advisor 的 MQL5 变体开始显示这种性能。

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


用 PositionGetTicket 取代PositionSelect,使回测速度提高了 7%!

MT4Orders 与经过最大优化的纯 MQL5 相比,落后不到一个百分点。

 
fxsaber:

名额分配如下

  1. Pure MQL5 - 100% 性能。

  2. SB Trade\Trade.mqh - ~84% 的性能。
SB 变得不那么滞后了。

我稍微调整了一下 SB 本身(Expert Advisor 使用的 SB 部件),但没有改变其功能和通用性。结果不是 84%,而是 97%。

使用 SB 的用户请注意加速的潜在可能性。

 

比较以不同方式转换的同一 MT4 多货币智能交易系统的性能变得非常有趣。

完全转换

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


和手工(快速)转换

交易、自动交易系统和交易策略测试论坛。

智能交易系统:价差器

fxsaber, 2016.09.03 11:18 AM.

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

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

void OnTick()
{
  start();
}

性能测量结果。


正式版

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


手工版

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


工匠版输得很惨。原因很简单--如果您想要快速的时间序列,就不能正面转换 MT4 时间序列。

交易、自动交易系统和交易策略测试论坛。

库:CPrice

fxsaber, 2016.08.28 10:36 AM

看看这样的选项

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

在 MT4 中,您可以调用 Open[bar]、High[bar]、Time[bar]、Volume[bar] 等。还可以调用 iHigh(...)、iClose(...) 等。

您需要使用(不仅在灌木丛中,而且在正式变体中)这样 解决方案。

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

而手工操作则损失惨重。原因很简单--如果想要快速的时间序列,就不能正面转换 MT4 时间序列。

结论错误。刹车的原因完全不同。让我们在每种 MT5 变体中只插入一条线

#define Comment(A)

并再次测量

  • 全能型

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

  • 手工版。

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


全功能版快 67%,手工版快 108%!总之,现在的差距很小--全功能版比手工版快 ~11%。看来,原因在于 OOP 的开销。


但这并不是主要原因。通过一行代码,我们成功地提高了 EA 的速度!而这是在优化器中,Comment 在其中不起任何作用。

 

这是在夏天与 SD 应用程序一起编写的。我很惊讶,一切都很平均!这符合逻辑吗?几乎所有的 EA(使用 SB)都在速度上失败,在云中吃钱,等等,怎么能说测试仪速度超快呢?显然,这很正常。


我简化了源代码,删除了历史记录和 MT4Orders 的工作。我只留下了纯粹的 MQL5 和 SB

#include <TesterBenchmark.mqh>

#include <Trade\Trade.mqh> // 在纯 MQL5 中注释以启用变量

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  // Er_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 // Er_user_invalid_handle
}


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


纯 MQL5 的结果

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

都是一样的 1.5 倍!但傻瓜 们不在乎,让我们继续使用超级 SB。

 
fxsaber:

同样是 1.5 倍!但笨蛋 们不在乎,让我们继续使用超级 SB。

这种 SB 调整

//+------------------------------------------------------------------+
//| 发送订单|
//+------------------------------------------------------------------+
bool CTrade::OrderSend(const MqlTradeRequest &request,MqlTradeResult &result)
  {
   static const bool IsTester = (::MQLInfoInteger(MQL_TESTER) || ::MQLInfoInteger(MQL_OPTIMIZATION));
   
   bool res;
   string action="";
   string fmt   ="";
//--- 行动
   if(m_async_mode)
      res=::OrderSendAsync(request,result);
   else
      res=::OrderSend(request,result);
//--- 检查
   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));
     }
//--- 返回结果
   return(res);
  }
//+------------------------------------------------------------------+
//| 在索引上选择一个位置|
//+------------------------------------------------------------------+
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);
  }

奇迹般地加快了 SB 顾问的速度。复杂吗?

老实说,在不改变 API 逻辑的情况下,交易 SB 几乎应该完全重写。它的任何部分都是糟糕的。