Библиотеки: Virtual - страница 46

 
fxsaber #:
Не помню свои коды. Во время просмотра придет решение.

Во время просмотра увидел, что можно ускорить тестирование. Оставляю на будущее советник для замера производительности.

// #include <MT4Orders.mqh>

#define VIRTUAL_TESTER // Запуск в виртуальном торговом окружении
#define VIRTUAL_LIMITS_TP_SLIPPAGE // Лимитники и TP исполняются по первой цене акцепта - положительные проскальзывания
#define VIRTUAL_CLOSEALL_BYEND // Закрывает принудительно все ордера в конце тестирования
#include <fxsaber\Virtual\Virtual.mqh>
/*
#define REPORT_TESTER             // В тестере будут автоматически записываться отчеты
#define REPORT_BROWSER            // Создание отчета с запуском браузера - требует разрешения DLL.
#include <Report.mqh>
*/
input int inAmount = 10;
input int inRange = 0;

bool OrdersBuy[];
bool OrdersSell[];

void OnInit()
{
  ArrayResize(OrdersBuy, inAmount + 1);
  ArrayResize(OrdersSell, inAmount + 1);
}

void OnTick()
{
  ArrayInitialize(OrdersBuy, false);
  ArrayInitialize(OrdersSell, false);
  
  MqlTick Tick;
  
  if (SymbolInfoTick(_Symbol, Tick))
  {
    for (uint i = OrdersTotal(); (bool)i--;)
      if (OrderSelect(i, SELECT_BY_POS))
      {
        const ulong Magic = OrderMagicNumber();                
        
        switch (OrderType())
        {
          case OP_BUY:
            OrderModify(OrderTicket(), OrderOpenPrice(), 0, Tick.bid + Magic * _Point, 0);
            OrdersBuy[Magic] = true;
            
            break;
          case OP_SELL:
            OrderModify(OrderTicket(), OrderOpenPrice(), 0, Tick.ask - Magic * _Point, 0);
            OrdersSell[Magic] = true;
            
            break;
          case OP_BUYLIMIT:
            OrderModify(OrderTicket(), Tick.ask - Magic * _Point, 0, 0, 0);
            OrdersBuy[Magic] = true;
            
            break;
          case OP_SELLLIMIT:          
            OrderModify(OrderTicket(), Tick.bid + Magic * _Point, 0, 0, 0);
            OrdersSell[Magic] = true;
            
            break;
        }
      }
    
    for (int i = 1; i <= inAmount; i++)
    {
      if (!OrdersBuy[i])
        OrderSend(_Symbol, OP_BUYLIMIT, 1, Tick.ask - i * _Point, 0, 0, 0, NULL, i);

      if (!OrdersSell[i])
        OrderSend(_Symbol, OP_SELLLIMIT, 1, Tick.bid + i * _Point, 0, 0, 0, NULL, i);
    }  
  }
}

Этот советник убьет штатный тестер, на виртуалке прогоняет быстро. Еще ускорить - пока не до этого.

 
Forester #:

А в Orders.mqh сразу после вызова AddHistoryOrder корректирую баланс на своп

