Your Bot Is Trading a Candle That Doesn't Actually Exist Yet
The log says NEW BAR DETECTED. Your bot checked the pattern conditions and fired. What it read as a closed candle may not have been closed at all — and the gap between those two things is where a specific, quiet class of bug lives.
Every live trading bot built around candle-close logic has the same core loop: wait for a new bar to appear, calculate indicators and events on the most recently closed candle, check pattern conditions, and act if everything matches. This is the correct approach in principle — acting on a closed candle rather than a live, still-forming one is exactly what prevents a bot from entering on a signal that could disappear before the candle actually finishes. It’s good practice, and it’s in every reasonable guide on building this kind of system.
What doesn’t get discussed is a specific and quiet failure mode hiding inside the phrase “new bar detected”: your bot’s definition of “closed” and your broker’s definition of “closed” are not necessarily checking the same thing at the same moment, and the gap between them is where a genuinely underdiscussed category of bug lives.
What “candle closed” actually means mechanically
A candle on your chart is an aggregation — a bucket of individual price ticks collapsed into an open, high, low, and close over a fixed time window. The M15 candle that just appeared on your chart didn’t get built all at once the instant the clock hit the 15-minute mark. It was built tick by tick throughout the previous 15 minutes, and the final tick that determines its actual close price is whatever the last trade was before the boundary.
Here’s the part that matters: your bot typically detects a “new bar” by polling for a new timestamp to appear in the historical data feed, or by checking whether the number of returned bars has increased. The moment that detection fires depends entirely on when the broker’s backend finalizes and publishes that bar into the feed your API call is reading from — and that is not instantaneous, and it is not perfectly consistent bar to bar.
During normal, liquid market conditions, this finalization happens fast enough that the gap is negligible. During periods of thin liquidity or unusual tick delivery — the transition around rollover, a sudden volatility spike, a brief feed hiccup on the broker’s side — there can be a real, measurable delay between the wall-clock moment a candle’s time window ends and the moment the broker’s data feed actually reports that candle as complete and available. Your bot’s “new bar detected” log entry is describing the moment your code observed the new data, not the actual moment of market close. Most of the time these are close enough not to matter. Sometimes they aren’t, and nothing in a typical bot’s logic distinguishes between those two situations.
Why this creates a genuinely different kind of false signal
This is not the same problem as a repainting indicator or an unstable in-progress candle, both of which get discussed reasonably often. This is specifically about the assumption baked into candle-close logic that “the data I just received represents a fully finalized, unchangeable bar.”
In most retail-facing MT5 setups, once a bar is returned as historical data, it generally is finalized and won’t be revised. But the timing of when it becomes available, and what the very last tick or two feeding into its close actually reflect, can occasionally include a data point that arrived late or was queued during a burst of volatility, meaning the close price your pattern conditions are evaluating against might be a slightly different number than what a chart rendered with a different data provider, or even the same broker’s own charting terminal, would show for the exact same timestamp at the exact same wall-clock moment. Small discrepancies here rarely matter for most pattern conditions. They matter a great deal for patterns that are sensitive to threshold proximity — a Bollinger Band touch that depends on the close being within a few pips of the band, or a pivot detection that depends on a specific candle being the local extreme by a narrow margin.
If your pattern discovery engine was built and backtested entirely against historical data pulled after the fact — clean, fully settled, no timing ambiguity — and then deployed live where candle finalization timing has this quiet variability, you can get a live signal firing on data that’s subtly different from what the same pattern would have seen in an idealized backtest, even though nothing about your code or your broker’s system is technically broken. This is a data integrity problem that exists specifically at the boundary between backtesting (clean, settled, retrospective) and live execution (real-time, occasionally delayed, occasionally ambiguous), and it rarely gets acknowledged as a distinct category of risk separate from spread, slippage, or overfitting.
The clock sync problem compounds this
Separately, and often interacting with the above, is the question of whose clock your bot is actually trusting. A bot polling MT5 for new bars is checking against the broker server’s clock, delivered through the terminal, while your bot’s own scheduling logic — if it’s running any local timing checks, like “only check for new bars every 60 seconds” — is running against your machine’s local clock. If your local machine’s clock has drifted even a few seconds from true time, or if there’s meaningful network latency between your bot process and the MT5 terminal it’s querying, you can end up in a situation where your polling loop checks for a new bar slightly before the broker has actually finalized and published it, gets a stale “no new bar yet” response, and then picks up the actual new bar on a subsequent poll — meaning the effective delay between true candle close and your bot’s reaction to it stretches out by however long your polling interval is, which for many hobbyist setups is checked only once every 30 to 60 seconds specifically to avoid hammering the API.
This means your bot’s own log, showing a clean timestamp for “NEW BAR DETECTED,” is describing when your polling loop happened to notice the new data, which can lag true candle close by anywhere from a few seconds to the better part of a minute depending on your polling frequency and network conditions — an amount of time that’s usually irrelevant for a 15-minute candle strategy, but becomes proportionally significant for anything running on M1 or M5 timeframes, where a minute of lag is a meaningful fraction of the entire candle’s lifespan.
How this connects back to pattern validity specifically
This matters more for some pattern types than others, and it’s worth being specific about which.
Fair Value Gap detection is particularly sensitive to this, because FVGs are defined by the precise relationship between the high of one candle and the low of a candle two bars later — a narrow geometric condition. If the exact close or open value of any candle in that three-bar sequence is subtly different due to a late-arriving tick being included or excluded, an FVG that should have formed might not register, or one that shouldn’t have formed might appear, purely as an artifact of exactly which ticks got bucketed into which bar before finalization.
Pivot high and low detection has the same sensitivity, since it depends on a specific candle being higher or lower than its neighbors by any margin, however small. A pivot that exists by a single pip is exactly the kind of signal that can flip in or out of existence depending on data finalization timing, in a way that a pivot formed by a wide, obvious margin never would.
Threshold-proximity patterns in general are more exposed than momentum-based ones. A pattern requiring “RSI crossed above 50” is less sensitive to this than a pattern requiring “price touched exactly the lower Bollinger Band,” because a crossing event is somewhat more robust to small close-price variance than an exact touch condition is. If you’re building patterns heavily reliant on precise touches or narrow geometric definitions, this candle finalization timing issue is a more relevant risk to you than if you’re working with broader momentum or crossing conditions.
What to actually do about it
This isn’t a problem you can fully eliminate, because it’s inherent to how live data delivery works, but there are concrete ways to reduce your exposure to it.
Add a small deliberate delay before evaluating a newly detected bar, rather than acting the instant your polling loop notices new data. Waiting an extra handful of seconds after “new bar detected” before pulling the bar’s data for pattern evaluation gives any last-moment tick settling a chance to finish, at the cost of a small amount of execution speed you almost never actually need for a pattern-based strategy running on M15 or higher.
Log the actual bar timestamp alongside your bot’s detection timestamp, not just the detection event itself. If you’re not already recording both values side by side in your log file, you have no way to audit after the fact whether a specific signal fired against genuinely finalized data or against something that arrived unusually late. This is a cheap addition to your logging that pays off specifically when you’re trying to diagnose why a pattern behaved differently live than in backtesting.
Be more cautious with threshold-proximity patterns on lower timeframes specifically, since that’s where this problem is proportionally largest relative to the candle’s own duration. If a pattern’s edge depends on a narrow geometric condition and you’re running it on M1 or M5, the finalization timing issue described here deserves more weight in your risk assessment than it would for the same pattern running on H1 or H4, where a few seconds of timing ambiguity is a rounding error against the candle’s full duration.
Don’t assume identical behavior between your backtest engine and your live bot just because the code looks the same. A backtester reading a clean, fully-settled historical CSV and a live bot reading a real-time feed through an API are fundamentally different data sources, even when the pattern logic applied to them is byte-for-byte identical. The pattern isn’t wrong. The assumption that both data sources represent the same thing with the same fidelity is what’s wrong, and it’s an assumption that’s easy to make without ever noticing you made it.