Skip to content
← Indicator library
tradingviewstructureindicatorpinescript-v6

BOS.AlgoNexta

Confirmed break-of-structure signals with optional higher-timeframe EMA alignment and ATR-based risk projections.

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 18, 2026#break-of-structure#bos#pivot#ema#atr#indicator#tradingview#pinescript-v6

Overview

What this indicator does

BOS.AlgoNexta is an overlay indicator that identifies confirmed swing highs and lows, then watches for a candle close that breaks an active structure level. A bullish BOS occurs when the confirmed close exceeds an active swing high; a bearish BOS occurs when it falls below an active swing low. It plots signals and can draw ATR-based risk/reward projections, but it is an indicator: it does not submit orders or manage positions.

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 structure becomes active
1

When structure becomes active

The script first waits for `Swing strength` bars on both sides of a potential high or low. With the default `Swing strength = 3`, accepted pivot levels are only activated after that confirmation delay. For alternating pivot types, the default minimum move is 40 units multiplied by the selected price-unit size; same-side candidates must be more extreme to replace an accepted level.

Diagram: What confirms a break
2

What confirms a break

A bullish BOS requires a confirmed chart-bar close strictly above the active high plus the configured buffer; a bearish BOS requires a close strictly below the active low minus that buffer. The default `BOS buffer (units)` is `0.0` and `Allow close equal to threshold` is false, so touching the level is insufficient. With the default HTF filter enabled, the prior completed higher-timeframe close must also be on the matching side of the 240 EMA.

Diagram: How signals and risk are displayed
3

How signals and risk are displayed

Filtered bullish and bearish BOS conditions plot `BOS BUY` and `BOS SELL` labels by default and drive the three alert conditions. If `Show risk projection` is enabled, the script draws a projection from the signal close using ATR 14: default stop distance is `1.5 × ATR`, with `1.5R`, `2R`, and `3R` target lines. These drawings do not place or fill trades.

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.

What it does

BOS.AlgoNexta is an overlay indicator that identifies confirmed swing highs and lows, then watches for a candle close that breaks an active structure level. A bullish BOS occurs when the confirmed close exceeds an active swing high; a bearish BOS occurs when it falls below an active swing low. It plots signals and can draw ATR-based risk/reward projections, but it is an indicator: it does not submit orders or manage positions.

How the structure is built

  • Swing strength defaults to 3. A pivot needs three bars on each side before it is confirmed, so pivots become known only after that delay.
  • Price unit mode defaults to Custom Pip Size, using Custom pip size of 0.1. The default Minimum structure move (units) is 40.0, so alternating accepted pivots must be at least 40 × 0.1 apart under the defaults.
  • Consecutive same-side pivot candidates replace the prior accepted pivot only when they are more extreme.
  • The active high and active low are the levels available for future BOS detection. A confirmed pivot cannot create and break its own level on the same bar.

What confirms a BOS signal

  • A bullish structural event requires a confirmed bar close above the active high plus BOS buffer (units). The default buffer is 0.0.
  • A bearish structural event requires a confirmed bar close below the active low minus that buffer.
  • Allow close equal to threshold is off by default, so equality does not qualify; the close must move strictly beyond the level.
  • By default, Require confirmed HTF EMA alignment is enabled. The script uses the prior completed higher-timeframe bar, with Higher timeframe set to 15 and EMA period set to 240. Bullish signals need the completed HTF close above its 240 EMA; bearish signals need it below.
  • The chart timeframe must be strictly lower than the selected higher timeframe when that filter is enabled. Otherwise, the script raises a runtime error.
  • Minimum bars between same-side signals defaults to 0; increase it to limit repeated bullish or bearish plotted signals. Structure levels are still consumed even when a trade filter or gap suppresses a signal.

A no-signal example: a candle may close beyond an active high and create a raw bullish BOS, but no BOS BUY marker appears if the prior completed 15-minute close is not above its 240 EMA while the HTF filter is required.

