DEV Community

Omais Ali
Omais Ali

Posted on

Measuring the real spread by hour of day with CopyTicks in MQL5

A spread filter needs a threshold, and most people guess it. The Market Watch spread you see while writing code is the daytime spread. The spread at rollover, before major news, or in the thin hour after the US close can be several times wider. A short script that reads the tick history and groups the spread by hour of day gives you real numbers to put into the filter and into the tester settings.

What the script does

  1. Reads the most recent bid and ask changes with CopyTicks and COPY_TICKS_INFO. Passing 0 as the start time asks for the latest ticks, so no date math is needed.
  2. Converts each tick's spread to points.
  3. Groups the ticks by hour in broker server time and prints the tick count, average spread and maximum spread for each hour.

The script

#property script_show_inputs

input int MaxTicks = 1000000;   // most recent ticks to read

void OnStart()
{
   MqlTick ticks[];
   // from = 0 means "the most recent MaxTicks ticks"
   int n = CopyTicks(_Symbol, ticks, COPY_TICKS_INFO, 0, MaxTicks);
   if(n <= 0)
   {
      Print("CopyTicks returned ", n, ", error ", GetLastError(), ". Tick history may still be downloading.");
      return;
   }

   double sum[24];
   double mx[24];
   int    cnt[24];
   ArrayInitialize(sum, 0.0);
   ArrayInitialize(mx, 0.0);
   ArrayInitialize(cnt, 0);

   MqlDateTime dt;
   for(int i = 0; i < n; i++)
   {
      if(ticks[i].bid <= 0.0 || ticks[i].ask <= 0.0)
         continue;
      double pts = (ticks[i].ask - ticks[i].bid) / _Point;
      TimeToStruct(ticks[i].time, dt);
      sum[dt.hour] += pts;
      cnt[dt.hour]++;
      if(pts > mx[dt.hour])
         mx[dt.hour] = pts;
   }

   PrintFormat("%s spread by server hour, %d ticks from %s to %s", _Symbol, n, TimeToString(ticks[0].time), TimeToString(ticks[n - 1].time));
   for(int h = 0; h < 24; h++)
   {
      if(cnt[h] == 0)
         continue;
      PrintFormat("hour %02d  ticks %7d  avg %6.1f  max %6.1f points", h, cnt[h], sum[h] / cnt[h], mx[h]);
   }
}
Enter fullscreen mode Exit fullscreen mode

Run it on the chart of the symbol you trade. The output goes to the Experts tab.

Reading the output

  1. The average shows the normal cost of trading in that hour. If it is far above your strategy's average profit per trade, that hour is a cost center, whatever the signal says.
  2. The maximum shows the spikes your filter must survive. A spike at the daily rollover is normal on most brokers. Spikes elsewhere often line up with news.
  3. Hours with very few ticks are thin markets. Fills there are worse than the spread alone suggests.
  4. Hours are in server time. If the broker shifts its server clock for daylight saving, run the script again after the change, because the rollover hour moves.

Practical points

  1. Check the time range printed in the first line. On a busy symbol a million ticks may cover only a day or two; on a quiet one it can cover weeks. You want at least a full week so every hour appears several times.
  2. Tick history is downloaded on demand. The first call can return fewer ticks or an error; running the script a second time usually fixes it.
  3. Start with a modest MaxTicks. Very large requests use a lot of memory and take a while to load. If you need a specific date range, CopyTicksRange takes start and end times in milliseconds instead.
  4. Spread is not the whole cost. Add commission per lot from your account type to get the full round trip.
  5. Use the numbers twice: as the threshold in the EA's spread filter, and as a check on your backtest. If the test data shows a much tighter spread in your trading hours than the script measured on your live account, the backtest is cheaper than real trading and its results are too optimistic.

Measuring the spread takes a few minutes. Guessing it wrong shows up in every trade.

Top comments (0)