Quantest

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 Quantest journal

A 1901 colour lithograph: a bull in a checked coat blows soap bubbles labelled “inflated values” while a crowd of people reaches up to catch them.
“Wall Street bubbles, always the same”, Udo Keppler for Puck, 1901.Public domain

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 a hole matters5m series
Neckline isthe lowest low BETWEEN the peaks
Bars presentthe ones that happened to be recorded
Bars missing8
Effectthe level moves to the wrong price
The confirmation rule was never wrong here. The series underneath it was.

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.

Flat zero-volume rows, one sessiontick-built table
Total1,494
Actually the auction print107
Quiet late-session bucketsthe rest
Fixmatch on TIME as well as shape
An untraded bar is not a missing bar. A rule written for one table was wrong on a table built a different way.

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.

Asking for a bar that has finishedclamped
Naive requestup to now, returns the forming bucket
Clamped tobucket start minus one minute
Why minus onethe end of the window is inclusive
Write rulereplace only on MORE volume
Resultmonotone: a complete bar is never overwritten
Flooring the request is not enough on its own. Both halves are needed, and the volume guard is what makes repairing an already-stored bar safe.

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.

Read next

When 98% complete is incomplete