Reading the chart

  • Active swing-high levels are red horizontal lines; active swing-low levels are lime horizontal lines. Show active structure levels is enabled by default.
  • Set Show broken structure levels to retain consumed levels as dashed gray diagnostics; it is off by default.
  • Set Show confirmed pivot markers to display small red or lime circles. They are visually offset back to the pivot bar, but the logical pivot only activates at its later confirmation bar.
  • With Show filtered signal text enabled, bullish signals appear as lime BOS BUY labels below bars and bearish signals as red BOS SELL labels above bars. Disable it to use triangles instead.
  • Show raw structural BOS events is off by default. When enabled, small BOS markers show raw breaks before the HTF alignment and same-side signal-gap filters; they are diagnostic events, not filtered BUY/SELL signals.
  • Show chart EMA optionally plots the chart-timeframe EMA using the same EMA period input. It is not the confirmed HTF EMA used by the filter.

ATR risk projection

When Show risk projection is enabled, each filtered BOS signal can create a visual projection from that signal bar’s close. Defaults are:

  • ATR length: 14
  • ATR SL multiplier: 1.5
  • TP1 RR / TP2 RR / TP3 RR: 1.5, 2.0, and 3.0
  • Projection width (bars): 15
  • Risk display mode: Latest Signal Only

For a bullish signal, the stop projection is 1.5 × ATR below the signal close and the three targets are above it. Bearish projections invert those directions. The white dotted line is entry, red is stop, and lime lines are TP1 dotted, TP2 dashed, and TP3 solid. Red and teal boxes can be toggled with Show risk/reward boxes.

These are visual calculations, not orders, fills, or guaranteed targets. In Active Projections mode, a projection is removed after a later bar touches its stop or TP3, or when its width expires. In Historical Projections mode, projections remain until the configured Maximum historical projections limit is reached. Individual lines and labels can be hidden with the Entry, Stop, and TP display inputs.

Alerts and limitations

  • Alerts are available for AlgoNexta Bullish BOS, AlgoNexta Bearish BOS, and AlgoNexta Any Confirmed BOS. They use the same filtered BOS conditions as the BUY/SELL visuals.
  • BOS decisions require barstate.isconfirmed, so the signal is based on a completed chart bar rather than an intrabar break.
  • Pivot identification is inherently delayed by Swing strength bars to the right. The offset pivot marker can look historical, but it was not logically available at the pivot bar.
  • The higher-timeframe filter intentionally uses only the previous fully closed HTF bar (close[1] and EMA [1]), avoiding use of a still-developing HTF bar. This can make the filter slower than an in-progress HTF reading.
  • The indicator detects its configured pivot-based breaks; it does not identify every possible market-structure concept, supply/demand zone, trend reversal, liquidity event, or position exit.
  • The code names no preferred symbol or chart timeframe. With the default Higher timeframe = 15, use a chart timeframe below 15 minutes, or select a higher HTF appropriate to the chart. Ensure the custom unit settings suit the instrument’s tick and quotation convention.

This script is not financial advice. Use TradingView’s Strategy Tester only with a separate, explicitly defined strategy; it is the appropriate source of truth for historical strategy results, while paper trading and live execution are separate evidence levels.

Source

Full source code, in every platform.

bos-algonexta.pine · 304 lines · 20.5 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
// AlgoNexta Pro-BOS Structure Signals
// Version 3.1
// Pine Script v6
// UI and risk-projection lifecycle remediation
indicator("AlgoNexta Pro-BOS Structure Signals", "AN Pro-BOS", overlay = true, max_labels_count = 300, max_lines_count = 500, max_boxes_count = 200)

string G_STRUCTURE = "1. Structure Discovery"
string G_BOS = "2. BOS Confirmation"
string G_FILTER = "3. Higher-Timeframe Filter"
string G_RISK = "4. ATR Risk Projection"
string G_STRUCTURE_DISPLAY = "5. Structure Display"
string G_SIGNAL_DISPLAY = "6. Signal Display and Alerts"

