Which day is it? Broker time, host time, and the trading-day boundary
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:
| Clock | What it is | Where it lies to you |
|---|---|---|
| Host clock | The machine running your code | Timezone and DST are local conventions; a VPS move or a hosting migration changes it silently |
| Broker / server clock | The clock of the system that defines your trading session | Usually the right answer - but it is not what datetime.now() returns |
| Last tick time | When the market last spoke | Stale 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:
- The cap resets early. Your guard starts a fresh day while the broker is still inside the old one, so the account can spend the full daily risk allowance twice inside a single broker day.
- The cap never resets (or resets at a strange hour), and a guard that was supposed to be daily behaves like a weekly or an undefined one.
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
- Your daily loss limit resets when MetaTrader restarts. Here is the fix.
- What a kill switch should actually do (and the four ways they fail)
- Silent failures in trading automation: the three that cost the most
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.