Strategies · intermediate · 8 min
Fair Value Gaps
A fair value gap (FVG), sometimes called an imbalance, is a three-candle pattern. In the bullish case, price moves up so quickly that the middle candle leaves a gap: the high of the first candle is below the low of the third. In between is a band of prices that barely traded — the market jumped through it. The popular claim is that markets "dislike" these inefficiencies and tend to return to fill the gap before continuing, so the gap becomes either a magnet for price or a zone to enter from.
Like the order block, it is a precise, observable pattern wrapped in an untestable story. This lesson keeps the pattern and tests it.
The observable part
The bullish FVG is fully defined by three consecutive candles where the third candle's low sits above the first candle's high. There is no ambiguity and no discretion — a chart either prints the gap or it does not. What is not observable is the claim about why it forms or what "should" happen next. That is the part the robustness gates exist to check.
The mechanical rule we tested
The version baked below fires the moment a bullish gap forms and reads it as momentum:
- Entry: today's low is above the high of two bars ago (
LOW > REF(HIGH, 2)) — a three-bar bullish imbalance. - Exit: a fixed 5% take-profit or 3% stop, whichever comes first.
Run on SPY, five years of daily bars.
What this captures — and what it leaves for the section below. The most-taught FVG entry is not "buy when the gap forms" — it is "wait for price to retrace into the gap, then enter." Testing that faithfully needs the strategy language to remember the gap's price band across many later bars and fire only when price re-enters it. Visor's DSL now has exactly that zone-memory capability (ZONE_FVG(gapPct, expiryBars) — and see Order Blocks, where the faithful ZONE_OB re-entry version has been run through the gates and failed harder than the momentum proxy). What is baked immediately below is still the imbalance-as-momentum reading — "a fast gap up tends to keep going." The retrace entry every course actually sells is now testable too, and is run through the same gates in its own section further down (The retrace entry — waiting for the fill). Both readings, stated honestly rather than dressed up as the whole idea.
The honest verdict
Random control
Beat 63.8% of 500 randomly-timed versions of itself (real 44.73% vs random average 41.81%).
Out-of-sample
In-sample 24% · held-out 16.73%.
Significance
t = 1.396 against a 3.5 threshold.
Deflated Sharpe
0.9188 — Sharpe of 0.19.
These checks describe how much of this backtest survives statistical scrutiny. Past simulated performance is not a guide to future results, and nothing here is a recommendation to trade.
The rule traded 54 times and returned about +45% — again a real number, and again it held up out-of-sample. And again the random control is where the story ends. The real return beat only about 64% of its 500 randomly-timed twins — nowhere near the 95% bar, and roughly one in three scrambled versions did better. The null average was about +42%, almost identical to the real result: on this window, a fair value gap up was essentially indistinguishable from entering at a random time and holding for the same target. The significance test agrees (t = 1.40, under the 3.5 threshold).
That near-equality is the whole point. The gap looks like a reason. Statistically, on this data, it added almost nothing over being long a rising market at any moment.
What to take from it
Fair value gaps are a clean way to mark where price moved fast — useful for describing structure, and worth being able to spot. What they are not, on this test, is a source of edge: the imbalance-momentum version cleared the beginner bars (beat zero, survived out-of-sample) and still could not beat its own scrambled timing. Whether the retrace version does better used to be a question Visor could not answer mechanically — now it can, and the answer is below.
The retrace entry — waiting for the fill
The rule above buys the gap forming. The version every course teaches waits for price to come back into the gap — to "fill the imbalance" — and enters on that return. Until zone memory existed, Visor could not test it, because doing so needs the strategy language to remember a price band across many bars and fire only when price returns to it. ZONE_FVG now provides exactly that, so here is the faithful retrace rule, on the same SPY window and the same 5%/3% exit — the only thing that changes from the momentum version is when it enters:
- Detect: a three-candle bullish imbalance (
ZONE_FVG, gap ≥ 0.1% — essentially the same gaps the momentum rule fires on) — the band between the first candle's high and the third candle's low becomes the gap's zone. - Enter: when price later trades back into that gap (the fill), not when the gap forms. A zone is remembered for 30 bars, dropped if price closes clean through it, and used at most once.
- Exit: the same fixed 5% take-profit or 3% stop.
Random control
Beat 37.6% of 500 randomly-timed versions of itself (real 28.16% vs random average 37.08%).
Out-of-sample
In-sample 7.31% · held-out 19.43%.
Significance
t = 1.028 against a 3.5 threshold.
Deflated Sharpe
0.8487 — Sharpe of 0.15.
These checks describe how much of this backtest survives statistical scrutiny. Past simulated performance is not a guide to future results, and nothing here is a recommendation to trade.
The verdict is the same word — fail — reached from the opposite direction to the momentum version, which is the instructive part. This rule fires on essentially the same gaps (47 trades to the momentum rule's 54) but waits for the fill before entering. Waiting cost return: it made about +28% where buying the gap outright made +45% and buy-and-hold made +73%. And it does worse against its own scrambled twin: the real +28% beat only about 38% of 500 randomly-timed versions (the momentum rule beat ~64%), so a random entry into the same 5%/3% target did better roughly three times out of five. The significance test agrees — t = 1.03 against a 3.5 bar. It clears out-of-sample, but only in the same accidental way the sibling rules do (it happened to make more in the back third), not as evidence of an edge.
So both readings of the fair value gap — buy the imbalance, or wait for the fill — now go through the identical gates, and the "wait for the fill" entry the courses sell hardest is the one that leaves the most money on the table. Being able to test the retrace faithfully is the whole point of zone memory; the verdict it earns is the same honest answer the rest of this track gives. (The same capability makes the order block re-entry and the supply and demand bounce testable — both also fail.)
Try it yourself
Load this configuration into the Strategy Editor, run it, and open the Robustness tab. It carries the momentum version; swap the entry script for ZONE_FVG(0.1, 30) to run the retrace version baked above.
What to read next
- Order Blocks — the sibling pattern, same test, same lesson.
- Liquidity Sweeps — a rule that fails harder, and shows the curve-fitting signature.
- The Overfitting Trap — why a pattern obvious in hindsight is not an edge.