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

 
hini #:
Exness-MT5Trial3, ECN-счет, с тестовым периодом с 1 января 2025 года по 5 февраля 2025 года.

Воспроизвел.


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

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

  1. Отключил действия в реальном окружении.
  2. Избавился от string.
  3. Отчет заменил на QuickReport.
MT4Orders QuickReport
MT4Orders QuickReport
  • www.mql5.com
Быстрая JavaScript версия библиотеки Report от fxsaber для торговых команд в стиле MT4 реализованных через MT4Orders или Virtual. Работает до 10 раз быстрее, размер НТМL файлов меньше, может выгрузить и отобразить до 5.4 млн. строк отчета.
Файлы:
virTestEA.mq5  19 kb
 
fxsaber #:

Воспроизвел.

Для Report.mqh воспроизвести, как здесь, не смог.
 
fxsaber #:
Для Report.mqh воспроизвести, как здесь, не смог.

Я ошибся с датой, это должно быть 15 января. 

  void SetSymbol( string sSymb = NULL )
  {
    if (sSymb == NULL)
      sSymb = _Symbol;

    this.SymbolID = 0;
    this.Symbol = sSymb;

    this.Point = ::SymbolInfoDouble(sSymb, SYMBOL_POINT);
    this.Digits = (int)::SymbolInfoInteger(sSymb, SYMBOL_DIGITS);

    if (false)
      // Режим прибыли в валюте.
      this.TickValue = ::SymbolInfoDouble(sSymb, SYMBOL_TRADE_TICK_VALUE) /
                       ::SymbolInfoDouble(sSymb, SYMBOL_TRADE_TICK_SIZE);
    else
      // Режим прибыли в пипсах.
      this.TickValue = 1 / this.Point;

    if (StringFind(sSymb, "XAUUSD") >= 0 && this.Digits == 3) {
      this.TickValue /= 10;
    }      
/*
    this.TickSize = ::SymbolInfoDouble(sSymb, SYMBOL_TRADE_TICK_SIZE);;

    this.SwapLong = ::SymbolInfoDouble(sSymb, SYMBOL_SWAP_LONG);
    this.SwapShort = ::SymbolInfoDouble(sSymb, SYMBOL_SWAP_SHORT);

    this.CommissionKoef = 0;
*/
    return;
  }


 
hini #:

Если такую функцию в дебаге вызывать в OnTick, то получите срабатывание условия.

double DiffBalance()
{  
  static double PrevBalance = 0;
  static int Pos = 0;
  
  const int Total = OrdersHistoryTotal();
  
  while (Pos < Total)
    if (OrderSelect(Pos++, SELECT_BY_POS, MODE_HISTORY))
      PrevBalance += OrderProfit() + OrderCommission() + OrderSwap();

  const double Res = NormalizeDouble(AccountBalance() - PrevBalance, 1);

  if (Res)
  {
    DebugBreak();
//    REPORT::ToFile();
  }
      
  return(Res);
}


