- www.mql5.com
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.
/* // 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.
| 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.
- 2023.12.25
- www.mql5.com
Forum on trading, automated trading systems and the testing of trading strategies
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.
- www.mql5.com
// 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. }
The library has been updated to accommodate the changes inMQL5_b5200+.
- 2025.07.31
- www.mql5.com
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use


OnTickMulti:
Multisymbol OnTick.
Author: fxsaber