Skip to content
← Indicator library
tradingviewstructureindicatorpinescript-v6

ICT.AlgoNexta

TradingView ICT-style structure, liquidity sweep, FVG retracement, and visual risk/reward scanner.

Published by AlgoNexta. AlgoNexta builds and documents evidence-first trading tools so you can inspect the logic before using it.About AlgoNextaIndicator libraryAlgoNexta Research
Published Oct 16, 2026#ict#market-structure#liquidity-sweep#fair-value-gap#price-action#indicator#pinescript-v6#tradingview

Overview

What this indicator does

ICT.AlgoNexta is an overlay indicator that scans confirmed bars for an ICT-style sequence: liquidity sweep, displacement, market-structure confirmation, a fair value gap (FVG), and a retracement into the selected FVG entry level. It draws BUY or SELL labels when its active profile qualifies a setup; it does not send orders or manage a position.

Type
indicator
Category
structure
Available in
Pine Script
Source
Open — copy or download

How it works

Read the logic before you use it.

Diagram: When a setup begins
1

When a setup begins

For the strict `Selective A+` sequence, a bullish setup starts when price trades below a confirmed swing low and closes back above it; a bearish setup does the inverse at a confirmed swing high. The swing levels use `Confirmed swing length = 5` by default, so they become available only after pivot confirmation.

Diagram: What confirms the imbalance
2

What confirms the imbalance

The script requires confirmed ATR-qualified displacement after the sweep, then accepts a bullish FVG when the current low is above the high from two bars earlier, or a bearish FVG when the current high is below the low from two bars earlier. With default settings, displacement body and range must reach 0.75 ATR and 1.20 ATR, while the FVG must be at least 0.10 ATR.

Diagram: How retracement signals and risk are framed
3

How retracement signals and risk are framed

A signal occurs only after a later bar reaches the selected FVG entry level; the default `50% FVG midpoint` uses the midpoint of the gap. If `Show Entry / SL / TP plan` is enabled, the indicator projects a stop at `Stop distance (ATR) = 1.5` ATR and targets at 1R, 2R, and 3R by default; these drawings are not orders.

Walkthrough

Watch how it reads the chart.

Live market chart

The logic on real candles.

Backtest evidence

What the numbers actually show.

This script's logic could not be confidently reconstructed into an automated reference backtest, so no performance figures are shown rather than estimated ones.

Verify it in TradingView's Strategy Tester (or the equivalent tool for tradingview) before relying on it.

Vision Grade

What was measured, and how confident we are in it.

Vision Grade requires a reference backtest with enough reconstructed entries to measure — not available for this script yet.

Full breakdown

How the script behaves.

About AlgoNexta

Published by AlgoNexta, an evidence-first platform for building, reviewing, and documenting algorithmic trading tools. This page explains the script's actual logic and intended use; it does not promise profitability or replace verification in TradingView.

ICT.AlgoNexta is an overlay indicator that scans confirmed bars for an ICT-style sequence: liquidity sweep, displacement, market-structure confirmation, a fair value gap (FVG), and a retracement into the selected FVG entry level. It draws BUY or SELL labels when its active profile qualifies a setup; it does not send orders or manage a position.

What it watches

The default Signal profile is Balanced. In this mode, the script looks for a bullish or bearish FVG that occurs within two bars of a qualifying displacement candle and aligns with the higher-timeframe 50 EMA direction. It then waits for price to retrace into the selected FVG entry level.

The Selective A+ profile uses the fuller state sequence:

  • A confirmed sell-side liquidity sweep for a bullish sequence, or buy-side liquidity sweep for a bearish sequence.
  • A displacement candle within Sweep memory (bars) (12 by default).
  • Structure confirmation via MSS or BOS by default.
  • A linked FVG, either formed with the displacement or within Bars allowed from MSS to FVG (10).
  • A later retracement into the FVG before Maximum setup age (bars) (60).

By default, the higher-timeframe context uses HTF bias mode = Structural HTF Bias on Higher timeframe = 60. Structural pivots use Confirmed swing length = 5, so those swing levels are only known after the configured number of right-side bars has elapsed. Select EMA Bias to use the higher-timeframe close versus its 50 EMA instead, or Bias Filter Off to remove that filter.

A liquidity sweep requires price to trade beyond a confirmed swing and close back inside it. Displacement requires a confirmed candle body of at least Displacement body (ATR) = 0.75 ATR and range of at least Displacement range (ATR) = 1.20 ATR. With Displacement closes beyond prior candle enabled by default, the close must also exceed the prior high for bullish displacement or fall below the prior low for bearish displacement. FVGs must be at least Minimum FVG size (ATR) = 0.10 ATR.

Reading the chart

  • Green BUY label: a qualified bullish retracement at the calculated FVG entry level.
  • Red SELL label: a qualified bearish retracement at the calculated FVG entry level.
  • Entry selection: Retracement entry defaults to 50% FVG midpoint. Touch FVG uses the near edge; Deep FVG retracement uses the deeper 25% level defined by the script.
  • Risk/reward projection: with Show Entry / SL / TP plan enabled, the dashed white line is the entry reference, red is the ATR-based stop reference, and green lines are TP1, TP2, and TP3. Defaults are Stop distance (ATR) = 1.5, TP1 (R) = 1.0, TP2 (R) = 2.0, and TP3 (R) = 3.0. These are drawings only, not orders, fills, or guaranteed outcomes.
  • Optional detail overlays: disable Clean signal mode — arrows only to enable structure, liquidity, equilibrium, FVG, or session visuals. Show active liquidity displays BSL/SSL swing levels; Show MSS / BOS displays structure events; Show dashboard adds the current bias, session, liquidity event, structure, setup state, grade, and score.
  • Grades and score: grades are labels derived from an event-based score, not a measured probability. Defaults are B threshold = 60, A threshold = 70, and A+ threshold = 85. Minimum displayed grade = A and Minimum score = 70 affect the strict Selective A+ path.

Sessions and duplicate-signal control

