Libraries: OnTickMulti

 

OnTickMulti:

Multisymbol OnTick.

OnTickMulti

Author: fxsaber

 
Мультисимвольное виртуальное окружение.
Мультисимвольное виртуальное окружение.
  • www.mql5.com
Данный пост посвящен дополнительному функционалу библиотеки виртуального окружения Virtual : Мультисимвольность. Теперь можно торговать в VIrtual не по одному символу, а по любым в одном виртуальном
 
   if (prev_calculated)
    EventChartCustom(Chart, 0, Index, 0, NULL);
You may miss ticks if an event is not queued. This can happen (mainly on a VPS or a slow computer).
 
Alain Verleyen on a VPS or a slow computer).

Unfortunately, even simple expert advisors with just the OnTick function miss ticks. For live trading expert advisors, CopyTicks functions and other workarounds are used to ensure they operate without missing ticks.

This library is guaranteed not to miss ticks only in the Tester. Everything works exactly the same as with OnTick.

 
Below is the code for a simple expert advisor written by someone else, which I’ve volunteered to use to demonstrate the use of OnTickMulti.
/*
// https://www.mql5.com/ru/blogs/post/755348
#define VIRTUAL_TESTER_MULTI // Run in the OnTickMulti virtual trading environment
#define VIRTUAL_LIMITS_TP_SLIPPAGE // Stop-loss and take-profit orders are executed at the first accepted price – positive slippage
#define VIRTUAL_CLOSEALL_BYEND // Forcibly closes all orders at the end of the test
// #define VIRTUAL_ALTERNATIVE // Alternative settlement speed
*/

#include <MT4Orders.mqh> // https://www.mql5.com/en/code/16006
#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // https://www.mql5.com/en/code/47647

/*
#define REPORT_TESTER             // Reports will be automatically saved in the tester
#define REPORT_BROWSER            // Generate a report by launching a browser – requires DLL authorisation.
#include <Report.mqh> // https://www.mql5.com/en/code/18801
*/

input group "Trading"
input double             LotSize=0.01;
input double             TPdist=0.1;                        // TP (%)
input double             SLdist=0.2;                        // SL (%)
input long               MyMagic=123;

input group "Benchmark: https://www.mql5.com/ru/forum/282062/page49#comment_50801337"
input int inRange = 0;

// Multi-character OnTick.
void OnTickMulti( const string &Symb, const int &Index )
{
  TradeStep(Symb, Index);
}

double OnTester()
{
   return AccountInfoDouble(ACCOUNT_BALANCE);
}

int GetHour( const datetime time )
{
  return((int)(time / 3600) % 24);
}

void TradeStep( const string &symbol, const int &Index )
{
   if(GetHour(TimeTradeServer()) < 2)
      return;

   DoTrade(symbol, Index);
}

bool IsMySymbol( const string &Symb, const int &Index )
{
  return(OnTickMulti.IsOnlySymbolBase ||
         (
        #ifdef __VIRTUAL__
          VIRTUAL::GetHandle() ? (OrderSymbolID() == Index) : // https://www.mql5.com/ru/forum/282062/page52#comment_51051261
        #endif // #ifdef __VIRTUAL__
          (OrderSymbol() == Symb)));
}

void DoTrade(const string &symbol, const int &Index )
{
   int total=OrdersTotal();
   bool haveBuy=false;
   bool haveSell=false;
   for(int i=0; i<total; i++)
   {
      if(OrderSelect(i, SELECT_BY_POS) &&
         OrderMagicNumber()==MyMagic &&
         IsMySymbol(symbol, Index))
      {
         int type=OrderType();
         if(type==OP_BUY)
            haveBuy=true;
         if(type==OP_SELL)
            haveSell=true;
      }
   }
   
   if(!haveBuy)
   {
      double price=SymbolInfoDouble(symbol,SYMBOL_ASK);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      double tp=NormalizeDouble(price*(1.0+TPdist/100.0), digits);
      double sl=NormalizeDouble(price*(1.0-SLdist/100.0), digits);
      OrderSend(
         symbol,
         OP_BUY,
         LotSize,
         price,
         0,
         sl,
         tp,
         NULL,
         MyMagic);
   }

   if(!haveSell)
   {
      double price=SymbolInfoDouble(symbol,SYMBOL_BID);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      double tp=NormalizeDouble(price*(1.0-TPdist/100.0), digits);
      double sl=NormalizeDouble(price*(1.0+SLdist/100.0), digits);
      OrderSend(
         symbol,
         OP_SELL,
         LotSize,
         price,
         0,
         sl,
         tp,
         NULL,
         MyMagic);
   }
}