Логичнее до вызова этой функции.
  bool IsChange( void )
  {
    bool Res = false;
    double Profit = 0;
    int j = 0;

    for (int i = 0; i < this.AmountOrders; i++)
    {
      Res |= this.Orders[i].IsChange(this.CurrentTick) && !this.Orders[i].IsClosed();

      if (this.Orders[i].IsClosed())
      {
      #ifdef ORDER_COMMISSION
        this.Balance += this.Orders[i].GetProfit() + this.Orders[i].GetCommission();
      #else // ORDER_COMMISSION
        this.Balance += this.Orders[i].GetProfit();
      #endif // ORDER_COMMISSION

        // Здесь.

        this.AddHistoryOrder(this.Orders[i]);
      }
 
fxsaber #:
Логичнее до вызова этой функции.
Если до, то он возьмет своп предыдущего перенесенного в историю ордера. А переносимый ордер посчитает своп во время выпонения AddHistoryOrder()

Если ваш вариант, то своп надо считать не для ордеров из истории, а для активных. Видимо надо делать аналог функции SetSwap()
double Swap = this.HistoryOrders[this.AmountHistoryOrders-1].SetSwap(SwapShort, SwapLong, RolloverTime, Rollover3Days);
Заодно будет возможность считать свопы в onTick в начале нового дня, если кому-то надо будет.
 
Forester #:
Если до, то он возьмет своп предыдущего перенесенного в историю ордера. А переносимый ордер посчитает своп во время выпонения AddHistoryOrder()

Исходил из оригинального исходника. В AddHistoryOrder нет ничего про свопы и не должно быть.

Пометил единственное место, где стал бы делать расчет свопа.

 
fxsaber #:

Исходил из оригинального исходника. В AddHistoryOrder нет ничего про свопы и не должно быть.

Пометил единственное место, где стал бы делать расчет свопа.

Удалил расчет в  AddHistoryOrder и поместил его в указанное вами место. Сравнил результаты, всё точно совпадает с реальным тестером.

  bool IsChange( void )
  {
    bool Res = false;
    double Profit = 0;
    int j = 0;

    for (int i = 0; i < this.AmountOrders; i++)
    {
      Res |= this.Orders[i].IsChange(this.CurrentTick) && !this.Orders[i].IsClosed();

      if (this.Orders[i].IsClosed())
      {
        #ifdef ORDER_COMMISSION
           this.Balance += this.Orders[i].GetProfit() + this.Orders[i].GetCommission();
        #else // ORDER_COMMISSION
           this.Balance += this.Orders[i].GetProfit();
        #endif // ORDER_COMMISSION

        #ifdef ORDER_SWAP
          if(this.Orders[i].IsNotNull()){//проверка как в AddHistoryOrder()
             datetime RolloverTime=((datetime)ORDER_SWAP % 86400);//86400 sec in day
             double SwapShort=::SymbolInfoDouble(this.Orders[i].GetSymbol(), SYMBOL_SWAP_SHORT), SwapLong=::SymbolInfoDouble(this.Orders[i].GetSymbol(), SYMBOL_SWAP_LONG);
             int Rollover3Days = (int)::SymbolInfoInteger(this.Orders[i].GetSymbol(), SYMBOL_SWAP_ROLLOVER3DAYS);
             this.Orders[i].SetSwap(SwapShort, SwapLong, RolloverTime, Rollover3Days);
             this.Balance += this.Orders[i].GetSwap();
          }
        #endif

        this.AddHistoryOrder(this.Orders[i]);

      }

Но что делать с другими местами, где вызывается AddHistoryOrder и происходит перенос в историю (еще в снепшотах)? Тоже вставлять этот код?
Например тут:

  bool AddOrder( const ORDER_BASE &Order )
  {
    if (Order.GetCloseTime())
    {
      this.AddHistoryOrder(Order);
      this.Balance += Order.GetProfit();
      this.Equity += Order.GetProfit();
    }

Кстати тут и комиссия к балансу не прибавляется. Наверное лучше везде использовать GetFullProfit(), чтобы не путаться.

 
fxsaber #:

Виртуал рассчитан на максимальную производительность, поэтому стараюсь избегать любых доп. проверок, что вызываются миллиардами. Свопы нужны очень редко. Более того, штатный тестер их считает неправильно, т.к. у него нет истории изменений свопов.

Сегодня с этим столкнулся. 2 дня назад итоговый результат был один, а сегодня другой. Свопы к сделкам перестали совпадать и как результат общий итог. Но главное, что 2 дня назад и сегодня виртуальный результат совпадает с тестером MQ.
Слева - 2 дня назад, справа сегодня.


+$5 к итогу за счет улучшенных свопов.
 
Forester #:

2 дня назад и сегодня виртуальный результат совпадает с тестером MQ.

Они оба берут данные по свопам из текущей спецификации символа. Посмотрите код выше с закомментированным Report. Так удобнее сравнивать.


Сам тестирую в режиме по пипсам. Там свопов нет. И быстрее пашет.

 
Forester #:

Но что делать с другими местами, где вызывается AddHistoryOrder и происходит перенос в историю (еще в снепшотах)? Тоже вставлять этот код?

Ничего не делать. Это же не относится к VIRTUAL::NewTick.

Вы можете объединять виртуалы, искусственно создавать любую историю и т.д. Для этого и нужен AddHistory в других местах.

 
Forester #:

Кстати тут и комиссия к балансу не прибавляется. Наверное лучше везде использовать GetFullProfit(), чтобы не путаться.

Похоже, Вы правы.

 

По #define VIRTUAL_CLOSEALL_BYEND

Он отрабатывает после моего OnDeinit() и OnTester() и в тестере  не получается получить эти финальные сделки.
Делаю распечатку так:

void OnDeinit(const int reason){ // или в OnTester() 
   for (int v = 0 ; v <= VIRTUAL::Total(); v++){
      if (VIRTUAL::SelectByIndex(v)){
         //VIRTUAL::NewTick(); VIRTUAL::Stop(); //последний тик для стопов// закрыть незавершенные сделки, но мне как раз они не нужны 
         for (int i = 0; i < OrdersHistoryTotal(); i++){
            if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) ){
              Print(i," ",OrderOpenTime()," ",OrderCloseTime()," ",OrderType()," ",DoubleToString(OrderOpenPrice(),5)," ",DoubleToString(OrderClosePrice(),5)," ",DoubleToString(OrderProfit(),2)," ",(int)round(OrderProfit()/OrderLots())," ",OrderCommission()," ",OrderSwap());//," ",OrderComment() ," "OrderOpenTimeMsc()," ",OrderCloseTimeMsc()
            }
         }
         Print("AccountBalance = ",AccountBalance(), "   AccountEquity = ",AccountEquity());
      }
   }

В распечатках закрытые сделки отсутствуют (т.к. ваш VIRTUAL_CLOSEALL_BYEND еще не отработал). И баланс с эквити не совпадают
AccountBalance = 100239.2   AccountEquity = 100145.71
т.к. сделки не закрыты еще.

Если убрать #define VIRTUAL_CLOSEALL_BYEND и раскомментировать в коде выше

//VIRTUAL::NewTick(); VIRTUAL::Stop();
то закрытые сделки попадают в распечатку. И баланс с эквити совпадут.
AccountBalance = 100143.69   AccountEquity = 100143.69
Видимо #define VIRTUAL_CLOSEALL_BYEND не имеет смысла, т.к. в тестере о закрытых сделках не узнать. И нужно делать VIRTUAL::Stop(); самостоятельно.
А VIRTUAL::NewTick(); делаю для того, чтобы закрытие сделок было как в тестере - по цене последнего тика в 23:59, я прерываю вызов NewTick() в 23:55 т.к. рынок закрыт и цена там другая, поэтому и нужен этот последний тик.

Обновление:
Подумал, что мой OnTester не дает вашему выполняться. Закомментировал свой и добавил в ваш Print(). На печать ничего не вышло, похоже что ваш OnTester не выполняется. Завтра посмотрю подробнее.
Причина обращения: