Advertisement IC Markets

Why your fill time doesn't match the moment you clicked

Compare ECN/Raw spread brokers, analyze execution speeds, report slippage, and evaluate commission structures for high-frequency traders.
PTScalper
Site Admin
Posts: 7647
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why your fill time doesn't match the moment you clicked

Post by PTScalper »

Interpreting the CSV Data

Once you have logged 100+ trades from your VPS, drop the CSV into Excel or Google Sheets.

Sort the sheet by the Latency(ms) column from highest to lowest. If you see latency randomly spike from your baseline 2-5ms up to 200-500ms specifically on trades that also incurred high slippage, you are looking at the mathematical fingerprint of a dealer plugin intentionally holding your order to check if the market will move against you.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 7647
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why your fill time doesn't match the moment you clicked

Post by PTScalper »

To automate this, you need two additional logic blocks: a randomized timer to schedule the next execution, and an immediate market close function. Because you are testing execution quality rather than a trading strategy, holding the position exposes you to unnecessary market risk. The script must enter the market, log the entry latency and slippage, and instantly close the position.

On a Raw ECN account like IC Markets, a 0.01 lot round-trip will cost you approximately $0.07 in commissions plus the fractional raw spread. Firing 20 test trades a day will cost roughly $1.50 to $2.00 daily—a small price for hard server data.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 7647
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why your fill time doesn't match the moment you clicked

Post by PTScalper »

Here is how to automate the random intervals and immediate closures in both environments.

