Kyle Donnelly, Algorithmic Trader & Market Technician
July 29, 2026 · 14 min read
TradingView Pine Script bug that ruined my live strategy
The most dangerous trading view pine script bug is rarely a syntax error. Syntax errors are merciful: the editor points at them in red, the script refuses to compile, and you have to deal with them.

The expensive errors compile cleanly. They produce an elegant equity curve, a comforting profit factor, and a list of trades that looks just orderly enough to stop you asking hostile questions. Then the strategy meets a live feed, an incomplete bar, a real spread, or a position with more than one entry attached to it. The discrepancy is not dramatic at first. A fill is a little worse. A stop exits a different lot than expected. A higher-timeframe signal appears early, then vanishes. By the time the gap is visible in the account, the backtest has already done its job: it has persuaded you to trust the wrong thing.
Pine Script is excellent at turning an idea into something testable. It is not a miniature exchange. A tradingview strategy script debug session starts getting productive when you stop treating the Strategy Tester as a verdict and start treating it as a model with assumptions you must expose.
The OHLC Approximation Trap: Why Backtests Lie
Historical bars are compressed records. A standard bar gives the broker emulator an open, high, low, and close. It does not provide the full sequence of trades that formed those values. That distinction sounds academic until your entry, stop, and profit target can all be touched inside one candle.
Suppose a five-minute bar opens below a breakout level, trades above it, drops through a protective stop, then rallies and closes strong. The final OHLC values tell us that all those prices existed. They do not always tell us the exact order in which the market visited them. For a strategy that places conditional orders, execution assumptions fill in the missing story.
That is the source of many pine script backtesting error reports. The chart looks obvious to the human eye: “price broke out, then stopped me.” The tester may model the path differently, depending on the order type, the bar, the strategy settings, and whether lower-timeframe information is available through Bar Magnifier. It is not lying maliciously. It is solving an incomplete-data problem.
The first mistake is assuming a signal at the close means a fill at that same close. By default, strategies commonly calculate at bar close and orders are processed on the following available execution opportunity. Settings such as process_orders_on_close can change the model, but they do not create a guaranteed exchange fill at the printed close. In a fast market, the difference between a chart close, a bid, an ask, and the next tradable price is where a fragile edge goes to die.
Slippage is not absent from TradingView’s modeling. It is configurable in a strategy declaration or in the Properties panel, and commission can be modeled there as well. The limitation is more specific: a fixed slippage setting is still a simplified assumption. It does not reproduce changing bid-ask spreads, queue position, partial fills, liquidity holes, or the way a stop order behaves during a sudden move. A fixed number of ticks can be useful, provided it is chosen pessimistically rather than as decoration.
A backtest does not show what happened tick by tick. It shows what your assumptions conclude from a compressed bar.
For strategies that depend on intrabar order sequencing, the useful comparison is not “backtest versus live” in the abstract. It is a set of deliberately harsher tests:
1. Run the strategy with a realistic commission and a conservative slippage value.
2. Compare ordinary historical-bar results with Bar Magnifier where that feature is available and appropriate for the symbol and timeframe.
3. Inspect trades around sharp reversals, wide-range bars, and session opens instead of admiring the average result.
4. Move the entry one bar later in a test version. If the edge disappears instantly, it may be an execution artifact rather than a robust signal.
5. Forward-test alerts and fills in a paper environment before assigning the strategy meaningful capital.
A strategy based on end-of-bar trend signals can survive this process. A strategy that needs a perfect fill inside a busy one-minute candle usually cannot.
FIFO vs. ANY: Managing Order Execution Logic
Pyramiding introduces a quieter problem: the position you see is one net number, but your strategy may contain several distinct entries. If you buy in layers, take partial profits, and trail the remainder, the identity of the lot being closed matters.
TradingView strategies use FIFO—first in, first out—as the default closing rule. In practical terms, a close can be attributed to the oldest eligible entry even when your mental model says you are trimming the newest add-on. This is especially easy to miss when a script uses generic close commands and the chart only shows a net position shrinking.
The alternative, close_entries_rule = "ANY", allows closing behavior that can match a designated entry more directly when the strategy’s orders and entry IDs are designed for it. It is not a magic “close the trade I meant” switch. The entry IDs, exit orders, and the way your broker ultimately accounts for positions still matter. But it makes the intent explicit rather than silently accepting the default convention.
A multi-entry strategy deserves a trade-list audit, not just an equity-curve glance.
| Situation | FIFO default | Explicit ANY logic |
|---|---|---|
| Several long entries are open | Older entry is generally closed first | A specified eligible entry can be targeted more directly |
| Scaling out of a position | P&L attribution may differ from the intended layer | Exit logic can align more closely with named entries |
| Trailing stops on separate adds | The remaining lot may not be the one you expected | Requires clear entry IDs and deliberate exit design |
| Broker-side execution | May not mirror broker lot accounting exactly | Still needs live or paper verification |
This is where many scripts become accidentally optimistic. A partial exit can leave a different entry open than expected, changing the cost basis, stop distance, and future behavior of the system. The overall net position may look similar, while the logic inside it is no longer the logic you tested.
If a strategy enters in layers, “long one contract” is not enough information. You need to know which entry remains and why.
Open the List of Trades. Match each exit against the entry ID you intended to close. Then test the ugly cases: two entries on the same bar, exits after a reversal, partial closes while a trailing exit is active, and a fresh entry arriving before the prior layer has fully cleared. If the answer is “the tester seems to figure it out,” the order logic is not finished.
The Hidden Danger of Lookahead Bias and Repainting
Lookahead bias is not a TradingView conspiracy. It is a request for future information dressed up as a clean historical signal.
The usual culprit is request.security(), used to bring a higher-timeframe series into a lower-timeframe chart. A daily strategy may request a weekly value; an intraday script may request a higher-timeframe moving average or range. With lookahead = barmerge.lookahead_on, historical bars can receive values from the requested higher-timeframe bar before that bar would have been complete in real time.
That can make a system look uncannily precise. The signal seems to catch the turn at exactly the right point because, on history, it has been allowed to see the completed higher-timeframe result.
One correction matters here: lookahead_on is not a global switch declared “at the top” that every request.security() call inherits. Lookahead behavior is specified per request. A script can contain one deliberately lookahead-enabled request for a visual study and another request using the safer behavior for trade logic. The danger is still real, because a single contaminated series can flow into a condition, and because copy-pasted helper functions can hide the setting far from the entry line.
The cleanest discipline is separation. Keep any series used for entries, exits, position sizing, or alerts free of lookahead. If you choose to create a retrospective visual plot with lookahead behavior, label it mentally as a charting device, not evidence that the strategy could have known that value at the time.
A tradingview repainting indicator problem often has a second source: the live bar itself. Until the bar closes, high, low, close, indicator values, and crossover conditions can change. A moving-average cross can exist halfway through a candle and disappear before the close. An alert configured to fire on that transient condition may be perfectly faithful to the live calculation while being impossible to reproduce from the final historical bar.
barstate.isconfirmed is a useful guard when the strategy is meant to trade confirmed bar-close signals. It forces the logic to wait until the bar is complete. That is not universally correct. A breakout system built to act intrabar may intentionally accept unconfirmed movement, but then it must be tested as an intrabar system, with the additional uncertainty that entails.
The important question is brutally simple: should this signal be allowed to disappear before the candle ends? If the answer is no, write the condition so it cannot trade before confirmation. If the answer is yes, do not compare its live behavior to a pristine bar-close backtest and call the mismatch repainting.
Optimizing for Real-Time: calc_on_every_tick and Barstate Constraints
calc_on_every_tick gets discussed as if it were an accuracy switch. It is not. It changes when a strategy recalculates on a real-time bar.
With the setting enabled, a strategy can recalculate as new real-time updates arrive. That may be necessary for logic intended to react before a bar closes. With it disabled, the strategy normally operates on the bar-close model during real-time operation. Neither option is automatically superior. They describe different systems.
The inaccurate claim is that every live script always recalculates on every tick. Strategies do not all do that. Whether they recalculate intrabar depends on their configuration and on the context in which they run. Indicators and strategies also have different practical expectations around updates, alerts, and order processing. Treat the setting as part of the strategy specification, not an afterthought.
There is also an unavoidable historical asymmetry. Enabling tick-by-tick calculation does not reconstruct a full tick history for ordinary historical bars. Historical testing remains bounded by available bar data and the emulator’s model. Bar Magnifier can improve intrabar precision by using lower-timeframe data where available, but it is not a time machine and should not be confused with the exact stream your broker saw.
This creates a useful split between two legitimate designs.
Bar-close strategies
A bar-close strategy should be boring about timing. It evaluates a completed bar, confirms the condition, emits an order according to its defined processing rules, and accepts that it will not capture the first flicker of a move. The reward is reproducibility: the signal you saw live is much more likely to be the signal that remains on the historical chart.
For this class of system, barstate.isconfirmed is not a patch. It is an expression of the idea.
Intrabar strategies
An intrabar strategy is allowed to be twitchy, but it must admit what it is. It needs alert handling that matches the intended frequency, defensive duplicate-order logic, and a forward test that measures actual fills rather than inferred ones. It should also be judged under adverse conditions: sudden volatility, delayed alerts, session transitions, and bars that trade through both a stop and a target.
If changing calc_on_every_tick radically changes the result, do not immediately choose the prettier report. Read it as a diagnostic. The strategy is sensitive to intrabar path and calculation timing. That may be acceptable, but it means the historical report has a wider uncertainty band than the smooth curve suggests.
Debugging Runtime Errors: Memory Buffers and Execution Timeouts
The errors that stop a Pine script are less romantic than lookahead bias and often more useful. They tell you that the script has asked the platform to do something it cannot safely do within its resource limits.
One frequent issue involves historical references. Pine needs enough stored history to evaluate expressions such as a long lookback, an offset series reference, or a drawing anchored far back in time. Sometimes Pine can infer the required buffer. Sometimes dynamic conditions, drawings, or references that only occur on particular bars make that inference unreliable.
max_bars_back can be declared explicitly for a script, and it can be useful when a strategy genuinely needs a deeper history buffer. It should not become a reflexive giant number pasted into every header. More buffer means more resource use, and a huge declaration can hide an inefficient design without fixing it. First identify the longest real dependency: the oldest series reference, the longest indicator window, or the earliest bar a drawing needs to revisit.
Arrays create a different kind of pressure. A loop that scans an ever-growing array on every bar may work on a short chart and become painfully slow on a deep history or a busy real-time feed. The answer is usually architectural:
- retain only the observations the logic still needs;
- update rolling values instead of recalculating an entire history;
- run one-time initialization under the appropriate first-bar condition;
- avoid creating labels, lines, or array elements endlessly when existing objects can be updated;
- make loop bounds explicit and proportional to the useful window, not to every bar the chart happens to load.
Execution-time limits exist to keep scripts from monopolizing the platform. The exact threshold and resulting message are less important than the pattern: a calculation that scales badly will eventually find a chart, symbol, or market session that exposes it.
When debugging, isolate the failure. Temporarily remove drawings. Reduce array work. Replace a complex conditional with a simple series. Check whether the error occurs only when a certain higher-timeframe request is active. The goal is not to silence the message; it is to learn which part of the strategy becomes unbounded.
The Strategy Tester Is Not Useless — It Is Misunderstood
The Strategy Tester is valuable for validating directional logic, entry conditions, exits, sizing rules, and the broad behavior of a system across different market regimes. It can model commissions and a fixed slippage assumption. It can reveal whether the position math survives pyramiding, whether a stop is actually attached, and whether a signal fires on the bars you thought it did.
What it cannot guarantee is your live execution. It cannot fully model changing liquidity, spread expansion, order queue priority, partial fills, broker-specific order handling, or every intrabar path hidden inside a historical candle. It also cannot protect you from a higher-timeframe series that was allowed to peek ahead, or from an alert that reacts to a signal before that signal is confirmed.
That is why paper routing matters. A tool such as CrossTrade can help bridge the distance between a TradingView signal and an execution workflow, but even a clean integration is not proof of a tradable strategy. It is a test of another layer: alert delivery, order translation, session handling, and the behavior of the receiving account.
The practical sequence is uncomplicated, even if it is less exciting than turning on a strategy after one good report: validate the logic historically, inspect the actual trade list, test realistic costs, run it forward in simulation, and begin live with exposure small enough to make mistakes educational rather than terminal.
Pine Script still wins because it makes this investigation accessible. You can prototype, visualize, and falsify an idea quickly. But speed is not the same as certainty. The platform gives you a calculator; the work is deciding which assumptions belong in the calculation, which do not, and which must be tested where the candles stop being clean historical objects and start becoming a live market.