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

 
Igor Makanu:

что-то не так работает в режиме OHLC M1

Virtual много сделок пропустил, приатачил отчеты, тест 01.01.2020 - 31.12.2020

Вы совсем не понимаете, результаты каких действий сравниваете.

  1. OHLC M1 - это режим, в котором SL имеет приоритет исполнения выше, чем TP. Т.к. неизвестна последовательность High/Low.
  2. Вы записали вот такие тики

Что сделал.

  1. Создал кастомный символ на основе приложенных тиков.
  2. Прогнал в Тестере на этом символе Вашу ТС в режиме OHLC M1.
  3. Прогнал в Тестере на этом символе Вашу ТС в режиме по реальным тикам.
  4. Результаты п.2 и п.3 абсолютно разные! Замечу, это только MT5-Тестер.
  5. Прогнал в Тестере на этом символе Вашу ТС в режиме по реальным тикам и в виртуальном окружении.
  6. Результаты п.3 и п.5 идентичные.
  7. Прогнал в Тестере в мат. режиме через Виртуал.
  8. Результаты п.5 и п.7 немного отличаются.

По пунктам все должно быть понятно, кроме отличий в п.8. Дело в том, что если у символа история только с НГ, то при всем желании делать бэктест с начала года MT5-Тестер не даст. Тест начнет где-то через неделю после НГ. Связано это с тем, что он умничает: подготавливает несколько тысяч M1-баров в качестве баровой истории.

А вот когда запускаете в Виртуал по тикам из файла, то никто уже не умничает, поэтому тест начинается с НГ. С этим и связано небольшое различие в п.8.


Нельзя было сохранять тики в OHLC M1 режиме. Нужно было взять честно тики (либо скриптом в Терминале, либо в Тестере по реальным тикам), затем их проредить самостоятельно (оставив не более четырех на бар) и только после этого сохранять.


Виртуал отработал на 100% правильно. Однако, есть еще один нюанс. В мат. режиме Тестер может подставлять любые значения в переменные _Symbol, _Digits, _Point и другие свойства символа. Это только на совести MT5-Тестера.

 
fxsaber:

Нельзя было сохранять тики в OHLC M1 режиме. Нужно было взять честно тики (либо скриптом в Терминале, либо в Тестере по реальным тикам), затем их проредить самостоятельно (оставив не более четырех на бар) и только после этого сохранять.

кто ж знал... вернее спиной чуял - дело не ладно! )))

в общем, рабочая идея - нужно брать конкретное время работы ЕА и проредить тики с помощью  ThirdPartyTicks https://www.mql5.com/ru/code/20225 

это позволит сделать небольшой по размеру файл исторических данных, да и сжать его тоже не проблема - цена + спред, время можно не сохранять, а эмулировать счетчиком при распаковке 

но пока вижу, что ускорить тестер с помощью Virtual и манипуляций с историей можно очень и очень значительно


Спасибо!

 

или получилось или... где то жестко накосячил, очень смущают цифры

оптимизировал 20 000 проходов за период 01.01.2020 - 31.21.2020 такой ЕА:

//+------------------------------------------------------------------+
input int TP_SL = 500;
#include <MT4Orders.mqh>
// раскомментировать для Virtual
/*
#resource "\\Files\\EURUSD_tick.bin" as const MqlTick HistoryData[]
//#define REPORT_TESTER             // В тестере будут автоматически записываться отчеты
//#define REPORT_INTERACTIVE_CHARTS // Добавляет в отчет интерактивные графики.
//#define REPORT_BROWSER
#include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/ru/code/22577
//#include <fxsaber\Report.mqh>

//+------------------------------------------------------------------+
double OnTester()
{
   VIRTUAL::Tester(HistoryData, OnTick, 1000000, true);
   return(AccountBalance());
}
#define _Symbol   "EURUSD"
#define _Point    0.00001
#define _Digits   5
*/
//+------------------------------------------------------------------+
void OnTick()
{
   static TICKET_TYPE ticket = -1;
   static datetime LastBarM1 = 0;
   MqlTick  Tick;
   SymbolInfoTick(_Symbol, Tick);

   if(Tick.time / 60 == LastBarM1) return; // открываемся по новому бару М1
   LastBarM1 = Tick.time / 60;

   if(ticket == -1)
   {
      ticket = OrderSend(_Symbol, OP_BUY, 0.01, Tick.ask, 100, NormalizeDouble(Tick.ask - TP_SL * _Point, _Digits), NormalizeDouble(Tick.ask + TP_SL * _Point, _Digits));
      return;
   }
   if(OrderSelect(ticket, SELECT_BY_TICKET) && OrderCloseTime() == 0) return;
   int cmd = OrderType();
   double lot = 0.01;
   if(OrderProfit() < 0.0)
   {
      cmd = OrderType() == OP_BUY ? OP_SELL : OP_BUY;
      lot = 2.0 * OrderLots();
   }
   double pr, sl, tp;
   if(cmd == OP_BUY)
   {
      pr = Tick.ask;
      sl = pr - TP_SL * _Point;
      tp = pr + TP_SL * _Point;
   }
   else
   {
      pr = Tick.bid;
      sl = pr + TP_SL * _Point;
      tp = pr - TP_SL * _Point;
   }
   ticket = OrderSend(_Symbol, cmd, lot, pr, 100, NormalizeDouble(sl, _Digits), NormalizeDouble(tp, _Digits));
}
//+------------------------------------------------------------------+
//2021.01.07 15:37:39.961       Core 1  OnTester result 1000109.78
//2021.01.07 15:38:55.477       Core 1  final balance 1001883.48 USD
// Virtual optimization = 11:09 min

