Price Action: Direction, Confirmation, and Fair Value Gap Entries

Video: PRICE ACTION: Once You Learn It, Trading Becomes Ridiculously Simple
Creator: Trading Notes
Original Video: Watch the original video
Difficulty: Advanced

The Big Idea

A simpler chart becomes useful when it supports a clear decision process. Trading Notes organizes that process around direction, confirmation, and invalidation: choose the side to consider, specify the event required for entry, and define what would disprove the trade.

The source applies those checks across two timeframes. A 15-minute chart supplies the directional map, while a one-minute chart supplies a structure shift and a fair value gap retest. This guide turns the framework into a decision sequence, clarifies the evidence behind its terminology, and provides a mechanical research approximation.

“Reacting” still involves a forecast: the trader expects the observed reaction to lead to continuation. The benefit is that the forecast has explicit conditions and a failure point. It does not remove uncertainty.

Direction: Establish a Map Before an Entry

For a bullish map, the video looks for higher highs and higher lows, followed by a close above an earlier high. It draws a trend line through two preceding lows and identifies the pullback area before that breakout. The bearish case reverses those relationships.

A break of structure describes a price event relative to a selected reference. It does not prove that buyers or sellers will retain control. Two traders can choose different swing points and label the same chart differently, especially when one uses small internal swings and another uses larger ones.

Choose the reference before seeing the outcome. Record the relevant swing prices, when each became identifiable, and whether a wick or a close counts as a break. If a pivot requires several subsequent bars for confirmation, the pivot was not available at its apparent turning point.

Trend lines also depend on selected anchors. A third touch can provide information about that particular line, but it does not turn the line into a physical barrier. Treat the mapped area as a place to observe a response.

Confirmation: Wait for a Defined Response

The source’s bullish sequence begins with a short-term decline into the mapped area. A one-minute close above a recent lower high signals a possible shift toward the higher-timeframe direction. A bullish fair value gap forms on the move, and price later returns to that gap.

The presenter waits for a bullish candle on the return rather than entering simply because the gap exists or is touched. The bearish example uses a short-term upward move, a downside structure shift, a bearish gap, and a bearish retest candle.

DecisionBullish exampleBearish example
15-minute directionUpward structure breakDownward structure break
One-minute shiftClose above a recent lower highClose below a recent higher low
Three-candle gapThird low exceeds first highThird high is below first low
Retest confirmationReturn into the gap followed by a bullish closeReturn into the gap followed by a bearish close
Failure planningProtection beneath the defined setupProtection above the defined setup

The rejection candle must occur after the gap becomes identifiable. Using the gap’s third candle as its own historical retest would use a level that did not yet exist during that candle’s earlier trading.

What a Fair Value Gap Actually Shows

A bullish three-candle gap exists when the first candle’s high is below the third candle’s low. The interval between those prices is the marked zone. A bearish gap exists when the first candle’s low exceeds the third candle’s high.

This is non-overlap between the outer candles. The middle candle may have traded through the entire zone, so the pattern does not establish an absence of transactions or a price region that the market literally skipped.

The source interprets the pattern as evidence of momentum and a possible continuation area. That is a testable interpretation. Candle geometry alone cannot identify unfilled institutional orders, determine a “fair” price, or guarantee that the same participants will defend a retest. Some gaps are revisited, some fail, and some are never revisited.

For an original bullish example, suppose candle one has a $100.20 high and candle three a $100.50 low. The gap spans $100.20–$100.50. A later candle trades down to $100.35 and closes at $100.60 above its $100.42 open. That supplies a visible bullish response. A later close below $100.20 instead weakens the proposed gap-hold thesis.

Invalidation: Separate the Idea From the Order

The video describes both a stop beyond the candle creating the gap and a close through the full gap as failure information. Those are different exit mechanisms. A price-based stop may trigger intrabar; a close-based exit waits until a candle finishes and then submits an order.

Choose the intended mechanism before trading. “The candle that created the gap” is also ambiguous without the chart: it might refer to the middle displacement candle or another candle in the formation. A test needs an explicit choice rather than quietly selecting whichever stop would have survived.

A higher-timeframe change of character can undermine the broader directional idea, but that does not require allowing an individual trade to run until such a change appears. The trade’s protective stop and the map’s structural invalidation can operate at different scales.