Session windows are evaluated in the America/New_York timezone. Defaults enable London from 0200-0500 and New York from 0700-1000. In Selective A+, Require active session is effective when Session filter and Require active session are enabled. The default Balanced path does not require these session gates, though session participation can still add to its displayed score.

Minimum bars between arrows defaults to 20. The script also blocks an immediately repeated signal in the same direction, so nearby qualifying retracements will not necessarily create another label.

Alerts

TradingView alert conditions are included for A BUY, A SELL, A+ BUY, A+ SELL, and Any qualified setup. Create an alert from the indicator in TradingView and select the desired condition. Alert messages include ticker, chart interval, direction or setup type, and the hidden Signal Score plot value.

Timeframes and markets

The source labels its defaults as Gold-oriented and uses a 60-minute higher-timeframe context, but it does not restrict the chart symbol or chart timeframe. Because it relies on confirmed pivots, ATR, multi-bar FVGs, and New York-time session windows, use a chart timeframe with enough bars for those structures to form and test settings on the specific market and session you trade.

Limitations

  • This is an indicator(), not a strategy(): it contains no strategy.entry, strategy.exit, broker execution, position sizing, or realized-profit calculation.
  • Signals are calculated on confirmed bars (barstate.isconfirmed), reducing intrabar signal changes. However, swing pivots are inherently delayed by Confirmed swing length bars, and higher-timeframe values update only as their higher-timeframe bars confirm because lookahead is off.
  • A BUY or SELL label means the coded conditions were met, not that a trade was filled at the projected entry level. A bar can span an entry level without proving a fill sequence.
  • The score and A/A+ labels are rule-based classifications. They do not measure win rate, probability, or future performance.
  • A no-signal outcome is normal when no qualifying FVG retracement occurs, the higher-timeframe bias disagrees, cooldown is active, or the selected strict gates are not met.
  • Allow wick structure breaks changes structure breaks from close-based to wick-based; it can make structure events occur sooner. Prefer EQH/EQL sweep adds a strict requirement that the swept prior pool was classified as equal liquidity using EQH/EQL tolerance (ATR) = 0.12 by default.

This material is educational and not financial advice. If you adapt these plotted conditions into a strategy, TradingView Strategy Tester is the appropriate source of truth for historical simulation results; it is not live verification.

Source

Full source code, in every platform.

ict-algonexta.pine · 493 lines · 34.6 KB

Not yet verified on the native platform — test it before relying on it.

The original uploaded source.

Download .pine
// ============================================================================
// © algonexta
// This script is for education and research; it does not guarantee results.
// Verify this source in TradingView before use.
// ============================================================================

//@version=6
indicator("ICT Gold Engine — A+ Setup Scanner", shorttitle="ICTGoldA+", overlay=true, max_boxes_count=100, max_lines_count=100, max_labels_count=150)

//──────────────────────────────────────────────────────────────────────────────
// Inputs — Gold defaults
//──────────────────────────────────────────────────────────────────────────────
groupContext = "1. Context and Structure"
biasMode       = input.string("Structural HTF Bias", "HTF bias mode", options=["Structural HTF Bias", "EMA Bias", "Bias Filter Off"], group=groupContext)
htf             = input.timeframe("60", "Higher timeframe", group=groupContext)
swingLength     = input.int(5, "Confirmed swing length", minval=2, maxval=20, group=groupContext)
atrLength       = input.int(14, "ATR length", minval=2, group=groupContext)
wickBreakMode   = input.bool(false, "Allow wick structure breaks", group=groupContext)
showStructure   = input.bool(false, "Show MSS / BOS", group=groupContext)

groupLiquidity = "2. Liquidity"
equalTolerance = input.float(0.12, "EQH/EQL tolerance (ATR)", minval=0.01, step=0.01, group=groupLiquidity)
sweepMemory    = input.int(12, "Sweep memory (bars)", minval=1, maxval=50, group=groupLiquidity)
preferEqual    = input.bool(false, "Prefer EQH/EQL sweep", group=groupLiquidity, tooltip="Adds a quality gate: the swept pool must have been classified as equal liquidity.")
showLiquidity  = input.bool(false, "Show active liquidity", group=groupLiquidity)

groupSequence = "3. Sequence"
signalProfile = input.string("Balanced", "Signal profile", options=["Balanced", "Selective A+"], group=groupSequence)
structureConfirmationMode = input.string("MSS or BOS", "Structure confirmation", options=["MSS or BOS", "MSS only"], group=groupSequence)
bodyFactor    = input.float(0.75, "Displacement body (ATR)", minval=0.1, step=0.05, group=groupSequence)
rangeFactor   = input.float(1.20, "Displacement range (ATR)", minval=0.1, step=0.05, group=groupSequence)
requireCloseExpansion = input.bool(true, "Displacement closes beyond prior candle", group=groupSequence)
fvgMinimum    = input.float(0.10, "Minimum FVG size (ATR)", minval=0.0, step=0.01, group=groupSequence)
fvgMemory     = input.int(10, "Bars allowed from MSS to FVG", minval=0, maxval=50, group=groupSequence)
entryMode     = input.string("50% FVG midpoint", "Retracement entry", options=["Touch FVG", "50% FVG midpoint", "Deep FVG retracement"], group=groupSequence)
maxSetupAge   = input.int(60, "Maximum setup age (bars)", minval=5, maxval=500, group=groupSequence)
signalCooldown = input.int(20, "Minimum bars between arrows", minval=1, maxval=100, group=groupSequence)
showFvg       = input.bool(false, "Show active setup FVG", group=groupSequence)

groupSessions = "4. Sessions"
sessionFilter = input.bool(true, "Session filter", group=groupSessions)
londonEnabled = input.bool(true, "London enabled", inline="L", group=groupSessions)
londonSession = input.session("0200-0500", "", inline="L", group=groupSessions)
newYorkEnabled = input.bool(true, "New York enabled", inline="N", group=groupSessions)
newYorkSession = input.session("0700-1000", "", inline="N", group=groupSessions)
shadeSessions = input.bool(false, "Shade enabled sessions", group=groupSessions)