время оптимизации:

Virtual 20 000 проходов в режиме мат. вычислений  11 мин 09 сек , остальные тесты останавливал через 11 минут

тестер OHLC M1 

тестер по реальным тикам: 

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


ЕА для тестера - сборщик тиков для эмуляции OHLC M1 в Virtual ( запускать в тестере по реальным тикам):

#define OPEN   0
#define HIGH   1
#define LOW    2
#define CLOSE  3

MqlTick HistoryData[];
MqlTick bar[4];
const datetime t_bar[] = {0, 20, 40, 59};
//+------------------------------------------------------------------+
int OnInit()
{
   ArrayResize(HistoryData, 1, 2000000);
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   int handle = FileOpen(_Symbol + "_tick.bin", FILE_WRITE | FILE_BIN | FILE_COMMON);
   if(handle < 0)
   {
      Print("Erorr write array # ", GetLastError());
      return;
   }
   FileWriteArray(handle, HistoryData, 1);
}
//+------------------------------------------------------------------+
void OnTick()
{
   MqlTick tick;
   if(!SymbolInfoTick(_Symbol, tick)) return;
   static datetime LastBarM1 = 0;
   datetime d_minutes = tick.time / 60;
   if(LastBarM1 == 0)   // первый запускt
   {
      for(int i = 0; i < 4; i++) bar[i] = tick; // проинициализируем
      LastBarM1 = d_minutes;
   }

   if(d_minutes != LastBarM1) //--- если новая минута
   {
      if(bar[HIGH].time_msc > bar[LOW].time_msc)   // поменяем местами по времени тика
      {
         MqlTick tmp = bar[LOW];
         bar[LOW] = bar[HIGH];
         bar[HIGH] = tmp;
      }
      datetime t = LastBarM1 * 60;  // посчитаем вренмя, sec = 0
      for(int i = 0; i < 4; i++)    // подменим время тика
      {
         bar[i].time = t + t_bar[i];
         bar[i].time_msc = bar[i].time * 1000;
      }
      ArrayInsert(HistoryData, bar, ArraySize(HistoryData));   // добавим в массив
      for(int i = 0; i < 4; i++) bar[i] = tick;                // проинициализируем
      LastBarM1 = d_minutes;                                   // запомним минуты
   }

   if(tick.ask > bar[HIGH].ask) bar[HIGH] = tick;
   if(tick.ask < bar[LOW].ask)  bar[LOW]  = tick;
   bar[CLOSE] = tick;
}
//+------------------------------------------------------------------+

EURUSD_tick.bin https://mega.nz/file/rVsHSQwY#BxO7hFjqFojfLm2TRp3WnwzmSiR2PvQDEgNTKdMLqEA

 
Igor Makanu:

или получилось или... где то жестко накосячил, очень смущают цифры

Чем хорош MT4-style, что код ТС читается лучше комментариев. Очень просто понять логику.

Вашу ТС можно запускать только на Неттинге. Если делали на Хедже, то на микрофлете это убийца MT5-Тестера (сетки катастрофически влияют на скорость) и Virtual может выйти за пределы массива, хоть и имеется некоторый запас.

#define MAX_ORDERS 100

ORDER Orders[MAX_ORDERS];

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


У Вас бывают закрытия по тейкам. Если хотите положительных проскальзываний, как это делает MT5-Тестер, то нужно прописывать.

#define VIRTUAL_LIMITS_TP_SLIPPAGE // Лимитники и TP исполняются по первой цене акцепта - положительные проскальзывания