In the example above, an assumed $100.60 entry and a $100.05 stop create $0.55 of price risk per share. A target at $101.70 offers $1.10, or 2R, before costs. An actual fill at $100.70 with unchanged levels produces $0.65 risk and $1.00 reward, about 1.54R. Entry slippage changes the trade even though the chart setup is identical.

Tools Can Draw the Pattern; They Cannot Validate the Edge

The source uses Price Action Toolkit by Flux Charts to draw market-structure labels and gap boxes, and promotes a discount. Those conveniences do not establish profitable expectancy. The transcript does not supply the toolkit’s complete structure algorithm, settings, or independently audited results.

Compare manual and automated labels using the same swing definitions and decision times. A label placed on an earlier candle can be visually persuasive even when the information required to confirm it arrived later. Keep the distinction between a label’s displayed location and its availability time.

Pine Script Research Strategy

Expand the complete Pine Script research strategy

//@version=6
strategy("Direction + FVG rejection - research proxy", overlay=true,
     initial_capital=25000, pyramiding=0, margin_long=100, margin_short=100,
     commission_type=strategy.commission.percent, commission_value=0.01,
     slippage=2, calc_on_order_fills=true, calc_on_every_tick=false,
     process_orders_on_close=false)

htfBars = input.int(20, "15-minute breakout lookback", minval=2)
localBars = input.int(5, "1-minute shift lookback", minval=2)
gapDelay = input.int(3, "Maximum bars from shift to gap", minval=0)
expiry = input.int(20, "Gap expiry in chart bars", minval=1)
minGapTicks = input.int(2, "Minimum gap size, ticks", minval=1)
reward = input.float(2, "Target / actual entry-to-stop distance", minval=0.1)
cashRisk = input.float(100, "Estimated cash risk before costs", minval=1)
lotStep = input.float(1, "Quantity increment", minval=0.000001)
maxQty = input.float(1000, "Maximum quantity", minval=0.000001)
useBias = input.bool(true, "Require 15-minute direction")
allowShorts = input.bool(true, "Allow shorts; verify actual borrow")
startMinute = input.int(570, "Entry start: NY minutes after midnight", minval=0, maxval=1439)
endMinute = input.int(660, "Entry end: NY minutes after midnight", minval=1, maxval=1440)
flattenMinute = input.int(960, "Flatten: NY minutes after midnight", minval=1, maxval=1440)

if barstate.isfirst and (not timeframe.isminutes or timeframe.multiplier != 1)
    runtime.error("Use standard one-minute candles.")
if barstate.isfirst and endMinute <= startMinute
    runtime.error("Entry end must be later than entry start.")

// Rolling-range breaks are our proxy, not a swing-labeling algorithm.
// Returned state is offset INSIDE the higher-timeframe context.
f_confirmedBias() =>
    upper = ta.highest(high, htfBars)[1]
    lower = ta.lowest(low, htfBars)[1]
    var int direction = 0
    if close > upper
        direction := 1
    else if close < lower
        direction := -1
    direction[1]

bias = request.security(syminfo.tickerid, "15", f_confirmedBias(),
     gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)
localHigh = ta.highest(high, localBars)[1]
localLow = ta.lowest(low, localBars)[1]
bullShift = close > localHigh and close[1] <= localHigh
bearShift = close < localLow and close[1] >= localLow
bullGap = low > high[2] and low - high[2] >= minGapTicks * syminfo.mintick
bearGap = high < low[2] and low[2] - high >= minGapTicks * syminfo.mintick

varip float seenPosition = 0.0
fillPass = strategy.position_size != seenPosition
seenPosition := strategy.position_size
varip int dayKey = na
varip bool usedToday = false
varip int shiftDirection = 0
varip int shiftBar = na
varip int zoneDirection = 0
varip int zoneBar = na
varip float zoneLow = na
varip float zoneHigh = na
varip float zoneStop = na
varip float tradeStop = na
varip float tradeTarget = na

// Fill passes read actual fill price and previously frozen stop only.
// They do not select setups using the fill bar's OHLC.
if fillPass and strategy.position_size != 0
    distance = strategy.position_size > 0 ? strategy.position_avg_price - tradeStop : tradeStop - strategy.position_avg_price
    if distance > 0
        tradeTarget := strategy.position_avg_price + (strategy.position_size > 0 ? 1 : -1) * reward * distance
    else
        strategy.close_all(comment="Fill beyond stop", immediately=true)

if not fillPass and barstate.isconfirmed
    key = year(time, "America/New_York") * 10000 + month(time, "America/New_York") * 100 + dayofmonth(time, "America/New_York")
    if na(dayKey) or key != dayKey
        dayKey := key
        usedToday := false
        shiftDirection := 0
        zoneDirection := 0
    minutesOpen = hour(time, "America/New_York") * 60 + minute(time, "America/New_York")
    minutesClose = hour(time_close, "America/New_York") * 60 + minute(time_close, "America/New_York")
    entryWindow = minutesOpen >= startMinute and minutesClose < endMinute

    if strategy.position_size == 0 and not usedToday
        if not entryWindow
            zoneDirection := 0
            shiftDirection := 0
        else
            if zoneDirection != 0
                invalid = bar_index - zoneBar > expiry or
                     (zoneDirection == 1 ? close < zoneLow : close > zoneHigh) or
                     (useBias and bias != zoneDirection)
                if invalid
                    zoneDirection := 0
            if zoneDirection == 0
                if bullShift and (not useBias or bias == 1)
                    shiftDirection := 1
                    shiftBar := bar_index
                else if bearShift and allowShorts and (not useBias or bias == -1)
                    shiftDirection := -1
                    shiftBar := bar_index
                if shiftDirection != 0 and bar_index - shiftBar <= gapDelay
                    if shiftDirection == 1 and bullGap
                        zoneDirection := 1
                        zoneLow := high[2]
                        zoneHigh := low
                        zoneStop := math.min(low[1], zoneLow) - syminfo.mintick
                        zoneBar := bar_index
                        shiftDirection := 0
                    else if shiftDirection == -1 and bearGap
                        zoneDirection := -1
                        zoneLow := high
                        zoneHigh := low[2]
                        zoneStop := math.max(high[1], zoneHigh) + syminfo.mintick
                        zoneBar := bar_index
                        shiftDirection := 0

            if zoneDirection != 0 and bar_index > zoneBar
                touched = low <= zoneHigh and high >= zoneLow
                rejection = zoneDirection == 1 ? close > open and close > zoneHigh : close < open and close < zoneLow
                distance = zoneDirection == 1 ? close - zoneStop : zoneStop - close
                if touched and rejection and distance > 0
                    unitRisk = distance * syminfo.pointvalue
                    quantity = math.floor(math.min(maxQty, cashRisk / unitRisk) / lotStep) * lotStep
                    if quantity >= lotStep
                        tradeStop := zoneStop
                        tradeTarget := na
                        usedToday := true
                        if zoneDirection == 1
                            strategy.entry("L", strategy.long, qty=quantity)
                            strategy.exit("L-bracket", "L", stop=tradeStop)
                        else
                            strategy.entry("S", strategy.short, qty=quantity)
                            strategy.exit("S-bracket", "S", stop=tradeStop)
                        zoneDirection := 0

    if minutesClose == flattenMinute
        strategy.cancel_all()
        strategy.close_all(comment="Session flatten", immediately=true)

if strategy.position_size > 0 and not na(tradeTarget)
    strategy.exit("L-bracket", "L", stop=tradeStop, limit=tradeTarget)
if strategy.position_size < 0 and not na(tradeTarget)
    strategy.exit("S-bracket", "S", stop=tradeStop, limit=tradeTarget)

plot(zoneDirection != 0 ? zoneLow : na, "Active gap low", color=color.orange, style=plot.style_linebr)
plot(zoneDirection != 0 ? zoneHigh : na, "Active gap high", color=color.orange, style=plot.style_linebr)
plot(strategy.position_size != 0 ? tradeStop : na, "Frozen stop", color=color.red, style=plot.style_linebr)
plot(strategy.position_size != 0 ? tradeTarget : na, "Fill-based target", color=color.green, style=plot.style_linebr)

The following strategy tests a reduced sequence on standard one-minute candles: a confirmed 15-minute directional state, a local range break, a same-direction three-candle gap, and a subsequent retest that closes beyond the gap in the trade direction.

The following choices are our research definitions: a 15-minute close beyond the prior 20-bar range changes the directional state; a one-minute close beyond the prior five-bar range supplies the local shift; a gap must appear within three bars, span at least two ticks, and receive its retest within 20 bars. The local break is a proxy, not a complete change-of-character algorithm requiring a preceding countertrend.

The rejection rule is stricter than the source’s candle-color description: a long retest must close above the gap’s upper edge; a short must close below its lower edge. The stop uses the middle candle’s extreme or the gap’s far boundary, whichever places it farther outside the zone, plus one tick.

One active gap is retained until used, invalidated, or expired. New gaps do not replace a valid active one. Entry is restricted to 9:30–11:00 New York time, with at most one submitted entry per calendar day. These time and frequency limits are ours. The target defaults to 2R from the actual simulated entry and frozen stop, replacing the source’s discretionary next 15-minute level.

Execution Checks That Affect the Result

Higher-timeframe direction uses the previous completed 15-minute state. The offset is applied inside the higher-timeframe calculation before requesting it with lookahead enabled. This follows TradingView’s documented confirmed higher-timeframe approach (paid link); an unshifted request would expose future information in historical tests.

Entries are market orders after a confirmed rejection candle, normally filled at the next bar’s open. A frozen protective stop is submitted with the entry. On the fill recalculation, the strategy reads the actual entry price and frozen stop to establish its target; it does not use the fill bar’s completed high, low, or close to select another setup. A fill beyond the stop prompts an immediate simulated exit, rather than creating an inverted bracket.

Position quantity is estimated at the rejection close. A gap at entry can exceed the intended cash risk even though the target is recalculated correctly. Commission defaults to 0.01% per fill and slippage to two ticks, adjustable in Strategy Properties. Borrow availability, borrow fees, spreads that vary through the session, and order rejection are not modeled. The 100% margin setting can prevent an otherwise plotted setup from receiving a fill.

The strategy flattens at the configured session close using an immediate simulated close. That assumes an obtainable closing fill and requires cost stress. Use 780 minutes after midnight for a 13:00 early close; the default is 960 for 16:00. The script does not automatically identify holiday sessions. Use a consistent chart session across comparisons because it affects the ranges and gaps.

Same-bar stop and target outcomes depend on TradingView’s broker emulator. Use Bar Magnifier where available and inspect ambiguous trades. Code logic has been reviewed, but this script has not been compiled or backtested in TradingView. No profitability or fill-quality result is claimed.

Backtest Lab: Find Out Which Check Helps

ExperimentChangeEvaluate
Directional contextToggle the 15-minute bias filterNet expectancy, drawdown, and trade count
Gap freshnessCompare short and long expiry periodsWhether delayed retests perform differently
Structure definitionChange the five-bar local lookbackStability rather than one best setting
Execution sensitivityIncrease slippage and commissionsHow much of the result survives realistic friction

Keep instruments and dates consistent, change one assumption at a time, and reserve an untouched period for validation. Include losing trades and record gaps that never produced entries. Selected chart winners do not establish a win rate.

This test does not reproduce the drawn trend line, the selected 15-minute pullback area, the toolkit’s proprietary labels, or its next-level targets. Its higher-timeframe bias can update while waiting; the source describes holding its map fixed during the trade. A useful manual comparison is to annotate the omitted context before seeing each outcome and check whether it improves the mechanical baseline.

Final Takeaway

Direction, confirmation, and invalidation make a trade easier to describe and review. Their value depends on definitions available at the time, realistic fills, and evidence across many opportunities. A fair value gap can mark a candidate retest area; a subsequent response supplies additional information, while the stop limits planned exposure when the idea fails.


Risk Disclosure: This material is for educational and research purposes only and is not financial or investment advice. Trading can result in substantial losses. Backtests are hypothetical, depend heavily on their assumptions, and cannot reproduce every aspect of live execution. Commissions, spreads, slippage, liquidity, gaps, order behavior, market regime changes, and discretionary decision-making can materially affect real-world results. Short selling adds borrow and potentially unlimited-loss risks. Historical results do not guarantee future performance.

This article is an independent educational companion to the video and is not affiliated with, sponsored by, endorsed by, or authorized by Trading Notes or Flux Charts.

As an Amazon Associate I earn from qualifying purchases.