groupFilters = "5. Quality Gates"
requireBias         = input.bool(true, "Require aligned HTF bias", group=groupFilters)
requireSweep        = input.bool(true, "Require liquidity sweep", group=groupFilters)
requireDisplacement = input.bool(true, "Require displacement", group=groupFilters)
requireFvg          = input.bool(true, "Require linked FVG", group=groupFilters)
requirePd           = input.bool(false, "Require premium/discount alignment", group=groupFilters)
requireSession      = input.bool(true, "Require active session", group=groupFilters)
minimumGrade        = input.string("A", "Minimum displayed grade", options=["B", "A", "A+"], group=groupFilters)
minimumScore        = input.int(70, "Minimum score", minval=0, maxval=100, group=groupFilters)
gradeB              = input.int(60, "B threshold", minval=0, maxval=100, group=groupFilters)
gradeA              = input.int(70, "A threshold", minval=0, maxval=100, group=groupFilters)
gradeAPlus          = input.int(85, "A+ threshold", minval=0, maxval=100, group=groupFilters)

groupVisual = "6. Display"
cleanSignalMode = input.bool(true, "Clean signal mode — arrows only", group=groupVisual)
signalMarkerSize = input.string("Large", "Signal marker size", options=["Normal", "Large", "Huge"], group=groupVisual)
showEquilibrium = input.bool(false, "Show structural equilibrium", group=groupVisual)
showDashboard   = input.bool(false, "Show dashboard", group=groupVisual)
showSignalDetails = input.bool(false, "Show grade and score beside arrow", group=groupVisual)

groupTradePlan = "7. Visual Risk / Reward"
showTradePlan = input.bool(true, "Show Entry / SL / TP plan", group=groupTradePlan)
atrStopMultiple = input.float(1.5, "Stop distance (ATR)", minval=0.25, maxval=10.0, step=0.25, group=groupTradePlan)
targetR1 = input.float(1.0, "TP1 (R)", minval=0.25, maxval=10.0, step=0.25, group=groupTradePlan)
targetR2 = input.float(2.0, "TP2 (R)", minval=0.5, maxval=15.0, step=0.25, group=groupTradePlan)
targetR3 = input.float(3.0, "TP3 (R)", minval=0.75, maxval=20.0, step=0.25, group=groupTradePlan)
planProjectionBars = input.int(16, "Plan width (bars)", minval=3, maxval=100, group=groupTradePlan)
rrHistoryCount = input.int(15, "Signal RR plans to keep", minval=1, maxval=20, group=groupTradePlan)

//──────────────────────────────────────────────────────────────────────────────
// Helpers
//──────────────────────────────────────────────────────────────────────────────
f_structuralBias(int len) =>
    float ph = ta.pivothigh(high, len, len)
    float pl = ta.pivotlow(low, len, len)
    var float lastH = na
    var float lastL = na
    var int direction = 0
    if not na(ph)
        lastH := ph
    if not na(pl)
        lastL := pl
    if not na(lastH) and close > lastH
        direction := 1
    if not na(lastL) and close < lastL
        direction := -1
    direction

f_grade(int score) => score >= gradeAPlus ? "A+" : score >= gradeA ? "A" : score >= gradeB ? "B" : "—"
f_gradeRank(string grade) => grade == "A+" ? 3 : grade == "A" ? 2 : grade == "B" ? 1 : 0
f_stateName(int state) => state == 0 ? "IDLE" : state == 1 ? "SWEEP" : state == 2 ? "DISPLACEMENT" : state == 3 ? "MSS" : state == 4 ? "WAITING FOR RETRACEMENT" : state == 5 ? "READY" : "INVALIDATED"

