Page 3 of 4
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:14 am
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:15 am
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:15 am
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);
}
}
}
}
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:15 am
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);
}
}
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:16 am
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 9:16 am
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 17, 2026 10:02 am
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Fri Oct 30, 2026 8:02 pm
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.
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 31, 2026 9:34 am
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?
Re: Why your fill time doesn't match the moment you clicked
Posted: Sat Oct 31, 2026 2:38 pm
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.