Tester settings.



Acceleration.

You can speed it up by uncommenting a few lines at the start.


Measurements.

Configuration Result
Without Virtual.mqh. Shortest pass 0:00:13.184, longest pass 0:00:15.031, average pass 0:00:13.633
With Virtual.mqh, inVirtualTester = false. shortest pass 0:00:14.735, longest pass 0:00:16.384, average pass 0:00:15.081
With Virtual.mqh, inVirtualTester = true. shortest pass 0:00:05.872, longest pass 0:00:05.964, average pass 0:00:05.919
With Virtual.mqh, inVirtualTester = true + VIRTUAL_ALTERNATIVE. shortest pass 0:00:06.172, longest pass 0:00:07.791, average pass 0:00:06.524

The expert advisor is extremely simple (0–2 open positions per instrument with fixed SL/TP, no pending orders), so in this configuration, the speed can only be increased by a factor of 2.5.


TickValue+Swap+Commission.

The tests above were carried out in pip mode (see screenshot of settings) to avoid having to recalculate the TickValue on every tick by connecting other instruments for this purpose. The results are identical in this mode.

If you need full-blown performance optimisation, then this is the solution to use.

OnTickMulti - с добавленными пересчетом прибыли в валюту депозита, свопами, комисссией в % за лот.
OnTickMulti - с добавленными пересчетом прибыли в валюту депозита, свопами, комисссией в % за лот.
  • 2023.12.25
  • www.mql5.com
Текущий вариант OnTickMulti https://www.mql5.com/ru/code/47647 считает прибыль в валюте каждого символа или можно получить в пипсах. Но на общий баланс они влияют в другой пропорции, согласно текущему
 

Forum on trading, automated trading systems and the testing of trading strategies

Libraries: OnTickMulti

fxsaber, 18 March 2024 18:36

void DoTrade(const string &symbol, const int &Index )
{
   int total=OrdersTotal();
   bool haveBuy=false;
   bool haveSell=false;
   for(int i=0; i<total; i++)
   {
      if(OrderSelect(i, SELECT_BY_POS) &&
         (OrderMagicNumber()==Index))
      {
         int type=OrderType();
         if(type==OP_BUY)
            haveBuy=true;
         if(type==OP_SELL)
            haveSell=true;
      }
   }
   
   if(!haveBuy)
   {
      double price=SymbolInfoDouble(symbol,SYMBOL_ASK);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      double tp=NormalizeDouble(price*(1.0+TPdist/100.0), digits);
      double sl=NormalizeDouble(price*(1.0-SLdist/100.0), digits);
      OrderSend(
         symbol,
         OP_BUY,
         LotSize,
         price,
         0,
         sl,
         tp,
         NULL,
         Index);
   }

   if(!haveSell)
   {
      double price=SymbolInfoDouble(symbol,SYMBOL_BID);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      double tp=NormalizeDouble(price*(1.0-TPdist/100.0), digits);
      double sl=NormalizeDouble(price*(1.0+SLdist/100.0), digits);
      OrderSend(
         symbol,
         OP_SELL,
         LotSize,
         price,
         0,
         sl,
         tp,
         NULL,
         Index);
   }
}

Configuration Result
Without Virtual.mqh. shortest pass 0:00:13.184, longest pass 0:00:15.031, average pass 0:00:13.633

Above, I have highlighted a simple way in the code to speed up a multi-currency expert advisor without using any Virtual libraries.

Configuration Result
Without Virtual.mqh +MagicNumberOrderSymbol shortest pass 0:00:08.608, longest pass 0:00:10.236, average pass 0:00:08.942

A 1.5-fold speed boost for free!

 

I tested the Tester’s slowdown when switching from single-symbol mode to multi-symbol mode. I didn’t do it quite perfectly – the expert advisor is just a dummy.


Single-symbol expert advisor.

#property tester_no_cache

input group "Benchmark: https://www.mql5.com/ru/forum/282062/page49#comment_50801337"
input int inRange = 0;

const ulong StartTime = GetMicrosecondCount();
int AmountTicks = 0;

double OnTester()
{
  return((double)(GetMicrosecondCount() - StartTime));
//  return(AmountTicks);
//  return((double)AmountTicks / (GetMicrosecondCount() - StartTime));
}

