A Highlight Column Is Not a Trading Signal
Adding a 'worth a look' label to an existing scanner sounds like a cosmetic change. It's actually one of the easiest ways to quietly grow a second, undocumented signal inside a presentation layer — and the two invariants that keep it from doing that.
A small feature request
A daily scanner that already computes real technical state — trend direction, distance from a moving average, recent price behavior around it — gets a small addition: color-code the rows so the interesting ones stand out. Green for “worth a look,” grey for “skip,” nothing for everything else. It sounds like a UI change.
It’s actually one of the higher-risk features you can add to a system like this, because the label is built entirely out of numbers the system already trusts for something else. Once a row is green, it’s very easy for that color to start meaning more than it was designed to mean — to a person skimming the sheet, or eventually to some later piece of code that reads the label instead of recomputing the thing it stands for.
What a label is actually made of
A highlight isn’t new information. It’s a rule applied to fields the pipeline already produced — is price near a trend line, is the trend aligned across multiple timeframes, did something notable happen in the last several bars. The label is a summary, not a measurement.
That distinction matters because summaries drift. The underlying computation is precise and auditable — the same inputs always produce the same distance, the same trend classification. A label is a threshold applied on top of that precision, chosen to be readable rather than exact, and readability is exactly the property that makes it tempting to treat the label as the finding instead of a pointer to one.
Two rules that kept it a label
Presentation is not allowed to change data. The labeling pass runs strictly after every real computation is finished. It can add a name and a fill color to a row. It cannot add, remove, or reorder a row, and it cannot feed back into anything computed earlier — no signal in the pipeline is allowed to depend on a label that exists only to describe that signal’s own output. The test is blunt and easy to apply: delete the labeling pass entirely, and if the underlying data is byte-for-byte identical, the boundary held. If even one row changes, it didn’t.
Every input has to land somewhere explicit. Separately from labeling, the pipeline commits to a stronger guarantee: everything it looks at is either classified or explicitly skipped with a named reason, and a final check asserts that the two counts add up to the number that went in. This has nothing to do with the labels — it’s what makes “nothing was flagged today” distinguishable from “three items silently fell out of the pipeline and nobody flagged that either.” A scanner that can silently drop input looks, from the outside, exactly like a scanner that correctly found nothing. The reconciliation check is what turns “probably fine” into something you can actually verify.
Neither rule is about the specific thresholds a label uses. They’re about keeping presentation, computation, and accounting from ever being able to quietly stand in for each other.
The bug that only a range check catches
A related lesson showed up while hardening the same scanner against bad instruments. A
volatility measure is supposed to be a small positive number for anything actively traded. For
a delisted or effectively untraded instrument, it can round to something so close to zero that
it’s no longer meaningfully different from noise — but it’s still a valid float. Not NaN, not
infinite, just wrong in a way that passes every “is this missing” check a pipeline normally
runs.
Downstream, a distance-from-trend metric divided by that near-zero number, and produced a value in the tens of thousands of “units” away — a number no real instrument could ever report, attached to a row that otherwise looked completely normal in the output.
NaN checks catch missing data. They don’t catch data that is present, technically valid, and
numerically absurd. That needs a second, different kind of check: a plausibility range on the
result, not just a null check on the input. The fix wasn’t a smarter volatility calculation —
it was accepting that some intermediate values need a sanity bound regardless of how they were
computed, and routing anything outside it to the same explicit “skipped, with a reason” bucket
the reconciliation check already required.
What this generalises to
Worth auditing your own system for:
- If you deleted your presentation or labeling layer completely, would any row of underlying data actually change — or only how it’s displayed?
- Does anything downstream, human or automated, read a label as if it were the measurement behind it?
- Does every item your pipeline touches land in an explicit bucket, with a check that the buckets sum to what went in?
- Do your data-quality guards only catch missing values, or do they also catch technically valid values that are implausible on their face?
A label earns the right to stay a label by being provably incapable of doing anything else. The moment that stops being provable, it has quietly become a second system living inside the first one’s UI.