Skip to content
ChartTailor
WorkServicesPricingField NotesAbout Start a build
← Pine Discoveries

The request.security lookahead fix can become the future leak

Offset and lookahead patterns are useful in Pine, but copied carelessly they can make historical bars see higher-timeframe data too early.

Lookahead discussions in Pine often turn into recipes. Use this setting, add this offset, and the repainting is fixed. Recipes are helpful until they are copied into the wrong timing problem. request.security() is not fixed by a magic word. It is fixed by knowing exactly what value the lower-timeframe bar is allowed to know.

This one matters because the dangerous version looks clean. The plot is stable. The historical chart looks sensible. The strategy report improves. But if the requested higher-timeframe value was not complete when the lower-timeframe bar used it, the script has leaked future information.

Why this catches people

The thing that catches people is using barmerge.lookahead_on without offsetting the requested expression correctly. On historical lower-timeframe bars, lookahead can place the higher-timeframe value earlier than it would have been known. If the expression requests the current daily close, the five-minute bars earlier in the day may effectively receive the day’s final close.

The usual safe pattern for confirmed higher-timeframe values is to request the previous higher-timeframe bar while using lookahead to align that completed value across the lower-timeframe bars. The offset belongs inside the requested expression. That detail is easy to miss.

The Pine bit

The question I ask is always concrete: on this lower-timeframe bar, am I using the last completed higher-timeframe value, or am I using the current developing higher-timeframe value? Both can be valid. Only one is confirmed.

If the script wants a confirmed daily close on an intraday chart, it should not use today’s final close during today’s earlier bars. If the script wants a developing daily close, it can request that, but the chart and alerts should treat it as developing.

confirmedDailyClose = request.security(
     syminfo.tickerid,
     "D",
     close[1],
     lookahead = barmerge.lookahead_on)

developingDailyClose = request.security(
     syminfo.tickerid,
     "D",
     close,
     lookahead = barmerge.lookahead_off)

Why it can survive a quick review

This can slip through review because the anti-repaint version can appear more stable than the repainting version. Stability feels like correctness. But a stable future leak is worse than a visibly developing value because it gives the backtest false confidence.

There are also legitimate cases where historical and realtime behaviour are intentionally different. A script may use one offset on historical bars and another on the realtime bar because the author accepts live repainting for early warning. That is not automatically wrong, but it must be described. Otherwise the next person reading the script will copy the pattern into a backtest and inherit the wrong claim.

How I handle it in builds

In production indicators, I name the values by timing and keep them separate. confirmedDailyClose should not be mixed casually with developingDailyClose. If both are shown, I give them different visual treatment. If only one drives alerts, I make that choice explicit.

For strategies, I default to confirmed values unless there is a specific reason not to. A backtest that uses developing higher-timeframe values can still be useful as an exploratory tool, but it should not be sold to the user as non-repainting confirmed logic.

Where this shows up

A practical audit is to put the confirmed and developing versions on the chart together. If they are identical most of the time, the bug can stay hidden. The useful moments are the transitions: the first lower-timeframe bars of a new higher-timeframe period, the live bar, and the bars around a higher-timeframe close. That is where future leaks reveal themselves.

I check alerts separately from plots. A plot can use an offset for readability while an alert should fire on the bar where the information is actually known. If the same expression drives both without review, the visual convenience can leak into the event logic.

The strongest test is narration. Pick one historical lower-timeframe bar and say out loud what higher-timeframe bar the value came from. If that sentence is hard to say, the code is not clear enough.

I keep an eye on where the offset is applied after the request returns. Offsetting the returned series can be useful for plotting, but it is not the same as offsetting the expression evaluated in the higher-timeframe context. That distinction is small in code and large in meaning.

A mature script should make the anti-lookahead pattern hard to accidentally break. If the requested value is confirmed, wrap it in a helper with a name that says so. Then future edits are less likely to remove the [1] because it looks unnecessary.

How I test it

I test repainting claims by separating location from knowledge. A marker can be drawn on an earlier bar for readability, but an alert or strategy can only act when the information is actually known. I test that difference around live bars, offsets, and higher-timeframe transitions.

When I am not sure, I build the failure case beside the fix. One plot shows the developing higher-timeframe value, one plot shows the confirmed value, and one label marks the first lower-timeframe bar of the new higher-timeframe period. If the confirmed value moves before the higher-timeframe bar has actually closed, the code is not a fix. It is just a cleaner-looking leak.

A final safeguard is to make future-leak tests part of review, not only part of debugging. Any time a script requests higher-timeframe data for a lower-timeframe signal, I expect a quick boundary test. That habit catches mistakes before they become impressive-looking screenshots.

On the first bar of a new daily or weekly period, I add temporary labels showing both requested values. If the confirmed value changes too early, the bug is visible immediately. Those labels come out before publishing, but while they are on the chart every higher-timeframe request has to explain itself.

Checks before I trust it

  • Review lookahead and expression offsets together.
  • Put the offset inside the requested expression when requesting prior higher-timeframe values.
  • Name values as confirmed or developing.
  • Avoid using final higher-timeframe values before they existed.
  • Treat copied anti-repaint snippets as timing patterns, not magic fixes.

The point I keep coming back to is that repainting fixes are timing rules. If the timing rule is not written down, the fix can quietly become the bug.