void OnTick()
{
  AmountTicks++;
}
Configuration (OnTick) TimeLength (mcs) AmountTicks Performance (ticks/mcs)
EURUSD 1079331 9,927,153 9.19
GBPUSD 1,229,032 11,309,740 9.20
USDJPY 1,307,322 11,986,756 9.16
USDCHF 1,121,541 10,359,511 9.23
AUDUSD 1,175,578 10,758,136 9.15

It is clear that the Tester’s performance, measured as the number of ticks per unit of time, does not depend on the instrument (pips mode).


Multi-symbol expert advisor.

#property tester_no_cache

input group "Benchmark: https://www.mql5.com/ru/forum/282062/page49#comment_50801337"
input int inRange = 0;

#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // https://www.mql5.com/en/code/47647

const ulong StartTime = GetMicrosecondCount();
int AmountTicks = 0;

double OnTester()
{
  return((double)(GetMicrosecondCount() - StartTime));
//  return(AmountTicks);
//  return((double)AmountTicks / (GetMicrosecondCount() - StartTime));
}

// Multi-character OnTick.
void OnTickMulti( const string &Symb, const int &Index )
{
  AmountTicks++;
}
Configuration (OnTickMulti) TimeLength (OnTickMulti) TimeLength (OnTick) AmountTicks Performance (OnTickMulti) Performance (OnTick)
EURUSD
3,455,901 1,079,331 9,927,153 2.87 9.19
EURUSD, GBPUSD
6617085 2,308,363
21,236,893 3.20 9.19
EURUSD, GBPUSD, USDJPY
9188781 3615685 33,223,649 3.61 9.18
EURUSD, GBPUSD, USDJPY, USDCHF
11517804 4,737,226 43,583,160 3.78 9.20
EURUSD, GBPUSD, USDJPY, USDCHF, AUDUSD
16058223 5912804 54,341,296 3.38 9.19

Performance is only slightly dependent on the number of characters, but is three times lower.


Conclusion.

If your multi-currency expert advisor consists of a hodgepodge of independent single-currency sub-EAs, it will be (at least) three times faster to optimise each sub-TS (and then combine them via Validate in a single pass) separately than to optimise them all together within a single multi-currency expert advisor.

 
Apart mode has been added.
Мультивалютный Apart-режим.
Мультивалютный Apart-режим.
  • www.mql5.com
Большинство мультивалютных советников представляют из себя портфель независимых (по логике торговых сигналов) друг от друга ТС, где если связь и есть, то за счет ММ - все ТС оказывают влияние на общие
 
The signature of OnTickMulti has changed slightly.
// Multi-character OnTick.
void OnTickMulti( const string &Symb, const u int &Index )
 

Hello,

It seems that on the latest builds (5200+), the code no longer compiles:

#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // Multi-character OnTick.

int TicksCounter[]; // A counter for the number of ticks received for each specified character.

void OnInit()
{
  // Initialise the counter for incoming ticks.
  ArrayResize(TicksCounter, ArraySize(OnTickMulti.Symbols));
  ArrayInitialize(TicksCounter, 0);
}

double OnTester()
{
  // Print the number of ticks received for each specified character.
  for (uint i = ArraySize(TicksCounter); (bool)i--;)
    Print(OnTickMulti.Symbols[i] + " - " + (string)TicksCounter[i] + " ticks.");

  ArrayPrint(OnTickMulti.Symbols); // List of OnTickMulti symbols.

  return(0);
}

// Multi-character OnTick.
void OnTickMulti( const string &Symb, const uint &Index )
{
  TicksCounter[Index]++; // Incremented the counter of incoming ticks for the specified character.
}
 
Marat Sultanov #:

It seems that on the latest builds (5200+), the code no longer compiles

The library has been updated to accommodate the changes inMQL5_b5200+.

Новая версия платформы MetaTrader 5 build 5200: расширение OpenBLAS и усиление контроля в MQL5 - Версия MetaTrader 5 расширила поддержку библиотеки линейной алгебры OpenBLAS.
Новая версия платформы MetaTrader 5 build 5200: расширение OpenBLAS и усиление контроля в MQL5 - Версия MetaTrader 5 расширила поддержку библиотеки линейной алгебры OpenBLAS.
  • 2025.07.31
  • www.mql5.com
SylvesterEquationTriangular решает уравнение Сильвестра для вещественных квазитреугольных или комплексных треугольных матриц верхнетреугольные. SylvesterEquationTriangularBlocked решает уравнение Сильвестра для вещественных квазитреугольных или комплексных треугольных матриц верхнетреугольные матрицы