The simulated clock was an hour behind the price

15 September 2026

Simulation replays real market history. One round is one simulated hour, so the replay walks a timeline of hourly bars and hands out prices as it goes.

Each bar arrives with a timestamp. The natural reading is "this is the price at that time." That reading is wrong, and the direction it is wrong in matters.

The detail

An hourly bar stamped 13:00 covers the hour beginning at 13:00. Its closing price is the price at 13:59, not 13:00. The timestamp marks the start of the interval, not the moment the number was true.

So the replay was showing the clock as 13:00 while showing a price that was not reached until 13:59. Everything was internally consistent and everything was an hour out of step.

Why it mattered more than it sounds

On its own, a cosmetic offset. But the replay is built on a promise: brokers cannot see the future. The range summaries handed to a broker are truncated at the current simulated moment for exactly that reason, and that truncation is verified — I tested it against a synthetic series of monotonically rising prices at four positions in the window, where any leak from the future shows up immediately as a number too high to be possible.

An off-by-one-hour clock puts a crack in that promise. If the displayed moment is 13:00 and the price in hand is the 13:59 close, then the game is showing a price from the future relative to the time on its own clock. Small, but it is the same class of error as lookahead, and the whole value of practice mode is that it does not lie about what was knowable.

The fix

The simulated clock reports the bar's closing moment, not its opening stamp. Clock and price now describe the same instant.

Transferable version: every time-series API makes a choice about whether a timestamp labels the start or the end of its interval, and the documentation does not always say which. Check it against a bar you can verify by hand before building anything on top of it.

Previous: The clock announced a new day. The rebaseline checked the wrong one.

Next: Replay worked, except on the path everyone takes

All devlog posts