int swingStrength = input.int(3, "Swing strength", minval = 2, maxval = 20, group = G_STRUCTURE, tooltip = "Bars required on both sides of a candidate pivot before it becomes confirmed.")
string priceUnitMode = input.string("Custom Pip Size", "Price unit mode", options = ["Custom Pip Size", "Minimum Tick"], group = G_STRUCTURE, tooltip = "Select the unit used by minimum structure move and BOS buffer.")
float customPipSize = input.float(0.1, "Custom pip size", minval = 0.0000001, step = 0.01, group = G_STRUCTURE, tooltip = "Used only in Custom Pip Size mode. XAUUSD conventions vary by feed.")
float minMovePips = input.float(40.0, "Minimum structure move (units)", minval = 0.0, step = 1.0, group = G_STRUCTURE, tooltip = "Applied between the last accepted pivot and an alternating candidate. Same-side candidates replace only when more extreme.")
float bosBufferPips = input.float(0.0, "BOS buffer (units)", minval = 0.0, step = 0.1, group = G_BOS, tooltip = "Extra distance beyond the active level required from the confirmed candle close.")
bool allowThresholdEquality = input.bool(false, "Allow close equal to threshold", group = G_BOS)
int minSignalGap = input.int(0, "Minimum bars between same-side signals", minval = 0, maxval = 1000, group = G_BOS)

bool requireHtf = input.bool(true, "Require confirmed HTF EMA alignment", group = G_FILTER, tooltip = "Uses only the previous fully closed higher-timeframe close and EMA.")
string higherTf = input.timeframe("15", "Higher timeframe", group = G_FILTER)
int emaPeriod = input.int(240, "EMA period", minval = 5, group = G_FILTER)

bool showRisk = input.bool(true, "Show risk projection", group = G_RISK)
string riskDisplayMode = input.string("Latest Signal Only", "Risk display mode", options = ["Latest Signal Only", "Active Projections", "Historical Projections"], group = G_RISK, tooltip = "Latest keeps one projection; Active keeps unresolved projections; Historical keeps a bounded audit trail.")
int atrLength = input.int(14, "ATR length", minval = 1, group = G_RISK)
float atrSlMultiplier = input.float(1.5, "ATR SL multiplier", minval = 0.1, step = 0.1, group = G_RISK)
float tp1Rr = input.float(1.5, "TP1 RR", minval = 0.1, step = 0.1, group = G_RISK)
float tp2Rr = input.float(2.0, "TP2 RR", minval = 0.1, step = 0.1, group = G_RISK)
float tp3Rr = input.float(3.0, "TP3 RR", minval = 0.1, step = 0.1, group = G_RISK)
int projectionBars = input.int(15, "Projection width (bars)", minval = 1, maxval = 300, group = G_RISK, tooltip = "Horizontal width and Active-mode expiry measured from the signal bar.")
int maxActiveProjections = input.int(3, "Maximum active projections", minval = 1, maxval = 10, group = G_RISK)
int maxHistoricalProjections = input.int(5, "Maximum historical projections", minval = 1, maxval = 20, group = G_RISK)
bool showRiskBoxes = input.bool(true, "Show risk/reward boxes", group = G_RISK)
bool showEntryComponent = input.bool(true, "Show Entry line and label", group = G_RISK)
bool showStopComponent = input.bool(true, "Show Stop line and label", group = G_RISK)
bool showTp1Component = input.bool(true, "Show TP1", group = G_RISK)
bool showTp2Component = input.bool(true, "Show TP2", group = G_RISK)
bool showTp3Component = input.bool(true, "Show TP3", group = G_RISK)
string riskLabelStyle = input.string("Compact", "Risk label style", options = ["Full", "Compact"], group = G_RISK, tooltip = "Compact uses E, SL and RR values; Full includes Entry, ATR and TP names.")

