← Quantery Blog

How should a backtest handle stale prices? | Quantery Blog

November 1, 2026 · 7 min readbacktestingdata-qualityexecution

A backtest should never treat an old price as a price you could trade today. If a stock has no fresh observation on the decision date, block new entries, flag the position as stale, and defer any sale until an executable price appears. If trading never resumes, apply a declared terminal-value policy and test more than one assumption.

That rule sounds fussy until a stale close slips into a portfolio. The engine sees a number, so it assumes a market. It may buy at a price from before bad news, sell while the stock is halted, or keep a dead position flat while everything else moves. None of those trades was available. A stale-price policy is part of the strategy because it decides which trades can exist.

What is a stale price in a backtest?

A stale price is the latest recorded price for a security when that observation is older than the market session being evaluated. The database may contain a perfectly valid close. It just belongs to an earlier day.

There are several ways this happens. An exchange can halt a stock while material news is pending. A regulator can stop trading. A thin security may go through a session without a qualifying trade. A data vendor can miss a bar. A delisted company may stop producing listed prices altogether. Nasdaq's official halt-code list separates news-pending halts, regulatory concerns, and cases where quotations aren't available. Those causes don't have the same economics, even though they all leave a blank in a daily price table.

Don't measure freshness in calendar days. A Friday close is the current close on Saturday. It may still be the latest valid observation when the market opens Monday. Count expected trading sessions against the security's market calendar, then ask whether a bar exists for the session your rules use.

A missing bar also isn't proof of a halt. It could be a vendor problem, an identifier change, or a corporate action that broke the symbol history. The backtest should preserve that uncertainty. Calling every gap a delisting invents losses; filling every gap invents liquidity.

Why does forward-filling create impossible trades?

Forward-filling copies the last known price into later empty rows. That's useful for drawing a continuous portfolio-value chart. It's dangerous when the copied value reaches the execution engine.

Consider a hypothetical position whose last close is P. Trading stops before the next rebalance. The thesis fails on newly filed fundamentals, so the model sends a sell order. If the engine forward-fills P, the sale appears to complete at P. But the absence of a current quote is the fact that matters. There may have been no buyer at that price, and the first executable trade after the halt may be far away from it.

The same error can enter from the other side. Suppose a stock qualifies while its price field still holds an old close. A valuation ratio built from that close may look cheap because the filing is current and the denominator isn't. The backtest can then enter at the old close, capturing a move that had already happened before trading reopened.

There are two clocks here:

A valid historical trade needs both. Point-in-time fundamentals stop the first clock from leaking. A freshness rule stops the second. The same filing-date discipline described in how much reporting lag a backtest should use doesn't rescue a trade made on an unavailable quote.

Entries and exits need different stale-price rules

The clean rule for a new entry is strict: no fresh execution price, no entry. A backtest doesn't own the stock yet, so it has no need to estimate its value. It can reject the signal for that rebalance or hold the order until a later session, depending on the strategy definition.

Those choices test different claims. Rejecting the signal asks whether the thesis works using only securities tradable at each scheduled decision. Deferring the order asks whether the signal survives until trading becomes possible. If you defer, recompute any price-dependent feature at the eventual execution time. You can't qualify on stale valuation and then execute later as if nothing changed.

An existing holding is harder. Removing it from the portfolio when its quote goes stale makes the economic exposure vanish. Selling it at the last close invents a fill. The useful separation is:

Carrying the last price serves provisional portfolio valuation. Keep that path separate from execution in code and in the report. Otherwise the chart's need for a number can leak into the order simulator.

The rule also affects cash. A blocked exit means the proceeds aren't available for a replacement position. Letting another purchase use that imaginary cash creates leverage the strategy never had. This is the same portfolio-accounting issue covered in how a backtest should handle idle cash, except the cash is trapped inside an untradeable holding.

What if trading never resumes?

Some gaps end with a normal reopening. Some end with an over-the-counter quote, a cash merger payment, a bankruptcy distribution, or nothing recoverable in the dataset. A backtest needs a terminal-value hierarchy before it encounters the case.

Use an actual corporate-action or delisting return when the data provides one. If a later executable price exists, use that price on its real date. If neither exists, apply a predeclared fallback to the unresolved position. The fallback might be a severe haircut or a total loss, but it must be visible in the assumptions and varied in a sensitivity run.

Immediate zero can be too punitive when a later distribution existed. Carrying the last close forever is usually flattering. The range between those outcomes is uncertainty the backtest can't solve from prices alone. Show it. The detailed treatment of delisted stocks explains why terminal returns and historical universe membership need their own audit trail.

Don't hide unresolved positions inside an aggregate return. Report how much starting capital became subject to the fallback and which cohorts contained it. A good overall result can depend on a forgiving rule applied to a small but disastrous corner of the portfolio.

How should you test the stale-price policy?

Start with a base policy, then rerun the same thesis under alternatives. The language-neutral pseudocode below describes backtest behavior, not fields in Quantery's thesis DSL. Quantery's bundled templates and DSL documentation are the reference for supported thesis fields.

WHEN a candidate has no fresh execution price:
  skip the entry

WHEN a held position has no fresh execution price:
  carry its last price for provisional valuation
  label that valuation as stale
  block the exit and keep its cash invested

WHEN trading resumes:
  fill the pending exit at the first eligible price

WHEN no later executable price or terminal return exists:
  apply the declared fallback recovery value

Then change one assumption at a time:

Backtest returns from these runs are hypothetical and exclude costs. More important, the spread between the runs is part of the result. If the thesis survives only when stale candidates receive old prices or blocked exits release cash immediately, the apparent edge came from the simulator.

Audit a few affected events by hand. Open the raw price series, identify the last valid bar, check the decision timestamp, and find the first later quote. Nasdaq publishes a current halt record and halt-history tools, which can help distinguish an exchange event from a broken data row. Historical coverage won't be perfect, so keep the engine's data-quality flag even after the spot check.

The report should expose stale exposure

A backtest report needs to show when valuation rests on carried prices. At minimum, identify affected positions and the capital tied up in them. Keep a record of blocked orders, eventual fill dates, and any terminal fallback used. Without that trail, a smooth equity curve can conceal a portfolio that couldn't have been traded.

A halt or missing bar doesn't invalidate the thesis. Real portfolios encounter both. Make the rule explicit and rerun it. The useful workflow is to choose the policy, inspect the affected events, change the assumption, and see whether the conclusion holds.

A stale close records the last time a market existed. A trade requires a current executable price. Keep valuation and execution apart, and the backtest has to live with the market that was actually available.

Want to try this on your own rules? Quantery is free for 14 days: the full app, no card required.

← All articles