Expert Advisors • Indicators • Scripts • Libraries

MQL.RobotFX.org is the biggest collection of MetaTrader expert advisors (MT5 & MT4), indicators, scripts and libraries that can be used to improve trading results, minimize risks or simply automate trading tasks

Metatrader 5 Libraries | MathTicker - tick generator in mathematical mode

Alternative for EAToMath library https://www.mql5.com/en/code/61283

Records ticks in real ticks mode and reads them in maths mode calling your strategy with each recorded tick.

Reason for creation: MQ tester, writes tick data files to each agent every time the optimiser is run. I have 36 agents writing 10GB each for one of the tools and the test period - a total of 360GB on a 480GB drive. This process takes about 1 hour before each optimisation. Typical SSDs have a lifespan of 500-1000 write cycles. By rewriting 360GB every time the resource will be exhausted very quickly. This library writes only 1 file and then all 36 agents will read data from this one file. All this was the reason for writing the library: we use only 1 file + saving 1 hour for writing data to each agent + acceleration in comparison with MQ tester and even with Virtual in real ticks mode.
The problem was investigated simultaneously with fxsaber (the author of EAToMath), each with his own version. My code is more clear to me, so I use it.

For trading operations MT4Orders library is used https://www.mql5.com/en/code/16006
For virtual trading it is necessary to use Virtual library https://www.mql5.com/en/code/22577
To view trading results you can use MT4Orders QuickReport https://www.mql5.com/en/code/47816 or Report
To compress ticks TickCompressorhttps://www.mql5.com/en/code/66201
To remove possibly unnecessary ticks Control_Trade_Sessions library https://www.mql5.com/en/code/48059 is connected, for example, if a quote session is larger than a trading session. It can also be deleted if all ticks are used, i.e. sessions coincide.


Differences from EAToMath:

Pros:

  • The code is shorter and simpler, only 5 plug-in libraries. If you need to modify it, it will be easier to understand.

  • Data are compressed better due to a different algorithm https://www.mql5.com/en/code/66201. When saving only time_msc, ask and bid - up to 86% of ticks are saved as 3 char numbers, i.e. 3 bytes. Average size per tick = 3.266 Bytes when saving BTCUSDT tick data for 2023.
    When saving with volumes average = 4.835 Bytes. And when saving full tick = 8.439 Bytes. Below is the table with the test results.
    Additionally, you can use the built-in ZIP archiving. The file size is reduced by 2 times. Such a file takes 245 Mb, while the sum of file sizes in .tcs for 2023 takes 364 Mb, i.e. compression is 1.5 times better than in MQ. And the speed of tick generation in the maths mode is ~2 times faster. See the table below.

  • There are more options of saving:


  • The file can be saved to either SSD or RAM disc by making a link in the system. Files can take up a lot of space and RAM drive may not be enough, so you can choose to save to the primary drive. SSD and RAM read speeds are about the same, I've read that the SSD caches up to 5% of the full capacity of the most frequently requested data.
    There is a bit of wear and tear on the SSD when reading, as you have to overwrite memory cells more often than when storing without reading. I don't know the exact numbers, but for example 1 overwrite per 10 reads or per 1000 reads.... But this is of little significance compared to the wear of the disc by the MQ tester.

