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

 
Forester #:

Аналогичная проблема с MQ тестером при тесте по USDCHF и валюте аккаунта EUR.

Скорее всего, проблема в отсутствии в Обзоре рынка EURUSD/EURCHF. У меня пашет.

 
fxsaber #:

Скорее всего, проблема в отсутствии в Обзоре рынка EURUSD/EURCHF. У меня пашет.

Все 3 символа есть


Кажется вычислил где проблема.
Если в OnInit добавить проверку символа, то советник перестает торговать, как описал выше. Не работает именно на MQ тестере, виртуальный работает. Подключение Virtual даже удалил из кода:

 #include <MT4Orders.mqh>

input int inAmount = 10;
input int inRange = 0;

bool OrdersBuy[];
bool OrdersSell[];

void OnInit()
{
  ArrayResize(OrdersBuy, inAmount + 1);
  ArrayResize(OrdersSell, inAmount + 1);
  bool is_custom;  SymbolExist("USDCHF", is_custom);
}

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);
    }  
  }
}

Символ EURUSD, валюта депозита CHF. а проверяем SymbolExist("USDCHF", is_custom);

Проверьте у себя пожалуйста.

 
Forester #:

Символ EURUSD, валюта депозита CHF. а проверяем SymbolExist("USDCHF", is_custom);

Подтверждаю! Это баг тестера. Попробуйте сделать лаконичный код воспроизведения.

 

В Virtual используется аналогичный код https://www.mql5.com/ru/forum/170952/page208#comment_24667438

Я использовал у себя этот код для вычисления длительности удержания позиции и налетел на следующую неприятность.

Гоню в тестере тест советника за прошлую неделю, ставлю диапазон 27.11.2023-04.12.2023 (ПН прошлой недели-ПН следующей недели). И получаю разные результаты в зависимости от того, гоню я это в сб (02.12.2023), вс  (03.12.2023) или в пн (04.12.2023). Дело в том, что тестер принудительно закрывает в конце теста все открытые позы. Поскольку дата окончания больше текущей даты, он закроет принудительно за 15 секунд до начала следующих суток либо в пт, либо в сб, либо в вс. И указанный выше код выдаст разные результаты.

Для себя заменил определение выходных не как переход с сб на вс и с вс на пн, а как переход с пт на сб и с сб на вс, так:

int GetAmountWorkingDays( const datetime Begin, const datetime End )
{
  const int Res = (int)(End / DAY - Begin / DAY);
  
  return(Res ? Res - GetAmountWeekDay(Begin, End, FRIDAY) - GetAmountWeekDay(Begin, End, SATURDAY) : 0);
}

Так показывает одинаковый результат.

Понятно, что случай граничный, и тут скорее всего не встретится (не проверял, как виртуал закрывает принудильно в конце), но особенность, о которой решил сообщить.

Особенности языка mql5, тонкости и приёмы работы - Когда попал в историю - появился ORDER DONE.
Особенности языка mql5, тонкости и приёмы работы - Когда попал в историю - появился ORDER DONE.
  • 2021.08.12
  • www.mql5.com
Если сделать частичное закрытие позиции через CloseBy. то оставшаяся часть открытой позиции лишается свопа - обнуляется. При частичном закрытии OrderClose не на полный OrderLots своп дербанится соответствующим образом
 
traveller00 #:

В Virtual используется аналогичный код https://www.mql5.com/ru/forum/170952/page208#comment_24667438

Я использовал у себя этот код для вычисления длительности удержания позиции

Хорошо бы код привести.

Гоню в тестере тест советника за прошлую неделю, ставлю диапазон 27.11.2023-04.12.2023 (ПН прошлой недели-ПН следующей недели). И получаю разные результаты в зависимости от того, гоню я это в сб (02.12.2023), вс  (03.12.2023) или в пн (04.12.2023). Дело в том, что тестер принудительно закрывает в конце теста все открытые позы. Поскольку дата окончания больше текущей даты, он закроет принудительно за 15 секунд до начала следующих суток либо в пт, либо в сб, либо в вс. И указанный выше код выдаст разные результаты.

К сожалению, это текущая особенность Тестера.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Тестер стратегий MetaTrader 5: ошибки, баги, предложения по улучшению работы

fxsaber, 2023.11.06 14:56

b4040, при включенном таймере принудительная сделка закрытия бэктеста приходится на внерыночное время.

На скрине закрытие в конце субботы.

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

#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

const bool Init = EventSetTimer(1) && OrderSend(_Symbol, OP_BUY, 0.1, Ask, 0, 0, 0);

void OnTimer() {}
Строка для поиска: Uluchshenie 077.

Для себя заменил определение выходных не как переход с сб на вс и с вс на пн, а как переход с пт на сб и с сб на вс, так:

Так показывает одинаковый результат.

Поэтому и написал про код выше, т.к. результат такого изменения не годится для расчетов свопов.