// HTF calculations execute in the HTF context; lookahead is explicitly off.
htfStructuralBias = request.security(syminfo.tickerid, htf, f_structuralBias(swingLength), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
htfClose = request.security(syminfo.tickerid, htf, close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
htfEma   = request.security(syminfo.tickerid, htf, ta.ema(close, 50), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
int htfBias = biasMode == "Bias Filter Off" ? 0 : biasMode == "EMA Bias" ? (htfClose > htfEma ? 1 : htfClose < htfEma ? -1 : 0) : htfStructuralBias

float atr = ta.atr(atrLength)
float pivotHigh = ta.pivothigh(high, swingLength, swingLength)
float pivotLow  = ta.pivotlow(low, swingLength, swingLength)
var float lastSwingHigh = na
var float priorSwingHigh = na
var float lastSwingLow = na
var float priorSwingLow = na
var bool lastHighEqual = false
var bool lastLowEqual = false
var bool highLiquidityAvailable = false
var bool lowLiquidityAvailable = false
var bool highStructureAvailable = false
var bool lowStructureAvailable = false

bool newEqh = false
bool newEql = false
if not na(pivotHigh)
    priorSwingHigh := lastSwingHigh
    lastSwingHigh := pivotHigh
    lastHighEqual := not na(priorSwingHigh) and math.abs(lastSwingHigh - priorSwingHigh) <= atr[swingLength] * equalTolerance
    highLiquidityAvailable := true
    highStructureAvailable := true
    newEqh := lastHighEqual
if not na(pivotLow)
    priorSwingLow := lastSwingLow
    lastSwingLow := pivotLow
    lastLowEqual := not na(priorSwingLow) and math.abs(lastSwingLow - priorSwingLow) <= atr[swingLength] * equalTolerance
    lowLiquidityAvailable := true
    lowStructureAvailable := true
    newEql := lastLowEqual

// Structure is based on confirmed swing levels and close breaks by default.
float bullBreakPrice = wickBreakMode ? high : close
float bearBreakPrice = wickBreakMode ? low : close
bool breakHigh = highStructureAvailable and not na(lastSwingHigh) and bullBreakPrice > lastSwingHigh
bool breakLow  = lowStructureAvailable and not na(lastSwingLow) and bearBreakPrice < lastSwingLow
var int structureDirection = 0
bool bullMss = breakHigh and structureDirection <= -1
bool bearMss = breakLow and structureDirection >= 1
bool bullBos = breakHigh and structureDirection == 1
bool bearBos = breakLow and structureDirection == -1
if breakHigh
    structureDirection := 1
    highStructureAvailable := false
if breakLow
    structureDirection := -1
    lowStructureAvailable := false

// Structural dealing range: most recently confirmed swing low and high.
float equilibrium = not na(lastSwingHigh) and not na(lastSwingLow) ? (lastSwingHigh + lastSwingLow) * 0.5 : na
bool inDiscount = not na(equilibrium) and close <= equilibrium
bool inPremium  = not na(equilibrium) and close >= equilibrium

bool inLondon = londonEnabled and not na(time(timeframe.period, londonSession, "America/New_York"))
bool inNewYork = newYorkEnabled and not na(time(timeframe.period, newYorkSession, "America/New_York"))
bool inKillZone = inLondon or inNewYork
string sessionName = inLondon ? "LONDON" : inNewYork ? "NEW YORK" : "OFF SESSION"

// True rejection: trade through confirmed liquidity, then close back inside.
bool sslSweep = barstate.isconfirmed and lowLiquidityAvailable and not na(lastSwingLow) and low < lastSwingLow and close > lastSwingLow
bool bslSweep = barstate.isconfirmed and highLiquidityAvailable and not na(lastSwingHigh) and high > lastSwingHigh and close < lastSwingHigh
bool bullDisplacement = barstate.isconfirmed and close > open and math.abs(close-open) >= atr*bodyFactor and (high-low) >= atr*rangeFactor and (not requireCloseExpansion or close > high[1])
bool bearDisplacement = barstate.isconfirmed and close < open and math.abs(close-open) >= atr*bodyFactor and (high-low) >= atr*rangeFactor and (not requireCloseExpansion or close < low[1])
bool bullFvgNow = barstate.isconfirmed and low > high[2] and (low-high[2]) >= atr*fvgMinimum
bool bearFvgNow = barstate.isconfirmed and high < low[2] and (low[2]-high) >= atr*fvgMinimum

//──────────────────────────────────────────────────────────────────────────────
// Sequence state. 0 idle, 1 sweep, 2 displacement, 3 MSS, 4 wait, 5 ready.
//──────────────────────────────────────────────────────────────────────────────
var int bullState = 0
var int bearState = 0
var int bullStart = na
var int bearStart = na
var int bullStateBar = na
var int bearStateBar = na
var float bullFvgTop = na
var float bullFvgBottom = na
var float bearFvgTop = na
var float bearFvgBottom = na
var int bullFvgBar = na
var int bearFvgBar = na
var bool bullSweptEqual = false
var bool bearSweptEqual = false
var box bullBox = na
var box bearBox = na
var string lastLiquidityEvent = "NONE"

if sslSweep
    lowLiquidityAvailable := false
    bullState := 1
    bullStart := bar_index
    bullStateBar := bar_index
    bullSweptEqual := lastLowEqual
    lastLiquidityEvent := "SSL SWEEP"
    bearState := 0
    if not na(bearBox)
        box.delete(bearBox)
        bearBox := na
if bslSweep
    highLiquidityAvailable := false
    bearState := 1
    bearStart := bar_index
    bearStateBar := bar_index
    bearSweptEqual := lastHighEqual
    lastLiquidityEvent := "BSL SWEEP"
    bullState := 0
    if not na(bullBox)
        box.delete(bullBox)
        bullBox := na

if bullState == 1 and bar_index - bullStateBar <= sweepMemory and bullDisplacement
    bullState := 2
    bullStateBar := bar_index
if bearState == 1 and bar_index - bearStateBar <= sweepMemory and bearDisplacement
    bearState := 2
    bearStateBar := bar_index

// The imbalance commonly forms on the displacement candle before structure is
// confirmed. Preserve that setup-owned FVG instead of searching for a random one.
if bullState == 2 and bullFvgNow
    bullFvgTop := low
    bullFvgBottom := high[2]
    bullFvgBar := bar_index
if bearState == 2 and bearFvgNow
    bearFvgTop := low[2]
    bearFvgBottom := high
    bearFvgBar := bar_index

bool bullStructureConfirmed = bullMss or (structureConfirmationMode == "MSS or BOS" and breakHigh)
bool bearStructureConfirmed = bearMss or (structureConfirmationMode == "MSS or BOS" and breakLow)
if bullState == 2 and bullStructureConfirmed
    bullState := not na(bullFvgBar) and bullFvgBar >= bullStart ? 4 : 3
    bullStateBar := bar_index
if bearState == 2 and bearStructureConfirmed
    bearState := not na(bearFvgBar) and bearFvgBar >= bearStart ? 4 : 3
    bearStateBar := bar_index

// Only an FVG formed after the sequence MSS owns the setup.
if bullState == 3 and bar_index - bullStateBar <= fvgMemory and bullFvgNow
    bullFvgTop := low
    bullFvgBottom := high[2]
    bullFvgBar := bar_index
    bullState := 4
    if not na(bullBox)
        box.delete(bullBox)
    bullBox := box.new(bar_index-2, bullFvgTop, bar_index+1, bullFvgBottom, border_color=cleanSignalMode ? color.new(color.teal, 100) : color.new(color.teal, 45), bgcolor=showFvg and not cleanSignalMode ? color.new(color.teal, 87) : color.new(color.teal, 100), extend=extend.right)
if bearState == 3 and bar_index - bearStateBar <= fvgMemory and bearFvgNow
    bearFvgTop := low[2]
    bearFvgBottom := high
    bearFvgBar := bar_index
    bearState := 4
    if not na(bearBox)
        box.delete(bearBox)
    bearBox := box.new(bar_index-2, bearFvgTop, bar_index+1, bearFvgBottom, border_color=cleanSignalMode ? color.new(color.orange, 100) : color.new(color.orange, 45), bgcolor=showFvg and not cleanSignalMode ? color.new(color.orange, 87) : color.new(color.orange, 100), extend=extend.right)

float bullEntryLevel = entryMode == "Touch FVG" ? bullFvgTop : entryMode == "50% FVG midpoint" ? (bullFvgTop+bullFvgBottom)*0.5 : bullFvgBottom + (bullFvgTop-bullFvgBottom)*0.25
float bearEntryLevel = entryMode == "Touch FVG" ? bearFvgBottom : entryMode == "50% FVG midpoint" ? (bearFvgTop+bearFvgBottom)*0.5 : bearFvgTop - (bearFvgTop-bearFvgBottom)*0.25
bool bullRetrace = bullState == 4 and bar_index > bullFvgBar and low <= bullEntryLevel and high >= bullFvgBottom
bool bearRetrace = bearState == 4 and bar_index > bearFvgBar and high >= bearEntryLevel and low <= bearFvgTop

// Balanced public-facing path: impulse imbalance, then a real pullback into it.
// It remains confirmed-bar and HTF-filtered, but does not require every strict
// ICT event to occur in one exact order.
var float balancedBullTop = na
var float balancedBullBottom = na
var float balancedBearTop = na
var float balancedBearBottom = na
var int balancedBullBar = na
var int balancedBearBar = na
var bool balancedBullActive = false
var bool balancedBearActive = false
var int lastBuyArrowBar = na
var int lastSellArrowBar = na
var int lastAnyArrowBar = na
var int lastSignalDirection = 0
var line[] rrLines = array.new_line()
var label[] rrLabels = array.new_label()

int barsSinceBullDisplacement = nz(ta.barssince(bullDisplacement), 10000)
int barsSinceBearDisplacement = nz(ta.barssince(bearDisplacement), 10000)
// Balanced mode uses the stable HTF EMA direction so a slowly-confirming swing
// bias cannot suppress an entire trend. Selective A+ retains structural bias.
bool balancedBullBias = biasMode == "Bias Filter Off" or htfClose >= htfEma
bool balancedBearBias = biasMode == "Bias Filter Off" or htfClose <= htfEma

if signalProfile == "Balanced" and not balancedBullActive and bullFvgNow and barsSinceBullDisplacement <= 2 and balancedBullBias
    balancedBullTop := low
    balancedBullBottom := high[2]
    balancedBullBar := bar_index
    balancedBullActive := true
if signalProfile == "Balanced" and not balancedBearActive and bearFvgNow and barsSinceBearDisplacement <= 2 and balancedBearBias
    balancedBearTop := low[2]
    balancedBearBottom := high
    balancedBearBar := bar_index
    balancedBearActive := true

float balancedBullEntry = entryMode == "Touch FVG" ? balancedBullTop : entryMode == "50% FVG midpoint" ? (balancedBullTop+balancedBullBottom)*0.5 : balancedBullBottom + (balancedBullTop-balancedBullBottom)*0.25
float balancedBearEntry = entryMode == "Touch FVG" ? balancedBearBottom : entryMode == "50% FVG midpoint" ? (balancedBearTop+balancedBearBottom)*0.5 : balancedBearTop - (balancedBearTop-balancedBearBottom)*0.25
bool balancedBuyRetrace = balancedBullActive and bar_index > balancedBullBar and low <= balancedBullEntry and high >= balancedBullBottom
bool balancedSellRetrace = balancedBearActive and bar_index > balancedBearBar and high >= balancedBearEntry and low <= balancedBearTop
bool globalCooldownReady = na(lastAnyArrowBar) or bar_index-lastAnyArrowBar >= signalCooldown
bool buyCooldownReady = globalCooldownReady and lastSignalDirection != 1 and (na(lastBuyArrowBar) or bar_index-lastBuyArrowBar >= signalCooldown)
bool sellCooldownReady = globalCooldownReady and lastSignalDirection != -1 and (na(lastSellArrowBar) or bar_index-lastSellArrowBar >= signalCooldown)
bool balancedBuySignal = barstate.isconfirmed and balancedBuyRetrace and balancedBullBias and buyCooldownReady
bool balancedSellSignal = barstate.isconfirmed and balancedSellRetrace and balancedBearBias and sellCooldownReady

int recentSslBars = nz(ta.barssince(sslSweep), 10000)
int recentBslBars = nz(ta.barssince(bslSweep), 10000)
int balancedBullScore = math.min(100, 70 + (htfBias == 1 ? 10 : 0) + (recentSslBars <= 20 ? 10 : 0) + (inDiscount ? 5 : 0) + (inKillZone ? 5 : 0))
int balancedBearScore = math.min(100, 70 + (htfBias == -1 ? 10 : 0) + (recentBslBars <= 20 ? 10 : 0) + (inPremium ? 5 : 0) + (inKillZone ? 5 : 0))

// Score is transparent and event-based. Sequence prerequisites are retained in state.
int bullScore = (htfBias == 1 ? 15 : 0) + (bullState >= 1 ? 20 : 0) + (bullState >= 2 ? 15 : 0) + (bullState >= 3 ? 15 : 0) + (bullState >= 4 ? 15 : 0) + (bullRetrace ? 10 : 0) + (inDiscount ? 5 : 0) + (inKillZone ? 5 : 0)
int bearScore = (htfBias == -1 ? 15 : 0) + (bearState >= 1 ? 20 : 0) + (bearState >= 2 ? 15 : 0) + (bearState >= 3 ? 15 : 0) + (bearState >= 4 ? 15 : 0) + (bearRetrace ? 10 : 0) + (inPremium ? 5 : 0) + (inKillZone ? 5 : 0)
string bullGrade = f_grade(bullScore)
string bearGrade = f_grade(bearScore)
int requiredRank = f_gradeRank(minimumGrade)

bool effectiveSessionRequired = signalProfile == "Selective A+" and requireSession
bool bullGates = (biasMode == "Bias Filter Off" or not requireBias or htfBias == 1) and (not requireSweep or bullState >= 1) and (not requireDisplacement or bullState >= 2) and (not requireFvg or bullState >= 4) and (not requirePd or inDiscount) and (not sessionFilter or not effectiveSessionRequired or inKillZone) and (not preferEqual or bullSweptEqual)
bool bearGates = (biasMode == "Bias Filter Off" or not requireBias or htfBias == -1) and (not requireSweep or bearState >= 1) and (not requireDisplacement or bearState >= 2) and (not requireFvg or bearState >= 4) and (not requirePd or inPremium) and (not sessionFilter or not effectiveSessionRequired or inKillZone) and (not preferEqual or bearSweptEqual)
bool strictBuySignal = barstate.isconfirmed and bullRetrace and bullGates and bullScore >= minimumScore and f_gradeRank(bullGrade) >= requiredRank
bool strictSellSignal = barstate.isconfirmed and bearRetrace and bearGates and bearScore >= minimumScore and f_gradeRank(bearGrade) >= requiredRank
bool buySignal = signalProfile == "Balanced" ? balancedBuySignal : strictBuySignal
bool sellSignal = signalProfile == "Balanced" ? balancedSellSignal : strictSellSignal
float signalBuyLevel = signalProfile == "Balanced" ? balancedBullEntry : bullEntryLevel
float signalSellLevel = signalProfile == "Balanced" ? balancedBearEntry : bearEntryLevel
int signalBullScore = signalProfile == "Balanced" ? balancedBullScore : bullScore
int signalBearScore = signalProfile == "Balanced" ? balancedBearScore : bearScore
string signalBullGrade = f_grade(signalBullScore)
string signalBearGrade = f_grade(signalBearScore)

if buySignal
    lastBuyArrowBar := bar_index
    lastAnyArrowBar := bar_index
    lastSignalDirection := 1
    balancedBullActive := false
    bullState := 5
    bullStateBar := bar_index
    string buyMarkerText = signalMarkerSize == "Huge" ? "▲\nBUY" : "BUY"
    label.new(bar_index, signalBuyLevel, buyMarkerText, style=label.style_label_up, color=color.rgb(0, 200, 83), textcolor=color.white, size=signalMarkerSize == "Normal" ? size.normal : signalMarkerSize == "Large" ? size.large : size.huge, tooltip="BUY entry · " + signalBullGrade + " · Score " + str.tostring(signalBullScore))
    if showTradePlan
        float buyRisk = atr*atrStopMultiple
        float buyStop = signalBuyLevel-buyRisk
        float buyTp1 = signalBuyLevel+buyRisk*targetR1
        float buyTp2 = signalBuyLevel+buyRisk*targetR2
        float buyTp3 = signalBuyLevel+buyRisk*targetR3
        line buyEntryLine = line.new(bar_index, signalBuyLevel, bar_index+planProjectionBars, signalBuyLevel, color=color.new(color.white, 15), style=line.style_dashed, width=1)
        line buySlLine = line.new(bar_index, buyStop, bar_index+planProjectionBars, buyStop, color=color.rgb(255, 45, 85), width=2)
        line buyTp1Line = line.new(bar_index, buyTp1, bar_index+planProjectionBars, buyTp1, color=color.new(color.lime, 35), width=1)
        line buyTp2Line = line.new(bar_index, buyTp2, bar_index+planProjectionBars, buyTp2, color=color.new(color.lime, 15), width=2)
        line buyTp3Line = line.new(bar_index, buyTp3, bar_index+planProjectionBars, buyTp3, color=color.rgb(0, 230, 118), width=2)
        label buySlLabel = label.new(bar_index+planProjectionBars, buyStop, "SL", style=label.style_label_left, color=color.new(color.red, 10), textcolor=color.white, size=size.small)
        label buyTp1Label = label.new(bar_index+planProjectionBars, buyTp1, "TP1 · " + str.tostring(targetR1, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 35), textcolor=color.white, size=size.tiny)
        label buyTp2Label = label.new(bar_index+planProjectionBars, buyTp2, "TP2 · " + str.tostring(targetR2, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 20), textcolor=color.white, size=size.tiny)
        label buyTp3Label = label.new(bar_index+planProjectionBars, buyTp3, "TP3 · " + str.tostring(targetR3, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 5), textcolor=color.white, size=size.tiny)
        array.push(rrLines, buyEntryLine)
        array.push(rrLines, buySlLine)
        array.push(rrLines, buyTp1Line)
        array.push(rrLines, buyTp2Line)
        array.push(rrLines, buyTp3Line)
        array.push(rrLabels, buySlLabel)
        array.push(rrLabels, buyTp1Label)
        array.push(rrLabels, buyTp2Label)
        array.push(rrLabels, buyTp3Label)
    if showSignalDetails and not cleanSignalMode
        label.new(bar_index, signalBuyLevel, signalBullGrade + " · " + str.tostring(signalBullScore), style=label.style_label_up, color=color.new(color.green, 15), textcolor=color.white, size=size.small)
    if not na(bullBox)
        box.set_extend(bullBox, extend.none)
        box.set_right(bullBox, bar_index)
if sellSignal
    lastSellArrowBar := bar_index
    lastAnyArrowBar := bar_index
    lastSignalDirection := -1
    balancedBearActive := false
    bearState := 5
    bearStateBar := bar_index
    string sellMarkerText = signalMarkerSize == "Huge" ? "SELL\n▼" : "SELL"
    label.new(bar_index, signalSellLevel, sellMarkerText, style=label.style_label_down, color=color.rgb(255, 45, 85), textcolor=color.white, size=signalMarkerSize == "Normal" ? size.normal : signalMarkerSize == "Large" ? size.large : size.huge, tooltip="SELL entry · " + signalBearGrade + " · Score " + str.tostring(signalBearScore))
    if showTradePlan
        float sellRisk = atr*atrStopMultiple
        float sellStop = signalSellLevel+sellRisk
        float sellTp1 = signalSellLevel-sellRisk*targetR1
        float sellTp2 = signalSellLevel-sellRisk*targetR2
        float sellTp3 = signalSellLevel-sellRisk*targetR3
        line sellEntryLine = line.new(bar_index, signalSellLevel, bar_index+planProjectionBars, signalSellLevel, color=color.new(color.white, 15), style=line.style_dashed, width=1)
        line sellSlLine = line.new(bar_index, sellStop, bar_index+planProjectionBars, sellStop, color=color.rgb(255, 45, 85), width=2)
        line sellTp1Line = line.new(bar_index, sellTp1, bar_index+planProjectionBars, sellTp1, color=color.new(color.lime, 35), width=1)
        line sellTp2Line = line.new(bar_index, sellTp2, bar_index+planProjectionBars, sellTp2, color=color.new(color.lime, 15), width=2)
        line sellTp3Line = line.new(bar_index, sellTp3, bar_index+planProjectionBars, sellTp3, color=color.rgb(0, 230, 118), width=2)
        label sellSlLabel = label.new(bar_index+planProjectionBars, sellStop, "SL", style=label.style_label_left, color=color.new(color.red, 10), textcolor=color.white, size=size.small)
        label sellTp1Label = label.new(bar_index+planProjectionBars, sellTp1, "TP1 · " + str.tostring(targetR1, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 35), textcolor=color.white, size=size.tiny)
        label sellTp2Label = label.new(bar_index+planProjectionBars, sellTp2, "TP2 · " + str.tostring(targetR2, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 20), textcolor=color.white, size=size.tiny)
        label sellTp3Label = label.new(bar_index+planProjectionBars, sellTp3, "TP3 · " + str.tostring(targetR3, "#.##") + "R", style=label.style_label_left, color=color.new(color.green, 5), textcolor=color.white, size=size.tiny)
        array.push(rrLines, sellEntryLine)
        array.push(rrLines, sellSlLine)
        array.push(rrLines, sellTp1Line)
        array.push(rrLines, sellTp2Line)
        array.push(rrLines, sellTp3Line)
        array.push(rrLabels, sellSlLabel)
        array.push(rrLabels, sellTp1Label)
        array.push(rrLabels, sellTp2Label)
        array.push(rrLabels, sellTp3Label)
    if showSignalDetails and not cleanSignalMode
        label.new(bar_index, signalSellLevel, signalBearGrade + " · " + str.tostring(signalBearScore), style=label.style_label_down, color=color.new(color.red, 15), textcolor=color.white, size=size.small)
    if not na(bearBox)
        box.set_extend(bearBox, extend.none)
        box.set_right(bearBox, bar_index)

// Invalidation: expiry, opposite structure, or close through the far FVG boundary.
bool bullInvalid = bullState > 0 and bullState < 5 and ((bar_index-bullStart > maxSetupAge) or (bullState == 1 and bar_index-bullStateBar > sweepMemory) or (bullState == 3 and bar_index-bullStateBar > fvgMemory) or bearMss or (bullState >= 4 and close < bullFvgBottom))
bool bearInvalid = bearState > 0 and bearState < 5 and ((bar_index-bearStart > maxSetupAge) or (bearState == 1 and bar_index-bearStateBar > sweepMemory) or (bearState == 3 and bar_index-bearStateBar > fvgMemory) or bullMss or (bearState >= 4 and close > bearFvgTop))
if bullInvalid
    bullState := 6
    bullStateBar := bar_index
    if not na(bullBox)
        box.set_extend(bullBox, extend.none)
        box.set_right(bullBox, bar_index)
        box.set_bgcolor(bullBox, color.new(color.gray, 94))
if bearInvalid
    bearState := 6
    bearStateBar := bar_index
    if not na(bearBox)
        box.set_extend(bearBox, extend.none)
        box.set_right(bearBox, bar_index)
        box.set_bgcolor(bearBox, color.new(color.gray, 94))
if balancedBullActive and (bar_index-balancedBullBar > maxSetupAge or close < balancedBullBottom)
    balancedBullActive := false
if balancedBearActive and (bar_index-balancedBearBar > maxSetupAge or close > balancedBearTop)
    balancedBearActive := false
if (bullState == 5 or bullState == 6) and bar_index > bullStateBar
    bullState := 0
if (bearState == 5 or bearState == 6) and bar_index > bearStateBar
    bearState := 0

// Recycle the oldest visual plans to keep the chart clean and object use bounded.
while array.size(rrLines) > rrHistoryCount*5
    line.delete(array.shift(rrLines))
while array.size(rrLabels) > rrHistoryCount*4
    label.delete(array.shift(rrLabels))

//──────────────────────────────────────────────────────────────────────────────
// Visuals
//──────────────────────────────────────────────────────────────────────────────
// BUY/SELL labels above are anchored by their arrow tip to the selected FVG entry.
plot(showLiquidity and not cleanSignalMode ? lastSwingHigh : na, "BSL", color=color.new(color.red, 55), linewidth=1, style=plot.style_stepline)
plot(showLiquidity and not cleanSignalMode ? lastSwingLow : na, "SSL", color=color.new(color.aqua, 55), linewidth=1, style=plot.style_stepline)
plot(showEquilibrium and not cleanSignalMode ? equilibrium : na, "Equilibrium", color=color.new(color.silver, 55), linewidth=1, style=plot.style_stepline)
plotshape(showLiquidity and not cleanSignalMode and newEqh, title="Equal highs confirmed", text="EQH", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 70), textcolor=color.white, size=size.tiny)
plotshape(showLiquidity and not cleanSignalMode and newEql, title="Equal lows confirmed", text="EQL", style=shape.labelup, location=location.belowbar, color=color.new(color.teal, 70), textcolor=color.white, size=size.tiny)
plotshape(showStructure and not cleanSignalMode and bullMss, title="Bullish MSS", text="MSS", style=shape.labelup, location=location.belowbar, color=color.new(color.teal, 55), textcolor=color.white, size=size.tiny)
plotshape(showStructure and not cleanSignalMode and bearMss, title="Bearish MSS", text="MSS", style=shape.labeldown, location=location.abovebar, color=color.new(color.orange, 55), textcolor=color.white, size=size.tiny)
plotshape(showStructure and not cleanSignalMode and bullBos, title="Bullish BOS", text="BOS", style=shape.labelup, location=location.belowbar, color=color.new(color.blue, 70), textcolor=color.white, size=size.tiny)
plotshape(showStructure and not cleanSignalMode and bearBos, title="Bearish BOS", text="BOS", style=shape.labeldown, location=location.abovebar, color=color.new(color.purple, 70), textcolor=color.white, size=size.tiny)
plotshape(showLiquidity and not cleanSignalMode and sslSweep, title="SSL Sweep", text="SSL Sweep", style=shape.labelup, location=location.belowbar, color=color.new(color.aqua, 55), textcolor=color.black, size=size.tiny)
plotshape(showLiquidity and not cleanSignalMode and bslSweep, title="BSL Sweep", text="BSL Sweep", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 55), textcolor=color.white, size=size.tiny)
bgcolor(not cleanSignalMode and shadeSessions and inKillZone ? color.new(inLondon ? color.blue : color.purple, 93) : na)

// Hidden plots provide dynamic values to alert placeholders.
plot(buySignal or sellSignal ? (buySignal ? signalBullScore : signalBearScore) : na, "Signal Score", display=display.none)
bool aBuy = buySignal and signalBullGrade == "A"
bool aSell = sellSignal and signalBearGrade == "A"
bool apBuy = buySignal and signalBullGrade == "A+"
bool apSell = sellSignal and signalBearGrade == "A+"
alertcondition(aBuy, "A BUY", "{{ticker}} {{interval}} BUY A | Score {{plot(\"Signal Score\")}}")
alertcondition(aSell, "A SELL", "{{ticker}} {{interval}} SELL A | Score {{plot(\"Signal Score\")}}")
alertcondition(apBuy, "A+ BUY", "{{ticker}} {{interval}} BUY A+ | Score {{plot(\"Signal Score\")}}")
alertcondition(apSell, "A+ SELL", "{{ticker}} {{interval}} SELL A+ | Score {{plot(\"Signal Score\")}}")
alertcondition(buySignal or sellSignal, "Any qualified setup", "{{ticker}} {{interval}} qualified ICT setup | Score {{plot(\"Signal Score\")}}")

var table dashboard = table.new(position.top_right, 2, 6, bgcolor=color.new(color.black, 18), border_color=color.new(color.gray, 65), border_width=1)
if barstate.islast and showDashboard and not cleanSignalMode
    int activeDirection = bullState > 0 ? 1 : bearState > 0 ? -1 : 0
    int activeState = activeDirection == 1 ? bullState : activeDirection == -1 ? bearState : 0
    int activeScore = activeDirection == 1 ? bullScore : activeDirection == -1 ? bearScore : 0
    string activeGrade = activeDirection == 1 ? bullGrade : activeDirection == -1 ? bearGrade : "—"
    table.cell(dashboard, 0, 0, "HTF Bias", text_color=color.silver)
    table.cell(dashboard, 1, 0, htfBias == 1 ? "BULLISH" : htfBias == -1 ? "BEARISH" : "NEUTRAL/OFF", text_color=htfBias == 1 ? color.lime : htfBias == -1 ? color.red : color.silver)
    table.cell(dashboard, 0, 1, "Session", text_color=color.silver)
    table.cell(dashboard, 1, 1, sessionName, text_color=inKillZone ? color.yellow : color.gray)
    table.cell(dashboard, 0, 2, "Liquidity", text_color=color.silver)
    table.cell(dashboard, 1, 2, lastLiquidityEvent, text_color=color.white)
    table.cell(dashboard, 0, 3, "Structure", text_color=color.silver)
    table.cell(dashboard, 1, 3, structureDirection == 1 ? "BULLISH" : structureDirection == -1 ? "BEARISH" : "UNSET", text_color=color.white)
    table.cell(dashboard, 0, 4, "Setup", text_color=color.silver)
    table.cell(dashboard, 1, 4, (activeDirection == 1 ? "BUY · " : activeDirection == -1 ? "SELL · " : "") + f_stateName(activeState), text_color=color.white)
    table.cell(dashboard, 0, 5, "Grade / Score", text_color=color.silver)
    table.cell(dashboard, 1, 5, activeGrade + " / " + str.tostring(activeScore), text_color=activeGrade == "A+" ? color.lime : color.white)

FAQ

Common questions.

What does ICT.AlgoNexta detect?▾

It detects confirmed ICT-style price-action sequences involving swing liquidity sweeps, ATR-qualified displacement, market-structure confirmation, fair value gaps, and retracements. The default Balanced profile uses a shorter displacement-FVG-retracement path, while Selective A+ applies the fuller sequence and configured gates.

Does this script place trades or manage stops and targets?▾

No. It is an overlay indicator, not a TradingView strategy, and it does not call strategy.entry or strategy.exit. Its entry, stop, and TP lines are visual projections generated after a signal.

Does the indicator repaint?▾

Signal conditions use confirmed bars, and higher-timeframe requests use lookahead off. However, confirmed swing pivots appear only after the configured right-side pivot delay, so structural levels are delayed rather than known at the pivot candle in real time.

Which sessions and timeframes does it use?▾

The default higher timeframe is 60 minutes, while the script runs on the chart timeframe. Its optional session logic uses America/New_York windows: London 0200-0500 and New York 0700-1000; the source does not enforce a specific symbol or chart timeframe.

What do the Balanced and Selective A+ profiles change?▾

Balanced looks for an FVG within two bars of displacement, applies higher-timeframe EMA direction, and waits for a retracement. Selective A+ follows the sweep, displacement, structure, FVG, and retracement state sequence, then applies the enabled quality gates and score/grade thresholds.

Are the A, A+, and score labels financial advice or proof of results?▾

No. They are code-defined event scores and grade thresholds, not performance statistics or financial advice. For any strategy adaptation, TradingView Strategy Tester is the source of truth for historical simulation, while paper trading and live results are separate evidence levels.

Want a version tuned to your market?

Describe the change in plain English and AlgoNexta rebuilds it as a fresh Pine v6 script — with its own explanation, evidence, and walkthrough.

Build your own version

Educational content only — not financial advice. TradingView is the source of truth for live data, compilation, and Strategy Tester results.

We use cookies to understand how the site is used and improve it. We only load analytics after you accept. Privacy Policy