Never draw a pattern across a missing bar
A detector cannot see a hole. Non-adjacent bars read as adjacent in a list, and the measurements that define a pattern are taken between bars.
The shape was real. The series was not.
On 9 October our intraday detector reported a confirmed formation on a large-cap IT name: two clean peaks, a neckline at 1024.65, a confirming close at 1024.00. Everything the rule asks for was satisfied. On the chart it had broken nothing at all.
Eight bars were missing from inside the valley. The exchange socket had dropped and reconnected forty-five minutes later, and in a Python list the bar before the gap and the bar after it sit next to each other like any other pair.
Why this is worse than a missing number
A missing price is obvious: something is blank. A missing bar is invisible, because the measurements that define a formation are taken between bars rather than on them. A double top’s neckline is the lowest low between its two peaks. Remove eight bars from the middle and the neckline becomes the lowest low of whatever survived, which can be a long way from the real one — and the result still looks like a clean detection.
The detector now rejects a formation when any step from the first pivot to the confirming bar exceeds the grid it is supposed to sit on: five minutes on the 5-minute series, fifteen on the 15-minute one. It is deliberately off for daily and weekly bars, whose spacing is irregular by nature — a holiday is not a hole.
Then the guard found a bug of its own
Rejecting gaps turned a second problem into a visible one. Our intraday bars are built from ticks, so a bucket in which nothing trades carries the price forward with a volume delta of zero. That produces a row where the open, high, low and close are all equal and the volume is nothing — which is exactly the shape of the closing-auction print the data provider appends at the end of every session, and which we filter out everywhere.
So the filter was deleting real, quiet buckets. That left holes, the gap guard refused to draw across them, and genuine formations in thin names disappeared. On one session there were 1,494 rows of that shape in the table and only 107 of them were the auction print.
And a bar caught while it was still forming
The last one in this family: ask a history endpoint for candles up to now and it returns the bucket now sits inside — a bar covering only the elapsed part of its interval, whose close is the price at the moment you asked rather than the bucket’s close. Written with a do-nothing conflict clause, that partial bar is then frozen in place forever.
Two halves to the fix, and both are needed. Requests are clamped to the last bucket that has genuinely finished. And the write became a conditional update that only replaces a stored bar when the incoming one carries more volume — a partial bar’s volume is always a subset of the complete bar’s, so the repair is monotone and can never overwrite a good bar with a stale response.
The general lesson
Every bug here produced output that looked correct. No exception was raised, no screen went blank, and in each case the thing on the chart was a confident, well-formed answer computed from a series that was quietly wrong. That is the failure mode worth building against, and it is why the nightly audit asks each table whether it is current instrument by instrument rather than counting rows.