Sitemap

Stale data is not data

4 min readOct 3, 2026

--

Why a pre-trade control must treat its inputs as expiring and reject an order based on data age alone, even when every value looks valid.

Press enter or click to view image in full size

Every pre-trade decision I design rests on a premise that it did not compute: the market data it reads. A price, a position, a limit snapshot. The arithmetic of the check can be flawless, yet the order is still wrong, because the error was never in the arithmetic. It was in the age of the premise. Stale data is not data a control can safely use, and rejecting staleness is one of the least visible yet most important guarantees a pre-trade system can offer.

I draw a sharp line between missing data and stale data because they fail in opposite ways. Absence is loud. Staleness is silent. A null announces itself and is easy to catch; a value that has quietly stopped changing does not and is not. A cached quote that stops updating still returns a plausible number, within the band and internally consistent. A limit check against it passes. The order executes against a market that has already moved. Nothing threw an error. The number was valid. It was simply old.

Get Zahava Levy’s stories in your inbox

Join Medium for free to get updates from this writer.

The discipline that prevents this is to make the premise carry its own time. Every input asserts two timestamps that must never be confused: the as-of time, when the fact was true in the world, and the receipt time, when the system came to hold it. A freshness check compares the as-of time against the present and rejects when the gap exceeds a defined horizon. The subtlety that determines whether the check works is which clock it reads. Measuring from receipt time hides exactly the failure that matters, because if a producer freezes while transport keeps redelivering the frozen value, receipt time stays current while the underlying fact rots. The horizon has to bind to the as-of time the datum asserts about the world, not to the moment the consumer last touched it.

The horizon itself is not a universal constant, and treating it as one is a mistake. A liquid equity’s quote is stale in seconds, a position snapshot tolerates minutes, and a slow reference datum tolerates a day. One number cannot govern inputs that decay at different rates, so each input class carries its own horizon, and each horizon encodes a risk appetite rather than a fact of nature. Set it too tight, and the control rejects good orders during ordinary jitter, starving throughput for no protection. Set it too loose, and it admits decisions built on expired premises, the very failure it was meant to prevent. The horizon belongs with the system's other tunable limits, owned and reviewed as policy.

A freshness guarantee is only as good as the clock behind it. Comparing an as-of time to the present assumes the producer and the consumer agree on the current time, and clock skew can make fresh data look expired or, more dangerously, make expired data look fresh. A backward step on a wall clock can resurrect a stale value as current. For that reason, I treat time as a dependency to be disciplined rather than assumed: synchronized sources, monotonic reasoning when ordering matters, and an explicit answer to what the control does when it cannot trust its own clock, which is to fail closed rather than guess.

Underlying the mechanics is a deliberate trade-off between safety and liveness. The safety property is simple: no order is decided on an input older than its horizon. Rejecting stale inputs buys that safety at the cost of liveness, since some orders backed by data that was merely late, not wrong, will be turned away. I accept that cost on purpose because the asymmetry is stark. An order wrongly rejected can be resubmitted a moment later with fresh data. An order wrongly released against a stale premise cannot be recalled from the market.

I insist on this for a deeper reason. It is tempting to reason about the correctness of a computation as though it were independent of its inputs, but a function can be pure, total, and correct and still return an answer that is wrong about the world because its arguments describe a world that no longer exists. Freshness is the part of correctness that lives outside the function, and no check inside the function can recover it. It has to be enforced at the boundary, before a stale value is ever allowed to become an argument.

When I review a pre-trade control, I do not only ask whether its inputs are valid. I ask whether they are current, whether the control measures age from the world’s clock or merely from its own, and whether it can distinguish between a missing value and one that has simply stopped changing. A valid number is not necessarily current. Stale data is not data, and a control that cannot reject on age alone is trusting a past it has no reason to trust.

--

--

Zahava Levy
Zahava Levy

Written by Zahava Levy

Trading systems & quant infrastructure engineer focused on the decision path: pre-trade risk, execution, and real-time systems in C++ and Python.