Причина в накапливаемой ошибке, т.к. баланс/эквити учитывают профит закрытых ордеров без нормализации.

      if (this.Orders[i].Ticket <= 0)
      {
      #ifdef ORDER_COMMISSION
        this.Balance += this.Orders[i].GetProfit() + this.Orders[i].GetCommission();

Но при помещении в историю торговли происходит нормализация профита.

  void ToHistory( void )
  {
    _VC
    if (this.IsClosed())
    {
      this.Ticket = -this.Ticket;

      // Чтобы было совпадение с реальным окружением. Важно для BestInterval.
      this.Profit = ::NormalizeDouble(this.Profit, SYMBOL_BASE::DigitsCurrency);
      this.Commission = ::NormalizeDouble(this.Commission, SYMBOL_BASE::DigitsCurrency);
      this.Swap = ::NormalizeDouble(this.Swap, SYMBOL_BASE::DigitsCurrency);
    }

    return;
  }

Вы можете добавить нормализацию при расчете баланса или отказаться от нормализации при добавлении в историю.


При такой правке изначально приведенная функция перестанет срабатывать. Однако, я не вижу однозначно правильного решения, и оставил бы так, как есть. Просто с пониманием, что такое может происходить. Вводить еще один #define для такой опции не хотел бы.

 
fxsaber #:

Если такую функцию в дебаге вызывать в OnTick, то получите срабатывание условия.


Причина в накапливаемой ошибке, т.к. баланс/эквити учитывают профит закрытых ордеров без нормализации.


Но при помещении в историю торговли происходит нормализация профита.


Вы можете добавить нормализацию при расчете баланса или отказаться от нормализации при добавлении в историю.


При такой правке изначально приведенная функция перестанет срабатывать. Однако, я не вижу однозначно правильного решения, и оставил бы так, как есть. Просто с пониманием, что такое может происходить. Вводить еще один #define для такой опции не хотел бы.

Почему тогда количество сделок отличается?
 
hini #:
Почему тогда количество сделок отличается?

Это текущая ошибка QuickReport. В Report.mqh этой ошибки нет.

 
fxsaber #:

Это текущая ошибка QuickReport. В Report.mqh этой ошибки нет.

ok

 

Скажите, пожалуйста, как вы восстанавливаете данные о виртуальных позициях из последнего удаленного ЭА? В настоящее время я использую CopyTicksRange в OnInit для копирования данных Tick, начиная с времени первой открытой позиции перед удалением ЭА и заканчивая текущим временем, затем циклически запускаю стратегию по этим Ticks. Существует ли лучший способ?

    long from = (long)firstOpenTime * 1000;
    long to = TimeCurrent() * 1000;
    MqlTick Ticks[];
    const int Amount = ::CopyTicksRange(_Symbol, Ticks, COPY_TICKS_ALL, from, to - 1);
    int err = _LastError;
    VIRTUAL::SelectByHandle(tfTrade2.m_virtualHandle);
    for(int i = 0; i < Amount; ++i) {
      VIRTUAL::NewTick_NoCheck(Ticks[i]);
      tfTrade2.Execute();
    }
 
hini #:

Скажите, пожалуйста, как вы восстанавливаете данные о виртуальных позициях из последнего удаленного ЭА? В настоящее время я использую CopyTicksRange в OnInit для копирования данных Tick, начиная с времени первой открытой позиции перед удалением ЭА и заканчивая текущим временем, затем циклически запускаю стратегию по этим Ticks. Существует ли лучший способ?

Если сохранять виртуальное окружение, то теряется весь смысл самовосстановления EA.

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


Использую более лаконичную запись.

void System() { tfTrade2.Execute(); }

void OnInit()
{
  long from = (long)firstOpenTime * 1000;
    MqlTick Ticks[];
    const int Amount = ::CopyTicksRange(_Symbol, Ticks, COPY_TICKS_ALL, from);

    {
      _VS(tfTrade2.m_virtualHandle);

      VIRTUAL::NewTick(Ticks, System);
    }
}


Очень важно, что всегда в боевом терминале (не в Тестере) VIRTUAL::NewTick надо вызывать для новых тиков, полученных только из CopyTicks*, а не из SystemInfoTick. Таким образом не будут теряться тики.

 
fxsaber #:

Если сохранять виртуальное окружение, то теряется весь смысл самовосстановления EA.

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


Использую более лаконичную запись.


Очень важно, что всегда в боевом терминале (не в Тестере) VIRTUAL::NewTick надо вызывать для новых тиков, полученных только из CopyTicks*, а не из SystemInfoTick. Таким образом не будут теряться тики.

У меня есть еще один вопрос. Если начальная дата указывается из входных параметров, то по мере прохождения времени, когда торговая система работает все дольше и дольше, она всегда будет использовать эту начальную дату? Например, если торговая система начала работать с 01.01.2025, и начальная дата также установлена на эту дату, то предположим, что на 01.03.2026 нужно перезагрузить EA. Будет ли начальной датой по-прежнему 01.01.2025? Как обновить эту дату? Начать новый период, когда все виртуальные/реальные торговые системы закроют свои позиции?