bool showActiveLevels = input.bool(true, "Show active structure levels", group = G_STRUCTURE_DISPLAY, tooltip = "Shows the current confirmed high and low levels available for a future BOS.")
bool showBrokenLevels = input.bool(false, "Show broken structure levels", group = G_STRUCTURE_DISPLAY, tooltip = "Keeps a bounded diagnostic history of consumed levels.")
bool showPivotMarkers = input.bool(false, "Show confirmed pivot markers", group = G_STRUCTURE_DISPLAY, tooltip = "Visual offset only; logical activation remains at confirmation.")
string levelLineStart = input.string("Confirmation Bar", "Active line starts at", options = ["Confirmation Bar", "Pivot Bar"], group = G_STRUCTURE_DISPLAY)
int maxBrokenLevelLines = input.int(50, "Broken level lines to keep", minval = 1, maxval = 200, group = G_STRUCTURE_DISPLAY)

bool showSignalText = input.bool(true, "Show filtered signal text", group = G_SIGNAL_DISPLAY)
bool showRawBosEvents = input.bool(false, "Show raw structural BOS events", group = G_SIGNAL_DISPLAY, tooltip = "Shows every confirmed structural break before HTF alignment and signal-gap filters. These markers are diagnostic events, not filtered BUY/SELL signals.")
bool showEma = input.bool(false, "Show chart EMA", group = G_SIGNAL_DISPLAY)

float effectivePipSize = priceUnitMode == "Minimum Tick" ? syminfo.mintick : customPipSize
float minStructureMove = minMovePips * effectivePipSize
float bosBuffer = bosBufferPips * effectivePipSize
float chartEma = ta.ema(close, emaPeriod)
float atr = ta.atr(atrLength)

int chartSeconds = timeframe.in_seconds(timeframe.period)
int htfSeconds = timeframe.in_seconds(higherTf)
bool validHtfSelection = not na(chartSeconds) and not na(htfSeconds) and htfSeconds > chartSeconds
if barstate.isfirst and requireHtf and not validHtfSelection
    runtime.error("AN Pro-BOS: Higher timeframe must be strictly higher than the chart timeframe.")
if barstate.isfirst and not (tp1Rr < tp2Rr and tp2Rr < tp3Rr)
    runtime.error("AN Pro-BOS: RR targets must satisfy TP1 < TP2 < TP3.")