1. The cTrader (C#) Automation

In your cBot, introduce a Random object and use the OnTimer() method to check the server time against your next scheduled execution. Once the trade is successful and logged, call ClosePosition() immediately.

Code: Select all

using System;
using System.Diagnostics;
using System.IO;
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class ExecutionLoggerAuto : Robot
    {
        [Parameter("Min Interval (Minutes)", DefaultValue = 15)]
        public int MinInterval { get; set; }

        [Parameter("Max Interval (Minutes)", DefaultValue = 120)]
        public int MaxInterval { get; set; }

        private string _filePath;
        private Random _random = new Random();
        private DateTime _nextTradeTime;

        protected override void OnStart()
        {
            string docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            _filePath = Path.Combine(docsPath, "ExecutionLog_cTrader.csv");
            
            if (!File.Exists(_filePath))
                File.AppendAllText(_filePath, "Timestamp,Symbol,Type,IntendedPrice,FillPrice,Slippage(Pips),Latency(ms)\n");

            ScheduleNextTrade();
            Timer.Start(1); // Check the clock every 1 second
        }

        protected override void OnTimer()
        {
            // Only fire if the market is open and the random time has arrived
            if (Server.Time >= _nextTradeTime && Symbol.MarketHours.IsOpened())
            {
                // Alternate between Buy and Sell to randomize the order book side
                TradeType side = _random.Next(0, 2) == 0 ? TradeType.Buy : TradeType.Sell;
                ExecuteTestTrade(side, Symbol.VolumeInUnitsMin);
                
                ScheduleNextTrade();
            }
        }

        private void ScheduleNextTrade()
        {
            int minutesToAdd = _random.Next(MinInterval, MaxInterval + 1);
            _nextTradeTime = Server.Time.AddMinutes(minutesToAdd);
            Print($"Next test trade scheduled in {minutesToAdd} minutes.");
        }

        private void ExecuteTestTrade(TradeType tradeType, double volume)
        {
            double intendedPrice = tradeType == TradeType.Buy ? Symbol.Ask : Symbol.Bid;
            
            Stopwatch sw = Stopwatch.StartNew();
            TradeResult result = ExecuteMarketOrder(tradeType, SymbolName, volume, "LatencyTest");
            sw.Stop();

            if (result.IsSuccessful)
            {
                double fillPrice = result.Position.EntryPrice;
                double slippage = tradeType == TradeType.Buy ? (fillPrice - intendedPrice) : (intendedPrice - fillPrice);
                double slippageInPips = slippage / Symbol.PipSize;

                string logLine = $"{Server.Time:yyyy-MM-dd HH:mm:ss.fff},{SymbolName},{tradeType},{intendedPrice},{fillPrice},{slippageInPips:F1},{sw.ElapsedMilliseconds}\n";
                File.AppendAllText(_filePath, logLine);

                // Close instantly to eliminate market risk
                ClosePosition(result.Position);
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 7647
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why your fill time doesn't match the moment you clicked

Post by PTScalper »

2. The MetaTrader 5 (MQL5) Automation

In MQL5, it is safer to rely on OnTick() rather than OnTimer() for execution. OnTick() guarantees that the market is actively quoting and connected when you attempt the trade, preventing errors from firing during weekend closures or server disconnects.

Code: Select all

#property strict

input int MinIntervalMinutes = 15;
input int MaxIntervalMinutes = 120;
input double TestVolume = 0.01;

int file_handle;
datetime next_trade_time;

int OnInit() {
    file_handle = FileOpen("ExecutionLog_MT5.csv", FILE_CSV|FILE_READ|FILE_WRITE|FILE_ANSI, ",");
    if(file_handle != INVALID_HANDLE) {
        FileSeek(file_handle, 0, SEEK_END);
        if(FileSize(file_handle) == 0) {
            FileWrite(file_handle, "Timestamp", "Symbol", "Type", "IntendedPrice", "FillPrice", "Slippage(Points)", "Latency(ms)");
        }
    }
    
    // Seed the random number generator using system uptime
    MathSrand(GetTickCount());
    ScheduleNextTrade();
    
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
    if(file_handle != INVALID_HANDLE) FileClose(file_handle);
}

void OnTick() {
    if(TimeCurrent() >= next_trade_time) {
        // Randomize Buy (0) or Sell (1)
        ENUM_ORDER_TYPE side = (MathRand() % 2 == 0) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
        ExecuteTestTradeAndClose(side, TestVolume);
        
        ScheduleNextTrade();
    }
}

void ScheduleNextTrade() {
    int range = MaxIntervalMinutes - MinIntervalMinutes;
    int random_mins = MinIntervalMinutes + (MathRand() % (range + 1));
    next_trade_time = TimeCurrent() + (random_mins * 60);
    Print("Next test trade scheduled in ", random_mins, " minutes.");
}

void ExecuteTestTradeAndClose(ENUM_ORDER_TYPE orderType, double volume) {
    MqlTradeRequest request={0};
    MqlTradeResult result={0};
    
    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = volume;
    request.type = orderType;
    request.price = (orderType == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    request.deviation = 50; 
    
    ulong start_time = GetMicrosecondCount();
    bool success = OrderSend(request, result);
    ulong latency_us = GetMicrosecondCount() - start_time;
    
    if(success && result.retcode == TRADE_RETCODE_DONE) {
        // 1. Log the entry execution
        double slippage = (orderType == ORDER_TYPE_BUY) ? (result.price - request.price) : (request.price - result.price);
        double slippage_points = slippage / _Point;
        double latency_ms = latency_us / 1000.0;
        
        string timestamp = TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS);
        FileWrite(file_handle, timestamp, _Symbol, EnumToString(orderType), request.price, result.price, slippage_points, latency_ms);
        
        // 2. Immediately send an opposite order to close the position
        MqlTradeRequest close_req={0};
        MqlTradeResult close_res={0};
        
        close_req.action = TRADE_ACTION_DEAL;
        close_req.symbol = _Symbol;
        close_req.volume = volume;
        close_req.type = (orderType == ORDER_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
        close_req.price = (close_req.type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
        close_req.position = result.deal; // Target the specific deal to close
        close_req.deviation = 50;
        
        OrderSend(close_req, close_res);
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 7647
Joined: Mon Jul 20, 2026 1:28 pm

Re: Why your fill time doesn't match the moment you clicked

Post by PTScalper »

Avoiding the Rollover Trap

When deploying these scripts, avoid running them between 23:55 and 00:15 broker server time. During the daily rollover, bank liquidity drops to near zero, spreads gap massively, and legitimate A-Book STP routing will naturally result in severe slippage and latency. Logging trades during this window will corrupt your dataset with natural market dysfunction, mimicking B-Book interference.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Fairman
Posts: 6757
Joined: Tue Jul 21, 2026 7:11 am
Location: Abuja

Re: Why your fill time doesn't match the moment you clicked

Post by Fairman »

The journal tab in MT4 and MT5 logs the request and the fill with timestamps, which is more detailed than the trade history. You can see when the order was sent and when it was confirmed. Looking at that for a few trades during busy times gives a better idea of the real delay than guessing from the trade list. If you see big gaps between request and confirmation, that points at the connection or the broker rather than your clicking speed. A VPS close to the broker's server usually narrows that gap, though it won't remove it entirely.
It’s Fairman :geek:
Fairman
Posts: 6757
Joined: Tue Jul 21, 2026 7:11 am
Location: Abuja

Re: Why your fill time doesn't match the moment you clicked

Post by Fairman »

A related detail: some brokers have a minimum hold time or a delay on certain order types, often mentioned in the execution policy. It's not common with reputable brokers, but if your fills are consistently delayed by a similar amount, reading the execution policy might explain it. If it doesn't, asking support for the server side timestamps on a couple of specific trades is reasonable. Good brokers will provide them. If the delays are random rather than consistent, the cause is more likely your connection or general load. Comparing a few trades from home with a few from a VPS can separate the two quite quickly.
It’s Fairman :geek:
FXS HongKong Scalper
Posts: 407
Joined: Sat Oct 24, 2026 10:15 pm

Re: Why your fill time doesn't match the moment you clicked

Post by FXS HongKong Scalper »

Good point about asking support for server timestamps. When we compare our own logs with what brokers send back, the most useful columns are request received, request sent to LP, and fill confirmed. If the gap sits between received and sent, it is usually a dealing or risk delay on the broker side. If it sits after sent, it is the LP. If both look normal but your click to received is long, it is your connection or the terminal.

One more cause that is easy to miss: some terminals queue orders while they are busy redrawing heavy indicators. On a chart with many custom indicators, a click during a tick burst can wait a few hundred milliseconds before it even leaves the machine.
FXS Official Team | Hong Kong Session Desk
Shadow Trader
Posts: 5438
Joined: Wed Sep 09, 2026 5:19 pm

Re: Why your fill time doesn't match the moment you clicked

Post by Shadow Trader »

Computer clocks drift. A typical desktop can be off by several hundred milliseconds after a few days without a sync, and some are off by seconds. Windows syncs on a schedule that can be as loose as once a week.

For most activities, nobody notices. For anyone comparing trade timestamps to news releases, it makes the whole exercise unreliable.

Tools exist that keep the clock aligned with network time servers to within a few milliseconds. Setting the sync interval to something short, such as every few minutes, costs almost nothing.

On a VPS, the clock is managed by the host, and the offset can be larger than expected. It's worth checking against a trusted time source and correcting if needed.

Without this, a statement like "the fill came before the release" can't be trusted.

Has the clock offset ever been checked against a reliable source?
Fairman
Posts: 6757
Joined: Tue Jul 21, 2026 7:11 am
Location: Abuja

Re: Why your fill time doesn't match the moment you clicked

Post by Fairman »

Your own internet connection matters too, especially if you trade from home. A slow or unstable connection adds delay between the click and the request reaching the broker. Running a speed and ping test to your broker's server, or using a VPS near the broker, shows how much of the delay is on your side. For most manual trading it's small, but on a bad Wi-Fi connection it can be hundreds of milliseconds, which on a fast market is enough to change the fill. A wired connection instead of Wi-Fi is the simplest fix for many people.
It’s Fairman :geek:
Post Reply