Skip to content

Instantly share code, notes, and snippets.

@szmeku
Last active February 6, 2026 14:13
Show Gist options
  • Select an option

  • Save szmeku/5670eae695438de2a61241e2f44509ec to your computer and use it in GitHub Desktop.

Select an option

Save szmeku/5670eae695438de2a61241e2f44509ec to your computer and use it in GitHub Desktop.
//+------------------------------------------------------------------+
//| TimeSync_Checker.mq5 |
//| Time Synchronization Analyzer |
//| |
//| PURPOSE: |
//| This script measures time synchronization between: |
//| 1. Your local PC clock |
//| 2. The broker's trade server clock |
//| 3. True internet time (via HTTP API) |
//| |
//| It also calculates the TRUE NETWORK LATENCY - how long it |
//| takes for a tick to travel from the server to your PC. |
//| |
//| WHY THIS MATTERS: |
//| - Server clocks can run fast/slow (affects execution times) |
//| - Network latency affects how "stale" your prices are |
//| - Clock drift can cause issues with time-sensitive strategies |
//| |
//| IMPORTANT LIMITATIONS: |
//| - MQ5 has NO real-time server time query function |
//| - TimeCurrent() returns the timestamp of the LAST RECEIVED TICK |
//| - If no ticks for 5 seconds, that time is 5 seconds stale |
//| - We use HTTP time APIs as the "true time" reference |
//| |
//| SETUP REQUIRED FOR HTTP TIME CHECK: |
//| Go to: Tools -> Options -> Expert Advisors |
//| Check: "Allow WebRequest for listed URL" |
//| Add: http://worldtimeapi.org |
//| http://timeapi.io |
//| |
//| SAFETY: This script is READ-ONLY. It does NOT: |
//| - Place any trades or orders |
//| - Modify any settings |
//| - Write any files |
//| - Change anything on your account |
//+------------------------------------------------------------------+
#property copyright "Time Sync Checker"
#property version "3.00"
#property script_show_inputs
#property strict
//--- Input Parameters
input int SampleCount = 10; // Number of tick samples to collect
input int SampleDelayMs = 500; // Delay between samples (ms)
input bool UseHTTPTime = true; // Check time via HTTP API (requires WebRequest permission)
input string CustomSymbol = ""; // Symbol for tick analysis (empty = current chart)
//--- Global variables for timezone calculations
long g_serverGmtOffsetMs = 0; // Server timezone offset from GMT in milliseconds
long g_serverClockDriftMs = 0; // How much server clock differs from true time (ms)
bool g_serverDriftKnown = false; // Whether we successfully measured server drift
bool g_tickLatencyMeasured = false; // Whether we got valid tick latency measurements
long g_measuredTickLatencyMs = 0; // Actual measured tick latency (if available)
long g_tickAgeMs = 0; // How old is the last tick
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
string symbol = (CustomSymbol == "") ? Symbol() : CustomSymbol;
Print("=============================================================");
Print(" TIME SYNCHRONIZATION & LATENCY ANALYZER v3.0 ");
Print("=============================================================");
Print("Analysis started at: ", TimeToString(TimeLocal(), TIME_DATE|TIME_SECONDS));
Print("Symbol for analysis: ", symbol);
//=========================================================================
// SECTION 1: BASIC TIME READINGS
// Collect timestamps from all available sources
//=========================================================================
datetime localTime = TimeLocal(); // Your PC's local time (with timezone)
datetime serverTime = TimeCurrent(); // Last tick's server timestamp (may be stale!)
datetime gmtTime = TimeGMT(); // Your PC's time converted to GMT/UTC
// Try to get the freshest server time by checking multiple symbols
datetime freshestServerTime = GetFreshestServerTime();
string freshestSource = "TimeCurrent()";
if(freshestServerTime > serverTime)
{
serverTime = freshestServerTime;
freshestSource = "Multi-symbol scan";
}
//=========================================================================
// SECTION 2: TIMEZONE DETECTION
// Calculate timezone offsets to normalize all times to GMT
//=========================================================================
long localGmtOffsetSec = (long)localTime - (long)gmtTime; // Your PC's timezone
long serverGmtOffsetSec = (long)serverTime - (long)gmtTime; // Server's timezone
g_serverGmtOffsetMs = serverGmtOffsetSec * 1000;
int localTzHours = (int)(localGmtOffsetSec / 3600);
int serverTzHours = (int)(serverGmtOffsetSec / 3600);
Print("");
Print(">>> SECTION 1: TIME READINGS <<<");
Print("Your PC Local Time: ", TimeToString(localTime, TIME_DATE|TIME_SECONDS));
Print("Your PC as GMT: ", TimeToString(gmtTime, TIME_DATE|TIME_SECONDS));
Print("Trade Server Time: ", TimeToString(serverTime, TIME_DATE|TIME_SECONDS));
Print("Server time source: ", freshestSource);
Print("");
Print("IMPORTANT: Server time is from the LAST RECEIVED TICK.");
Print(" MQ5 cannot query real-time server clock!");
Print("");
Print(">>> SECTION 2: TIMEZONE INFO <<<");
Print("Your PC Timezone: GMT", FormatTimezone(localGmtOffsetSec));
Print("Server Timezone: GMT", FormatTimezone(serverGmtOffsetSec));
Print("Difference: ", serverTzHours - localTzHours, " hours");
//=========================================================================
// SECTION 3: CLOCK DRIFT (PC vs Server, both normalized to GMT)
// This removes timezone differences to show actual clock drift
//=========================================================================
long localAsGmt = (long)localTime - localGmtOffsetSec;
long serverAsGmt = (long)serverTime - serverGmtOffsetSec;
long pcServerDriftSec = localAsGmt - serverAsGmt;
Print("");
Print(">>> SECTION 3: PC vs SERVER CLOCK DRIFT <<<");
Print("(Both times normalized to GMT, timezone removed)");
Print("Drift: ", pcServerDriftSec, " seconds");
if(MathAbs(pcServerDriftSec) <= 1)
Print("Status: SYNCHRONIZED - Both clocks agree");
else if(pcServerDriftSec > 0)
Print("Status: Your PC is ", pcServerDriftSec, "s AHEAD of server");
else
Print("Status: Your PC is ", MathAbs(pcServerDriftSec), "s BEHIND server");
Print("");
Print("NOTE: This only shows if PC and server agree with each other,");
Print(" NOT whether either is correct! Use HTTP check for truth.");
//=========================================================================
// SECTION 4: TRUE TIME CHECK VIA HTTP
// Query internet time APIs to get the REAL time and measure drift
//=========================================================================
if(UseHTTPTime)
{
Print("");
Print(">>> SECTION 4: TRUE INTERNET TIME CHECK <<<");
Print("Querying internet time servers for ground truth...");
CheckInternetTime(serverGmtOffsetSec);
}
//=========================================================================
// SECTION 5: TICK LATENCY ANALYSIS
// Measure how long ticks take to travel from server to your PC
//=========================================================================
Print("");
Print(">>> SECTION 5: TICK LATENCY ANALYSIS <<<");
Print("Symbol: ", symbol);
Print("");
Print("WHAT WE'RE MEASURING:");
Print(" Tick Latency = Time when you RECEIVE tick - Time when server CREATED tick");
Print("");
Print("HOW TO INTERPRET:");
Print(" Positive latency = Normal network delay (tick took X ms to reach you)");
Print(" Negative latency = IMPOSSIBLE in physics, means server clock is FAST");
Print(" High variance = Network instability or stale ticks (low liquidity)");
AnalyzeTickLatency(symbol, serverGmtOffsetSec);
//=========================================================================
// SECTION 6: TERMINAL AND ACCOUNT INFO
//=========================================================================
Print("");
Print(">>> SECTION 6: CONNECTION INFO <<<");
long pingUs = TerminalInfoInteger(TERMINAL_PING_LAST);
Print("Network Ping: ", pingUs, " us (", DoubleToString((double)pingUs / 1000.0, 2), " ms)");
Print(" This is MT5's internal ping measurement");
Print("Connected: ", TerminalInfoInteger(TERMINAL_CONNECTED) ? "Yes" : "No");
Print("Trade Allowed: ", TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) ? "Yes" : "No");
Print("");
Print("Account Server: ", AccountInfoString(ACCOUNT_SERVER));
Print("Account Company: ", AccountInfoString(ACCOUNT_COMPANY));
Print("Account Login: ", AccountInfoInteger(ACCOUNT_LOGIN));
//=========================================================================
// SECTION 7: FINAL SUMMARY
//=========================================================================
Print("");
Print(">>> SECTION 7: FINAL SUMMARY <<<");
PrintFinalSummary(pingUs);
Print("");
Print("=============================================================");
Print(" ANALYSIS COMPLETE ");
Print("=============================================================");
}
//+------------------------------------------------------------------+
//| Analyze tick latency with proper clock drift compensation |
//+------------------------------------------------------------------+
void AnalyzeTickLatency(string symbol, long serverGmtOffsetSec)
{
MqlTick lastTick;
if(!SymbolInfoTick(symbol, lastTick))
{
Print("ERROR: Could not get tick data for ", symbol);
Print(" Market may be closed or symbol not available.");
return;
}
//--- Check how OLD the last tick is
long tickTimeGmtMs = (long)lastTick.time_msc - g_serverGmtOffsetMs;
datetime nowGmt = TimeGMT();
long nowGmtMs = (long)nowGmt * 1000 + (long)(GetTickCount64() % 1000);
g_tickAgeMs = nowGmtMs - tickTimeGmtMs;
// Show last tick info
PrintTickInfo(lastTick, "Last available tick", serverGmtOffsetSec);
// WARN if tick is old
Print("");
if(g_tickAgeMs > 60000) // More than 1 minute old
{
long ageSeconds = g_tickAgeMs / 1000;
long ageMinutes = ageSeconds / 60;
long ageHours = ageMinutes / 60;
Print("!!! WARNING: LAST TICK IS VERY OLD !!!");
if(ageHours > 0)
Print(" Tick age: ", ageHours, " hours, ", ageMinutes % 60, " minutes");
else
Print(" Tick age: ", ageMinutes, " minutes, ", ageSeconds % 60, " seconds");
Print(" Market is likely CLOSED for this symbol!");
Print(" Run this script on an ACTIVE symbol (EURUSD, XAUUSD, BTCUSD)");
Print("");
}
else if(g_tickAgeMs > 5000) // More than 5 seconds
{
Print("NOTE: Last tick is ", g_tickAgeMs / 1000, " seconds old.");
Print(" Symbol may have low liquidity.");
Print("");
}
if(SampleCount < 1) return;
Print("");
Print("Collecting ", SampleCount, " tick samples (", SampleDelayMs, "ms apart)...");
Print("");
// Arrays to store measurements
long latencies[];
ArrayResize(latencies, 0);
long totalRawLatency = 0;
long minRawLatency = LONG_MAX;
long maxRawLatency = LONG_MIN;
int validSamples = 0;
int staleSamples = 0;
for(int i = 0; i < SampleCount; i++)
{
Sleep(SampleDelayMs);
MqlTick tick;
if(!SymbolInfoTick(symbol, tick))
continue;
// Calculate raw latency (before drift compensation)
// tick.time_msc = server time when tick was created (in ms, server timezone)
// We convert to GMT for comparison
long tickTimeGmtMs = (long)tick.time_msc - g_serverGmtOffsetMs;
// Current time in GMT (ms precision using GetTickCount64 for sub-second)
datetime nowGmt = TimeGMT();
ulong tickCount = GetTickCount64();
long nowGmtMs = (long)nowGmt * 1000 + (long)(tickCount % 1000);
// Raw latency = now - tick creation time
long rawLatencyMs = nowGmtMs - tickTimeGmtMs;
// Check if this is a new tick or stale
bool isNewTick = (tick.time_msc != lastTick.time_msc);
if(isNewTick)
{
// Store for statistics
int idx = ArraySize(latencies);
ArrayResize(latencies, idx + 1);
latencies[idx] = rawLatencyMs;
totalRawLatency += rawLatencyMs;
if(rawLatencyMs < minRawLatency) minRawLatency = rawLatencyMs;
if(rawLatencyMs > maxRawLatency) maxRawLatency = rawLatencyMs;
validSamples++;
Print("Sample ", StringFormat("%2d", i+1), ": Raw=", StringFormat("%6d", rawLatencyMs), " ms",
" | Bid=", DoubleToString(tick.bid, 5),
" | Ask=", DoubleToString(tick.ask, 5),
" | NEW TICK");
}
else
{
staleSamples++;
Print("Sample ", StringFormat("%2d", i+1), ": Raw=", StringFormat("%6d", rawLatencyMs), " ms",
" | (stale - same tick as before)");
}
lastTick = tick;
}
// Statistics
Print("");
Print("--- RAW LATENCY STATISTICS ---");
Print("(Raw = Your PC time - Server tick timestamp, before any correction)");
Print("");
if(validSamples == 0)
{
Print("No new ticks received during sampling period!");
Print("Market may be closed or symbol has very low liquidity.");
return;
}
long avgRawLatency = totalRawLatency / validSamples;
Print("New ticks received: ", validSamples, " out of ", SampleCount, " samples");
Print("Stale tick samples: ", staleSamples, " (no new data arrived)");
Print("");
Print("Average raw latency: ", avgRawLatency, " ms");
Print("Minimum raw latency: ", minRawLatency, " ms");
Print("Maximum raw latency: ", maxRawLatency, " ms");
Print("Jitter (range): ", maxRawLatency - minRawLatency, " ms");
// Calculate standard deviation for jitter analysis
if(validSamples > 1)
{
double variance = 0;
for(int i = 0; i < ArraySize(latencies); i++)
{
double diff = (double)(latencies[i] - avgRawLatency);
variance += diff * diff;
}
variance /= validSamples;
double stdDev = MathSqrt(variance);
Print("Std deviation: ", DoubleToString(stdDev, 1), " ms");
}
//=========================================================================
// TRUE LATENCY CALCULATION
// If we know the server clock drift, we can calculate REAL network latency
//=========================================================================
Print("");
Print("--- TRUE NETWORK LATENCY ---");
if(g_serverDriftKnown)
{
// True latency = Raw latency - Server clock drift
// If server is 200ms fast, raw shows -200ms, true = -200 - (-200) = 0 + network delay
long trueAvgLatency = avgRawLatency - g_serverClockDriftMs;
long trueMinLatency = minRawLatency - g_serverClockDriftMs;
long trueMaxLatency = maxRawLatency - g_serverClockDriftMs;
// Store for final summary
g_tickLatencyMeasured = true;
g_measuredTickLatencyMs = trueAvgLatency;
Print("Server clock drift: ", g_serverClockDriftMs, " ms (from HTTP check)");
Print("");
Print("TRUE Average latency: ", trueAvgLatency, " ms");
Print("TRUE Minimum latency: ", trueMinLatency, " ms");
Print("TRUE Maximum latency: ", trueMaxLatency, " ms");
Print("");
Print("INTERPRETATION:");
Print("This is the REAL network delay - how long ticks take to reach you.");
Print("You should expect your prices to be ~", trueAvgLatency, " ms behind the market.");
if(trueAvgLatency < 50)
Print("EXCELLENT: Sub-50ms latency is very good for retail.");
else if(trueAvgLatency < 100)
Print("GOOD: Under 100ms is normal for retail connections.");
else if(trueAvgLatency < 200)
Print("FAIR: 100-200ms is acceptable but not ideal.");
else
Print("POOR: Over 200ms latency may affect execution quality.");
}
else
{
Print("Server clock drift is UNKNOWN (HTTP check failed or disabled).");
Print("");
Print("Without true time reference, we cannot separate:");
Print(" - Actual network latency");
Print(" - Server clock drift");
Print("");
Print("ESTIMATED based on raw data:");
if(avgRawLatency < 0)
{
Print("Negative raw latency indicates server clock is FAST.");
Print("Server appears to be ~", MathAbs(avgRawLatency), " ms ahead of true time.");
Print("True network latency is likely 50-150ms (typical for retail).");
}
else if(avgRawLatency < 500)
{
Print("Raw latency ", avgRawLatency, " ms could be:");
Print(" - Pure network delay (if server clock is accurate)");
Print(" - Mix of network delay + slow server clock");
}
else
{
Print("High raw latency (", avgRawLatency, " ms) likely includes stale ticks.");
Print("Minimum value (", minRawLatency, " ms) is more representative.");
}
Print("");
Print("Enable HTTP time check for accurate measurement!");
}
}
//+------------------------------------------------------------------+
//| Check time against Internet time API for ground truth |
//+------------------------------------------------------------------+
void CheckInternetTime(long serverGmtOffsetSec)
{
// HTTP Time API endpoints (return UTC/GMT time)
string urls[] = {
"http://worldtimeapi.org/api/timezone/Etc/UTC",
"http://timeapi.io/api/Time/current/zone?timeZone=UTC"
};
string urlNames[] = {"WorldTimeAPI", "TimeAPI.io"};
bool gotTime = false;
for(int u = 0; u < ArraySize(urls); u++)
{
if(gotTime) break;
string url = urls[u];
string cookie = "", headers = "";
char post[], result[];
int timeout = 5000;
Print("");
Print("Querying ", urlNames[u], "...");
//--- PRECISE TIMING: Capture local time BEFORE and AFTER request
// This lets us calculate the midpoint when the server generated its response
ulong startTickCount = GetTickCount64();
datetime startGmt = TimeGMT();
long startGmtMs = (long)startGmt * 1000 + (long)(startTickCount % 1000);
int res = WebRequest("GET", url, cookie, NULL, timeout, post, 0, result, headers);
ulong endTickCount = GetTickCount64();
datetime endGmt = TimeGMT();
long endGmtMs = (long)endGmt * 1000 + (long)(endTickCount % 1000);
ulong httpRoundtripMs = endTickCount - startTickCount;
// The server processed our request at approximately the midpoint
// (assuming symmetric upload/download latency)
long midpointLocalGmtMs = startGmtMs + (long)(httpRoundtripMs / 2);
//--- Handle errors
if(res == -1)
{
int err = GetLastError();
if(err == 4014)
{
Print("ERROR: WebRequest not allowed!");
Print("TO FIX: Go to Tools -> Options -> Expert Advisors");
Print(" Check 'Allow WebRequest for listed URL'");
Print(" Add: ", url);
}
else
{
Print("ERROR: WebRequest failed (code ", err, ")");
}
continue;
}
if(res != 200)
{
Print("ERROR: HTTP ", res, " response");
continue;
}
//--- Parse response
string response = CharArrayToString(result);
datetime httpUtcTime = 0;
long httpUtcMs = 0;
if(u == 0) // WorldTimeAPI returns unixtime
{
httpUtcTime = ParseUnixTime(response);
httpUtcMs = ParseUnixTimeMs(response);
}
else // TimeAPI.io returns ISO datetime
{
httpUtcTime = ParseISOTime(response);
httpUtcMs = (long)httpUtcTime * 1000; // No ms precision
}
if(httpUtcTime == 0)
{
Print("ERROR: Could not parse time from response");
continue;
}
if(httpUtcMs == 0)
httpUtcMs = (long)httpUtcTime * 1000;
gotTime = true;
//=========================================================================
// CALCULATE DRIFT VALUES
//=========================================================================
// PC drift = Our local GMT time at midpoint - True internet time
// Positive = PC is ahead, Negative = PC is behind
long pcDriftMs = midpointLocalGmtMs - httpUtcMs;
// For server drift, get fresh tick and compensate for elapsed time
datetime freshServerTime = GetFreshestServerTime();
long freshServerGmtMs = ((long)freshServerTime - serverGmtOffsetSec) * 1000;
long elapsedSinceMidpoint = endGmtMs - midpointLocalGmtMs;
long serverAtMidpointMs = freshServerGmtMs - elapsedSinceMidpoint;
long serverDriftMs = serverAtMidpointMs - httpUtcMs;
// Store for tick latency correction
g_serverClockDriftMs = serverDriftMs;
g_serverDriftKnown = true;
//=========================================================================
// DISPLAY RESULTS
//=========================================================================
Print("");
Print("--- ", urlNames[u], " Results ---");
Print("HTTP Roundtrip: ", httpRoundtripMs, " ms");
Print(" (Request -> Server -> Response took ", httpRoundtripMs, " ms total)");
Print("One-way estimate: ~", httpRoundtripMs/2, " ms");
Print(" (Assuming symmetric latency, each direction ~", httpRoundtripMs/2, " ms)");
Print("");
Print("True Internet Time: ", TimeToString(httpUtcTime, TIME_DATE|TIME_SECONDS),
".", StringFormat("%03d", (int)(httpUtcMs % 1000)));
Print("Your PC at midpoint: ", TimeToString((datetime)(midpointLocalGmtMs/1000), TIME_DATE|TIME_SECONDS),
".", StringFormat("%03d", (int)(midpointLocalGmtMs % 1000)));
Print("");
Print(">>> YOUR PC CLOCK ACCURACY <<<");
Print("Drift from true time: ", pcDriftMs, " ms");
if(MathAbs(pcDriftMs) < 100)
Print("Status: EXCELLENT - Your PC clock is very accurate");
else if(MathAbs(pcDriftMs) < 500)
Print("Status: GOOD - Minor drift, acceptable");
else if(pcDriftMs > 0)
Print("Status: Your PC is ", pcDriftMs, " ms AHEAD of true time");
else
Print("Status: Your PC is ", MathAbs(pcDriftMs), " ms BEHIND true time");
Print("");
Print(">>> TRADE SERVER CLOCK ACCURACY <<<");
Print("Drift from true time: ", serverDriftMs, " ms");
if(MathAbs(serverDriftMs) < 100)
Print("Status: EXCELLENT - Server clock is accurate");
else if(serverDriftMs > 0)
{
Print("Status: SERVER CLOCK IS FAST by ", serverDriftMs, " ms!");
Print(" Ticks are timestamped in the 'future'");
}
else
{
Print("Status: SERVER CLOCK IS SLOW by ", MathAbs(serverDriftMs), " ms");
Print(" Ticks are timestamped in the 'past'");
}
Print("");
Print(">>> WHAT THIS MEANS FOR TRADING <<<");
long relDrift = serverDriftMs - pcDriftMs;
Print("Server relative to your PC: ", relDrift, " ms");
if(MathAbs(relDrift) < 100)
{
Print("Your PC and server are well synchronized.");
Print("Tick timestamps should be reliable.");
}
else if(relDrift > 0)
{
Print("Server is ", relDrift, " ms FASTER than your PC.");
Print("Raw tick latency will appear ", relDrift, " ms too LOW (or negative).");
}
else
{
Print("Server is ", MathAbs(relDrift), " ms SLOWER than your PC.");
Print("Raw tick latency will appear ", MathAbs(relDrift), " ms too HIGH.");
}
}
if(!gotTime)
{
Print("");
Print("Could not verify time from any HTTP source.");
Print("");
Print("TO ENABLE HTTP TIME CHECK:");
Print("1. Go to: Tools -> Options -> Expert Advisors");
Print("2. Check: 'Allow WebRequest for listed URL'");
Print("3. Add these URLs to the list:");
Print(" http://worldtimeapi.org");
Print(" http://timeapi.io");
Print("4. Click OK and run this script again");
}
}
//+------------------------------------------------------------------+
//| Print final summary with actionable insights |
//+------------------------------------------------------------------+
void PrintFinalSummary(long pingUs)
{
Print("Network Ping (MT5): ", pingUs/1000, " ms");
//--- Check if tick data was too old to be useful
bool tickDataUsable = (g_tickAgeMs < 60000); // Less than 1 minute old
if(!tickDataUsable)
{
Print("");
Print("!!! TICK DATA NOT USABLE !!!");
Print("Last tick is ", g_tickAgeMs / 1000 / 60, " minutes old!");
Print("Cannot measure true latency on a CLOSED market.");
Print("");
Print("TO GET ACCURATE RESULTS:");
Print(" Run this script on an ACTIVE symbol:");
Print(" - EURUSD (forex, 24h Mon-Fri)");
Print(" - XAUUSD (gold, 24h Mon-Fri)");
Print(" - BTCUSD (crypto, 24/7)");
Print("");
Print("The numbers below are NOT reliable!");
Print("-------------------------------------------");
}
if(g_serverDriftKnown)
{
Print("Server Clock Drift: ", g_serverClockDriftMs, " ms vs true time");
if(MathAbs(g_serverClockDriftMs) > 500)
{
Print(" NOTE: Large drift may be measurement error");
Print(" (HTTP API has only 1-second precision)");
}
else if(g_serverClockDriftMs > 100)
Print(" WARNING: Server clock is FAST!");
else if(g_serverClockDriftMs < -100)
Print(" WARNING: Server clock is SLOW!");
else
Print(" Server clock is accurate");
}
else
{
Print("Server Clock Drift: UNKNOWN (enable HTTP check)");
}
//--- TRUE TICK LATENCY - the main number user wants
Print("");
Print("=== YOUR TRUE TICK LATENCY ===");
if(g_tickLatencyMeasured && tickDataUsable)
{
// We have actual measurements from fresh ticks
Print("MEASURED: ", g_measuredTickLatencyMs, " ms");
Print(" (Based on actual tick arrival times)");
Print("");
Print("This means your prices are ~", g_measuredTickLatencyMs, " ms behind the market.");
if(g_measuredTickLatencyMs < 50)
Print("Rating: EXCELLENT");
else if(g_measuredTickLatencyMs < 100)
Print("Rating: GOOD");
else if(g_measuredTickLatencyMs < 200)
Print("Rating: FAIR");
else
Print("Rating: POOR - consider VPS closer to broker");
}
else if(tickDataUsable && g_serverDriftKnown)
{
// No new ticks but market should be open - estimate from ping
long estimatedLatency = pingUs/1000 + 20;
Print("ESTIMATED: ~", estimatedLatency, " ms");
Print(" (Based on ping, no fresh ticks received)");
}
else
{
Print("UNKNOWN - Market is closed or no ticks received");
Print(" Cannot measure latency without fresh ticks!");
Print("");
Print("Ping suggests: ~", pingUs/1000 + 20, " ms (rough estimate only)");
}
Print("");
Print("KEY TAKEAWAYS:");
if(pingUs < 100000)
Print("- Network ping is FAST (<100ms)");
else if(pingUs < 200000)
Print("- Network ping is NORMAL for retail (100-200ms)");
else
Print("- Network ping is SLOW (>200ms) - consider VPS closer to server");
if(!tickDataUsable)
{
Print("- MARKET IS CLOSED - run on active symbol for accurate results!");
}
else if(g_tickLatencyMeasured)
{
if(g_measuredTickLatencyMs < 150)
Print("- Your tick latency is acceptable for most trading strategies");
else
Print("- High latency may affect scalping/HFT strategies");
}
if(g_serverDriftKnown && MathAbs(g_serverClockDriftMs) > 200)
{
Print("- Server clock has drift (", g_serverClockDriftMs, " ms) - timestamps may be off");
}
}
//+------------------------------------------------------------------+
//| Get freshest server time by checking multiple active symbols |
//+------------------------------------------------------------------+
datetime GetFreshestServerTime()
{
datetime freshest = TimeCurrent();
// Check popular liquid symbols for fresher tick times
string symbols[] = {
"EURUSD", "GBPUSD", "USDJPY", "XAUUSD", "BTCUSD",
"US30", "US500", "NAS100", "GER40", "AUDUSD"
};
// Check current symbol
datetime currentSymbolTime = (datetime)SymbolInfoInteger(Symbol(), SYMBOL_TIME);
if(currentSymbolTime > freshest)
freshest = currentSymbolTime;
// Check other liquid symbols
for(int i = 0; i < ArraySize(symbols); i++)
{
if(SymbolInfoInteger(symbols[i], SYMBOL_EXIST) == 0) continue;
if(SymbolInfoInteger(symbols[i], SYMBOL_SELECT) == 0) continue;
datetime symTime = (datetime)SymbolInfoInteger(symbols[i], SYMBOL_TIME);
if(symTime > freshest)
freshest = symTime;
}
return freshest;
}
//+------------------------------------------------------------------+
//| Parse Unix timestamp from WorldTimeAPI JSON |
//+------------------------------------------------------------------+
datetime ParseUnixTime(string json)
{
int pos = StringFind(json, "\"unixtime\":");
if(pos < 0) return 0;
pos += 11;
int endPos = pos;
while(endPos < StringLen(json))
{
ushort ch = StringGetCharacter(json, endPos);
if(ch < '0' || ch > '9') break;
endPos++;
}
return (datetime)StringToInteger(StringSubstr(json, pos, endPos - pos));
}
//+------------------------------------------------------------------+
//| Parse Unix timestamp with milliseconds from WorldTimeAPI |
//+------------------------------------------------------------------+
long ParseUnixTimeMs(string json)
{
datetime unixSec = ParseUnixTime(json);
if(unixSec == 0) return 0;
// Extract milliseconds from datetime field
int pos = StringFind(json, "\"datetime\":\"");
if(pos < 0) return (long)unixSec * 1000;
int dotPos = StringFind(json, ".", pos);
if(dotPos < 0) return (long)unixSec * 1000;
string msStr = StringSubstr(json, dotPos + 1, 3);
return (long)unixSec * 1000 + StringToInteger(msStr);
}
//+------------------------------------------------------------------+
//| Parse ISO 8601 datetime from TimeAPI.io JSON |
//+------------------------------------------------------------------+
datetime ParseISOTime(string json)
{
int pos = StringFind(json, "\"dateTime\":\"");
if(pos < 0)
{
pos = StringFind(json, "\"datetime\":\"");
if(pos < 0) return 0;
}
pos += 12;
string dtStr = StringSubstr(json, pos, 19);
// Convert from ISO (YYYY-MM-DDTHH:MM:SS) to MT5 format
string mt5Str = StringSubstr(dtStr, 0, 4) + "." +
StringSubstr(dtStr, 5, 2) + "." +
StringSubstr(dtStr, 8, 2) + " " +
StringSubstr(dtStr, 11, 2) + ":" +
StringSubstr(dtStr, 14, 2) + ":" +
StringSubstr(dtStr, 17, 2);
return StringToTime(mt5Str);
}
//+------------------------------------------------------------------+
//| Format timezone offset as readable string |
//+------------------------------------------------------------------+
string FormatTimezone(long offsetSeconds)
{
string sign = offsetSeconds >= 0 ? "+" : "-";
offsetSeconds = MathAbs(offsetSeconds);
int hours = (int)(offsetSeconds / 3600);
int mins = (int)((offsetSeconds % 3600) / 60);
if(mins == 0)
return StringFormat("%s%d", sign, hours);
else
return StringFormat("%s%d:%02d", sign, hours, mins);
}
//+------------------------------------------------------------------+
//| Print detailed tick information |
//+------------------------------------------------------------------+
void PrintTickInfo(MqlTick &tick, string label, long serverGmtOffsetSec)
{
long tickGmtMs = (long)tick.time_msc - (serverGmtOffsetSec * 1000);
datetime tickGmt = (datetime)(tickGmtMs / 1000);
Print("");
Print("--- ", label, " ---");
Print("Server timestamp: ", TimeToString(tick.time, TIME_DATE|TIME_SECONDS),
".", StringFormat("%03d", (int)(tick.time_msc % 1000)));
Print("As GMT: ", TimeToString(tickGmt, TIME_DATE|TIME_SECONDS),
".", StringFormat("%03d", (int)(tickGmtMs % 1000)));
Print("Bid: ", DoubleToString(tick.bid, 5),
" | Ask: ", DoubleToString(tick.ask, 5),
" | Spread: ", DoubleToString((tick.ask - tick.bid) / SymbolInfoDouble(Symbol(), SYMBOL_POINT), 1), " pts");
}
//+------------------------------------------------------------------+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment