TradingView's Strategy Tester does not replay real ticks — its broker emulator reconstructs an assumed intrabar price path from each bar's open, high, low, and close, and that assumption silently decides whether your stop loss or take profit was hit first whenever both fall inside one bar. This article documents the exact path rules, explains the same-bar SL/TP ambiguity they create, and gives you a Pine v6 script to measure how much of your backtest depends on the assumption. Educational material only, not financial advice; a backtest is a historical simulation under stated assumptions, never a forecast, and trading carries a real risk of loss.
It doesn't decide — it assumes. A chart bar carries only four prices (open, high, low, close), so when both your stop loss and your take profit sit inside a single bar's range, the broker emulator cannot know which level real trading touched first. Instead it reconstructs a plausible intrabar path from the bar's shape and fills your orders along that path: if the bar's high is closer to its open than its low is, the emulator assumes price travelled open → high → low → close; otherwise it assumes open → low → high → close. Whichever of your exit levels that assumed path reaches first is the one that fills.
That one heuristic is the load-bearing wall of every TradingView backtest. Most of the time it is invisible: if a bar only contains your take profit, or only your stop, there is no ambiguity and the assumption changes nothing. But the moment a single bar's range spans both levels, the emulator's guess — not the market — determines whether that trade is recorded as a winner or a loser. On a strategy with wide targets and a daily chart this might affect a handful of trades. On a tight-bracket scalping strategy running on a 15-minute chart, it can affect most of them.
Understanding this is not pedantry. It is the difference between reading a backtest as "what would have happened" and reading it correctly as "what would have happened if price moved through each bar in the emulator's assumed order." The rest of this article unpacks the full set of assumptions, shows where they bite, and gives you a concrete way to measure your own strategy's exposure to them.
TradingView documents the broker emulator's behaviour in its Pine Script user manual, and the rules are worth knowing verbatim rather than by folklore.
First, the path rule from the previous section: the emulator compares the distance from open to high against the distance from open to low, and assumes price visited the closer extreme first. A bar that opens near its low is assumed to have dipped to the low, run to the high, then settled at the close — and vice versa.
Second, no intrabar gaps: the emulator assumes price moved continuously along that path, touching every intermediate price. If your stop level lies anywhere on the assumed path, it fills exactly at your price — no slippage, no gapping through the level. Live markets do both, which means emulated stop fills are systematically at least as good as real ones.
Third, order timing: by default, orders generated on a bar are placed at that bar's close and market orders fill at the next bar's open. Price-based orders — stops and limits registered via strategy.exit() — can fill intrabar, at the moment the assumed path crosses their level. Settings like process_orders_on_close and calc_on_every_tick modify this, but the defaults above govern the vast majority of published scripts.
Fourth, on realtime bars the emulator uses actual incoming ticks — which is precisely why a strategy can behave differently on historical bars versus live bars even with identical logic. The historical fills came from an assumed path; the realtime fills come from the tape. When a strategy's live results diverge from its backtest immediately, this boundary is one of the first places to look.
Call a bar ambiguous when its range contains both your stop loss and your take profit for an open position. On every ambiguous bar, the recorded outcome of the trade is determined entirely by the open-closer-to-high-or-low heuristic. Real intrabar movement is far messier than a single sweep to one extreme and back — price can touch the low, rally, revisit the low, then close high — so on ambiguous bars the emulator is not approximating reality; it is substituting a convention for it.
The practical consequences follow directly. Tight brackets maximise ambiguity. If your stop is 10 ticks away and your target 15, almost any bar of normal volatility can span both, and your win rate becomes largely an artefact of bar shapes. Lower timeframes reduce it; higher timeframes amplify it. The same strategy backtested on 1-hour bars will contain far more ambiguous bars than on 1-minute bars, because each bar's range is wider relative to your levels. Volatile instruments are worse. A crypto pair whose hourly range routinely exceeds both your exit distances is close to un-backtestable at that resolution without finer data.
There is also an asymmetric failure mode worth naming: the emulator's no-gap, exact-price fill assumption flatters stops (you always exit exactly at your level) while the path assumption can flatter targets (an assumed early touch of the favourable extreme books a win the real path may never have delivered). Both biases push in the same direction — toward a backtest that looks better than the live experience will be. That is why ForexCodes treats same-bar ambiguity as a correctness issue to be measured, not a nuance to be shrugged at.
You do not have to guess how exposed you are — you can count. The script below marks every bar whose range spans both a hypothetical stop and target placed at your strategy's typical distances from the bar's open, and keeps a running total. If a large fraction of bars are ambiguous at your bracket widths and timeframe, your backtest's win/loss ledger is substantially emulator-decided.
//@version=6
// Educational only — validate before trading; not financial advice.
indicator("Same-bar SL/TP ambiguity counter", overlay = true)
stopTicks = input.float(50.0, "Stop distance (ticks)", minval = 1.0)
tgtTicks = input.float(50.0, "Target distance (ticks)", minval = 1.0)
// Hypothetical long entered at this bar's open.
entry = open
sl = entry - stopTicks * syminfo.mintick
tp = entry + tgtTicks * syminfo.mintick
// Ambiguous: this bar's range contains BOTH levels, so the fill order
// depends entirely on the emulator's OHLC path assumption.
ambiguous = high >= tp and low <= sl
var int ambiguousCount = 0
var int totalCount = 0
if barstate.isconfirmed
totalCount += 1
if ambiguous
ambiguousCount += 1
plotshape(ambiguous and barstate.isconfirmed, title = "Ambiguous bar",
style = shape.triangledown, location = location.abovebar,
color = color.orange)
var table t = table.new(position.top_right, 1, 1)
if barstate.islast
table.cell(t, 0, 0,
"Ambiguous: " + str.tostring(ambiguousCount) + " / " +
str.tostring(totalCount))Read the result as a fragility score, not a verdict. A strategy showing 2% ambiguous bars is largely path-independent; one showing 40% is reporting the emulator's coin-flips as skill. A complementary test: rerun your actual strategy with the stop and target each widened and narrowed by one bar's average range. If headline metrics swing violently, the assumption — not the logic — is driving them.
TradingView's partial remedy is the Bar Magnifier (use_bar_magnifier = true in strategy(), available on Premium and higher plans). Instead of assuming a path through each chart bar, the emulator inspects data from a lower timeframe — for example, 1-minute bars inside a 60-minute chart bar — and orders the fills according to that finer sequence. This genuinely resolves many ambiguous bars: if the 1-minute data shows the low printed before the high, your stop correctly fills before your target.
But be precise about what it does not do. The magnifier replaces one bar's assumption with thirty smaller bars' assumptions — each intrabar is still an OHLC candle subject to the same path heuristic, just at a scale where ambiguity is rarer. It is bounded by data availability (the magnifier's lower-timeframe depth is limited, so older history may remain unmagnified), and it is still not tick data: sub-minute whipsaws that hit a tight stop remain invisible. A cheaper diagnostic available on every plan is simply rerunning the strategy on a lower chart timeframe with logic adjusted to match, and comparing the trade list — divergence localises exactly which historical trades were assumption-dependent.
The honest conclusion: TradingView's broker emulator is a reasonable, documented convention, and its assumptions are conservative for some strategies and flattering for others. Your job is to know which side of that line your script sits on before trusting its numbers. Measure the ambiguity, magnify where you can, and treat any strategy whose edge lives inside single-bar ranges with proportionate suspicion. None of this is financial advice: a backtest — however carefully de-biased — remains a description of the past under stated assumptions, and trading always carries a real risk of loss.
Educational only — not financial advice. Trading involves substantial risk of loss.