Print(GetAmountWorkingDays(D'27.11.2023', D'02.12.2023')); // 4


Понятно, что случай граничный, и тут скорее всего не встретится (не проверял, как виртуал закрывает принудильно в конце), но особенность, о которой решил сообщить.

Виртуал не закрывает принудительно. Может это сделать, если его попросить. В этом случае закроет по времени последнего тика. Таймера в виртуал нет.

 
fxsaber #:

Хорошо бы код привести.

У себя делаю так

int CountWeekDay(datetime Begin,datetime End,int DayWeek)
{
  datetime OffsetTime=(DayWeek-WEDNESDAY)*24*60*60;
  return (int)((End-OffsetTime)/7/24/60/60-(Begin-OffsetTime)/7/24/60/60);
}

datetime GetDealHoldTime(datetime Begin,datetime End)
{
  return End-Begin-(CountWeekDay(Begin,End,FRIDAY)+CountWeekDay(Begin,End,SATURDAY))*24*60*60;
}

Вызывается так

datetime HoldTime=GetDealHoldTime(OrderOpenTime(),OrderCloseTime());

Делал для себя, чтобы перенос через выходные не портил статистику длительности удержания.


fxsaber #:

Поэтому и написал про код выше, т.к. результат такого изменения не годится для расчетов свопов.

У нас немного разные задачи, в Virtual я не вижу необходимости что-то править. Скорее хочу обратить внимание, что код может потребовать правки под другие задачи из-за особенностей тестера.

 
Forester #:

Есть предложение по ускорению. Orders.mqh IsChange() строка 80. Если у вас например 90 ордеров, и закрылся один из них, например 5-й, то потом код все ордера выше сместит их копированием 6->5, 7->6 ... 90->89. Работает, но долго.
Можно просто переместить последний на место удаленного 90->5. Вместо 85 копирований, всего 1 (может и несколько, если они все на 1 тике закрылись).

Пол секунды разница, ускорение около 6%.

П.С.
Хотя возможно не стоит это применять. Любители сравнивать распечатки (вроде меня) тестера MQ и виртуального будут озадачены не полным совпадением.

Вернулся к этой функции https://www.mql5.com/ru/forum/282062/page47#comment_50742667

Замерил ей время с новым шагом изменения размера массива истории (с ним всё значительно ускорилось и надо пересчитывать).

По 100 тестов в оптимизации
Со старой IsChange ()
shortest pass 0:00:00.188, longest pass 0:00:00.365, average pass 0:00:00.190 USDCHF
shortest pass 0:00:00.191, longest pass 0:00:00.373, average pass 0:00:00.194 EURUSD
С новой IsChange ()
shortest pass 0:00:00.171, longest pass 0:00:00.347, average pass 0:00:00.174 USDCHF
shortest pass 0:00:00.174, longest pass 0:00:00.337, average pass 0:00:00.176 EURUSD

Среднее ускорение на 15мс или 9% и на 18мс или 10%. Т.е. с новым шагом массива истории, % ускорения от новой IsChangе() стал более значительным.

Ну и немного еще статистики по добавленным мной функциям (с новой IsChange (), т.е. изменения считаю от нее) :
Расчет комиссии в % от USD c переводом в валюту аккаунта
shortest pass 0:00:00.245, longest pass 0:00:00.422, average pass 0:00:00.248 - замедление на 41%

перевод профита в валюту аккаунта
shortest pass 0:00:00.248, longest pass 0:00:00.424, average pass 0:00:00.250 - замедление на 42%

расчет свопов при переносе в историю (быстрый код ниже)
shortest pass 0:00:00.180, longest pass 0:00:00.357, average pass 0:00:00.182 - замедление на 3%

контроль торговой сессии
shortest pass 0:00:00.186, longest pass 0:00:00.363, average pass 0:00:00.189 - замедление на 7%

Всё вместе:
shortest pass 0:00:00.278, longest pass 0:00:00.457, average pass 0:00:00.281 - замедление на 60%

Т.е. это минимальное замедление, если нужно будет делать мультивалютный тестер. + др. изменения в коде могут добавить.

Без мультивалютного тестера эти все расчеты конечно не нужны. Достаточно профита в пипсах. Теперь вот думаю, зачем время на это потратил? Видимо чтобы убедиться в точном соответствии с тестером MQ. Соответствие точное. Наверное свою версию в архив, а пользоваться буду вашей с моей IsChange(). Если вы её и свопы при переносе в историю к себе добавили, то может выложите новый код со всеми обновлениями?

По свопам. Для ускорения
SwapShort, SwapLong, RolloverTime, Rollover3Days, вычисляю не при каждом вызове, а 1 раз при создании вирт. тестера в ORDERS

//#define ORDER_SWAP 0 // calc swap with rollower time  = seconds from 0:00. E.g. 3:00 = 3*60*60
datetime RolloverTime_;  double SwapShort_, SwapLong_;  int Rollover3Days_;//для свопов
   