if(OrderSelect(ticket, SELECT_BY_TICKET) && OrderCloseTime() == 0) return;

Безумно дорогая операция для ежетикового использования. Особенно, если ордер уже в истории. Самое разумное - SELECT_BY_POS.

В Виртуале забыл расскоментировать одну строку. Если вздумаете дальше использовать SELECT_BY_TICKET, то лучше задействовать ее.

//    for (int i = this.AmountHistoryOrders - 1; i >= 0; i--) // Чаще ищутся позиции, что закрылись недавно.

Но лучше никогда не использовать в Тестерах какие-либо варианты SELECT_BY_TICKET. Да и для VPS - не лучший вариант.


#define _Symbol   "EURUSD"
#define _Point    0.00001
#define _Digits   5

Этой инфой хорошо бы помочь не только своей ТС, но и Виртуалу. Поэтому нужно прописывать ДО инклуда.


Советник записи тиков не смотрел, как и сгенерированный файл. Саму ТС смотрел только с экрана. Запусков не делал.

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

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

Библиотеки: Virtual

fxsaber, 2021.01.04 23:21

Если Virtual использовать только, как Тестер выше, то он еще может быть ускорен. Сеточные ТС, конечно, гораздо быстрее в виртуале, чем в штатном Тестере. Но библа под них не затачивалась, т.к. не практикую. Ускоритель сеток теоретически возможен.

 

мой вопрос, в целом, это изучение возможностей тестера МТ5

@fxsaber спасибо! Virtual - крутая штука!

 

Hi All,

I read all this thread and maybe I am wrong, but I understood Virtual Tester is working only with the Tester Currency.

Is there a way to make it works on multi-currency mode?

::SymbolInfoTick(symbols [i], tick); 

is not retrieving tick for other currency than the one of the Tester.

The bot is working as expected when not using Virtual.mqh library.

//#define VIRTUAL_TESTER                 // Launch in the virtual trading environment 

and not working at all when using Virtual.mqh library

#define VIRTUAL_TESTER                 // Launch in the virtual trading environment

Is there any chance something like that works? (I did not managed) 

#include <MT4Orders.mqh>               // https://www.mql5.com/ru/code/16006
#define VIRTUAL_TESTER                 // Launch in the virtual trading environment
#ifdef VIRTUAL_TESTER
   #include <fxsaber\Virtual_01\Virtual.mqh> 
   //#define MAX_ORDERS 1000
#endif
#include <Trade\SymbolInfo.mqh>

input double Lots = 1;
input int Interval = 100;  

#ifdef VIRTUAL_TESTER
   int env_handle = VIRTUAL::Create(); 
#endif
string symbols[] ={"EURUSD","GBPUSD","GBPJPY"};
CSymbolInfo *symbol_info[];


