OPEN-SOURCE SCRIPT
Market Structure- Zig Zag, BoS and Supply/Demand Zones LIMITLESS

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org/MPL/2.0/
// © The_Forex_Steward
//version=6
indicator("Market Structure- Zig Zag, BoS and Supply/Demand Zones", overlay=true)
// === User Inputs ===
htf = input.timeframe("", title="Timeframe")
internalShiftMode = input.string("Engulfment", title="Calculate Zig-Zag By", options=["Engulfment", "Market Shift (Engulfment)"])
x = input.int(1, minval=1, maxval= 3, title="# of Candles for Zones (1-3)")
showBearishOrderBlocks = input.bool(true, title="Show Supply Zones")
showBullishOrderBlocks = input.bool(true, title="Show Demand Zones")
orderBlockDuration = input.int(10, title="Zone Duration (bars)")
deleteMitigatedBoxes = input.bool(false, title="Delete Mitigated Zones")
deleteBrokenBoxes= input.bool(true, title = "Delete Broken Zones")
dimMitigatedBoxes = input.bool(true, title="Dim Mitigated Zones")
bearishBlockColor = input.color(color.rgb(255, 82, 82, 50), title="Supply Zone Fill")
bullishBlockColor = input.color(color.rgb(76, 175, 79, 50), title="Demand Zone Fill")
lighterBullishColor = color.new(bullishBlockColor, 85) // More transparent
lighterBearishColor = color.new(bearishBlockColor, 85)
zigzagLineColor = input.color(color.black, title="ZigZag Line Color")
zigzagLineWidth = input.int(2, title="Width of Lines", minval=1, maxval=10)
zigzagLineStyle = input.string("Solid", title="ZigZag Line Style", options=["Solid", "Dotted", "Dashed"])
internalShiftColor = color.new(zigzagLineColor, 75) // More transparent
bosBullishLineColor = input.color(color.green, title="Bullish BOS Line Color")
bosBearishLineColor = input.color(color.red, title="Bearish BOS Line Color")
bosLineStyle = input.string("Dotted", title="BOS Line Style", options=["Solid", "Dotted", "Dashed"])
bosLineStyleConst = bosLineStyle == "Solid" ? line.style_solid : bosLineStyle == "Dotted" ? line.style_dotted : line.style_dashed
alertMode = input.string("MTF", title= "Enable/Disable for Any Alert() Function Call↓ Alert Status→", options= ["LTF", "MTF", "HTF"])
alertSupplyandDemand = input.bool(true, title= "Supply & Demand Zones")
alertHighsandLows = input.bool(true, title= "Swing Highs & Lows")
alertBoS = input.bool(true, title= "BoS")
alertMS = input.bool(true, title= "Market Shifts")
hhBackgroundColor = color.rgb(76, 175, 79, 100)
hhTextColor = color.green
lhBackgroundColor = color.rgb(0, 137, 123, 100)
lhTextColor = color.red
llBackgroundColor = color.rgb(255, 82, 82, 100)
llTextColor = color.red
hlBackgroundColor = color.rgb(255, 153, 0, 100)
hlTextColor = color.green
HtfOpen = request.security(syminfo.tickerid, htf, open)
HtfHigh = request.security(syminfo.tickerid, htf, high)
HtfLow = request.security(syminfo.tickerid, htf, low)
HtfClose = request.security(syminfo.tickerid, htf, close)
prevHtfHigh = request.security(syminfo.tickerid, htf, high[1])
prevHtfLow = request.security(syminfo.tickerid, htf, low[1])
isHTFBarClose = ta.change(HtfClose) != 0
// Track the bar_index of the current bar when HTF closes
var int HtfBarIndex = na
if isHTFBarClose
HtfBarIndex := bar_index
// === Initialization ===
var int lastSignal = 0 // 0 = none, 1 = bull, -1 = bear
var float runningLowestHigh = na
var float runningHighestLow = na
// Track engulfed ranges
var float engulfedHigh = na
var float engulfedLow = na
// === Step 1: Detect "starter" engulfing ===
starterBull = HtfClose[1] < HtfOpen[1] and HtfClose > HtfOpen and HtfClose > HtfHigh[1]
starterBear = HtfClose[1] > HtfOpen[1] and HtfClose < HtfOpen and HtfClose < HtfLow[1]
if lastSignal == 0
if starterBull
lastSignal := 1
runningHighestLow := HtfLow
engulfedHigh := HtfHigh[1]
engulfedLow := HtfLow[1]
else if starterBear
lastSignal := -1
runningLowestHigh := HtfHigh
engulfedHigh := HtfHigh[1]
engulfedLow := HtfLow[1]
// === Step 2: Update running references ===
if lastSignal == -1 // last was bearish → waiting for bullish
runningLowestHigh := na(runningLowestHigh) ? HtfHigh : math.min(runningLowestHigh, HtfHigh)
else if lastSignal == 1 // last was bullish → waiting for bearish
runningHighestLow := na(runningHighestLow) ? HtfLow : math.max(runningHighestLow, HtfLow)
// === Step 3: Check for new engulfment ===
newBull = lastSignal == -1 and not na(runningLowestHigh) and HtfClose > runningLowestHigh
newBear = lastSignal == 1 and not na(runningHighestLow) and HtfClose < runningHighestLow
var int lastBullIndex = na
var int lastBearIndex = na
if newBull
lastBullIndex := HtfBarIndex
// store engulfed candle values (the one we just broke over)
engulfedHigh := runningLowestHigh
engulfedLow := HtfLow[1] // or HtfLow depending on how you define "engulfed"
if newBear
lastBearIndex := HtfBarIndex
engulfedLow := runningHighestLow
engulfedHigh := HtfHigh[1]
// === Step 4: Confirm and flip state ===
if newBull
lastSignal := 1
runningLowestHigh := na
runningHighestLow := HtfLow
else if newBear
lastSignal := -1
runningHighestLow := na
runningLowestHigh := HtfHigh
// === Track Boxes ===
var box[] bullishBoxes = array.new<box>()
var box[] bearishBoxes = array.new<box>()
// === Mitigation Flags ===
var bool bullishMitigated = false
var bool bearishMitigated = false
var bool bullishBreak = false
var bool bearishBreak = false
// === Delete invalidated boxes ===
if deleteBrokenBoxes
if array.size(bullishBoxes) > 0
for i = array.size(bullishBoxes) - 1 to 0
boxItem = array.get(bullishBoxes, i)
if HtfClose < box.get_bottom(boxItem)
box.delete(boxItem)
array.remove(bullishBoxes, i)
if array.size(bearishBoxes) > 0
for i = array.size(bearishBoxes) - 1 to 0
boxItem = array.get(bearishBoxes, i)
if HtfClose > box.get_top(boxItem)
box.delete(boxItem)
array.remove(bearishBoxes, i)
// === Delete mitigated boxes (optional) ===
if deleteMitigatedBoxes
if array.size(bullishBoxes) > 0
for i = array.size(bullishBoxes) - 1 to 0
boxItem = array.get(bullishBoxes, i)
if HtfLow < box.get_top(boxItem)
bullishMitigated := true
box.delete(boxItem)
array.remove(bullishBoxes, i)
if array.size(bearishBoxes) > 0
for i = array.size(bearishBoxes) - 1 to 0
boxItem = array.get(bearishBoxes, i)
if HtfHigh > box.get_bottom(boxItem)
bearishMitigated := true
box.delete(boxItem)
array.remove(bearishBoxes, i)
if dimMitigatedBoxes
if array.size(bullishBoxes) > 0
for i = 0 to array.size(bullishBoxes) - 1
boxItem = array.get(bullishBoxes, i)
if HtfLow < box.get_top(boxItem)
bullishMitigated := true
box.set_bgcolor(boxItem, lighterBullishColor)
box.set_border_color(boxItem, lighterBullishColor)
if array.size(bearishBoxes) > 0
for i = 0 to array.size(bearishBoxes) - 1
boxItem = array.get(bearishBoxes, i)
if HtfHigh > box.get_bottom(boxItem)
bearishMitigated := true
box.set_bgcolor(boxItem, lighterBearishColor)
box.set_border_color(boxItem, lighterBearishColor)
// Peramters for boxes
zoneHigh = ta.highest(HtfHigh[1], x)
zoneLow = ta.lowest(HtfLow[1], x)
// Create new order blocks with adjusted alignment
if showBullishOrderBlocks and newBull
bullishBox = box.new(left= HtfBarIndex[x], right=HtfBarIndex[x] + orderBlockDuration, top=zoneHigh, bottom=zoneLow, border_color=bullishBlockColor, bgcolor=bullishBlockColor)
array.push(bullishBoxes, bullishBox)
if showBearishOrderBlocks and newBear
bearishBox = box.new(left= HtfBarIndex[x], right=HtfBarIndex[x] + orderBlockDuration, top=zoneHigh, bottom=zoneLow, border_color=bearishBlockColor, bgcolor=bearishBlockColor)
array.push(bearishBoxes, bearishBox)
// === Internal Structure Logic ===
var int bullishCount = 0
var int bearishCount = 0
var float lowestBullishPrice = na
var float highestBearishPrice = na
var float firstBullishOpen = na
var float firstBearishOpen = na
var int lastInternalShift = 0
var float lastBullishInternalShiftPrice = na
var float lastBearishInternalShiftPrice = na
var float currentSwingHigh = na
var int currentSwingHighIndex = na
var float currentSwingLow = na
var int currentSwingLowIndex = na
var float prevSwingHigh = na
var float prevSwingLow = na
var bool isHH = false
var bool isHL = false
var bool isLL = false
var bool isLH = false
var bool isLiquiditySweep = false
var float lastOpposingLow = na // For HH
var float lastOpposingHigh = na // For LL
var bool internalShiftBullish = false
var bool internalShiftBearish = false
if ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
internalShiftBullish := newBull
internalShiftBearish := newBear
allowInternalShiftBearish = internalShiftBearish and lastInternalShift != -1
allowInternalShiftBullish = internalShiftBullish and lastInternalShift != 1
var bool plotBearishInternalShift = false
var bool plotBullishInternalShift = false
// === Determine Internal Shift Based on User Input ===
plotBearishInternalShift := false
plotBullishInternalShift := false
if allowInternalShiftBearish
plotBearishInternalShift := true
lastInternalShift := -1
if allowInternalShiftBullish
plotBullishInternalShift := true
lastInternalShift := 1
// === Plot internal shift markers ==
plotshape(plotBullishInternalShift, title="Bullish Internal Shift", location=location.belowbar, color=internalShiftColor, style=shape.triangleup, size=size.tiny)
plotshape(plotBearishInternalShift, title="Bearish Internal Shift", location=location.abovebar, color=internalShiftColor, style=shape.triangledown, size=size.tiny)
// === Highest High Between Alternate Bearish Break and Last Bullish Break (Safe) ===
var float localHigh = na
var int localHighIndex = na
maxHistory = 10000
if plotBearishInternalShift and ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
float highestHigh = na
int highestIndex = na
int startIndex = math.max(lastBullIndex, bar_index - maxHistory)
int endIndex = HtfBarIndex
for i = startIndex to endIndex
int lookback = bar_index - i // Convert i to relative offset for series access
if lookback >= 0 and lookback < maxHistory and not na(HtfHigh[lookback])
if na(highestHigh) or HtfHigh[lookback] > highestHigh
highestHigh := HtfHigh[lookback]
highestIndex := i
localHigh := highestHigh
localHighIndex := highestIndex
// === Lowest Low Between Alternate Bullish Break and Last Bearish Break (Safe) ===
var float localLow = na
var int localLowIndex = na
if plotBullishInternalShift and ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
float lowestLow = na
int lowestIndex = na
int startIndex = math.max(lastBearIndex, HtfBarIndex - maxHistory)
int endIndex = bar_index
for i = startIndex to endIndex
int lookback = bar_index - i // Convert i to relative offset
if lookback >= 0 and lookback < maxHistory and not na(HtfLow[lookback])
if na(lowestLow) or HtfLow[lookback] < lowestLow
lowestLow := HtfLow[lookback]
lowestIndex := i
localLow := lowestLow
localLowIndex := lowestIndex
// === Track Last Non-Alternating Break of Structure (BoS) ===
var int lastBullishBoSBarNA = na
var int lastBearishBoSBarNA = na
var float lastBullishBoSPriceNA = na
var float lastBearishBoSPriceNA = na
var bool bullishBOSOccurred = false
var bool bearishBOSOccurred = false
var int lastLowIndex = na
var int lastHighIndex = na
var float lastSwingHigh = na
var float lastSwingLow = na
// Reset flags
var bool canBreakBullish = true
var bool canBreakBearish = true
// BoS Conditions (non-alternating)
bullishBoS = canBreakBullish and HtfOpen[1] < localHigh and HtfClose > localHigh
bearishBoS = canBreakBearish and HtfOpen[1] > localLow and HtfClose < localLow
if bullishBoS and internalShiftMode == "Engulfment"
lastBullishBoSBarNA := bar_index
lastBullishBoSPriceNA := HtfClose
canBreakBullish := false // prevent further BoS on same localHigh
bullishBOSOccurred := true
line.new(x1=localHighIndex, y1=localHigh, x2=bar_index, y2=localHigh, color=bosBullishLineColor, width=zigzagLineWidth, style=bosLineStyleConst)
lastSwingHigh := na
if bearishBoS and internalShiftMode == "Engulfment"
lastBearishBoSBarNA := bar_index
lastBearishBoSPriceNA := HtfClose
canBreakBearish := false // prevent further BoS on same localLow
bearishBOSOccurred := true
line.new(x1=localLowIndex, y1=localLow, x2=bar_index, y2=localLow, color=bosBearishLineColor, width=zigzagLineWidth, style=bosLineStyleConst)
lastSwingLow := na
// Reset logic — allow new break only if local high/low changes
if ta.change(localHigh) != 0
canBreakBullish := true
if ta.change(localLow) != 0
canBreakBearish := true
// === Track Last MS Event ===
var int lastBullishBoSBar = na
var int lastBearishBoSBar = na
var float lastBullishBoSPrice = na
var float lastBearishBoSPrice = na
var bool SwingHighBOSOccurred = false
var bool SwingLowBOSOccurred = false
var int lastSwingLowIndex = na
var int lastSwingHighIndex = na
var float lastSSwingHigh = na
var float lastSSwingLow = na
// Track last BoS type: 1 = bullish, -1 = bearish, 0 = none yet
var int lastBoSType = 0
// === Track Last MS Type ===
var int lastMSType = na // 1 = bullish, -1 = bearish
// === MS Detection Logic ===
rawBullishMS = HtfClose > localHigh
rawBearishMS = HtfClose < localLow
// === Enforce Alternation ===
canBullishMS = na(lastMSType) or lastMSType == -1
canBearishMS = na(lastMSType) or lastMSType == 1
bullishMS = rawBullishMS and canBullishMS
bearishMS = rawBearishMS and canBearishMS
plotshape(bullishMS, title="Bullish Market Shift", location=location.belowbar, color=zigzagLineColor, style=shape.triangleup, size=size.tiny)
plotshape(bearishMS, title="Bearish Market Shift", location=location.abovebar, color=zigzagLineColor, style=shape.triangledown, size=size.tiny)
// === Update Last MS Type and BoS Bars ===
if bullishMS
lastMSType := 1
lastBullishBoSBar := bar_index
if bearishMS
lastMSType := -1
lastBearishBoSBar := bar_index
// === Lowest Low Between Last Bearish MS and This Bullish MS ===
var float msLocalLow = na
var int msLocalLowIndex = na
msMaxHistory = 5000
if bullishMS
float msLowestLow = na
int msLowestIndex = na
int msStartIndex = na(lastBearishBoSBar) ? bar_index - msMaxHistory : lastBearishBoSBar
int msEndIndex = bar_index // safer than using HtfBarIndex unless defined
for i = msStartIndex to msEndIndex
int msLookback = bar_index - i
if msLookback >= 0 and msLookback < msMaxHistory and not na(HtfLow[msLookback])
if na(msLowestLow) or HtfLow[msLookback] < msLowestLow
msLowestLow := HtfLow[msLookback]
msLowestIndex := i
msLocalLow := msLowestLow
msLocalLowIndex := msLowestIndex
// === Highest High Between Last Bullish MS and This Bearish MS ===
var float msLocalHigh = na
var int msLocalHighIndex = na
if bearishMS
float msHighestHigh = na
int msHighestIndex = na
int msStartIndex = na(lastBullishBoSBar) ? bar_index - msMaxHistory : lastBullishBoSBar
int msEndIndex = bar_index
for i = msStartIndex to msEndIndex
int msLookback = bar_index - i
if msLookback >= 0 and msLookback < msMaxHistory and not na(HtfHigh[msLookback])
if na(msHighestHigh) or HtfHigh[msLookback] > msHighestHigh
msHighestHigh := HtfHigh[msLookback]
msHighestIndex := i
msLocalHigh := msHighestHigh
msLocalHighIndex := msHighestIndex
// === Persistent variables for multiple line handling ===
var line[] zigzagLines = array.new<line>()
var int lastBearishShiftBar = na
var int lastBullishShiftBar = na
var float lastZigzagPrice = na
var string lastSwingType = ""
// Save shift bar indices
if plotBearishInternalShift
lastBearishShiftBar := bar_index
if plotBullishInternalShift
lastBullishShiftBar := bar_index
// Bearish shift followed by Bullish shift → Track lowest low
if plotBullishInternalShift and internalShiftMode == "Engulfment"
// Plot zigzag line
// Plot zigzag line for LL and HL separately
if not na(prevSwingLow)
if localLow < prevSwingLow // LL
if zigzagLineStyle == "Solid"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing low and plot label (HL or LL)
if not na(prevSwingLow)
isLL := not na(prevSwingLow) and localLow < prevSwingLow
isHL := not na(prevSwingLow) and localLow > prevSwingLow
if isLL
if bearishBOSOccurred
label.new(localLowIndex, localLow, "LL", color=llBackgroundColor, style=label.style_label_up, textcolor=llTextColor, size=size.small)
isLiquiditySweep := false // Definitely not a sweep if BOS occurred
else
label.new(localLowIndex, localLow, "LS", color=color.rgb(155, 39, 176, 100), style=label.style_label_up, textcolor=color.orange, size=size.small)
isLiquiditySweep := true
else
isLiquiditySweep := false // Reset only if not LL
lastOpposingHigh := prevSwingHigh
bearishBOSOccurred := false
if isHL
label.new(localLowIndex, localLow, "HL", color=hlBackgroundColor, style=label.style_label_up, textcolor=hlTextColor, size=size.small)
lastOpposingHigh := prevSwingHigh
bearishBOSOccurred := false
prevSwingLow := localLow
lastZigzagPrice := localLow
lastSwingLow := localLow
lastLowIndex := localLowIndex
lastBearishShiftBar := bar_index
if bullishMS and internalShiftMode == "Market Shift (Engulfment)"
// Plot zigzag line
// Plot zigzag line for LL and HL separately
if not na(prevSwingLow)
if msLocalLow < prevSwingLow // LL
if zigzagLineStyle == "Solid"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing low and plot label (HL or LL)
if not na(prevSwingLow)
isLL := not na(prevSwingLow) and msLocalLow < prevSwingLow
isHL := not na(prevSwingLow) and msLocalLow > prevSwingLow
if isLL
label.new(msLocalLowIndex, msLocalLow, "LL", color=llBackgroundColor, style=label.style_label_up, textcolor=llTextColor, size=size.small)
if isHL
label.new(msLocalLowIndex, msLocalLow, "HL", color=hlBackgroundColor, style=label.style_label_up, textcolor=hlTextColor, size=size.small)
lastOpposingHigh := prevSwingHigh
SwingLowBOSOccurred := false
prevSwingLow := msLocalLow
lastZigzagPrice := msLocalLow
lastSwingLow := msLocalLow
lastLowIndex := msLocalLowIndex
lastBearishShiftBar := bar_index
//========================================================================================
if plotBearishInternalShift and internalShiftMode == "Engulfment"
// Plot zigzag line
if not na(prevSwingHigh)
if localHigh > prevSwingHigh // HH
if zigzagLineStyle == "Solid"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing high and plot label (HH or LH)
if not na(prevSwingHigh)
isHH := not na(prevSwingHigh) and localHigh > prevSwingHigh
isLH := not na(prevSwingHigh) and localHigh < prevSwingHigh
if isHH
if bullishBOSOccurred
label.new(localHighIndex, localHigh, "HH", color=hhBackgroundColor, style=label.style_label_down, textcolor=hhTextColor, size=size.small)
isLiquiditySweep := false
else
label.new(localHighIndex, localHigh, "LS", color=color.rgb(155, 39, 176, 100), style=label.style_label_down, textcolor=color.orange, size=size.small)
isLiquiditySweep := true
else
isLiquiditySweep := false
bullishBOSOccurred := false
if isLH
label.new(localHighIndex, localHigh, "LH", color=lhBackgroundColor, style=label.style_label_down, textcolor=lhTextColor, size=size.small)
lastOpposingLow := prevSwingLow
bullishBOSOccurred := false
prevSwingHigh := localHigh
lastZigzagPrice := localHigh
lastSwingHigh := localHigh
lastHighIndex := localHighIndex
lastBullishShiftBar := bar_index
if bearishMS and internalShiftMode == "Market Shift (Engulfment)"
// Plot zigzag line
if not na(prevSwingHigh)
if msLocalHigh > prevSwingHigh // HH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing high and plot label (HH or LH)
if not na(prevSwingHigh)
isHH := not na(prevSwingHigh) and msLocalHigh > prevSwingHigh
isLH := not na(prevSwingHigh) and msLocalHigh < prevSwingHigh
if isHH
label.new(msLocalHighIndex, msLocalHigh, "HH", color=hhBackgroundColor, style=label.style_label_down, textcolor=hhTextColor, size=size.small)
SwingHighBOSOccurred := false
if isLH
label.new(msLocalHighIndex, msLocalHigh, "LH", color=lhBackgroundColor, style=label.style_label_down, textcolor=lhTextColor, size=size.small)
lastOpposingLow := prevSwingLow
SwingHighBOSOccurred := false
prevSwingHigh := msLocalHigh
lastZigzagPrice := msLocalHigh
lastSwingHigh := msLocalHigh
lastHighIndex := msLocalHighIndex
lastBullishShiftBar := bar_index
// === Alert Conditions ===
alertcondition(newBull, title="New Supply Zone", message="New supply zone available.")
alertcondition(newBear, title="New Demand Zone", message="New demand zone available.")
alertcondition(plotBullishInternalShift, title="Bullish Internal Shift (All Lows)", message="Bullish Internal Shift detected! Check Swing Low.")
alertcondition(plotBearishInternalShift, title="Bearish Internal Shift (All Highs)", message="Bearish Internal Shift detected! Check Swing High.")
alertcondition(bullishBOSOccurred, title="Bullish Break of Structure", message="Bullish BoS detected.")
alertcondition(bearishBOSOccurred, title="Bearish Break of Structure", message="Bearish BoS detected.")
alertcondition(bullishMS, title="Bullish Market Shift", message="Bullish market shift detected.")
alertcondition(bearishMS, title="Bearish Market Shift", message="Bearish market shift detected.")
alertcondition(isHH and plotBearishInternalShift and not isLiquiditySweep, title="Higher High (HH)", message="Higher High (HH) detected")
alertcondition(isHL and plotBullishInternalShift, title="Higher Low (HL)", message="Higher Low (HL) detected")
alertcondition(isLL and plotBullishInternalShift and not isLiquiditySweep, title="Lower Low (LL)", message="Lower Low (LL) detected")
alertcondition(isLH and plotBearishInternalShift, title="Lower High (LH)", message="Lower High (LH) detected")
alertcondition((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift), title="Liquidity Sweep (LS)", message="Liquidity Sweep (LS) detected")
// === Alerts ===
if alertMode == "LTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (LTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (LTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (LTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (LTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (LTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (LTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (LTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (LTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (LTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (LTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (LTF).", alert.freq_once_per_bar_close)
if alertMode == "MTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (MTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (MTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (MTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (MTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (MTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (MTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (MTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (MTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (MTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (MTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (MTF).", alert.freq_once_per_bar_close)
if alertMode == "HTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (HTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (HTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (HTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (HTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (HTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (HTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (HTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (HTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (HTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (HTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (HTF).", alert.freq_once_per_bar_close)
// © The_Forex_Steward
//version=6
indicator("Market Structure- Zig Zag, BoS and Supply/Demand Zones", overlay=true)
// === User Inputs ===
htf = input.timeframe("", title="Timeframe")
internalShiftMode = input.string("Engulfment", title="Calculate Zig-Zag By", options=["Engulfment", "Market Shift (Engulfment)"])
x = input.int(1, minval=1, maxval= 3, title="# of Candles for Zones (1-3)")
showBearishOrderBlocks = input.bool(true, title="Show Supply Zones")
showBullishOrderBlocks = input.bool(true, title="Show Demand Zones")
orderBlockDuration = input.int(10, title="Zone Duration (bars)")
deleteMitigatedBoxes = input.bool(false, title="Delete Mitigated Zones")
deleteBrokenBoxes= input.bool(true, title = "Delete Broken Zones")
dimMitigatedBoxes = input.bool(true, title="Dim Mitigated Zones")
bearishBlockColor = input.color(color.rgb(255, 82, 82, 50), title="Supply Zone Fill")
bullishBlockColor = input.color(color.rgb(76, 175, 79, 50), title="Demand Zone Fill")
lighterBullishColor = color.new(bullishBlockColor, 85) // More transparent
lighterBearishColor = color.new(bearishBlockColor, 85)
zigzagLineColor = input.color(color.black, title="ZigZag Line Color")
zigzagLineWidth = input.int(2, title="Width of Lines", minval=1, maxval=10)
zigzagLineStyle = input.string("Solid", title="ZigZag Line Style", options=["Solid", "Dotted", "Dashed"])
internalShiftColor = color.new(zigzagLineColor, 75) // More transparent
bosBullishLineColor = input.color(color.green, title="Bullish BOS Line Color")
bosBearishLineColor = input.color(color.red, title="Bearish BOS Line Color")
bosLineStyle = input.string("Dotted", title="BOS Line Style", options=["Solid", "Dotted", "Dashed"])
bosLineStyleConst = bosLineStyle == "Solid" ? line.style_solid : bosLineStyle == "Dotted" ? line.style_dotted : line.style_dashed
alertMode = input.string("MTF", title= "Enable/Disable for Any Alert() Function Call↓ Alert Status→", options= ["LTF", "MTF", "HTF"])
alertSupplyandDemand = input.bool(true, title= "Supply & Demand Zones")
alertHighsandLows = input.bool(true, title= "Swing Highs & Lows")
alertBoS = input.bool(true, title= "BoS")
alertMS = input.bool(true, title= "Market Shifts")
hhBackgroundColor = color.rgb(76, 175, 79, 100)
hhTextColor = color.green
lhBackgroundColor = color.rgb(0, 137, 123, 100)
lhTextColor = color.red
llBackgroundColor = color.rgb(255, 82, 82, 100)
llTextColor = color.red
hlBackgroundColor = color.rgb(255, 153, 0, 100)
hlTextColor = color.green
HtfOpen = request.security(syminfo.tickerid, htf, open)
HtfHigh = request.security(syminfo.tickerid, htf, high)
HtfLow = request.security(syminfo.tickerid, htf, low)
HtfClose = request.security(syminfo.tickerid, htf, close)
prevHtfHigh = request.security(syminfo.tickerid, htf, high[1])
prevHtfLow = request.security(syminfo.tickerid, htf, low[1])
isHTFBarClose = ta.change(HtfClose) != 0
// Track the bar_index of the current bar when HTF closes
var int HtfBarIndex = na
if isHTFBarClose
HtfBarIndex := bar_index
// === Initialization ===
var int lastSignal = 0 // 0 = none, 1 = bull, -1 = bear
var float runningLowestHigh = na
var float runningHighestLow = na
// Track engulfed ranges
var float engulfedHigh = na
var float engulfedLow = na
// === Step 1: Detect "starter" engulfing ===
starterBull = HtfClose[1] < HtfOpen[1] and HtfClose > HtfOpen and HtfClose > HtfHigh[1]
starterBear = HtfClose[1] > HtfOpen[1] and HtfClose < HtfOpen and HtfClose < HtfLow[1]
if lastSignal == 0
if starterBull
lastSignal := 1
runningHighestLow := HtfLow
engulfedHigh := HtfHigh[1]
engulfedLow := HtfLow[1]
else if starterBear
lastSignal := -1
runningLowestHigh := HtfHigh
engulfedHigh := HtfHigh[1]
engulfedLow := HtfLow[1]
// === Step 2: Update running references ===
if lastSignal == -1 // last was bearish → waiting for bullish
runningLowestHigh := na(runningLowestHigh) ? HtfHigh : math.min(runningLowestHigh, HtfHigh)
else if lastSignal == 1 // last was bullish → waiting for bearish
runningHighestLow := na(runningHighestLow) ? HtfLow : math.max(runningHighestLow, HtfLow)
// === Step 3: Check for new engulfment ===
newBull = lastSignal == -1 and not na(runningLowestHigh) and HtfClose > runningLowestHigh
newBear = lastSignal == 1 and not na(runningHighestLow) and HtfClose < runningHighestLow
var int lastBullIndex = na
var int lastBearIndex = na
if newBull
lastBullIndex := HtfBarIndex
// store engulfed candle values (the one we just broke over)
engulfedHigh := runningLowestHigh
engulfedLow := HtfLow[1] // or HtfLow depending on how you define "engulfed"
if newBear
lastBearIndex := HtfBarIndex
engulfedLow := runningHighestLow
engulfedHigh := HtfHigh[1]
// === Step 4: Confirm and flip state ===
if newBull
lastSignal := 1
runningLowestHigh := na
runningHighestLow := HtfLow
else if newBear
lastSignal := -1
runningHighestLow := na
runningLowestHigh := HtfHigh
// === Track Boxes ===
var box[] bullishBoxes = array.new<box>()
var box[] bearishBoxes = array.new<box>()
// === Mitigation Flags ===
var bool bullishMitigated = false
var bool bearishMitigated = false
var bool bullishBreak = false
var bool bearishBreak = false
// === Delete invalidated boxes ===
if deleteBrokenBoxes
if array.size(bullishBoxes) > 0
for i = array.size(bullishBoxes) - 1 to 0
boxItem = array.get(bullishBoxes, i)
if HtfClose < box.get_bottom(boxItem)
box.delete(boxItem)
array.remove(bullishBoxes, i)
if array.size(bearishBoxes) > 0
for i = array.size(bearishBoxes) - 1 to 0
boxItem = array.get(bearishBoxes, i)
if HtfClose > box.get_top(boxItem)
box.delete(boxItem)
array.remove(bearishBoxes, i)
// === Delete mitigated boxes (optional) ===
if deleteMitigatedBoxes
if array.size(bullishBoxes) > 0
for i = array.size(bullishBoxes) - 1 to 0
boxItem = array.get(bullishBoxes, i)
if HtfLow < box.get_top(boxItem)
bullishMitigated := true
box.delete(boxItem)
array.remove(bullishBoxes, i)
if array.size(bearishBoxes) > 0
for i = array.size(bearishBoxes) - 1 to 0
boxItem = array.get(bearishBoxes, i)
if HtfHigh > box.get_bottom(boxItem)
bearishMitigated := true
box.delete(boxItem)
array.remove(bearishBoxes, i)
if dimMitigatedBoxes
if array.size(bullishBoxes) > 0
for i = 0 to array.size(bullishBoxes) - 1
boxItem = array.get(bullishBoxes, i)
if HtfLow < box.get_top(boxItem)
bullishMitigated := true
box.set_bgcolor(boxItem, lighterBullishColor)
box.set_border_color(boxItem, lighterBullishColor)
if array.size(bearishBoxes) > 0
for i = 0 to array.size(bearishBoxes) - 1
boxItem = array.get(bearishBoxes, i)
if HtfHigh > box.get_bottom(boxItem)
bearishMitigated := true
box.set_bgcolor(boxItem, lighterBearishColor)
box.set_border_color(boxItem, lighterBearishColor)
// Peramters for boxes
zoneHigh = ta.highest(HtfHigh[1], x)
zoneLow = ta.lowest(HtfLow[1], x)
// Create new order blocks with adjusted alignment
if showBullishOrderBlocks and newBull
bullishBox = box.new(left= HtfBarIndex[x], right=HtfBarIndex[x] + orderBlockDuration, top=zoneHigh, bottom=zoneLow, border_color=bullishBlockColor, bgcolor=bullishBlockColor)
array.push(bullishBoxes, bullishBox)
if showBearishOrderBlocks and newBear
bearishBox = box.new(left= HtfBarIndex[x], right=HtfBarIndex[x] + orderBlockDuration, top=zoneHigh, bottom=zoneLow, border_color=bearishBlockColor, bgcolor=bearishBlockColor)
array.push(bearishBoxes, bearishBox)
// === Internal Structure Logic ===
var int bullishCount = 0
var int bearishCount = 0
var float lowestBullishPrice = na
var float highestBearishPrice = na
var float firstBullishOpen = na
var float firstBearishOpen = na
var int lastInternalShift = 0
var float lastBullishInternalShiftPrice = na
var float lastBearishInternalShiftPrice = na
var float currentSwingHigh = na
var int currentSwingHighIndex = na
var float currentSwingLow = na
var int currentSwingLowIndex = na
var float prevSwingHigh = na
var float prevSwingLow = na
var bool isHH = false
var bool isHL = false
var bool isLL = false
var bool isLH = false
var bool isLiquiditySweep = false
var float lastOpposingLow = na // For HH
var float lastOpposingHigh = na // For LL
var bool internalShiftBullish = false
var bool internalShiftBearish = false
if ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
internalShiftBullish := newBull
internalShiftBearish := newBear
allowInternalShiftBearish = internalShiftBearish and lastInternalShift != -1
allowInternalShiftBullish = internalShiftBullish and lastInternalShift != 1
var bool plotBearishInternalShift = false
var bool plotBullishInternalShift = false
// === Determine Internal Shift Based on User Input ===
plotBearishInternalShift := false
plotBullishInternalShift := false
if allowInternalShiftBearish
plotBearishInternalShift := true
lastInternalShift := -1
if allowInternalShiftBullish
plotBullishInternalShift := true
lastInternalShift := 1
// === Plot internal shift markers ==
plotshape(plotBullishInternalShift, title="Bullish Internal Shift", location=location.belowbar, color=internalShiftColor, style=shape.triangleup, size=size.tiny)
plotshape(plotBearishInternalShift, title="Bearish Internal Shift", location=location.abovebar, color=internalShiftColor, style=shape.triangledown, size=size.tiny)
// === Highest High Between Alternate Bearish Break and Last Bullish Break (Safe) ===
var float localHigh = na
var int localHighIndex = na
maxHistory = 10000
if plotBearishInternalShift and ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
float highestHigh = na
int highestIndex = na
int startIndex = math.max(lastBullIndex, bar_index - maxHistory)
int endIndex = HtfBarIndex
for i = startIndex to endIndex
int lookback = bar_index - i // Convert i to relative offset for series access
if lookback >= 0 and lookback < maxHistory and not na(HtfHigh[lookback])
if na(highestHigh) or HtfHigh[lookback] > highestHigh
highestHigh := HtfHigh[lookback]
highestIndex := i
localHigh := highestHigh
localHighIndex := highestIndex
// === Lowest Low Between Alternate Bullish Break and Last Bearish Break (Safe) ===
var float localLow = na
var int localLowIndex = na
if plotBullishInternalShift and ((internalShiftMode == "Engulfment") or (internalShiftMode == "Market Shift (Engulfment)"))
float lowestLow = na
int lowestIndex = na
int startIndex = math.max(lastBearIndex, HtfBarIndex - maxHistory)
int endIndex = bar_index
for i = startIndex to endIndex
int lookback = bar_index - i // Convert i to relative offset
if lookback >= 0 and lookback < maxHistory and not na(HtfLow[lookback])
if na(lowestLow) or HtfLow[lookback] < lowestLow
lowestLow := HtfLow[lookback]
lowestIndex := i
localLow := lowestLow
localLowIndex := lowestIndex
// === Track Last Non-Alternating Break of Structure (BoS) ===
var int lastBullishBoSBarNA = na
var int lastBearishBoSBarNA = na
var float lastBullishBoSPriceNA = na
var float lastBearishBoSPriceNA = na
var bool bullishBOSOccurred = false
var bool bearishBOSOccurred = false
var int lastLowIndex = na
var int lastHighIndex = na
var float lastSwingHigh = na
var float lastSwingLow = na
// Reset flags
var bool canBreakBullish = true
var bool canBreakBearish = true
// BoS Conditions (non-alternating)
bullishBoS = canBreakBullish and HtfOpen[1] < localHigh and HtfClose > localHigh
bearishBoS = canBreakBearish and HtfOpen[1] > localLow and HtfClose < localLow
if bullishBoS and internalShiftMode == "Engulfment"
lastBullishBoSBarNA := bar_index
lastBullishBoSPriceNA := HtfClose
canBreakBullish := false // prevent further BoS on same localHigh
bullishBOSOccurred := true
line.new(x1=localHighIndex, y1=localHigh, x2=bar_index, y2=localHigh, color=bosBullishLineColor, width=zigzagLineWidth, style=bosLineStyleConst)
lastSwingHigh := na
if bearishBoS and internalShiftMode == "Engulfment"
lastBearishBoSBarNA := bar_index
lastBearishBoSPriceNA := HtfClose
canBreakBearish := false // prevent further BoS on same localLow
bearishBOSOccurred := true
line.new(x1=localLowIndex, y1=localLow, x2=bar_index, y2=localLow, color=bosBearishLineColor, width=zigzagLineWidth, style=bosLineStyleConst)
lastSwingLow := na
// Reset logic — allow new break only if local high/low changes
if ta.change(localHigh) != 0
canBreakBullish := true
if ta.change(localLow) != 0
canBreakBearish := true
// === Track Last MS Event ===
var int lastBullishBoSBar = na
var int lastBearishBoSBar = na
var float lastBullishBoSPrice = na
var float lastBearishBoSPrice = na
var bool SwingHighBOSOccurred = false
var bool SwingLowBOSOccurred = false
var int lastSwingLowIndex = na
var int lastSwingHighIndex = na
var float lastSSwingHigh = na
var float lastSSwingLow = na
// Track last BoS type: 1 = bullish, -1 = bearish, 0 = none yet
var int lastBoSType = 0
// === Track Last MS Type ===
var int lastMSType = na // 1 = bullish, -1 = bearish
// === MS Detection Logic ===
rawBullishMS = HtfClose > localHigh
rawBearishMS = HtfClose < localLow
// === Enforce Alternation ===
canBullishMS = na(lastMSType) or lastMSType == -1
canBearishMS = na(lastMSType) or lastMSType == 1
bullishMS = rawBullishMS and canBullishMS
bearishMS = rawBearishMS and canBearishMS
plotshape(bullishMS, title="Bullish Market Shift", location=location.belowbar, color=zigzagLineColor, style=shape.triangleup, size=size.tiny)
plotshape(bearishMS, title="Bearish Market Shift", location=location.abovebar, color=zigzagLineColor, style=shape.triangledown, size=size.tiny)
// === Update Last MS Type and BoS Bars ===
if bullishMS
lastMSType := 1
lastBullishBoSBar := bar_index
if bearishMS
lastMSType := -1
lastBearishBoSBar := bar_index
// === Lowest Low Between Last Bearish MS and This Bullish MS ===
var float msLocalLow = na
var int msLocalLowIndex = na
msMaxHistory = 5000
if bullishMS
float msLowestLow = na
int msLowestIndex = na
int msStartIndex = na(lastBearishBoSBar) ? bar_index - msMaxHistory : lastBearishBoSBar
int msEndIndex = bar_index // safer than using HtfBarIndex unless defined
for i = msStartIndex to msEndIndex
int msLookback = bar_index - i
if msLookback >= 0 and msLookback < msMaxHistory and not na(HtfLow[msLookback])
if na(msLowestLow) or HtfLow[msLookback] < msLowestLow
msLowestLow := HtfLow[msLookback]
msLowestIndex := i
msLocalLow := msLowestLow
msLocalLowIndex := msLowestIndex
// === Highest High Between Last Bullish MS and This Bearish MS ===
var float msLocalHigh = na
var int msLocalHighIndex = na
if bearishMS
float msHighestHigh = na
int msHighestIndex = na
int msStartIndex = na(lastBullishBoSBar) ? bar_index - msMaxHistory : lastBullishBoSBar
int msEndIndex = bar_index
for i = msStartIndex to msEndIndex
int msLookback = bar_index - i
if msLookback >= 0 and msLookback < msMaxHistory and not na(HtfHigh[msLookback])
if na(msHighestHigh) or HtfHigh[msLookback] > msHighestHigh
msHighestHigh := HtfHigh[msLookback]
msHighestIndex := i
msLocalHigh := msHighestHigh
msLocalHighIndex := msHighestIndex
// === Persistent variables for multiple line handling ===
var line[] zigzagLines = array.new<line>()
var int lastBearishShiftBar = na
var int lastBullishShiftBar = na
var float lastZigzagPrice = na
var string lastSwingType = ""
// Save shift bar indices
if plotBearishInternalShift
lastBearishShiftBar := bar_index
if plotBullishInternalShift
lastBullishShiftBar := bar_index
// Bearish shift followed by Bullish shift → Track lowest low
if plotBullishInternalShift and internalShiftMode == "Engulfment"
// Plot zigzag line
// Plot zigzag line for LL and HL separately
if not na(prevSwingLow)
if localLow < prevSwingLow // LL
if zigzagLineStyle == "Solid"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localHighIndex, y1=localHigh, x2=localLowIndex, y2=localLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing low and plot label (HL or LL)
if not na(prevSwingLow)
isLL := not na(prevSwingLow) and localLow < prevSwingLow
isHL := not na(prevSwingLow) and localLow > prevSwingLow
if isLL
if bearishBOSOccurred
label.new(localLowIndex, localLow, "LL", color=llBackgroundColor, style=label.style_label_up, textcolor=llTextColor, size=size.small)
isLiquiditySweep := false // Definitely not a sweep if BOS occurred
else
label.new(localLowIndex, localLow, "LS", color=color.rgb(155, 39, 176, 100), style=label.style_label_up, textcolor=color.orange, size=size.small)
isLiquiditySweep := true
else
isLiquiditySweep := false // Reset only if not LL
lastOpposingHigh := prevSwingHigh
bearishBOSOccurred := false
if isHL
label.new(localLowIndex, localLow, "HL", color=hlBackgroundColor, style=label.style_label_up, textcolor=hlTextColor, size=size.small)
lastOpposingHigh := prevSwingHigh
bearishBOSOccurred := false
prevSwingLow := localLow
lastZigzagPrice := localLow
lastSwingLow := localLow
lastLowIndex := localLowIndex
lastBearishShiftBar := bar_index
if bullishMS and internalShiftMode == "Market Shift (Engulfment)"
// Plot zigzag line
// Plot zigzag line for LL and HL separately
if not na(prevSwingLow)
if msLocalLow < prevSwingLow // LL
if zigzagLineStyle == "Solid"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalHighIndex, y1=msLocalHigh, x2=msLocalLowIndex, y2=msLocalLow, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing low and plot label (HL or LL)
if not na(prevSwingLow)
isLL := not na(prevSwingLow) and msLocalLow < prevSwingLow
isHL := not na(prevSwingLow) and msLocalLow > prevSwingLow
if isLL
label.new(msLocalLowIndex, msLocalLow, "LL", color=llBackgroundColor, style=label.style_label_up, textcolor=llTextColor, size=size.small)
if isHL
label.new(msLocalLowIndex, msLocalLow, "HL", color=hlBackgroundColor, style=label.style_label_up, textcolor=hlTextColor, size=size.small)
lastOpposingHigh := prevSwingHigh
SwingLowBOSOccurred := false
prevSwingLow := msLocalLow
lastZigzagPrice := msLocalLow
lastSwingLow := msLocalLow
lastLowIndex := msLocalLowIndex
lastBearishShiftBar := bar_index
//========================================================================================
if plotBearishInternalShift and internalShiftMode == "Engulfment"
// Plot zigzag line
if not na(prevSwingHigh)
if localHigh > prevSwingHigh // HH
if zigzagLineStyle == "Solid"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=localLowIndex, y1=localLow, x2=localHighIndex, y2=localHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing high and plot label (HH or LH)
if not na(prevSwingHigh)
isHH := not na(prevSwingHigh) and localHigh > prevSwingHigh
isLH := not na(prevSwingHigh) and localHigh < prevSwingHigh
if isHH
if bullishBOSOccurred
label.new(localHighIndex, localHigh, "HH", color=hhBackgroundColor, style=label.style_label_down, textcolor=hhTextColor, size=size.small)
isLiquiditySweep := false
else
label.new(localHighIndex, localHigh, "LS", color=color.rgb(155, 39, 176, 100), style=label.style_label_down, textcolor=color.orange, size=size.small)
isLiquiditySweep := true
else
isLiquiditySweep := false
bullishBOSOccurred := false
if isLH
label.new(localHighIndex, localHigh, "LH", color=lhBackgroundColor, style=label.style_label_down, textcolor=lhTextColor, size=size.small)
lastOpposingLow := prevSwingLow
bullishBOSOccurred := false
prevSwingHigh := localHigh
lastZigzagPrice := localHigh
lastSwingHigh := localHigh
lastHighIndex := localHighIndex
lastBullishShiftBar := bar_index
if bearishMS and internalShiftMode == "Market Shift (Engulfment)"
// Plot zigzag line
if not na(prevSwingHigh)
if msLocalHigh > prevSwingHigh // HH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
else // LH
if zigzagLineStyle == "Solid"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_solid)
else if zigzagLineStyle == "Dotted"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dotted)
else if zigzagLineStyle == "Dashed"
line.new(x1=msLocalLowIndex, y1=msLocalLow, x2=msLocalHighIndex, y2=msLocalHigh, color=zigzagLineColor, width=zigzagLineWidth, style=line.style_dashed)
// Update swing high and plot label (HH or LH)
if not na(prevSwingHigh)
isHH := not na(prevSwingHigh) and msLocalHigh > prevSwingHigh
isLH := not na(prevSwingHigh) and msLocalHigh < prevSwingHigh
if isHH
label.new(msLocalHighIndex, msLocalHigh, "HH", color=hhBackgroundColor, style=label.style_label_down, textcolor=hhTextColor, size=size.small)
SwingHighBOSOccurred := false
if isLH
label.new(msLocalHighIndex, msLocalHigh, "LH", color=lhBackgroundColor, style=label.style_label_down, textcolor=lhTextColor, size=size.small)
lastOpposingLow := prevSwingLow
SwingHighBOSOccurred := false
prevSwingHigh := msLocalHigh
lastZigzagPrice := msLocalHigh
lastSwingHigh := msLocalHigh
lastHighIndex := msLocalHighIndex
lastBullishShiftBar := bar_index
// === Alert Conditions ===
alertcondition(newBull, title="New Supply Zone", message="New supply zone available.")
alertcondition(newBear, title="New Demand Zone", message="New demand zone available.")
alertcondition(plotBullishInternalShift, title="Bullish Internal Shift (All Lows)", message="Bullish Internal Shift detected! Check Swing Low.")
alertcondition(plotBearishInternalShift, title="Bearish Internal Shift (All Highs)", message="Bearish Internal Shift detected! Check Swing High.")
alertcondition(bullishBOSOccurred, title="Bullish Break of Structure", message="Bullish BoS detected.")
alertcondition(bearishBOSOccurred, title="Bearish Break of Structure", message="Bearish BoS detected.")
alertcondition(bullishMS, title="Bullish Market Shift", message="Bullish market shift detected.")
alertcondition(bearishMS, title="Bearish Market Shift", message="Bearish market shift detected.")
alertcondition(isHH and plotBearishInternalShift and not isLiquiditySweep, title="Higher High (HH)", message="Higher High (HH) detected")
alertcondition(isHL and plotBullishInternalShift, title="Higher Low (HL)", message="Higher Low (HL) detected")
alertcondition(isLL and plotBullishInternalShift and not isLiquiditySweep, title="Lower Low (LL)", message="Lower Low (LL) detected")
alertcondition(isLH and plotBearishInternalShift, title="Lower High (LH)", message="Lower High (LH) detected")
alertcondition((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift), title="Liquidity Sweep (LS)", message="Liquidity Sweep (LS) detected")
// === Alerts ===
if alertMode == "LTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (LTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (LTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (LTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (LTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (LTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (LTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (LTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (LTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (LTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (LTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (LTF).", alert.freq_once_per_bar_close)
if alertMode == "MTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (MTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (MTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (MTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (MTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (MTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (MTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (MTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (MTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (MTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (MTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (MTF).", alert.freq_once_per_bar_close)
if alertMode == "HTF"
if isHH and plotBearishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Higher High (HH) detected (HTF)", alert.freq_once_per_bar_close)
if isHL and plotBullishInternalShift and (alertHighsandLows == true)
alert("Higher Low (HL) detected (HTF)", alert.freq_once_per_bar_close)
if isLL and plotBullishInternalShift and not isLiquiditySweep and (alertHighsandLows == true)
alert("Lower Low (LL) detected (HTF)" , alert.freq_once_per_bar_close)
if isLH and plotBearishInternalShift and (alertHighsandLows == true)
alert("Lower High (LH) detected (HTF)", alert.freq_once_per_bar_close)
if ((isLiquiditySweep and isLL and plotBullishInternalShift) or (isLiquiditySweep and isHH and plotBearishInternalShift)) and (alertHighsandLows == true)
alert("Liquidity Sweep (LS) detected (HTF)", alert.freq_once_per_bar_close)
if newBear and (alertSupplyandDemand == true)
alert("New supply zone available. (HTF)", alert.freq_once_per_bar_close)
if newBull and (alertSupplyandDemand == true)
alert("New demand zone available. (HTF)", alert.freq_once_per_bar_close)
if bullishBOSOccurred and (alertBoS == true)
alert("Bullish BoS detected. (HTF)", alert.freq_once_per_bar_close)
if bearishBOSOccurred and (alertBoS == true)
alert("Bearish BoS detected. (HTF)", alert.freq_once_per_bar_close)
if bullishMS and (alertMS == true)
alert("Bullish market shift detected (HTF).", alert.freq_once_per_bar_close)
if bearishMS and (alertMS == true)
alert("Bearish market shift detected (HTF).", alert.freq_once_per_bar_close)
סקריפט קוד פתוח
ברוח האמיתית של TradingView, יוצר הסקריפט הזה הפך אותו לקוד פתוח, כך שסוחרים יוכלו לעיין בו ולאמת את פעולתו. כל הכבוד למחבר! אמנם ניתן להשתמש בו בחינם, אך זכור כי פרסום חוזר של הקוד כפוף ל־כללי הבית שלנו.
כתב ויתור
המידע והפרסומים אינם מיועדים להיות, ואינם מהווים, ייעוץ או המלצה פיננסית, השקעתית, מסחרית או מכל סוג אחר המסופקת או מאושרת על ידי TradingView. קרא עוד ב־תנאי השימוש.
סקריפט קוד פתוח
ברוח האמיתית של TradingView, יוצר הסקריפט הזה הפך אותו לקוד פתוח, כך שסוחרים יוכלו לעיין בו ולאמת את פעולתו. כל הכבוד למחבר! אמנם ניתן להשתמש בו בחינם, אך זכור כי פרסום חוזר של הקוד כפוף ל־כללי הבית שלנו.
כתב ויתור
המידע והפרסומים אינם מיועדים להיות, ואינם מהווים, ייעוץ או המלצה פיננסית, השקעתית, מסחרית או מכל סוג אחר המסופקת או מאושרת על ידי TradingView. קרא עוד ב־תנאי השימוש.