В   ORDERS( const int iHandle, const datetime StartTime = 0 )
.....
    //свопы
    #ifdef ORDER_SWAP
      this.RolloverTime_=((datetime)ORDER_SWAP % 86400);//86400 sec in day
      this.SwapShort_=::SymbolInfoDouble(_Symbol, SYMBOL_SWAP_SHORT);
      this.SwapLong_=::SymbolInfoDouble(_Symbol, SYMBOL_SWAP_LONG);
      this.Rollover3Days_ = (int)::SymbolInfoInteger(_Symbol, SYMBOL_SWAP_ROLLOVER3DAYS);
    #endif
    //--свопы

А новая IsChange() с быстрым расчетом свопов и быстрым переносом в историю:

    bool IsChange( void )
  {
    bool Res = false;
    double Profit = 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()
             this.Orders[i].SetSwap(this.SwapShort_, this.SwapLong_, this.RolloverTime_, this.Rollover3Days_);
             this.Balance += this.Orders[i].GetSwap();
          }
        #endif
        this.AddHistoryOrder(this.Orders[i]);
        
        this.Orders[i--] = this.Orders[--this.AmountOrders];
        //this.AmountOrders--;i--;
      }
      else
      {
      #ifdef ORDER_COMMISSION
        Profit += this.Orders[i].GetFullProfit();
      #else // ORDER_COMMISSION
        Profit += this.Orders[i].GetProfit();
      #endif // ORDER_COMMISSION
      }
    }

    this.Equity = this.Balance + Profit;
    this.FlagChange |= Res;
    return(Res);
  }
Библиотеки: Virtual - Проверьте, почему в вашем тестере не округляются комиссии на каждую сделку. Попробуйте выставить OrderSend BuyLimit Bid.
Библиотеки: Virtual - Проверьте, почему в вашем тестере не округляются комиссии на каждую сделку. Попробуйте выставить OrderSend BuyLimit Bid.
  • 2023.11.23
  • www.mql5.com
а как в реальности на реальном счете - надо кому-то проверить. POSITION OPEN может быть ненормализованным числом при открытии позиции несколькими сделками. В реальности комиссия может округляться в обе стороны
 
Forester #:

Наверное свою версию в архив, а пользоваться буду вашей с моей IsChange(). Если вы её и свопы при переносе в историю к себе добавили, то может выложите новый код со всеми обновлениями?

Не делал пока никаких правок. Надо радикально править и затем сравнивать. Не до этого пока.

 
fxsaber #:

Не делал пока никаких правок. Надо радикально править и затем сравнивать. Не до этого пока.

Еще 5-16% ускорения нашел:
На каждом тике после проверок СП/СЛ/лимиток, если никаких изменений нет в сделке, то ей рассчитывается GetCalcProfit(); и GetCalcClosePrice(Tick);. Видимо для расчета текущей эквити.
В итоговых данных в Report() у вас не выводится просадка по эквити, видимо она и не рассчитывается. Удалил GetCalcProfi() получил ускорение на 5%. Эквити может быть нужна кому-то для ее контроля на лету, кто-то может ее в процессе теста запрашивать, можно ему это подключать  по  #ifdef EQUITY_CONTROL например. Большинству эквити не нужна, если ее и так нет в отчете.
Отключение GetCalcClosePrice(Tick) ещё 11% дала, не  уверен, но кажется она совсем не нужна в этом месте (где изменений в сделках нет, только лишние вызовы if (!this.IsClosed()), если без изменений, то и закрытий не было):
bool IsChange( const MqlTick &Tick )
      if (Res)
      {... }
      else
      {
      //  this.ClosePrice = this.GetCalcClosePrice(Tick);
      //  this.Profit = this.GetCalcProfit();
      }


как сейчас с моей IsChange().
shortest pass 0:00:00.177, longest pass 0:00:00.189, average pass 0:00:00.178

без контроля профита this.Profit = this.GetCalcProfit();
shortest pass 0:00:00.166, longest pass 0:00:00.344, average pass 0:00:00.169 - ускорение 9мс или 5%

и без this.GetCalcClosePrice(Tick);
shortest pass 0:00:00.150, longest pass 0:00:00.330, average pass 0:00:00.153 - ускорение 25мс или 16%

Отчеты абсолютно одинаковые.

Итого уже есть 10% ускорения в IsChange() и тут до 16%. Итого 26% - может и нет смысла перелопачивать ядро? И тратить на это несколько дней... И так мега быстро) Большая часть времени в проверках ТП/СЛ/лимиток
 
Forester #:
может и нет смысла перелопачивать ядро? И тратить на это несколько дней... И так мега быстро)

Для большого количества живых ордеров - имеет смысл.

Касаемо ускорения OrderProfit, OrderClosePrice, AccountEquity - тоже было запланировано в радикальном варианте.