// Last fully closed HTF values only. lookahead_off (the default) never reads future HTF data,
// and the [1] offset excludes the still-developing HTF bar, so historical and live values match.
float confirmedHtfClose = request.security(syminfo.tickerid, higherTf, close[1], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
float confirmedHtfEma = request.security(syminfo.tickerid, higherTf, ta.ema(close, emaPeriod)[1], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
bool htfBullAligned = validHtfSelection and not na(confirmedHtfClose) and not na(confirmedHtfEma) and confirmedHtfClose > confirmedHtfEma
bool htfBearAligned = validHtfSelection and not na(confirmedHtfClose) and not na(confirmedHtfEma) and confirmedHtfClose < confirmedHtfEma

float confirmedPivotHigh = ta.pivothigh(high, swingStrength, swingStrength)
float confirmedPivotLow = ta.pivotlow(low, swingStrength, swingStrength)
bool hasConfirmedHigh = not na(confirmedPivotHigh)
bool hasConfirmedLow = not na(confirmedPivotLow)
int confirmedPivotBar = bar_index - swingStrength

var float activeHighPrice = na
var int activeHighPivotBar = na
var int activeHighConfirmationBar = na
var bool activeHighBroken = true
var float activeLowPrice = na
var int activeLowPivotBar = na
var int activeLowConfirmationBar = na
var bool activeLowBroken = true
var float lastAcceptedPivotPrice = na
var int lastAcceptedPivotType = 0
var int lastBullSignalBar = na
var int lastBearSignalBar = na
var line activeHighLine = na
var line activeLowLine = na
var line[] retiredLevelLines = array.new_line()
var label[] bosEventLabels = array.new_label()

f_retireLevel(line levelLine, bool broken) =>
    if not na(levelLine)
        line.set_extend(levelLine, extend.none)
        line.set_x2(levelLine, bar_index)
        if broken and showBrokenLevels
            line.set_color(levelLine, color.new(color.gray, 25))
            line.set_style(levelLine, line.style_dashed)
            array.push(retiredLevelLines, levelLine)
        else
            line.delete(levelLine)
    while array.size(retiredLevelLines) > maxBrokenLevelLines
        line.delete(array.shift(retiredLevelLines))

f_newLevelLine(bool isHigh, int pivotBar, int confirmationBar, float price) =>
    int startBar = levelLineStart == "Pivot Bar" ? pivotBar : confirmationBar
    color levelColor = isHigh ? color.new(color.red, 15) : color.new(color.lime, 15)
    showActiveLevels ? line.new(startBar, price, bar_index, price, xloc = xloc.bar_index, extend = extend.right, color = levelColor, width = 1) : na

// State before this bar is evaluated first. A pivot confirmed on this bar cannot
// break itself and cannot backdate a BOS.
bool rawBullishBos = barstate.isconfirmed and not na(activeHighPrice) and not activeHighBroken and bar_index > activeHighConfirmationBar and (allowThresholdEquality ? close >= activeHighPrice + bosBuffer : close > activeHighPrice + bosBuffer)
bool rawBearishBos = barstate.isconfirmed and not na(activeLowPrice) and not activeLowBroken and bar_index > activeLowConfirmationBar and (allowThresholdEquality ? close <= activeLowPrice - bosBuffer : close < activeLowPrice - bosBuffer)
bool ambiguousDualBos = rawBullishBos and rawBearishBos

// Structural consumption is independent from trade filters and frequency gaps.
if rawBullishBos
    activeHighBroken := true
    f_retireLevel(activeHighLine, true)
    activeHighLine := na
if rawBearishBos
    activeLowBroken := true
    f_retireLevel(activeLowLine, true)
    activeLowLine := na

bool bullishGapOk = na(lastBullSignalBar) or bar_index - lastBullSignalBar >= minSignalGap
bool bearishGapOk = na(lastBearSignalBar) or bar_index - lastBearSignalBar >= minSignalGap
bool bullishBosSignal = rawBullishBos and not ambiguousDualBos and (not requireHtf or htfBullAligned) and bullishGapOk
bool bearishBosSignal = rawBearishBos and not ambiguousDualBos and (not requireHtf or htfBearAligned) and bearishGapOk
if bullishBosSignal
    lastBullSignalBar := bar_index
if bearishBosSignal
    lastBearSignalBar := bar_index

if showRawBosEvents and rawBullishBos and not ambiguousDualBos
    label eventLabel = label.new(bar_index, high, "BOS", xloc = xloc.bar_index, yloc = yloc.abovebar, style = label.style_label_down, color = color.new(color.green, 55), textcolor = color.white, size = size.tiny)
    array.push(bosEventLabels, eventLabel)
if showRawBosEvents and rawBearishBos and not ambiguousDualBos
    label eventLabel = label.new(bar_index, low, "BOS", xloc = xloc.bar_index, yloc = yloc.belowbar, style = label.style_label_up, color = color.new(color.red, 55), textcolor = color.white, size = size.tiny)
    array.push(bosEventLabels, eventLabel)
while array.size(bosEventLabels) > 100
    label.delete(array.shift(bosEventLabels))

// Minimum move applies to alternating pivots. Consecutive same-side candidates
// replace only when strictly more extreme. High is processed first if both exist.
if barstate.isconfirmed and hasConfirmedHigh
    bool sameSideHigh = lastAcceptedPivotType == 1
    bool moreExtremeHigh = sameSideHigh and (na(lastAcceptedPivotPrice) or confirmedPivotHigh > lastAcceptedPivotPrice)
    bool alternatingHighAccepted = lastAcceptedPivotType == 0 or (lastAcceptedPivotType == -1 and math.abs(confirmedPivotHigh - lastAcceptedPivotPrice) >= minStructureMove)
    if moreExtremeHigh or alternatingHighAccepted
        f_retireLevel(activeHighLine, false)
        activeHighPrice := confirmedPivotHigh
        activeHighPivotBar := confirmedPivotBar
        activeHighConfirmationBar := bar_index
        activeHighBroken := false
        activeHighLine := f_newLevelLine(true, activeHighPivotBar, activeHighConfirmationBar, activeHighPrice)
        lastAcceptedPivotPrice := confirmedPivotHigh
        lastAcceptedPivotType := 1

if barstate.isconfirmed and hasConfirmedLow
    bool sameSideLow = lastAcceptedPivotType == -1
    bool moreExtremeLow = sameSideLow and (na(lastAcceptedPivotPrice) or confirmedPivotLow < lastAcceptedPivotPrice)
    bool alternatingLowAccepted = lastAcceptedPivotType == 0 or (lastAcceptedPivotType == 1 and math.abs(confirmedPivotLow - lastAcceptedPivotPrice) >= minStructureMove)
    if moreExtremeLow or alternatingLowAccepted
        f_retireLevel(activeLowLine, false)
        activeLowPrice := confirmedPivotLow
        activeLowPivotBar := confirmedPivotBar
        activeLowConfirmationBar := bar_index
        activeLowBroken := false
        activeLowLine := f_newLevelLine(false, activeLowPivotBar, activeLowConfirmationBar, activeLowPrice)
        lastAcceptedPivotPrice := confirmedPivotLow
        lastAcceptedPivotType := -1

plotshape(showPivotMarkers and hasConfirmedHigh, "Confirmed pivot high", shape.circle, location.abovebar, color = color.new(color.red, 20), size = size.tiny, offset = -swingStrength)
plotshape(showPivotMarkers and hasConfirmedLow, "Confirmed pivot low", shape.circle, location.belowbar, color = color.new(color.lime, 20), size = size.tiny, offset = -swingStrength)
plotshape(showSignalText and bullishBosSignal, "Bullish BOS Signal", shape.labelup, location.belowbar, color = color.lime, textcolor = color.black, text = "BOS\nBUY", size = size.small)
plotshape(showSignalText and bearishBosSignal, "Bearish BOS Signal", shape.labeldown, location.abovebar, color = color.red, textcolor = color.white, text = "BOS\nSELL", size = size.small)
plotshape(not showSignalText and bullishBosSignal, "Bullish BOS Arrow", shape.triangleup, location.belowbar, color = color.lime, size = size.small)
plotshape(not showSignalText and bearishBosSignal, "Bearish BOS Arrow", shape.triangledown, location.abovebar, color = color.red, size = size.small)
plot(showEma ? chartEma : na, "Chart EMA", color = color.blue, linewidth = 2)

type RiskProjection
    bool isBuy
    int signalBar
    int expirationBar
    float entry
    float stop
    float tp3
    box riskBox
    box rewardBox
    line entryLine
    line stopLine
    line tp1Line
    line tp2Line
    line tp3Line
    label entryLabel
    label stopLabel
    label tp1Label
    label tp2Label
    label tp3Label

var RiskProjection[] riskProjections = array.new<RiskProjection>()

f_deleteProjection(RiskProjection projection) =>
    if not na(projection.riskBox)
        box.delete(projection.riskBox)
    if not na(projection.rewardBox)
        box.delete(projection.rewardBox)
    if not na(projection.entryLine)
        line.delete(projection.entryLine)
    if not na(projection.stopLine)
        line.delete(projection.stopLine)
    if not na(projection.tp1Line)
        line.delete(projection.tp1Line)
    if not na(projection.tp2Line)
        line.delete(projection.tp2Line)
    if not na(projection.tp3Line)
        line.delete(projection.tp3Line)
    if not na(projection.entryLabel)
        label.delete(projection.entryLabel)
    if not na(projection.stopLabel)
        label.delete(projection.stopLabel)
    if not na(projection.tp1Label)
        label.delete(projection.tp1Label)
    if not na(projection.tp2Label)
        label.delete(projection.tp2Label)
    if not na(projection.tp3Label)
        label.delete(projection.tp3Label)

f_clearProjections() =>
    while array.size(riskProjections) > 0
        f_deleteProjection(array.pop(riskProjections))

f_removeOldestProjection() =>
    if array.size(riskProjections) > 0
        f_deleteProjection(array.shift(riskProjections))

f_createProjection(bool isBuy) =>
    float entry = close
    float risk = atr * atrSlMultiplier
    float stopPrice = isBuy ? entry - risk : entry + risk
    float tp1Price = isBuy ? entry + risk * tp1Rr : entry - risk * tp1Rr
    float tp2Price = isBuy ? entry + risk * tp2Rr : entry - risk * tp2Rr
    float tp3Price = isBuy ? entry + risk * tp3Rr : entry - risk * tp3Rr
    int projectionEnd = bar_index + projectionBars
    box riskBox = showRiskBoxes ? box.new(bar_index, math.max(entry, stopPrice), projectionEnd, math.min(entry, stopPrice), xloc = xloc.bar_index, border_color = color.new(color.red, 55), bgcolor = color.new(color.red, 88)) : na
    box rewardBox = showRiskBoxes ? box.new(bar_index, math.max(entry, tp3Price), projectionEnd, math.min(entry, tp3Price), xloc = xloc.bar_index, border_color = color.new(color.teal, 55), bgcolor = color.new(color.teal, 88)) : na
    line entryLine = showEntryComponent ? line.new(bar_index, entry, projectionEnd, entry, xloc = xloc.bar_index, color = color.white, style = line.style_dotted) : na
    line stopLine = showStopComponent ? line.new(bar_index, stopPrice, projectionEnd, stopPrice, xloc = xloc.bar_index, color = color.red) : na
    line tp1Line = showTp1Component ? line.new(bar_index, tp1Price, projectionEnd, tp1Price, xloc = xloc.bar_index, color = color.lime, style = line.style_dotted) : na
    line tp2Line = showTp2Component ? line.new(bar_index, tp2Price, projectionEnd, tp2Price, xloc = xloc.bar_index, color = color.lime, style = line.style_dashed) : na
    line tp3Line = showTp3Component ? line.new(bar_index, tp3Price, projectionEnd, tp3Price, xloc = xloc.bar_index, color = color.lime) : na
    string entryText = riskLabelStyle == "Compact" ? "E" : "Entry"
    string stopText = riskLabelStyle == "Compact" ? "SL" : "SL " + str.tostring(atrSlMultiplier) + " ATR"
    string tp1Text = riskLabelStyle == "Compact" ? str.tostring(tp1Rr) + "R" : "TP1 " + str.tostring(tp1Rr) + "R"
    string tp2Text = riskLabelStyle == "Compact" ? str.tostring(tp2Rr) + "R" : "TP2 " + str.tostring(tp2Rr) + "R"
    string tp3Text = riskLabelStyle == "Compact" ? str.tostring(tp3Rr) + "R" : "TP3 " + str.tostring(tp3Rr) + "R"
    label entryLabel = showEntryComponent ? label.new(projectionEnd, entry, entryText, xloc = xloc.bar_index, style = label.style_label_left, color = color.white, textcolor = color.black, size = size.small) : na
    label stopLabel = showStopComponent ? label.new(projectionEnd, stopPrice, stopText, xloc = xloc.bar_index, style = label.style_label_left, color = color.red, textcolor = color.white, size = size.small) : na
    label tp1Label = showTp1Component ? label.new(projectionEnd, tp1Price, tp1Text, xloc = xloc.bar_index, style = label.style_label_left, color = color.lime, textcolor = color.black, size = size.small) : na
    label tp2Label = showTp2Component ? label.new(projectionEnd, tp2Price, tp2Text, xloc = xloc.bar_index, style = label.style_label_left, color = color.lime, textcolor = color.black, size = size.small) : na
    label tp3Label = showTp3Component ? label.new(projectionEnd, tp3Price, tp3Text, xloc = xloc.bar_index, style = label.style_label_left, color = color.lime, textcolor = color.black, size = size.small) : na
    RiskProjection.new(isBuy, bar_index, projectionEnd, entry, stopPrice, tp3Price, riskBox, rewardBox, entryLine, stopLine, tp1Line, tp2Line, tp3Line, entryLabel, stopLabel, tp1Label, tp2Label, tp3Label)

// Risk lifecycle is downstream from BOS and cannot change signals or alerts.
if not showRisk
    f_clearProjections()
else if riskDisplayMode == "Active Projections" and array.size(riskProjections) > 0
    for projectionIndex = array.size(riskProjections) - 1 to 0
        RiskProjection projection = array.get(riskProjections, projectionIndex)
        bool afterSignal = bar_index > projection.signalBar
        bool stopTouched = projection.isBuy ? low <= projection.stop : high >= projection.stop
        bool tp3Touched = projection.isBuy ? high >= projection.tp3 : low <= projection.tp3
        bool expired = bar_index >= projection.expirationBar
        if afterSignal and (stopTouched or tp3Touched or expired)
            f_deleteProjection(projection)
            array.remove(riskProjections, projectionIndex)

bool riskReady = not na(atr) and atr > 0 and not na(close)
if showRisk and riskReady and (bullishBosSignal or bearishBosSignal)
    if riskDisplayMode == "Latest Signal Only"
        f_clearProjections()
    RiskProjection newProjection = f_createProjection(bullishBosSignal)
    array.push(riskProjections, newProjection)
    int projectionLimit = riskDisplayMode == "Latest Signal Only" ? 1 : riskDisplayMode == "Active Projections" ? maxActiveProjections : maxHistoricalProjections
    while array.size(riskProjections) > projectionLimit
        f_removeOldestProjection()

alertcondition(bullishBosSignal, "AlgoNexta Bullish BOS", "AlgoNexta confirmed bullish BOS on {{ticker}} {{interval}} at close {{close}}")
alertcondition(bearishBosSignal, "AlgoNexta Bearish BOS", "AlgoNexta confirmed bearish BOS on {{ticker}} {{interval}} at close {{close}}")
alertcondition(bullishBosSignal or bearishBosSignal, "AlgoNexta Any Confirmed BOS", "AlgoNexta confirmed BOS on {{ticker}} {{interval}} at close {{close}}")

FAQ

Common questions.

What does BOS.AlgoNexta detect?▾

It detects a confirmed close beyond an active, confirmed pivot high or low. The active levels are built from `ta.pivothigh` and `ta.pivotlow` using the `Swing strength` input, which defaults to 3.

Does it repaint?▾

The BOS signal itself is evaluated only on confirmed chart bars, and the HTF filter uses the previous fully closed HTF bar with `lookahead_off`. However, pivots are only confirmed after `Swing strength` bars to their right; the optional pivot marker is drawn back at the pivot location for display, not because it was known then.

What does the higher-timeframe EMA filter do?▾

With `Require confirmed HTF EMA alignment` enabled by default, bullish BOS signals require the prior completed HTF close above its 240 EMA, while bearish signals require it below. The default higher timeframe is 15 minutes, and it must be strictly higher than the chart timeframe.

Does this indicator place trades or manage a position?▾

No. It is an indicator and creates no `strategy.entry` or exit orders. Its ATR stop and TP lines, boxes, and labels are visual projections only.

How do alerts work?▾

Create a TradingView alert from one of the three supplied alert conditions: bullish BOS, bearish BOS, or any confirmed BOS. Alerts use the same filtered signal conditions as the plotted BUY and SELL markers.

Are the ATR projections a backtest or financial advice?▾

No. The projections use the signal-bar close, ATR length 14, a 1.5 ATR stop multiplier, and default 1.5R, 2R, and 3R levels; they do not establish live or historical performance. This is not financial advice, and TradingView Strategy Tester with a separate strategy is the source of truth for historical strategy results.

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