datetime bartime[];
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool och_NewBar(string _symbol, ENUM_TIMEFRAMES _timeframe, datetime& _lasttime)
{

if(::iTime(_symbol,_timeframe,0) != _lasttime)
  {
   _lasttime=::iTime(_symbol,_timeframe,0);
   return (true);
  }
else
   return (false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string och_OrderTypeToString(int order_type)
 {
   switch(order_type){
     case OP_BUY       : return("BUY");
     case OP_BUYSTOP   : return("BUY STOP");
     case OP_BUYLIMIT  : return("BUY LIMIT");
     case OP_SELL      : return("SELL");
     case OP_SELLSTOP  : return("SELL STOP");
     case OP_SELLLIMIT : return("SELL LIMIT");
   }
   return("UNKNOWN");
 }


void OnInit()
{
   #ifdef VIRTUAL_TESTER
      VIRTUAL::SelectByHandle(env_handle);
   #endif
   ArrayResize(bartime, ArraySize(symbols));
   ArrayResize(symbol_info, ArraySize(symbols));
   for(int i=0;i<ArraySize(symbol_info);i++){
      symbol_info[i] = new CSymbolInfo;
      symbol_info[i].Name(symbols[i]);    
   }
}

bool OrderSelectBySymbol(string symbol)
{
   bool founded=false;
   int total = OrdersTotal();
   for(int i=0;i<total;i++){
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         if (OrderSymbol() == symbol){
            founded=true;
            PrintFormat("Order#%d founded, OpenTime()=%s, Time to close=%s",OrderTicket(), TimeToString(OrderOpenTime(), TIME_DATE|TIME_SECONDS), string((TimeCurrent() - OrderOpenTime() > Interval)));
            break;
         }
   }
   return(founded);   
}

void System(string symbol, MqlTick &tick)
{
  if (!OrderSelectBySymbol(symbol))
    OrderSend(symbol, OP_BUY, Lots, tick.ask, 100, 0, 0); 
  else if (TimeCurrent() - OrderOpenTime() > Interval) 
  {
    PrintFormat("Try to close order#%d, OrderType()=%s and open reverse order", OrderTicket(), och_OrderTypeToString(OrderType()));
    OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 100);
    TICKET_TYPE result = OrderSend(symbol, 1 - OrderType(), Lots, OrderClosePrice(), 100, 0, 0);
    PrintFormat("Open order, TICKET_TYPE =%d", result);
  }
}

void OnTick()
{
    
    MqlTick tick;
    for(int i=0;i<ArraySize(symbols);i++){
      if (!och_NewBar(symbols[i], _Period, bartime[i])) continue;
      #ifdef VIRTUAL_TESTER
         VIRTUAL::SelectByHandle(env_handle);
      #endif
      //::SymbolInfoTick(symbols[i], tick);
      symbol_info[i].RefreshRates();
      tick.time         = symbol_info[i].Time();
      tick.bid          = symbol_info[i].Bid();
      tick.ask          = symbol_info[i].Ask();
      tick.last         = symbol_info[i].Last();
      tick.volume       = symbol_info[i].Volume();
      //tick.flags        = symbol_info[i].Fl();
      //tick.volume_real  = symbol_info[i].Time();
      //PrintFormat("symbols[%d]=%s, tick.ask=%s/%s, tick.time=%s", 
      //             i, symbols[i], DoubleToString(tick.ask, int(SymbolInfoInteger(symbols[i], SYMBOL_DIGITS))), 
      //             DoubleToString(symbol_info[i].Ask(), int(SymbolInfoInteger(symbols[i], SYMBOL_DIGITS))), TimeToString(tick.time, TIME_MINUTES|TIME_SECONDS));
      #ifdef VIRTUAL_TESTER
         VIRTUAL::NewTick(tick);      
      #endif
      System(symbols[i], tick);                
    }
}

#define REPORT_TESTER                  // Le testeur rédigera automatiquement des rapports
//#define REPORT_INTERACTIVE_CHARTS   // Ajoute des graphiques interactifs au rapport.
#define REPORT_BROWSER                 // Génération d'un rapport avec le lancement du navigateur - Nécessite l'autorisation DLL.
#include <fxsaber\Report.mqh>                  // https://www.mql5.com/ru/code/22577


void OnDeinit(const int reason)
{
  double deposit, balance, equity, profit, trades;
  #ifdef VIRTUAL_TESTER
    VIRTUAL::SelectByHandle(env_handle);
    VIRTUAL::Stop();
    balance =  AccountBalance();
    equity  =  AccountEquity();
    OrderSelect(0, SELECT_BY_POS, MODE_HISTORY);
    deposit =  OrderProfit();
    profit  =  balance - deposit;
    trades  =  OrdersHistoryTotal();
  #else
    deposit = TesterStatistics(STAT_INITIAL_DEPOSIT);
    balance = AccountInfoDouble(ACCOUNT_BALANCE);
    equity  = AccountInfoDouble(ACCOUNT_EQUITY);
    profit  = TesterStatistics(STAT_PROFIT);
    trades  = TesterStatistics(STAT_LONG_TRADES) + TesterStatistics(STAT_SHORT_TRADES);
  #endif
  string env_type = "real";
  #ifdef VIRTUAL_TESTER
    env_type = ((env_handle==1)?"Real":"Virtual");
  #endif

  PrintFormat("(%s-%s)::%s - Initial deposit=%.2f, Balance=%.2f, Equity=%.2f, Profit=%G, Trades=%d", _Symbol, EnumToString(_Period), env_type, deposit, balance, equity, profit, trades);
} 
 
och:

I read all this thread and maybe I am wrong, but I understood Virtual Tester is working only with the Tester Currency.

Торговля ведется только по одному символу. Это покрывает 99% задач. Мультисимвольная торговля не реализовывалась, как невостребованная в используемых сценариях.

 
fxsaber :

Only one symbol is traded. This covers 99% of the tasks. Multi-symbol trading was not implemented as unclaimed in the scenarios used.

@fxsaber,

Thanks for your reply. This should be why I didn't manage to make it works! ;-)

Do you plan to add the feature?

Regards,

och

 
och:

Do you plan to add the feature?

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

 
fxsaber :

I do not plan, because I do not see where it could be useful to me.

@fxsaber,

Not that much to do, I did it, thanks for reply.