Cons:

  • Connecting Virtual has to be done yourself (instructions here https://www.mql5.com/en/code/22577), EAToMath will pass your strategy to Virtual itself.

The speed for the BidAsk variant is comparable to EAToMath. Other variants are slower because they either contain more data or have additional ZIP compression.

Usage Features:

You cannot use the standard functions Symbol(), Digits( ) (=4), Point() (=0.0001) in the strategy, as they will produce default values, not related to the symbol under test. Instead of them use _Symbol, _Digits, _Point which are overridden on values read from the file. Also, new constants _TickSize and _TickValue with values from the recorded symbol have been added - they are necessary for correct calculation of profit, commission and swaps in the deposit currency.

The order of work with the testing period selected when saving ticks:

  1. Select the testing mode by real ticks, the required instrument and testing dates. Set the Task variable to one of Save and select the option of saving ticks. Start the tester. After that a file with ticks will be created in the specified folder.

  2. Set the Task variable to Run_Strategy. You can leave the mode by real ticks to compare later. Start the tester. Calculations are made by real ticks, not from the file. Get the result.


  3. Set the testing mode to mathematical calculations. Start the tester. Calculations are made by ticks from the file. Compare with the result from step 2. It should be the same, but several times faster.

The order of work with the archive:

  1. Create an archive with all ticks from the history: Select the mode of testing by real ticks, the required instrument. Set testing dates from <= first tick to >= last tick in the available history. Set the Task variable to one of Save...To_Archive and select the option of saving ticks. Start the tester. After that a folder with the name of the tool will be created in the specified folder where the files with ticks for each year will be saved. The last year can be overwritten as necessary, for this purpose select only the current year in the dates to avoid overwriting the previous years.
  2. Set the test mode to Maths. Set the Task variable to Run_Strategy_Fron_Archive.
  3. In the MathTicker: using the full archive group, set:
    Instrument - to Instrument name (must match the name of the folder where its ticks are stored), start and end date of the test.

  4. Start the tester. Calculations are made by ticks from the required annual files. Due to the fact that the work is done not with one file, but with several, it is a little slower, because it takes time to open and close files. For example, instead of 1.7 seconds it will take 2.7 seconds to generate ticks for 3 years.
  5. The sum of ticks obtained by the Expert Advisor below may differ by a small value of one first tick. When testing on custom characters in real ticks mode, the first tick produces only Ask or Bid (if you didn't save both). When testing from archive, they are both restored from previous ticks.

An example of the simplest Expert Advisor to estimate the speed of work:

#property tester_no_cache
#include <Forester\MathTicker.mqh> // connecting trade in maths mode
input int rep=0;//Repeats for optimisation
sinput bool AddVolumes=true;
void OnInit(){}

void OnTick(){
   static MqlTick Tick;
   if (SymbolInfoTick(_Symbol, Tick)){
      #ifdef _MathTick_
         if(MathTick.SaveTick(Tick)){ return; }//if we save ticks, then exit and do not trade. 
      #endif
       Strategy(Tick);
   }
}
double Sum = 0;int tk=0;
void Strategy(MqlTick& Tick){ // the simplest strategy - used to compare reading speed with EAToMath 
   Sum += Tick.bid+Tick.ask+(AddVolumes?Tick.volume_real:0.0); tk++;
   //if(tk<100){Print(Tick.time," " ",Tick.ask," ",Tick.bid," ",Tick.last," ",Tick.volume_real," ",Tick.flags);}
}

ulong StartTime  = GetMicrosecondCount();
double OnTester(){
   #ifdef _MathTick_ // run with MathTick - it counts symbol parameters from the file with ticks. For tests in mat mode
      if(MathTick.SaveTicksEnd()){return 0;}// close the file after recording ticks and exit
      if(MathTick.ReadSymbolVars()){
         MathTick.Ticker();// in mat mode will feed all ticks to Strategy(MqlTick &Tick).
      }
   #endif
   Print("ticks: ",tk);
   long work_time = (long)(GetMicrosecondCount() - StartTime)/1000;
   //return(NormalizeDouble(work_time, 1)); // to get the speed of work and
   return Sum;// to compare the calculation results
}

You can toggle 1 setting:

//#define RestoreFlags // восстановить флаги тика из изменения ask, bid, volume - добавит 7% к времени генерации тиков 931 вместо 869 мс

When generating ticks, statistics about tick compression will be displayed.
Below are printouts of statistics, volumes and time of tick generation.

-----------
MQtester without volumes
pass 1 returned result 4345830621850.311523 in 0:00:08.232

C ZIP compression
AskBid. File size: 225 mb
-------------------- Statistics: --------------------
3 bytes: 86.6%, 62644158 ticks
4 bytes: 0.6%, 412167 ticks
5 bytes: 12.7%, 9185484 ticks
6 bytes: 0.0%, 15274 ticks
11 bytes: 0.1%, 46214 ticks
12 bytes: 0.0%, 1 ticks
24 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 236108596 bytes.
Average: 3.266 bytes per tick
final balance 0.00 USD

pass 10 returned result 4345830621850.311523 in 0:00:01.485
no normalisation
pass 1 returned result 4345830621850.311523 in 0:00:00.892
AskBid_Zipped. File size: 106 mb
-------------------- Statistics: --------------------
3 bytes: 86.6%, 62644158 ticks
4 bytes: 0.6%, 412167 ticks
5 bytes: 12.7%, 9185484 ticks
6 bytes: 0.0%, 15274 ticks
11 bytes: 0.1%, 46214 ticks
12 bytes: 0.0%, 1 ticks
24 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 236108596 bytes.
Average: 3.266 bytes per tick
UnZipped size:236108596. Zipped size:111720863. ZIP compression: 47.3%

pass 10 returned result 4345830621850.311523 in 0:00:02.548
no normalisation
pass 2 returned result 4345830621850.311523 in 0:00:01.890



MQ tester with volumes
pass 1 returned result 4345879117123.356445 in 0:00:07.962

C ZIP compression
AskBidVolume. File size: 333 mb
-------------------- Statistics: --------------------
4 bytes: 60.4%, 43684907 ticks
5 bytes: 1.1%, 809676 ticks
6 bytes: 33.5%, 24194111 ticks
7 bytes: 4.9%, 3548666 ticks
8 bytes: 0.0%, 7909 ticks
12 bytes: 0.1%, 40022 ticks
13 bytes: 0.0%, 17964 ticks
14 bytes: 0.0%, 2 ticks
19 bytes: 0.0%, 41 ticks
32 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 349571243 bytes.
Average: 4.835 bytes per tick

pass 1 returned result 4345879117123.356445 in 0:00:02.803
no normalisation
pass 4 returned result 4345879117123.356445 in 0:00:01.659
AskBidVolume_Zipped. File size: 204 mb
-------------------- Statistics: --------------------
4 bytes: 60.4%, 43684907 ticks
5 bytes: 1.1%, 809676 ticks
6 bytes: 33.5%, 24194111 ticks
7 bytes: 4.9%, 3548666 ticks
8 bytes: 0.0%, 7909 ticks
12 bytes: 0.1%, 40022 ticks
13 bytes: 0.0%, 17964 ticks
14 bytes: 0.0%, 2 ticks
19 bytes: 0.0%, 41 ticks
32 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 349571243 bytes.
Average: 4.835 bytes per tick
UnZipped size:349571243. Zipped size:214897079. ZIP compression: 61.5%

pass 2 returned result 4345879117123.356445 in 0:00:04.260
no normalisation
pass 2 returned result 4345879117123.356445 in 0:00:03.096
All. File size: 582 mb
-------------------- Statistics: --------------------
8 bytes: 61.5%, 44494583 ticks
9 bytes: 33.5%, 24194111 ticks
10 bytes: 4.9%, 3548666 ticks
11 bytes: 0.0%, 7909 ticks
15 bytes: 0.1%, 40022 ticks
16 bytes: 0.0%, 17964 ticks
17 bytes: 0.0%, 2 ticks
22 bytes: 0.0%, 41 ticks
44 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 610166056 bytes.
Average: 8.439 bytes per tick

pass 2 returned result 4345879117123.356445 in 0:00:03.768
no normalisation
pass 1 returned result 4345879117123.356445 in 0:00:02.256
All_Zipped. File size: 245 mb
-------------------- Statistics: --------------------
8 bytes: 61.5%, 44494583 ticks
9 bytes: 33.5%, 24194111 ticks
10 bytes: 4.9%, 3548666 ticks
11 bytes: 0.0%, 7909 ticks
15 bytes: 0.1%, 40022 ticks
16 bytes: 0.0%, 17964 ticks
17 bytes: 0.0%, 2 ticks
22 bytes: 0.0%, 41 ticks
44 bytes: 0.0%, 1 ticks
Total: 72303299 ticks, 610166056 bytes.
Average: 8.439 bytes per tick
UnZipped size:610166056. Zipped size:257105213. ZIP compression: 42.1 %

pass 1 returned result 4345879117123.356445 in 0:00:05.388
no normalisation
pass 10 returned result 4345879117123.356445 in 0:00:03.936

The size of the .tcs files for the same year 2023:

All variants with ZIP, even full tick saving are more compact (3.5 to 1.5 times).

Expert Advisor example for virtual trading and report output:

#property tester_no_cache

#include <MT4Orders.mqh> // https://www.mql5.com/en/code/16006
#include <Forester\MathTicker.mqh> // connecting trade in maths mode

#define  ORDER_CURRENCY_DIGITS 2 // Digits setting for calculating the profit/commission/swap when placed in the trading history.
#define  VIRTUAL_LIMITS_TP_SLIPPAGE // Limiters and TPs are executed at the first acceptance price - positive slippages
#define  ORDER_COMMISSION -0 // Commission assignment = Lots * ORDER_COMMISSION. 
#include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/en/code/22577


#define  REPORT_TESTER             // The tester will automatically record reports
#define  REPORT_BROWSER            // Creating a report with browser startup - requires DLL permission.
#define  USE_highcharts //- You can download and try out all Highcharts products for free. Once your project/product is ready for launch, purchase a commercial licence. https://shop.highcharts.com/
#include <MT4Orders_QuickReport.mqh>//


enum VirtTyp {MQ_Tester=0,Virtual1=1,Virtual2=2};
sinput VirtTyp tester1=1;//Tester 1
sinput VirtTyp tester2=2;//Tester 2

input int rep=0;//Repeats for optimisation

bool isOptimization = false, isTester=false; double balInit=0;
VIRTUAL_POINTER Virtual[10];

void OnInit(){
   Virtual[0] = 0; // 0 - real trading environment
   Virtual[1] = VIRTUAL::Create(AccountBalance()); // Created virtualisation 1.
   Virtual[2] = VIRTUAL::Create(AccountBalance()); // Created virtualisation 2.
   //Virtual[tester1].Select();
   isOptimization = MQLInfoInteger(MQL_OPTIMIZATION) ;
   isTester = MQLInfoInteger(MQL_TESTER);
   balInit=AccountBalance();
}

void OnTick(){
   //Virtual[0].Select();
   VIRTUAL::NewTick();// send the tick to the current virtual machine
   static MqlTick Tick;
   if (SymbolInfoTick(_Symbol, Tick)){
      #ifdef _MathTick_
         if(MathTick.SaveTick(Tick)){ return; }//when writing ticks will exit the function, Strategy() will not be called
      #endif
       Strategy(Tick);//trading
   }
}

void Strategy(MqlTick& Tick){ // the simplest strategy - used to compare reading speed with EAToMath 
   if(Tick.ask==0 || Tick.bid==0){return;}//MQ tester trades on a failed tick, Virtual does not. Prohibition for MQ as well
   if(tester1>0){Virtual[tester1].Select(); VIRTUAL::NewTick(Tick);}//select virtualisation 1 and send a tick
   if(tester2>0){Virtual[tester2].Select(); VIRTUAL::NewTick(Tick);}//select virtualisation 2 and send a tick
   if(isNewHour(Tick.time)){//the first tick of every hour
      if(GetHour0(Tick.time) % 2==0){// buy at even hours in tester 1
         Virtual[tester1].Select();//select virtualisation 1
         OrderSend(_Symbol, OP_BUY, 1, Tick.ask, 0, Tick.ask - 100 * _Point, Tick.ask + 100 * _Point); 
      }else{//sell at odd hours in tester 2
         Virtual[tester2].Select();//select virtualisation 2
         OrderSend(_Symbol, OP_SELL, 1, Tick.bid, 0, Tick.bid + 100 * _Point, Tick.bid - 100 * _Point);   
      } 
   }
}

double OnTester(){
   #ifdef _MathTick_ // run with MathTick - it will read symbol parameters from the tick file. For tests in mat mode
      if(MathTick.SaveTicksEnd()){return 0;}//return after ticks saving
      if(MathTick.isMath && MathTick.ReadSymbolVars()){
         if(tester1==0){Alert("   >>>>>>>>>   Virtual tester 1=MQ. In math mode can be used only virtual tester.   <<<<<<<<");return 0;}
         if(tester2==0){Alert("   >>>>>>>>>   Virtual tester 1=MQ. In math mode can be used only virtual tester.   <<<<<<<<");return 0;}

         SYMBOL_BASE sb; sb.Point=_Point; sb.Digits=_Digits; sb.Symbol=_Symbol; sb.SymbolID=0; sb.TickSize=_TickSize; sb.TickValue=_TickValue / _TickSize;//this.TickValue_ /= this.TickSize_; //as in SetSymbol() in \fxsaber\Virtual\Symbol_Base.mqh
         Virtual[1].Select(); VIRTUAL::SetSymbolBase(sb); Virtual[2].Select(); VIRTUAL::SetSymbolBase(sb);
         //minFreezeLevel = _minFreezeLevel*_Point; minStopLevel = _minStopLevel*_Point;

         Virtual[tester1].Select();
         MathTick.Ticker();// in mat mode will feed all ticks to Strategy(MqlTick &Tick). 
      }
   #endif

  double ret_val=0;
  for (int v = 0 ; v <= VIRTUAL::Total(); v++){
    if(Virtual[v].Select()){
      if(v > 0){
         VIRTUAL::Stop();
         #ifdef _MathTick_ // run with MathTick - it will read symbol parameters from the tick file. For tests in mat mode
            if(MathTick.isMath){ VIRTUAL::CalcSwaps( MathTick.swapShort, MathTick.swapLong, 0, MathTick.swap3days ); }//swaps from the tick file
            else{VIRTUAL::CalcSwaps( _Symbol, 0 );}
         #else
           VIRTUAL::CalcSwaps( _Symbol, 0 );//calculate swaps - all trades have one swap, i.e. if 2+ different instruments, they will both have the swap of the main symbol.
         #endif
      }// close incomplete trades at the price of the last tick, as in the tester

      if( !isOptimization){QuickReport("report_"+(string)v, true, v,false,true);}
      Print((string)v+" AccountBalance = ",AccountBalance(), "   AccountEquity = ",AccountEquity());
      double prib=AccountBalance()-balInit;
      ret_val += prib; //
  }}
  return ret_val;// to compare the calculation results
}



bool isNewHour  (datetime &t){ static int next_h=-1; if(t < next_h){ return false; } else { next_h = (GetHour0(t)+1)*3600;return true;}}
int GetHour0    (datetime &t){return((int)( t / 3600));}//current hour from 1 Jan 1971

This example creates 2 virtual machines with different trading. On even hours one tester is buying, the other one is selling on odd hours.

It is a complex example with 2 testers, it can be simplified if you need to work with one tester.

You can also select the MQ tester and compare it with the results of virtual testers to control the correctness of calculations. Only the commission may not coincide, because there are many different commissions, and only one variant is programmed in the virtual tester.

65821

Best MetaTrader Indicators + Profitable Expert Advisors