Heikin Ashi backtests look spectacular because the broker emulator fills orders at synthetic Heikin Ashi prices that never traded — a one-sided bias that always flatters the strategy. This guide walks through the exact fill mechanics, TradingView's 'Fill orders using standard OHLC' fix, and a cleaner pattern that computes Heikin Ashi signals on a standard chart. Educational material only, not financial advice: a backtest — even a corrected one — is a historical description, not a forecast, and trading carries a real risk of loss.
A Heikin Ashi strategy shows unrealistic backtest results because Heikin Ashi prices are synthetic averages, not prices that ever traded. When a strategy runs on a Heikin Ashi chart, TradingView's broker emulator fills orders at those synthetic values — most importantly the Heikin Ashi open, which in a trend sits systematically on the favourable side of the real market. Your longs appear to buy below the real price and your exits appear to sell above it, trade after trade.
The important word is systematically. This is not random noise that averages out over a large sample. The Heikin Ashi open is a smoothed, lagging average that trails behind real price in every sustained move — and trending stretches are exactly when a Heikin Ashi strategy trades most. So the fill error is one-sided: it almost always books a fictional discount on entry and a fictional premium on exit. Over hundreds of trades those small invented edges compound into the smooth, too-good equity curves that fill Heikin Ashi strategy screenshots.
TradingView's own help centre is explicit that strategies on non-standard chart types (Heikin Ashi, Renko, Kagi, Point & Figure) produce unrealistic results for this reason, and the platform ships a specific setting — Fill orders using standard OHLC — to correct it. The rest of this article covers how the synthetic prices are built, exactly where the phantom profit comes from, how to apply the fix in Pine Script v6, and a cleaner pattern that avoids the problem entirely. Everything here is educational only — the goal is a truthful backtest, and a truthful backtest is still not a promise about live performance.
Heikin Ashi (roughly "average bar" in Japanese) rebuilds every candle from averages of real OHLC data:
The HA open is the troublemaker. It is defined recursively — each value is built from the previous synthetic values, not from any traded price. It is an average of an average of an average, all the way back to the first bar of the chart. In a sustained trend it lags well behind the real open and never snaps back to a real quote. You can see the gap directly:
//@version=6
// Educational only — validate before trading; not financial advice.
indicator("Heikin Ashi open vs real open", overlay = true)
haClose = (open + high + low + close) / 4
var float haOpen = na
haOpen := na(haOpen[1]) ? (open + close) / 2 : (haOpen[1] + haClose[1]) / 2
plot(haOpen, "HA open (synthetic)", color = color.new(color.orange, 0), linewidth = 2)
plot(open, "Real open", color = color.new(color.teal, 0))Drop this on any trending chart and the orange line — the price your Heikin Ashi backtest thinks it can transact at — visibly floats away from the teal line, the price a broker would actually quote. That vertical gap, bar after bar, is the raw material of the phantom profit. The smoothing that makes Heikin Ashi charts pleasant to read is precisely what makes them dishonest to fill orders against.
TradingView's broker emulator fills a market order generated on one bar at the open of the next bar. On a standard chart that is a real traded price. On a Heikin Ashi chart, "the open of the next bar" is the synthetic HA open — the midpoint of the previous HA body. That single substitution creates the bias.
Walk through a long entry in an uptrend. Real price is climbing, so the recursive HA open lags below the real market. Your strategy signals on a bullish HA bar; the emulator fills the buy at the HA open — a price below anything a broker would have quoted at that moment. The trade starts with instant, fictional open profit. Later, the exit signal fires and the sell is again filled at an HA open — now lagging above real price as the move rolls over. Both ends of the trade are flattered. Mirror the logic for shorts and the same thing happens in reverse.
Two further distortions stack on top. First, the HA high and low are themselves partly synthetic — max and min over values that include HA open and HA close — so stop and limit touch logic is evaluated against a partially fictional range: stops can appear untouched when the real bar would have swept them. Second, because Heikin Ashi suppresses small counter-moves by construction, whipsaw entries that would have bled money on a real chart simply never fire in the backtest.
No broker will ever fill you at a Heikin Ashi price. The gap between the HA open and the real open is money the backtest invents — and because the bias always points in the strategy's favour during trends, more trades means more fiction, not convergence to truth.
TradingView addresses this with a dedicated setting: Fill orders using standard OHLC (Strategy Tester → Properties), or declaratively in Pine Script v6 via the fill_orders_on_standard_ohlc parameter of strategy(). With it enabled, your script still computes signals from the Heikin Ashi values on the chart, but the broker emulator fills orders at the real instrument's OHLC prices.
First, the ❌ wrong version — a strategy dropped straight onto a Heikin Ashi chart with default settings:
//@version=6
// ❌ WRONG — do not trade this configuration on a Heikin Ashi chart:
// orders fill at synthetic HA prices that never traded.
strategy("HA chart strategy — misleading fills", overlay = true)
longSig = close > open // on an HA chart these are HA values
if longSig and barstate.isconfirmed and strategy.position_size == 0
strategy.entry("Long", strategy.long)
if close < open and barstate.isconfirmed and strategy.position_size > 0
strategy.close("Long")And the ✅ corrected version — one parameter changes where fills happen:
//@version=6
// Educational only — validate before trading; not financial advice.
// ✅ Same HA-based signals, but orders fill at REAL OHLC prices.
strategy("HA signals, real fills", overlay = true,
fill_orders_on_standard_ohlc = true)
longSig = close > open
if longSig and barstate.isconfirmed and strategy.position_size == 0
strategy.entry("Long", strategy.long)
if close < open and barstate.isconfirmed and strategy.position_size > 0
strategy.close("Long")Expect the equity curve to get worse when you flip this on — often dramatically. That deterioration is not the setting breaking your strategy; it is the fiction being removed. If a strategy only "works" with synthetic fills, the edge was the fill artifact, not the signal. That before/after comparison is one of the fastest honesty checks in all of backtesting.
The most robust approach skips the Heikin Ashi chart entirely: run your strategy on a standard chart and compute the Heikin Ashi values yourself. Signals come from HA maths; fills happen at real prices by default; and you can plot both series to see exactly what the smoothing is doing.
//@version=6
// Educational only — validate before trading; not financial advice.
// ✅ Standard chart: HA values computed manually for signals,
// so every fill happens at a real traded price.
strategy("HA-filter on standard chart", overlay = true)
haClose = (open + high + low + close) / 4
var float haOpen = na
haOpen := na(haOpen[1]) ? (open + close) / 2 : (haOpen[1] + haClose[1]) / 2
haBull = haClose > haOpen
if haBull and not haBull[1] and barstate.isconfirmed and strategy.position_size == 0
strategy.entry("Long", strategy.long)
if not haBull and haBull[1] and barstate.isconfirmed and strategy.position_size > 0
strategy.close("Long")Entries are gated on barstate.isconfirmed so the recursive HA state is only ever read from closed bars — Heikin Ashi values wobble intrabar just like any other computed series, and acting on an unconfirmed HA flip is a repainting bug of its own.
Honest caveats remain even with correct fills. Heikin Ashi is a smoothing transform, so its signals lag by construction: you enter after a move is established and exit after it has already turned. Real spreads, slippage, and commissions still apply on top of anything the tester shows. And the deeper rule holds regardless of chart type: a clean backtest is a description of the past, not a forecast. Tools like the ForexCodes Audit can flag the non-standard-chart fill trap automatically, but no tool can turn a historical curve into a promise. Educational only — nothing here is financial advice, and trading carries a real risk of loss.
Educational only — not financial advice. Trading involves substantial risk of loss.