Which day is it? Broker time, host time, and the trading-day boundary

VigilDesk engineering notes

Every "daily" rule in trading software eventually asks the same question: which day is it right now? A daily loss cap, a daily trade counter, a daily reset, a "one trade per day" limit, a prop-firm drawdown window - they all resolve to a date, and the date comes from a clock.

The trouble is that a trading system has at least three clocks, and they can disagree:

ClockWhat it isWhere it lies to you
Host clockThe machine running your codeTimezone and DST are local conventions; a VPS move or a hosting migration changes it silently
Broker / server clockThe clock of the system that defines your trading sessionUsually the right answer - but it is not what datetime.now() returns
Last tick timeWhen the market last spokeStale during quiet periods and after a disconnect; it is data, not time

Why the failure is silent

When the host clock and the broker clock disagree, nothing throws. Every timestamp inside your system is consistent with every other timestamp - they are all wrong together. Logs read coherently. The tests pass, because tests usually run on one machine in one timezone. The only thing that changes is the moment your daily boundary is crossed, and that changes by hours.

Two ways this bites:

A public postmortem from another developer shows the same family from the other direction: a 16-second clock drift was enough to trip a kill switch by mistake. Sixteen seconds is not a rounding error in a system that compares timestamps to decide whether to stop trading.

Pick the clock that defines the day

For anything with a daily boundary, derive the date from the system that owns the boundary. In MetaTrader 5:

long TradingDayStamp()
{
   MqlDateTime t;
   TimeToStruct(TimeTradeServer(), t);   // broker clock, not TimeLocal(), not TimeCurrent()
   return((long)t.year * 10000 + t.mon * 100 + t.day);
}

TimeLocal() is your machine. TimeCurrent() is the last known market tick, which can lag when the market is quiet. TimeTradeServer() is the broker's clock - the one the broker will use when it decides whether you broke a rule.

Store the day, not just the counter

A daily counter without its date is meaningless. Persist the day stamp alongside the value, so that on the next start you can answer the only question that matters: does this counter belong to today? Without that field, a restored state is either ignored or - worse - applied forever, and a guard that latched "blocked" last Tuesday will keep you flat all week.

Log the drift, do not just monitor the clock

One line per cycle turns an invisible bug into a number:

[guard] host=2026.10.04 03:12:07 broker=2026.10.04 05:12:07 delta=+2.0h day=20261004

Anything above a threshold should be loud. If you only ever check that "the clock exists", you will never see a two-hour disagreement between two clocks that are both technically working.

Test the boundary, not the function

The bug lives at the boundary, so test the boundary: set the clock to 23:59:50, watch what the rollover does, then change the timezone and repeat. Unit-testing TradingDayStamp() at 12:00 on a Tuesday proves nothing about the only moment that matters.

The uncomfortable version

The nastiest variant is an asymmetric drift: fine after a reconnect, wrong on the first tick after a long disconnect. "Is my data stale?" must be answered by comparing timestamps explicitly - not by "did I receive a tick", because you always received something.

None of this is exotic. It is the ordinary consequence of asking a computer what day it is, and accepting an answer from a clock that does not own the calendar you care about.


Related notes

Get the guard

The free MT5 daily-loss guard that uses the broker's trading day (and persists it across restarts): https://xuks124.github.io/vigildesk/free.html

Risk disclosure

Algorithmic trading carries both technical and market risk. No tool eliminates the possibility of loss. Nothing on this page is investment advice, and no performance is implied or promised.