// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // Neo Hyena Dynamic Controller LITE // Complete merged edition: NHV price-chart systems plus the Dynamics Controller pane. //@version=6 indicator("AV26", shorttitle="The Algo Vison", overlay=false, format=format.price, explicit_plot_zorder=true, max_boxes_count=500, max_lines_count=500, max_labels_count=300) // ═════════════════════════════════════════════════════════════════════════════ // NEO HYENA VISION LITE — MAIN PRICE CHART // ═════════════════════════════════════════════════════════════════════════════ // ───────────────────────────────────────────────────────────────────────────── // Helpers // ───────────────────────────────────────────────────────────────────────────── f_lineStyle(string selection) => selection == "Dashed" ? line.style_dashed : selection == "Dotted" ? line.style_dotted : line.style_solid f_touched(float price) => not na(price) and high >= price and low <= price f_addTouch(string touches, bool touched, string levelName) => touched ? (str.length(touches) == 0 ? levelName : touches + ", " + levelName) : touches f_syncLevelLabel(label levelLabel, bool enabled, float price, string levelName, color levelColor) => label result = levelLabel if enabled and not na(price) if na(result) result := label.new(time, price, levelName, xloc=xloc.bar_time, style=label.style_label_left, color=color.new(levelColor, 100), textcolor=levelColor, size=size.tiny, force_overlay=true) else label.set_xy(result, time, price) label.set_text(result, levelName) label.set_color(result, color.new(levelColor, 100)) label.set_textcolor(result, levelColor) else if not na(result) label.delete(result) result := na result // ───────────────────────────────────────────────────────────────────────────── // STANDARD ATR ZONE TREATMENT — always enforced on all price zones // Only ATR length and maximum height can be adjusted. There is no bypass. // Includes institutional blocks, absorption proxies and volume-break zones. // ───────────────────────────────────────────────────────────────────────────── string GROUP_ZONE_ATR = "Standard ATR Zone Filter (Always On)" int zoneAtrLength = input.int(14, "ATR Length", minval=1, maxval=500, group=GROUP_ZONE_ATR) float zoneMaxAtr = input.float(0.75, "Maximum Zone Height (ATR)", minval=0.05, maxval=3.0, step=0.05, group=GROUP_ZONE_ATR) float zoneAtr = ta.atr(zoneAtrLength) f_zoneFitsAtr(float zoneTop, float zoneBottom, float atrValue) => not na(atrValue) and atrValue > 0 and not na(zoneTop) and not na(zoneBottom) and zoneTop >= zoneBottom and (zoneTop - zoneBottom) <= atrValue * zoneMaxAtr // ───────────────────────────────────────────────────────────────────────────── // Tokyo range — New York time, ending at 1:00 AM // ───────────────────────────────────────────────────────────────────────────── string GROUP_TOKYO = "Tokyo Range" bool showTokyo = input.bool(true, "Show Tokyo Range", group=GROUP_TOKYO) string tokyoSession = input.session("1715-0100", "Session", group=GROUP_TOKYO, tooltip="Default: 5:15 PM to 1:00 AM New York time.") string tokyoTimezone = input.string("America/New_York", "Time Zone", options=["America/New_York", "Etc/UTC"], group=GROUP_TOKYO) color tokyoBorderColor = input.color(color.silver, "Border Color", group=GROUP_TOKYO, inline="tokyoBorder") int tokyoBorderWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_TOKYO, inline="tokyoBorder") string tokyoBorderStyle = input.string("Solid", "Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_TOKYO, inline="tokyoBorder") color tokyoFillColor = input.color(color.new(color.silver, 88), "Fill Color", group=GROUP_TOKYO) int tokyoRangesToKeep = input.int(20, "Ranges to Keep", minval=1, maxval=100, group=GROUP_TOKYO) bool inTokyo = showTokyo and not na(time(timeframe.period, tokyoSession, tokyoTimezone)) bool tokyoStarted = inTokyo and not inTokyo[1] bool tokyoEnded = not inTokyo and inTokyo[1] var box activeTokyoBox = na var array tokyoBoxes = array.new() if tokyoStarted activeTokyoBox := box.new(time, high, time_close, low, xloc=xloc.bar_time, border_color=tokyoBorderColor, border_width=tokyoBorderWidth, border_style=f_lineStyle(tokyoBorderStyle), bgcolor=tokyoFillColor, force_overlay=true) array.push(tokyoBoxes, activeTokyoBox) if array.size(tokyoBoxes) > tokyoRangesToKeep box.delete(array.shift(tokyoBoxes)) if inTokyo and not na(activeTokyoBox) box.set_right(activeTokyoBox, time_close) box.set_top(activeTokyoBox, math.max(box.get_top(activeTokyoBox), high)) box.set_bottom(activeTokyoBox, math.min(box.get_bottom(activeTokyoBox), low)) box.set_border_color(activeTokyoBox, tokyoBorderColor) box.set_border_width(activeTokyoBox, tokyoBorderWidth) box.set_border_style(activeTokyoBox, f_lineStyle(tokyoBorderStyle)) box.set_bgcolor(activeTokyoBox, tokyoFillColor) if tokyoEnded activeTokyoBox := na // ───────────────────────────────────────────────────────────────────────────── // London Open, New York Open, and London Close session range boxes. // The same gray fill, border, history count, and high-to-low treatment used // for Tokyo apply to each session. Times follow the America/New_York clock // (EST in winter, EDT in summer) and are independent of the chart timezone. // These are session ranges, NOT ATR-filtered signal zones. // ───────────────────────────────────────────────────────────────────────────── string GROUP_ADDITIONAL_SESSIONS = "London / NY Session Boxes" bool showLondonOpen = input.bool(true, "Show London Open (3:00-5:00 AM NY)", group=GROUP_ADDITIONAL_SESSIONS) bool showNewYorkOpen = input.bool(true, "Show NY Session (8:00-10:00 AM NY)", group=GROUP_ADDITIONAL_SESSIONS) bool showLondonClose = input.bool(true, "Show London Close (11:00 AM-1:00 PM NY)", group=GROUP_ADDITIONAL_SESSIONS, tooltip="All three session boxes inherit Tokyo Range fill, border, and Ranges to Keep settings.") string sessionBoxesTimezone = "America/New_York" // London Open: 3:00 AM-5:00 AM New York time. bool inLondonOpen = showLondonOpen and not na(time(timeframe.period, "0300-0500", sessionBoxesTimezone)) bool londonOpenStarted = inLondonOpen and not inLondonOpen[1] bool londonOpenEnded = not inLondonOpen and inLondonOpen[1] var box activeLondonOpenBox = na var array londonOpenBoxes = array.new() if londonOpenStarted activeLondonOpenBox := box.new(time, high, time_close, low, xloc=xloc.bar_time, border_color=tokyoBorderColor, border_width=tokyoBorderWidth, border_style=f_lineStyle(tokyoBorderStyle), bgcolor=tokyoFillColor, force_overlay=true) array.push(londonOpenBoxes, activeLondonOpenBox) if array.size(londonOpenBoxes) > tokyoRangesToKeep box.delete(array.shift(londonOpenBoxes)) if inLondonOpen and not na(activeLondonOpenBox) box.set_right(activeLondonOpenBox, time_close) box.set_top(activeLondonOpenBox, math.max(box.get_top(activeLondonOpenBox), high)) box.set_bottom(activeLondonOpenBox, math.min(box.get_bottom(activeLondonOpenBox), low)) box.set_border_color(activeLondonOpenBox, tokyoBorderColor) box.set_border_width(activeLondonOpenBox, tokyoBorderWidth) box.set_border_style(activeLondonOpenBox, f_lineStyle(tokyoBorderStyle)) box.set_bgcolor(activeLondonOpenBox, tokyoFillColor) if londonOpenEnded activeLondonOpenBox := na // New York Open: 8:00 AM-10:00 AM New York time. bool inNewYorkOpen = showNewYorkOpen and not na(time(timeframe.period, "0800-1000", sessionBoxesTimezone)) bool newYorkOpenStarted = inNewYorkOpen and not inNewYorkOpen[1] bool newYorkOpenEnded = not inNewYorkOpen and inNewYorkOpen[1] var box activeNewYorkOpenBox = na var array newYorkOpenBoxes = array.new() if newYorkOpenStarted activeNewYorkOpenBox := box.new(time, high, time_close, low, xloc=xloc.bar_time, border_color=tokyoBorderColor, border_width=tokyoBorderWidth, border_style=f_lineStyle(tokyoBorderStyle), bgcolor=tokyoFillColor, force_overlay=true) array.push(newYorkOpenBoxes, activeNewYorkOpenBox) if array.size(newYorkOpenBoxes) > tokyoRangesToKeep box.delete(array.shift(newYorkOpenBoxes)) if inNewYorkOpen and not na(activeNewYorkOpenBox) box.set_right(activeNewYorkOpenBox, time_close) box.set_top(activeNewYorkOpenBox, math.max(box.get_top(activeNewYorkOpenBox), high)) box.set_bottom(activeNewYorkOpenBox, math.min(box.get_bottom(activeNewYorkOpenBox), low)) box.set_border_color(activeNewYorkOpenBox, tokyoBorderColor) box.set_border_width(activeNewYorkOpenBox, tokyoBorderWidth) box.set_border_style(activeNewYorkOpenBox, f_lineStyle(tokyoBorderStyle)) box.set_bgcolor(activeNewYorkOpenBox, tokyoFillColor) if newYorkOpenEnded activeNewYorkOpenBox := na // London Close: 11:00 AM-1:00 PM New York time. bool inLondonClose = showLondonClose and not na(time(timeframe.period, "1100-1300", sessionBoxesTimezone)) bool londonCloseStarted = inLondonClose and not inLondonClose[1] bool londonCloseEnded = not inLondonClose and inLondonClose[1] var box activeLondonCloseBox = na var array londonCloseBoxes = array.new() if londonCloseStarted activeLondonCloseBox := box.new(time, high, time_close, low, xloc=xloc.bar_time, border_color=tokyoBorderColor, border_width=tokyoBorderWidth, border_style=f_lineStyle(tokyoBorderStyle), bgcolor=tokyoFillColor, force_overlay=true) array.push(londonCloseBoxes, activeLondonCloseBox) if array.size(londonCloseBoxes) > tokyoRangesToKeep box.delete(array.shift(londonCloseBoxes)) if inLondonClose and not na(activeLondonCloseBox) box.set_right(activeLondonCloseBox, time_close) box.set_top(activeLondonCloseBox, math.max(box.get_top(activeLondonCloseBox), high)) box.set_bottom(activeLondonCloseBox, math.min(box.get_bottom(activeLondonCloseBox), low)) box.set_border_color(activeLondonCloseBox, tokyoBorderColor) box.set_border_width(activeLondonCloseBox, tokyoBorderWidth) box.set_border_style(activeLondonCloseBox, f_lineStyle(tokyoBorderStyle)) box.set_bgcolor(activeLondonCloseBox, tokyoFillColor) if londonCloseEnded activeLondonCloseBox := na // ───────────────────────────────────────────────────────────────────────────── // Institutional levels and institutional extremes // ───────────────────────────────────────────────────────────────────────────── [dailyInstitutionalLevel, dailyExtremeUpper, dailyExtremeLower] = request.security(syminfo.tickerid, "D", [open, high[1], low[1]], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on) [weeklyInstitutionalLevel, weeklyExtremeUpper, weeklyExtremeLower] = request.security(syminfo.tickerid, "W", [open, high[1], low[1]], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on) [monthlyInstitutionalLevel, monthlyExtremeUpper, monthlyExtremeLower] = request.security(syminfo.tickerid, "M", [open, high[1], low[1]], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on) bool newDay = timeframe.change("D") bool newWeek = timeframe.change("W") bool newMonth = timeframe.change("M") string GROUP_DIL = "Daily Institutional Level" bool showDIL = input.bool(true, "Show", group=GROUP_DIL, inline="dil") color dilColor = input.color(color.black, "Color", group=GROUP_DIL, inline="dil") int dilWidth = input.int(2, "Width", minval=1, maxval=5, group=GROUP_DIL, inline="dil") string dilStyle = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_DIL) string GROUP_WIL = "Weekly Institutional Level" bool showWIL = input.bool(true, "Show", group=GROUP_WIL, inline="wil") color wilColor = input.color(color.blue, "Color", group=GROUP_WIL, inline="wil") int wilWidth = input.int(2, "Width", minval=1, maxval=5, group=GROUP_WIL, inline="wil") string wilStyle = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_WIL) string GROUP_MIL = "Monthly Institutional Level" bool showMIL = input.bool(true, "Show", group=GROUP_MIL, inline="mil") color milColor = input.color(color.maroon, "Color", group=GROUP_MIL, inline="mil") int milWidth = input.int(2, "Width", minval=1, maxval=5, group=GROUP_MIL, inline="mil") string milStyle = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_MIL) string GROUP_DIE = "Daily Institutional Extremes" bool showDailyUpper = input.bool(true, "Show Upper", group=GROUP_DIE, inline="dieU") color dailyUpperColor = input.color(color.black, "Color", group=GROUP_DIE, inline="dieU") int dailyUpperWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_DIE, inline="dieU") string dailyUpperStyle = input.string("Dashed", "Upper Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_DIE) bool showDailyLower = input.bool(true, "Show Lower", group=GROUP_DIE, inline="dieL") color dailyLowerColor = input.color(color.black, "Color", group=GROUP_DIE, inline="dieL") int dailyLowerWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_DIE, inline="dieL") string dailyLowerStyle = input.string("Dashed", "Lower Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_DIE) string GROUP_WIE = "Weekly Institutional Extremes" bool showWeeklyUpper = input.bool(true, "Show Upper", group=GROUP_WIE, inline="wieU") color weeklyUpperColor = input.color(color.maroon, "Color", group=GROUP_WIE, inline="wieU") int weeklyUpperWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_WIE, inline="wieU") string weeklyUpperStyle = input.string("Dashed", "Upper Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_WIE) bool showWeeklyLower = input.bool(true, "Show Lower", group=GROUP_WIE, inline="wieL") color weeklyLowerColor = input.color(color.navy, "Color", group=GROUP_WIE, inline="wieL") int weeklyLowerWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_WIE, inline="wieL") string weeklyLowerStyle = input.string("Dashed", "Lower Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_WIE) string GROUP_MIE = "Monthly Institutional Extremes" bool showMonthlyUpper = input.bool(true, "Show Upper", group=GROUP_MIE, inline="mieU") color monthlyUpperColor = input.color(color.red, "Color", group=GROUP_MIE, inline="mieU") int monthlyUpperWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_MIE, inline="mieU") string monthlyUpperStyle = input.string("Dashed", "Upper Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_MIE) bool showMonthlyLower = input.bool(true, "Show Lower", group=GROUP_MIE, inline="mieL") color monthlyLowerColor = input.color(color.green, "Color", group=GROUP_MIE, inline="mieL") int monthlyLowerWidth = input.int(1, "Width", minval=1, maxval=5, group=GROUP_MIE, inline="mieL") string monthlyLowerStyle = input.string("Dashed", "Lower Style", options=["Solid", "Dashed", "Dotted"], group=GROUP_MIE) // Historical plots preserve every loaded period for visual backtesting. A // one-bar series gap plus line-break rendering prevents a diagonal connector // from the old level to the new one. dilPlotStyle = dilStyle == "Dashed" ? plot.linestyle_dashed : dilStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid wilPlotStyle = wilStyle == "Dashed" ? plot.linestyle_dashed : wilStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid milPlotStyle = milStyle == "Dashed" ? plot.linestyle_dashed : milStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid dailyUpperPlotStyle = dailyUpperStyle == "Dashed" ? plot.linestyle_dashed : dailyUpperStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid dailyLowerPlotStyle = dailyLowerStyle == "Dashed" ? plot.linestyle_dashed : dailyLowerStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid weeklyUpperPlotStyle = weeklyUpperStyle == "Dashed" ? plot.linestyle_dashed : weeklyUpperStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid weeklyLowerPlotStyle = weeklyLowerStyle == "Dashed" ? plot.linestyle_dashed : weeklyLowerStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid monthlyUpperPlotStyle = monthlyUpperStyle == "Dashed" ? plot.linestyle_dashed : monthlyUpperStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid monthlyLowerPlotStyle = monthlyLowerStyle == "Dashed" ? plot.linestyle_dashed : monthlyLowerStyle == "Dotted" ? plot.linestyle_dotted : plot.linestyle_solid plot(newDay ? na : dailyInstitutionalLevel, "Daily Institutional Level", color=showDIL ? dilColor : na, linewidth=dilWidth, style=plot.style_linebr, linestyle=dilPlotStyle, force_overlay=true) plot(newWeek ? na : weeklyInstitutionalLevel, "Weekly Institutional Level", color=showWIL ? wilColor : na, linewidth=wilWidth, style=plot.style_linebr, linestyle=wilPlotStyle, force_overlay=true) plot(newMonth ? na : monthlyInstitutionalLevel, "Monthly Institutional Level", color=showMIL ? milColor : na, linewidth=milWidth, style=plot.style_linebr, linestyle=milPlotStyle, force_overlay=true) plot(newDay ? na : dailyExtremeUpper, "Daily Institutional Extreme Upper", color=showDailyUpper ? dailyUpperColor : na, linewidth=dailyUpperWidth, style=plot.style_linebr, linestyle=dailyUpperPlotStyle, force_overlay=true) plot(newDay ? na : dailyExtremeLower, "Daily Institutional Extreme Lower", color=showDailyLower ? dailyLowerColor : na, linewidth=dailyLowerWidth, style=plot.style_linebr, linestyle=dailyLowerPlotStyle, force_overlay=true) plot(newWeek ? na : weeklyExtremeUpper, "Weekly Institutional Extreme Upper", color=showWeeklyUpper ? weeklyUpperColor : na, linewidth=weeklyUpperWidth, style=plot.style_linebr, linestyle=weeklyUpperPlotStyle, force_overlay=true) plot(newWeek ? na : weeklyExtremeLower, "Weekly Institutional Extreme Lower", color=showWeeklyLower ? weeklyLowerColor : na, linewidth=weeklyLowerWidth, style=plot.style_linebr, linestyle=weeklyLowerPlotStyle, force_overlay=true) plot(newMonth ? na : monthlyExtremeUpper, "Monthly Institutional Extreme Upper", color=showMonthlyUpper ? monthlyUpperColor : na, linewidth=monthlyUpperWidth, style=plot.style_linebr, linestyle=monthlyUpperPlotStyle, force_overlay=true) plot(newMonth ? na : monthlyExtremeLower, "Monthly Institutional Extreme Lower", color=showMonthlyLower ? monthlyLowerColor : na, linewidth=monthlyLowerWidth, style=plot.style_linebr, linestyle=monthlyLowerPlotStyle, force_overlay=true) string GROUP_LEVEL_LABELS = "Institutional Line Identification" bool showInstitutionalLabels = input.bool(true, "Show Institutional Line Names", group=GROUP_LEVEL_LABELS, tooltip="Displays a small name at the current edge of each institutional line so dashed extremes are not anonymous.") var label dilLabel = na var label wilLabel = na var label milLabel = na var label dailyUpperLabel = na var label dailyLowerLabel = na var label weeklyUpperLabel = na var label weeklyLowerLabel = na var label monthlyUpperLabel = na var label monthlyLowerLabel = na dilLabel := f_syncLevelLabel(dilLabel, showInstitutionalLabels and showDIL, dailyInstitutionalLevel, "Daily Institutional Level", dilColor) wilLabel := f_syncLevelLabel(wilLabel, showInstitutionalLabels and showWIL, weeklyInstitutionalLevel, "Weekly Institutional Level", wilColor) milLabel := f_syncLevelLabel(milLabel, showInstitutionalLabels and showMIL, monthlyInstitutionalLevel, "Monthly Institutional Level", milColor) dailyUpperLabel := f_syncLevelLabel(dailyUpperLabel, showInstitutionalLabels and showDailyUpper, dailyExtremeUpper, "Daily Institutional Extreme Upper", dailyUpperColor) dailyLowerLabel := f_syncLevelLabel(dailyLowerLabel, showInstitutionalLabels and showDailyLower, dailyExtremeLower, "Daily Institutional Extreme Lower", dailyLowerColor) weeklyUpperLabel := f_syncLevelLabel(weeklyUpperLabel, showInstitutionalLabels and showWeeklyUpper, weeklyExtremeUpper, "Weekly Institutional Extreme Upper", weeklyUpperColor) weeklyLowerLabel := f_syncLevelLabel(weeklyLowerLabel, showInstitutionalLabels and showWeeklyLower, weeklyExtremeLower, "Weekly Institutional Extreme Lower", weeklyLowerColor) monthlyUpperLabel := f_syncLevelLabel(monthlyUpperLabel, showInstitutionalLabels and showMonthlyUpper, monthlyExtremeUpper, "Monthly Institutional Extreme Upper", monthlyUpperColor) monthlyLowerLabel := f_syncLevelLabel(monthlyLowerLabel, showInstitutionalLabels and showMonthlyLower, monthlyExtremeLower, "Monthly Institutional Extreme Lower", monthlyLowerColor) // ───────────────────────────────────────────────────────────────────────────── // VWAP // ───────────────────────────────────────────────────────────────────────────── string GROUP_VWAP = "VWAP" bool showVwap = input.bool(true, "Show VWAP", group=GROUP_VWAP) string vwapAnchor = input.string("Daily", "Anchor", options=["Daily", "Weekly", "Monthly"], group=GROUP_VWAP) float vwapSource = input.source(hlc3, "Source", group=GROUP_VWAP) color vwapColor = input.color(color.rgb(58, 108, 168), "VWAP Color", group=GROUP_VWAP, inline="vwap") int vwapWidth = input.int(2, "Width", minval=1, maxval=5, group=GROUP_VWAP, inline="vwap") bool showVwapBands = input.bool(true, "Show Standard Deviation Bands", group=GROUP_VWAP) float vwapBandMultiplier = input.float(1.0, "Band Multiplier", minval=0.1, step=0.1, group=GROUP_VWAP) color vwapBandColor = input.color(color.green, "Band Color", group=GROUP_VWAP) bool vwapReset = switch vwapAnchor "Weekly" => newWeek "Monthly" => newMonth => newDay [vwapValue, vwapUpper, vwapLower] = ta.vwap(vwapSource, vwapReset, vwapBandMultiplier) plot(showVwap ? vwapValue : na, "VWAP", color=vwapColor, linewidth=vwapWidth, force_overlay=true) vwapUpperPlot = plot(showVwap and showVwapBands ? vwapUpper : na, "VWAP Upper Band", color=vwapBandColor, force_overlay=true) vwapLowerPlot = plot(showVwap and showVwapBands ? vwapLower : na, "VWAP Lower Band", color=vwapBandColor, force_overlay=true) fill(vwapUpperPlot, vwapLowerPlot, title="VWAP Bands Fill", color=showVwap and showVwapBands ? color.new(vwapBandColor, 92) : na) // ───────────────────────────────────────────────────────────────────────────── // Institutional Order Blocks (IOBs) // ATR-qualified candles become borderless high-to-low landscape zones that // remain visible through the close of their symbol-defined trading day. // A dotted line marks the zone's 50% level. // ───────────────────────────────────────────────────────────────────────────── string GROUP_IOB = "Institutional Order Blocks (IOBs)" bool showIobs = input.bool(true, "Show IOB Zones", group=GROUP_IOB) float iobMinimumSigma = input.float(6.0, "Minimum Sigma", minval=1.0, maxval=20.0, step=0.5, group=GROUP_IOB) int iobSigmaLength = input.int(50, "Sigma Length", minval=10, maxval=500, group=GROUP_IOB) int iobZonesToKeep = input.int(30, "Zones to Keep", minval=1, maxval=100, group=GROUP_IOB) color iobColor = input.color(color.gray, "Zone Color", group=GROUP_IOB, inline="iobColor") int iobTransparency = input.int(82, "Transparency", minval=0, maxval=100, group=GROUP_IOB, inline="iobColor") int iobMidpointWidth = input.int(1, "50% Line Width", minval=1, maxval=5, group=GROUP_IOB) float candleBody = math.abs(open - close) float normalizedVolume = nz(volume, 0.0) float sellIntensity = close < open and candleBody > 0 and close != 0 ? (normalizedVolume / candleBody) / math.abs(close) : 0.0 float buyIntensity = close > open and candleBody > 0 and close != 0 ? (normalizedVolume / candleBody) / math.abs(close) : 0.0 float sellDeviation = ta.stdev(sellIntensity, iobSigmaLength) float buyDeviation = ta.stdev(buyIntensity, iobSigmaLength) float sellSigma = sellDeviation > 0 ? sellIntensity / sellDeviation : 0.0 float buySigma = buyDeviation > 0 ? buyIntensity / buyDeviation : 0.0 bool newIob = showIobs and barstate.isconfirmed and (sellSigma >= iobMinimumSigma or buySigma >= iobMinimumSigma) and f_zoneFitsAtr(high, low, zoneAtr) var array iobBoxes = array.new() var array iobMidpoints = array.new() if newIob float midpoint = math.avg(high, low) int iobDayClose = time_close("D") box iobBox = box.new(time, high, iobDayClose, low, xloc=xloc.bar_time, border_color=na, bgcolor=color.new(iobColor, iobTransparency), force_overlay=true) line iobMidpoint = line.new(time, midpoint, iobDayClose, midpoint, xloc=xloc.bar_time, color=iobColor, width=iobMidpointWidth, style=line.style_dotted, force_overlay=true) array.push(iobBoxes, iobBox) array.push(iobMidpoints, iobMidpoint) if array.size(iobBoxes) > iobZonesToKeep box.delete(array.shift(iobBoxes)) line.delete(array.shift(iobMidpoints)) // ───────────────────────────────────────────────────────────────────────────── // Dynamic institutional-touch alerts // Create one TradingView alert using: “Any alert() function call”. // ───────────────────────────────────────────────────────────────────────────── string GROUP_ALERTS = "Institutional Touch Alerts" bool enableTouchAlerts = input.bool(true, "Enable Any-Function Touch Alerts", group=GROUP_ALERTS) bool alertDailyLevel = input.bool(true, "Daily Institutional Level", group=GROUP_ALERTS, inline="alertLevels") bool alertWeeklyLevel = input.bool(true, "Weekly", group=GROUP_ALERTS, inline="alertLevels") bool alertMonthlyLevel = input.bool(true, "Monthly", group=GROUP_ALERTS, inline="alertLevels") bool alertDailyExtremes = input.bool(true, "Daily Institutional Extremes", group=GROUP_ALERTS, inline="alertExtremes") bool alertWeeklyExtremes = input.bool(true, "Weekly", group=GROUP_ALERTS, inline="alertExtremes") bool alertMonthlyExtremes = input.bool(true, "Monthly", group=GROUP_ALERTS, inline="alertExtremes") bool alertsOnBarClose = input.bool(false, "Wait for Bar Close", group=GROUP_ALERTS, tooltip="Off alerts on the first touch during the bar. On waits until the bar closes.") string touchedLevels = "" touchedLevels := f_addTouch(touchedLevels, alertDailyLevel and f_touched(dailyInstitutionalLevel), "Daily Institutional Level") touchedLevels := f_addTouch(touchedLevels, alertWeeklyLevel and f_touched(weeklyInstitutionalLevel), "Weekly Institutional Level") touchedLevels := f_addTouch(touchedLevels, alertMonthlyLevel and f_touched(monthlyInstitutionalLevel), "Monthly Institutional Level") touchedLevels := f_addTouch(touchedLevels, alertDailyExtremes and f_touched(dailyExtremeUpper), "Daily Institutional Extreme — Upper") touchedLevels := f_addTouch(touchedLevels, alertDailyExtremes and f_touched(dailyExtremeLower), "Daily Institutional Extreme — Lower") touchedLevels := f_addTouch(touchedLevels, alertWeeklyExtremes and f_touched(weeklyExtremeUpper), "Weekly Institutional Extreme — Upper") touchedLevels := f_addTouch(touchedLevels, alertWeeklyExtremes and f_touched(weeklyExtremeLower), "Weekly Institutional Extreme — Lower") touchedLevels := f_addTouch(touchedLevels, alertMonthlyExtremes and f_touched(monthlyExtremeUpper), "Monthly Institutional Extreme — Upper") touchedLevels := f_addTouch(touchedLevels, alertMonthlyExtremes and f_touched(monthlyExtremeLower), "Monthly Institutional Extreme — Lower") bool anyInstitutionalTouch = str.length(touchedLevels) > 0 string touchMessage = syminfo.ticker + " touched: " + touchedLevels + " | Price: " + str.tostring(close, format.mintick) + " | Chart: " + timeframe.period if enableTouchAlerts and anyInstitutionalTouch if alertsOnBarClose if barstate.isconfirmed alert(touchMessage, alert.freq_once_per_bar_close) else alert(touchMessage, alert.freq_once_per_bar) // ═════════════════════════════════════════════════════════════════════════════ // DYNAMICS CONTROLLER LITE — INDICATOR PANE // ═════════════════════════════════════════════════════════════════════════════ // ───────────────────────────────────────────────────────────────────────────── // Inputs // ───────────────────────────────────────────────────────────────────────────── string GROUP_CONTROLLER = "Controller" int rsiLength = input.int(50, "RSI Length", minval=2, group=GROUP_CONTROLLER) int fastLength = input.int(50, "Liquidity Equilibrium Length", minval=1, group=GROUP_CONTROLLER) int slowLength = input.int(200, "Market Momentum Length", minval=2, group=GROUP_CONTROLLER) int bbLength = input.int(50, "Bollinger Length", minval=2, group=GROUP_CONTROLLER) float bbMultiplier = input.float(2.0, "Bollinger Multiplier", minval=0.1, step=0.1, group=GROUP_CONTROLLER) bool showBollinger = input.bool(true, "Show Bollinger Bands", group=GROUP_CONTROLLER) bool showFast = input.bool(true, "Show Liquidity Equilibrium", group=GROUP_CONTROLLER) bool showSlow = input.bool(true, "Show Market Momentum", group=GROUP_CONTROLLER) string GROUP_ZONES = "Dynamics Zones" bool showZones = input.bool(true, "Show Zones", group=GROUP_ZONES) bool showMediumVolumeBreakZones = input.bool(true, "Show Medium Volume Break Zones", group=GROUP_ZONES) bool showHighVolumeBreakZones = input.bool(true, "Show Golden High Volume Break Zones", group=GROUP_ZONES) bool showAbsorptionZones = input.bool(true, "Show Volume Absorption Proxy Zones", tooltip="Detected from OHLCV pressure proxies, not actual bid/ask or footprint absorption. Each zone must pass the standard ATR height filter.", group=GROUP_ZONES) int flushBars = input.int(8, "Triple-Break Window", minval=1, maxval=50, group=GROUP_ZONES) int maxZones = input.int(100, "Maximum Stored Zones", minval=10, maxval=200, group=GROUP_ZONES) bool deleteInvalidatedZones = input.bool(false, "Delete Invalidated Zones", tooltip="Off preserves invalidated zones as faint historical landscape. On removes them.", group=GROUP_ZONES) color bullishZoneColor = input.color(color.rgb(0, 190, 145), "Bullish Medium Volume Break Zone", group=GROUP_ZONES) color bearishZoneColor = input.color(color.rgb(215, 55, 70), "Bearish Medium Volume Break Zone", group=GROUP_ZONES) color highVolumeBreakColor = input.color(color.rgb(255, 215, 0), "Golden High Volume Break Zone", group=GROUP_ZONES) color bullishAbsorptionColor = input.color(color.aqua, "Bullish Absorption Proxy Zone", group=GROUP_ZONES) color bearishAbsorptionColor = input.color(color.orange, "Bearish Absorption Proxy Zone", group=GROUP_ZONES) int absorptionZoneTransparency = input.int(82, "Absorption Zone Transparency", minval=0, maxval=100, group=GROUP_ZONES) int normalZoneTransparency = input.int(70, "Medium Volume Break Transparency", minval=0, maxval=100, group=GROUP_ZONES) int highVolumeBreakTransparency = input.int(50, "High Volume Break Transparency", minval=0, maxval=100, group=GROUP_ZONES) string GROUP_PRESSURE = "Volume Pressure Proxy" bool enablePressure = input.bool(false, "Enable Volume Pressure Signals", tooltip="Enables the pressure signals/dots for decision gating. Absorption proxy zones are controlled separately in Dynamics Zones and remain ATR-filtered. OHLCV estimates are not true bid/ask delta.", group=GROUP_PRESSURE) int pressureVolumeLength = input.int(20, "Relative Volume Length", minval=2, maxval=500, group=GROUP_PRESSURE) float pressureMinRelativeVolume = input.float(1.5, "Minimum Relative Volume", minval=0.1, step=0.1, group=GROUP_PRESSURE) float pressureMaxBodyRatio = input.float(0.35, "Maximum Body / Range", minval=0.05, maxval=1.0, step=0.05, group=GROUP_PRESSURE) float pressureMinWickRatio = input.float(0.35, "Minimum Rejection Wick / Range", minval=0.05, maxval=1.0, step=0.05, group=GROUP_PRESSURE) float pressureThreshold = input.float(0.35, "Directional Pressure Threshold", minval=0.05, maxval=0.95, step=0.05, group=GROUP_PRESSURE) bool pressureRequiresZone = input.bool(true, "Require Active Dynamics Zone", group=GROUP_PRESSURE) bool pressureConfirmsDecisions = input.bool(false, "Require Pressure for Decision Signals", active=enablePressure, group=GROUP_PRESSURE) bool showPressureDots = input.bool(false, "Show Pressure Dots on Price", active=enablePressure, group=GROUP_PRESSURE) string GROUP_CASCADE = "Decision Engine" bool showDecisionHud = input.bool(true, "Show Decision HUD", group=GROUP_CASCADE) bool useHtfContext = input.bool(true, "Use 4H / 1H Context", group=GROUP_CASCADE) int regime20dDays = input.int(20, "20D Regime Block (trading days)", minval=5, group=GROUP_CASCADE) float regime20dThresholdPct = input.float(5.0, "20D Regime Threshold (%)", minval=0.1, group=GROUP_CASCADE) int regime20bBars = input.int(20, "Current Timeframe Regime Bars", minval=5, group=GROUP_CASCADE) float regime20bThresholdAtr = input.float(1.0, "Current Timeframe Regime Threshold (ATR)", minval=0.1, step=0.1, group=GROUP_CASCADE) bool useEnergyFilter = input.bool(true, "Use ATR Energy Filter", group=GROUP_CASCADE) int energyAtrLength = input.int(14, "Energy ATR Length", minval=1, maxval=500, group=GROUP_CASCADE) int energyBaselineLength = input.int(50, "Energy Baseline", minval=10, maxval=500, group=GROUP_CASCADE) float quietEnergyRatio = input.float(0.80, "Minimum 15M / 5M Energy", minval=0.10, maxval=1.50, step=0.05, group=GROUP_CASCADE) float triggerEnergyRatio = input.float(1.00, "Minimum 1M Trigger Energy", minval=0.10, maxval=3.00, step=0.05, group=GROUP_CASCADE) float regimeMidBand = input.float(2.5, "Consolidation Distance from 50", minval=0.5, maxval=10.0, step=0.5, group=GROUP_CASCADE) float regimeMaxSpread = input.float(3.0, "Consolidation Stack Spread", minval=0.5, maxval=15.0, step=0.5, group=GROUP_CASCADE) int compressionLookback = input.int(100, "Compression Lookback", minval=20, maxval=500, group=GROUP_CASCADE) float compressionRatio = input.float(0.70, "Compression Ratio", minval=0.10, maxval=1.0, step=0.05, group=GROUP_CASCADE) // ───────────────────────────────────────────────────────────────────────────── // Core RSI controller // ───────────────────────────────────────────────────────────────────────────── float controller = ta.rsi(close, rsiLength) float fastController = ta.sma(controller, fastLength) float slowController = ta.sma(controller, slowLength) float controllerBasis = ta.sma(controller, bbLength) float controllerDeviation = bbMultiplier * ta.stdev(controller, bbLength) float controllerUpper = controllerBasis + controllerDeviation float controllerLower = controllerBasis - controllerDeviation float currentRegimeAtr = ta.atr(regime20bBars) float regime20bMoveAtr = not na(close[regime20bBars]) and not na(currentRegimeAtr) and currentRegimeAtr > 0 ? (close - close[regime20bBars]) / currentRegimeAtr : na int regime20b = na(regime20bMoveAtr) ? 0 : regime20bMoveAtr >= regime20bThresholdAtr ? 1 : regime20bMoveAtr <= -regime20bThresholdAtr ? -1 : 0 // ───────────────────────────────────────────────────────────────────────────── // Portable multi-timeframe regime and energy functions // ───────────────────────────────────────────────────────────────────────────── f_regime() => float r = ta.rsi(close, rsiLength) float f = ta.sma(r, fastLength) float s = ta.sma(r, slowLength) float basis = ta.sma(r, bbLength) float dev = bbMultiplier * ta.stdev(r, bbLength) float width = dev * 2.0 float averageWidth = ta.sma(width, compressionLookback) float stackHigh = math.max(r, math.max(f, s)) float stackLow = math.min(r, math.min(f, s)) float spread = stackHigh - stackLow bool compressed = not na(averageWidth) and averageWidth > 0 and width <= averageWidth * compressionRatio bool consolidation = math.abs(r - 50.0) <= regimeMidBand and math.abs(f - 50.0) <= regimeMidBand and math.abs(s - 50.0) <= regimeMidBand and spread <= regimeMaxSpread and compressed int bullVotes = (r > 50.0 ? 1 : 0) + (r > f ? 1 : 0) + (r > s ? 1 : 0) int bearVotes = (r < 50.0 ? 1 : 0) + (r < f ? 1 : 0) + (r < s ? 1 : 0) int code = consolidation ? 0 : bullVotes == 3 ? (r >= 55.0 ? 3 : 2) : bearVotes == 3 ? (r <= 45.0 ? -3 : -2) : bullVotes >= 2 ? 1 : bearVotes >= 2 ? -1 : 0 code // Evaluated in the daily context; returns only the last completed day's state. // Reassess once every N trading days, using the preceding N-day return. f_20DayRegime() => var int completedDays = 0 var int regimeCode = 0 var float blockReturnPct = na completedDays += 1 if completedDays >= regime20dDays * 2 and completedDays % regime20dDays == 0 blockReturnPct := (close / close[regime20dDays] - 1.0) * 100.0 if blockReturnPct >= regime20dThresholdPct regimeCode := 1 else if blockReturnPct <= -regime20dThresholdPct regimeCode := -1 else regimeCode := 0 [regimeCode[1], blockReturnPct[1]] f_energyRatio() => float currentAtr = ta.atr(energyAtrLength) float averageAtr = ta.sma(currentAtr, energyBaselineLength) not na(averageAtr) and averageAtr > 0 ? currentAtr / averageAtr : na f_direction(int code) => code > 0 ? 1 : code < 0 ? -1 : 0 f_regimeName(int code) => code == 3 ? "STRONG BULLISH" : code == 2 ? "BULLISH" : code == 1 ? "BULLISH TRANSITION" : code == 0 ? "CONSOLIDATION" : code == -1 ? "BEARISH TRANSITION" : code == -2 ? "BEARISH" : "STRONG BEARISH" [regime20d, regime20dReturnPct] = request.security(syminfo.tickerid, "D", f_20DayRegime(), lookahead=barmerge.lookahead_on) int regime4h = request.security(syminfo.tickerid, "240", f_regime(), lookahead=barmerge.lookahead_off) int regime1h = request.security(syminfo.tickerid, "60", f_regime(), lookahead=barmerge.lookahead_off) int regime15m = request.security(syminfo.tickerid, "15", f_regime(), lookahead=barmerge.lookahead_off) int regime5m = request.security(syminfo.tickerid, "5", f_regime(), lookahead=barmerge.lookahead_off) int regime1m = request.security(syminfo.tickerid, "1", f_regime(), lookahead=barmerge.lookahead_off) float energy15m = request.security(syminfo.tickerid, "15", f_energyRatio(), lookahead=barmerge.lookahead_off) float energy5m = request.security(syminfo.tickerid, "5", f_energyRatio(), lookahead=barmerge.lookahead_off) float energy1m = request.security(syminfo.tickerid, "1", f_energyRatio(), lookahead=barmerge.lookahead_off) int direction4h = f_direction(regime4h) int direction1h = f_direction(regime1h) int direction15m = f_direction(regime15m) int direction5m = f_direction(regime5m) int direction1m = f_direction(regime1m) bool bullishHtfSupport = not useHtfContext or direction4h == 1 or direction1h == 1 bool bearishHtfSupport = not useHtfContext or direction4h == -1 or direction1h == -1 string htfState = regime15m == 0 ? "CONSOLIDATION" : direction15m == 1 and bullishHtfSupport ? "BULLISH" : direction15m == -1 and bearishHtfSupport ? "BEARISH" : "MIXED" int bullishRegimeVotes = (htfState == "BULLISH" ? 1 : 0) + (not na(regime20dReturnPct) and regime20d == 1 ? 1 : 0) + (not na(regime20bMoveAtr) and regime20b == 1 ? 1 : 0) int bearishRegimeVotes = (htfState == "BEARISH" ? 1 : 0) + (not na(regime20dReturnPct) and regime20d == -1 ? 1 : 0) + (not na(regime20bMoveAtr) and regime20b == -1 ? 1 : 0) bool bullishRegimeMatch = bullishRegimeVotes >= 2 and bearishRegimeVotes == 0 bool bearishRegimeMatch = bearishRegimeVotes >= 2 and bullishRegimeVotes == 0 bool sidewaysRegimeMatch = htfState == "CONSOLIDATION" and not na(regime20dReturnPct) and regime20d == 0 and not na(regime20bMoveAtr) and regime20b == 0 string regimeMatchState = bullishRegimeMatch ? "BULLISH " + str.tostring(bullishRegimeVotes) + "/3" : bearishRegimeMatch ? "BEARISH " + str.tostring(bearishRegimeVotes) + "/3" : sidewaysRegimeMatch ? "SIDEWAYS 3/3" : "NO MATCH" bool bullishCascade = regime15m >= 2 and regime5m >= 1 and regime1m >= 1 bool bearishCascade = regime15m <= -2 and regime5m <= -1 and regime1m <= -1 bool bullishPullback = direction15m == 1 and (direction5m == -1 or direction1m == -1) bool bearishPullback = direction15m == -1 and (direction5m == 1 or direction1m == 1) bool energyReady = not na(energy15m) and not na(energy5m) and not na(energy1m) and energy15m >= quietEnergyRatio and energy5m >= quietEnergyRatio and energy1m >= triggerEnergyRatio bool longReady = htfState == "BULLISH" and bullishCascade and (not useEnergyFilter or energyReady) bool shortReady = htfState == "BEARISH" and bearishCascade and (not useEnergyFilter or energyReady) string decisionState = htfState == "CONSOLIDATION" ? "CONSOLIDATION" : longReady ? "LONG READY" : shortReady ? "SHORT READY" : bullishPullback ? "BULLISH PULLBACK" : bearishPullback ? "BEARISH PULLBACK" : "WAIT" // ───────────────────────────────────────────────────────────────────────────── // Dynamics zone engine // ───────────────────────────────────────────────────────────────────────────── bool fastCrossUp = ta.crossover(controller, fastController) bool slowCrossUp = ta.crossover(controller, slowController) bool midCrossUp = ta.crossover(controller, 50.0) bool fastCrossDown = ta.crossunder(controller, fastController) bool slowCrossDown = ta.crossunder(controller, slowController) bool midCrossDown = ta.crossunder(controller, 50.0) // Each equilibrium tracks its own price origin. This keeps a single Fast, // Slow, or RSI-50 cross anchored to the actual low/high established before // that cross instead of inheriting an older extreme from another reference. // Bullish breaks use bullish origin candles only; bearish breaks use bearish // origin candles only. var array equilibriumBullLows = array.new_float(3, na) var array equilibriumBullHighs = array.new_float(3, na) var array equilibriumBullBars = array.new_int(3, na) var array equilibriumBearHighs = array.new_float(3, na) var array equilibriumBearLows = array.new_float(3, na) var array equilibriumBearBars = array.new_int(3, na) for equilibriumIndex = 0 to 2 float equilibrium = equilibriumIndex == 0 ? fastController : equilibriumIndex == 1 ? slowController : 50.0 if not na(equilibrium) if controller < equilibrium and close > open float trackedBullLow = array.get(equilibriumBullLows, equilibriumIndex) if na(trackedBullLow) or low < trackedBullLow array.set(equilibriumBullLows, equilibriumIndex, low) array.set(equilibriumBullHighs, equilibriumIndex, high) array.set(equilibriumBullBars, equilibriumIndex, bar_index) else if low == trackedBullLow array.set(equilibriumBullHighs, equilibriumIndex, math.max(array.get(equilibriumBullHighs, equilibriumIndex), high)) if controller > equilibrium and close < open float trackedBearHigh = array.get(equilibriumBearHighs, equilibriumIndex) if na(trackedBearHigh) or high > trackedBearHigh array.set(equilibriumBearHighs, equilibriumIndex, high) array.set(equilibriumBearLows, equilibriumIndex, low) array.set(equilibriumBearBars, equilibriumIndex, bar_index) else if high == trackedBearHigh array.set(equilibriumBearLows, equilibriumIndex, math.min(array.get(equilibriumBearLows, equilibriumIndex), low)) float bullOriginLow = na float bullOriginHigh = na int bullOriginBar = na if fastCrossUp and not na(array.get(equilibriumBullLows, 0)) bullOriginLow := array.get(equilibriumBullLows, 0) bullOriginHigh := array.get(equilibriumBullHighs, 0) bullOriginBar := array.get(equilibriumBullBars, 0) if slowCrossUp and not na(array.get(equilibriumBullLows, 1)) and (na(bullOriginLow) or array.get(equilibriumBullLows, 1) < bullOriginLow) bullOriginLow := array.get(equilibriumBullLows, 1) bullOriginHigh := array.get(equilibriumBullHighs, 1) bullOriginBar := array.get(equilibriumBullBars, 1) if midCrossUp and not na(array.get(equilibriumBullLows, 2)) and (na(bullOriginLow) or array.get(equilibriumBullLows, 2) < bullOriginLow) bullOriginLow := array.get(equilibriumBullLows, 2) bullOriginHigh := array.get(equilibriumBullHighs, 2) bullOriginBar := array.get(equilibriumBullBars, 2) float bearOriginHigh = na float bearOriginLow = na int bearOriginBar = na if fastCrossDown and not na(array.get(equilibriumBearHighs, 0)) bearOriginHigh := array.get(equilibriumBearHighs, 0) bearOriginLow := array.get(equilibriumBearLows, 0) bearOriginBar := array.get(equilibriumBearBars, 0) if slowCrossDown and not na(array.get(equilibriumBearHighs, 1)) and (na(bearOriginHigh) or array.get(equilibriumBearHighs, 1) > bearOriginHigh) bearOriginHigh := array.get(equilibriumBearHighs, 1) bearOriginLow := array.get(equilibriumBearLows, 1) bearOriginBar := array.get(equilibriumBearBars, 1) if midCrossDown and not na(array.get(equilibriumBearHighs, 2)) and (na(bearOriginHigh) or array.get(equilibriumBearHighs, 2) > bearOriginHigh) bearOriginHigh := array.get(equilibriumBearHighs, 2) bearOriginLow := array.get(equilibriumBearLows, 2) bearOriginBar := array.get(equilibriumBearBars, 2) bool bullishBreak = barstate.isconfirmed and not na(bullOriginLow) and (fastCrossUp or slowCrossUp or midCrossUp) bool bearishBreak = barstate.isconfirmed and not na(bearOriginHigh) and (fastCrossDown or slowCrossDown or midCrossDown) var array zoneBoxes = array.new() var array zoneDirections = array.new() var array zoneActive = array.new() var bool bullFlushActive = false var bool bullFlushFast = false var bool bullFlushSlow = false var bool bullFlushMid = false var int bullFlushStart = na var float bullFlushLow = na var float bullFlushHigh = na var int bullFlushBar = na var box bullFlushBox = na var bool bearFlushActive = false var bool bearFlushFast = false var bool bearFlushSlow = false var bool bearFlushMid = false var int bearFlushStart = na var float bearFlushHigh = na var float bearFlushLow = na var int bearFlushBar = na var box bearFlushBox = na bool bullFlushExpired = bullFlushActive and not na(bullFlushStart) and bar_index - bullFlushStart > flushBars bool bullFlushLost = bullFlushActive and ((bullFlushFast and controller < fastController) or (bullFlushSlow and controller < slowController) or (bullFlushMid and controller < 50.0)) bool bullFlushInvalid = bullFlushActive and not na(bullFlushLow) and close < bullFlushLow if bullFlushExpired or bullFlushLost or bullFlushInvalid or bearishBreak bullFlushActive := false bullFlushBox := na bool bearFlushExpired = bearFlushActive and not na(bearFlushStart) and bar_index - bearFlushStart > flushBars bool bearFlushLost = bearFlushActive and ((bearFlushFast and controller > fastController) or (bearFlushSlow and controller > slowController) or (bearFlushMid and controller > 50.0)) bool bearFlushInvalid = bearFlushActive and not na(bearFlushHigh) and close > bearFlushHigh if bearFlushExpired or bearFlushLost or bearFlushInvalid or bullishBreak bearFlushActive := false bearFlushBox := na if array.size(zoneBoxes) > 0 for zoneIndex = array.size(zoneBoxes) - 1 to 0 if array.get(zoneActive, zoneIndex) box zoneBox = array.get(zoneBoxes, zoneIndex) int zoneDirection = array.get(zoneDirections, zoneIndex) bool invalidated = zoneDirection == 1 ? close < box.get_bottom(zoneBox) : close > box.get_top(zoneBox) if invalidated // Pine box IDs cannot be compared with ==. Identify each box // by its position in the stored box array instead. if not na(bullFlushBox) and array.indexof(zoneBoxes, bullFlushBox) == zoneIndex bullFlushBox := na if not na(bearFlushBox) and array.indexof(zoneBoxes, bearFlushBox) == zoneIndex bearFlushBox := na if deleteInvalidatedZones box.delete(zoneBox) array.remove(zoneBoxes, zoneIndex) array.remove(zoneDirections, zoneIndex) array.remove(zoneActive, zoneIndex) else box.set_extend(zoneBox, extend.none) box.set_right(zoneBox, bar_index) box.set_bgcolor(zoneBox, color.new(color.gray, 94)) array.set(zoneActive, zoneIndex, false) // Medium Volume Breaks are individual confirmed crosses of the Fast // equilibrium, Slow equilibrium, or RSI 50. The zone uses the complete // high-to-low range of the price candle that established the pre-cross extreme. if bullishBreak and showZones and showMediumVolumeBreakZones bool mediumBullQualified = f_zoneFitsAtr(bullOriginHigh, bullOriginLow, zoneAtr) if mediumBullQualified and not na(bullOriginBar) box mediumBullBox = box.new(bullOriginBar, bullOriginHigh, bar_index, bullOriginLow, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(bullishZoneColor, normalZoneTransparency), force_overlay=true) array.push(zoneBoxes, mediumBullBox) array.push(zoneDirections, 1) array.push(zoneActive, true) if bearishBreak and showZones and showMediumVolumeBreakZones bool mediumBearQualified = f_zoneFitsAtr(bearOriginHigh, bearOriginLow, zoneAtr) if mediumBearQualified and not na(bearOriginBar) box mediumBearBox = box.new(bearOriginBar, bearOriginHigh, bar_index, bearOriginLow, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(bearishZoneColor, normalZoneTransparency), force_overlay=true) array.push(zoneBoxes, mediumBearBox) array.push(zoneDirections, -1) array.push(zoneActive, true) bool bullishTripleBreak = false bool bearishTripleBreak = false if bullishBreak if not bullFlushActive bullFlushActive := true bullFlushFast := false bullFlushSlow := false bullFlushMid := false bullFlushStart := bar_index bullFlushLow := bullOriginLow bullFlushHigh := bullOriginHigh bullFlushBar := bullOriginBar bullFlushBox := na else if not na(bullOriginLow) and bullOriginLow < bullFlushLow bullFlushLow := bullOriginLow bullFlushHigh := bullOriginHigh bullFlushBar := bullOriginBar bool wasTriple = bullFlushFast and bullFlushSlow and bullFlushMid bullFlushFast := bullFlushFast or fastCrossUp bullFlushSlow := bullFlushSlow or slowCrossUp bullFlushMid := bullFlushMid or midCrossUp bool isTriple = bullFlushFast and bullFlushSlow and bullFlushMid bullishTripleBreak := isTriple and not wasTriple bool qualified = f_zoneFitsAtr(bullFlushHigh, bullFlushLow, zoneAtr) if not na(bullFlushBox) and not qualified int existingIndex = array.indexof(zoneBoxes, bullFlushBox) if existingIndex >= 0 array.remove(zoneBoxes, existingIndex) array.remove(zoneDirections, existingIndex) array.remove(zoneActive, existingIndex) box.delete(bullFlushBox) bullFlushBox := na if na(bullFlushBox) and showZones and showHighVolumeBreakZones and isTriple and qualified and not na(bullFlushLow) and not na(bullFlushHigh) and not na(bullFlushBar) bullFlushBox := box.new(bullFlushBar, bullFlushHigh, bar_index, bullFlushLow, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(highVolumeBreakColor, highVolumeBreakTransparency), force_overlay=true) array.push(zoneBoxes, bullFlushBox) array.push(zoneDirections, 1) array.push(zoneActive, true) if not na(bullFlushBox) box.set_left(bullFlushBox, bullFlushBar) box.set_top(bullFlushBox, bullFlushHigh) box.set_bottom(bullFlushBox, bullFlushLow) if isTriple box.set_bgcolor(bullFlushBox, color.new(highVolumeBreakColor, highVolumeBreakTransparency)) if bearishBreak if not bearFlushActive bearFlushActive := true bearFlushFast := false bearFlushSlow := false bearFlushMid := false bearFlushStart := bar_index bearFlushHigh := bearOriginHigh bearFlushLow := bearOriginLow bearFlushBar := bearOriginBar bearFlushBox := na else if not na(bearOriginHigh) and bearOriginHigh > bearFlushHigh bearFlushHigh := bearOriginHigh bearFlushLow := bearOriginLow bearFlushBar := bearOriginBar bool wasTriple = bearFlushFast and bearFlushSlow and bearFlushMid bearFlushFast := bearFlushFast or fastCrossDown bearFlushSlow := bearFlushSlow or slowCrossDown bearFlushMid := bearFlushMid or midCrossDown bool isTriple = bearFlushFast and bearFlushSlow and bearFlushMid bearishTripleBreak := isTriple and not wasTriple bool qualified = f_zoneFitsAtr(bearFlushHigh, bearFlushLow, zoneAtr) if not na(bearFlushBox) and not qualified int existingIndex = array.indexof(zoneBoxes, bearFlushBox) if existingIndex >= 0 array.remove(zoneBoxes, existingIndex) array.remove(zoneDirections, existingIndex) array.remove(zoneActive, existingIndex) box.delete(bearFlushBox) bearFlushBox := na if na(bearFlushBox) and showZones and showHighVolumeBreakZones and isTriple and qualified and not na(bearFlushHigh) and not na(bearFlushLow) and not na(bearFlushBar) bearFlushBox := box.new(bearFlushBar, bearFlushHigh, bar_index, bearFlushLow, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(highVolumeBreakColor, highVolumeBreakTransparency), force_overlay=true) array.push(zoneBoxes, bearFlushBox) array.push(zoneDirections, -1) array.push(zoneActive, true) if not na(bearFlushBox) box.set_left(bearFlushBox, bearFlushBar) box.set_top(bearFlushBox, bearFlushHigh) box.set_bottom(bearFlushBox, bearFlushLow) if isTriple box.set_bgcolor(bearFlushBox, color.new(highVolumeBreakColor, highVolumeBreakTransparency)) // Reset each side only after the current bar's cross has consumed its origin. for equilibriumIndex = 0 to 2 float equilibrium = equilibriumIndex == 0 ? fastController : equilibriumIndex == 1 ? slowController : 50.0 if not na(equilibrium) if controller >= equilibrium array.set(equilibriumBullLows, equilibriumIndex, na) array.set(equilibriumBullHighs, equilibriumIndex, na) array.set(equilibriumBullBars, equilibriumIndex, na) if controller <= equilibrium array.set(equilibriumBearHighs, equilibriumIndex, na) array.set(equilibriumBearLows, equilibriumIndex, na) array.set(equilibriumBearBars, equilibriumIndex, na) bool priceInsideBullishZone = false bool priceInsideBearishZone = false if array.size(zoneBoxes) > 0 for contactIndex = 0 to array.size(zoneBoxes) - 1 if array.get(zoneActive, contactIndex) box contactZone = array.get(zoneBoxes, contactIndex) if high >= box.get_bottom(contactZone) and low <= box.get_top(contactZone) int contactDirection = array.get(zoneDirections, contactIndex) if contactDirection == 1 priceInsideBullishZone := true else priceInsideBearishZone := true // ───────────────────────────────────────────────────────────────────────────── // OHLCV Volume Pressure proxy // ───────────────────────────────────────────────────────────────────────────── float candleRange = math.max(high - low, syminfo.mintick) float candleBodyRatio = math.abs(close - open) / candleRange float upperWickRatio = (high - math.max(open, close)) / candleRange float lowerWickRatio = (math.min(open, close) - low) / candleRange float closeLocation = (2.0 * close - high - low) / candleRange float bodyDirection = (close - open) / candleRange float wickBias = lowerWickRatio - upperWickRatio float averageVolume = ta.sma(volume, pressureVolumeLength) bool hasUsableVolume = not na(volume) and not na(averageVolume) and averageVolume > 0 float relativeVolume = hasUsableVolume ? volume / averageVolume : na float pressureRaw = hasUsableVolume ? relativeVolume * (0.50 * closeLocation + 0.30 * bodyDirection + 0.20 * wickBias) : na float volumePressure = not na(pressureRaw) ? pressureRaw / (1.0 + math.abs(pressureRaw)) : na // Independent absorption PROXY zones. These are pressure/wick/volume patterns, // NOT verified exchange-level absorption. The same ATR rule applies to both. bool bullishAbsorptionProxy = barstate.isconfirmed and hasUsableVolume and relativeVolume >= pressureMinRelativeVolume and candleBodyRatio <= pressureMaxBodyRatio and lowerWickRatio >= pressureMinWickRatio and volumePressure >= pressureThreshold bool bearishAbsorptionProxy = barstate.isconfirmed and hasUsableVolume and relativeVolume >= pressureMinRelativeVolume and candleBodyRatio <= pressureMaxBodyRatio and upperWickRatio >= pressureMinWickRatio and volumePressure <= -pressureThreshold bool absorptionAtrQualified = f_zoneFitsAtr(high, low, zoneAtr) if showZones and showAbsorptionZones and absorptionAtrQualified if bullishAbsorptionProxy box absorptionBullBox = box.new(bar_index, high, bar_index, low, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(bullishAbsorptionColor, absorptionZoneTransparency), force_overlay=true) array.push(zoneBoxes, absorptionBullBox) array.push(zoneDirections, 1) array.push(zoneActive, true) if bearishAbsorptionProxy box absorptionBearBox = box.new(bar_index, high, bar_index, low, xloc=xloc.bar_index, extend=extend.right, border_color=na, bgcolor=color.new(bearishAbsorptionColor, absorptionZoneTransparency), force_overlay=true) array.push(zoneBoxes, absorptionBearBox) array.push(zoneDirections, -1) array.push(zoneActive, true) // Keep all active box arrays synchronized, including newly made proxy zones. while array.size(zoneBoxes) > maxZones // The oldest stored zone sits at index 0 until it is shifted out. if not na(bullFlushBox) and array.indexof(zoneBoxes, bullFlushBox) == 0 bullFlushBox := na if not na(bearFlushBox) and array.indexof(zoneBoxes, bearFlushBox) == 0 bearFlushBox := na box.delete(array.shift(zoneBoxes)) array.shift(zoneDirections) array.shift(zoneActive) bool bullishPressureLocationValid = not pressureRequiresZone or priceInsideBullishZone bool bearishPressureLocationValid = not pressureRequiresZone or priceInsideBearishZone bool bullishPressure = enablePressure and bullishPressureLocationValid and bullishAbsorptionProxy bool bearishPressure = enablePressure and bearishPressureLocationValid and bearishAbsorptionProxy // ───────────────────────────────────────────────────────────────────────────── // Decisions, visuals, HUD, and alerts // ───────────────────────────────────────────────────────────────────────────── bool bullishDecisionSetup = priceInsideBullishZone and controller <= controllerLower and (not pressureConfirmsDecisions or not enablePressure or bullishPressure) bool bearishDecisionSetup = priceInsideBearishZone and controller >= controllerUpper and (not pressureConfirmsDecisions or not enablePressure or bearishPressure) bool bullishDecision = barstate.isconfirmed and bullishDecisionSetup and not bullishDecisionSetup[1] bool bearishDecision = barstate.isconfirmed and bearishDecisionSetup and not bearishDecisionSetup[1] color controllerColor = controller >= controllerBasis ? color.lime : color.red color fastColor = controller >= fastController ? color.rgb(45, 95, 220) : color.rgb(190, 45, 45) color slowColor = controller >= slowController ? color.navy : color.maroon upperPlot = plot(showBollinger ? controllerUpper : na, "Upper Bollinger Band", color=color.new(color.blue, 55), linewidth=1) lowerPlot = plot(showBollinger ? controllerLower : na, "Lower Bollinger Band", color=color.new(color.blue, 55), linewidth=1) plot(showFast ? fastController : na, "Liquidity Equilibrium", color=fastColor, linewidth=2) plot(showSlow ? slowController : na, "Market Momentum", color=slowColor, linewidth=2) plot(controller, "RSI Controller", color=controllerColor, linewidth=2) hline(70, "Supply", color=color.red, linestyle=hline.style_dotted) hline(50, "Equilibrium", color=color.gray, linestyle=hline.style_dotted) hline(30, "Demand", color=color.blue, linestyle=hline.style_dotted) plotshape(showPressureDots and bullishPressure, title="Bullish Volume Pressure", style=shape.circle, location=location.belowbar, color=color.aqua, size=size.tiny, force_overlay=true) plotshape(showPressureDots and bearishPressure, title="Bearish Volume Pressure", style=shape.circle, location=location.abovebar, color=color.orange, size=size.tiny, force_overlay=true) color htfBackground = htfState == "BULLISH" ? color.rgb(0, 105, 65) : htfState == "BEARISH" ? color.rgb(150, 25, 25) : color.rgb(75, 75, 75) color htfText = color.white color decisionBackground = decisionState == "LONG READY" ? color.rgb(0, 125, 65) : decisionState == "SHORT READY" ? color.rgb(180, 20, 20) : decisionState == "BULLISH PULLBACK" ? color.rgb(35, 70, 135) : decisionState == "BEARISH PULLBACK" ? color.rgb(155, 85, 25) : color.rgb(75, 75, 75) color decisionText = color.white string energyState = energyReady ? "READY" : "WAIT" string htfTooltip = "4H: " + f_regimeName(regime4h) + " | 1H: " + f_regimeName(regime1h) + " | 15M: " + f_regimeName(regime15m) string decisionTooltip = "15M: " + f_regimeName(regime15m) + " | 5M: " + f_regimeName(regime5m) + " | 1M: " + f_regimeName(regime1m) + "\nEnergy: " + energyState + "\nVolume Pressure: " + (enablePressure ? "ON" : "OFF") string regime20dName = regime20d == 1 ? "BULLISH" : regime20d == -1 ? "BEARISH" : "SIDEWAYS" color regime20dBackground = regime20d == 1 ? color.rgb(0, 105, 65) : regime20d == -1 ? color.rgb(150, 25, 25) : color.rgb(75, 75, 75) string regime20bName = regime20b == 1 ? "BULLISH" : regime20b == -1 ? "BEARISH" : "SIDEWAYS" color regime20bBackground = regime20b == 1 ? color.rgb(0, 105, 65) : regime20b == -1 ? color.rgb(150, 25, 25) : color.rgb(75, 75, 75) color regimeMatchBackground = bullishRegimeMatch ? color.rgb(0, 150, 80) : bearishRegimeMatch ? color.rgb(195, 35, 35) : sidewaysRegimeMatch ? color.rgb(185, 145, 25) : color.rgb(75, 75, 75) string regime20dTooltip = na(regime20dReturnPct) ? "Waiting for the first completed 20-day block comparison" : "Last 20-day block return: " + str.tostring(regime20dReturnPct, "#.##") + "% | Threshold: +/-" + str.tostring(regime20dThresholdPct, "#.##") + "% | Reassessed every " + str.tostring(regime20dDays) + " trading days" string regime20bTooltip = na(regime20bMoveAtr) ? "Waiting for chart bars" : "Current chart: " + str.tostring(regime20bBars) + "-bar move " + str.tostring(regime20bMoveAtr, "#.##") + " ATR | Direction threshold +/-" + str.tostring(regime20bThresholdAtr, "#.##") + " ATR" string regimeMatchTooltip = "HTF: " + htfState + " | 20D: " + regime20dName + " | 20B: " + regime20bName + "\nThe denominator is always three reads. Two matching directional reads with no opposite direction make a match; sideways occupies the remaining slot. Existing decisions and alerts are unchanged." var table decisionTable = table.new(position.bottom_right, 2, 5, frame_width=0, border_width=0) if barstate.islast if showDecisionHud table.cell(decisionTable, 0, 0, "HTF", text_color=color.white, bgcolor=color.rgb(25, 25, 25), text_size=size.tiny, tooltip=htfTooltip) table.cell(decisionTable, 1, 0, htfState, text_color=htfText, bgcolor=htfBackground, text_size=size.small, tooltip=htfTooltip) table.cell(decisionTable, 0, 1, "20D", text_color=color.white, bgcolor=color.rgb(25, 25, 25), text_size=size.tiny, tooltip=regime20dTooltip) table.cell(decisionTable, 1, 1, regime20dName, text_color=color.white, bgcolor=regime20dBackground, text_size=size.small, tooltip=regime20dTooltip) table.cell(decisionTable, 0, 2, "20B", text_color=color.white, bgcolor=color.rgb(25, 25, 25), text_size=size.tiny, tooltip=regime20bTooltip) table.cell(decisionTable, 1, 2, regime20bName, text_color=color.white, bgcolor=regime20bBackground, text_size=size.small, tooltip=regime20bTooltip) table.cell(decisionTable, 0, 3, "MATCH", text_color=color.white, bgcolor=color.rgb(25, 25, 25), text_size=size.tiny, tooltip=regimeMatchTooltip) table.cell(decisionTable, 1, 3, regimeMatchState, text_color=color.white, bgcolor=regimeMatchBackground, text_size=size.small, tooltip=regimeMatchTooltip) table.cell(decisionTable, 0, 4, "STATE", text_color=decisionText, bgcolor=color.rgb(25, 25, 25), text_size=size.tiny, tooltip=decisionTooltip) table.cell(decisionTable, 1, 4, decisionState, text_color=decisionText, bgcolor=decisionBackground, text_size=size.small, tooltip=decisionTooltip) else for row = 0 to 4 table.cell(decisionTable, 0, row, "", bgcolor=color.new(color.black, 100)) table.cell(decisionTable, 1, row, "", bgcolor=color.new(color.black, 100)) alertcondition(barstate.isconfirmed and bullishBreak, "Bullish Dynamics Break", "{{ticker}} {{interval}} bullish Dynamics Controller break.") alertcondition(barstate.isconfirmed and bearishBreak, "Bearish Dynamics Break", "{{ticker}} {{interval}} bearish Dynamics Controller break.") alertcondition(barstate.isconfirmed and bullishTripleBreak, "Bullish Triple Controller Break", "{{ticker}} {{interval}} bullish Triple Controller Break.") alertcondition(barstate.isconfirmed and bearishTripleBreak, "Bearish Triple Controller Break", "{{ticker}} {{interval}} bearish Triple Controller Break.") alertcondition(bullishDecision, "Bullish Zone Decision", "{{ticker}} {{interval}} bullish decision at an active Dynamics zone.") alertcondition(bearishDecision, "Bearish Zone Decision", "{{ticker}} {{interval}} bearish decision at an active Dynamics zone.") // ═════════════════════════════════════════════════════════════════════════════ // QUARTERLY OPENS — dotted historical price levels, first Sunday of each // quarter-start month at the configured New York evening-opening hour. // Preserves the specific first-Sunday convention from the supplied script. // ═════════════════════════════════════════════════════════════════════════════ string GROUP_QUARTER_OPENS = "Quarterly Opens" bool showQ1 = input.bool(true, "Q1 January", group=GROUP_QUARTER_OPENS) bool showQ2 = input.bool(true, "Q2 April", group=GROUP_QUARTER_OPENS) bool showQ3 = input.bool(true, "Q3 July", group=GROUP_QUARTER_OPENS) bool showQ4 = input.bool(true, "Q4 October", group=GROUP_QUARTER_OPENS) color q1Color = input.color(color.aqua, "Q1 Color", group=GROUP_QUARTER_OPENS) color q2Color = input.color(color.lime, "Q2 Color", group=GROUP_QUARTER_OPENS) color q3Color = input.color(color.orange, "Q3 Color", group=GROUP_QUARTER_OPENS) color q4Color = input.color(color.fuchsia, "Q4 Color", group=GROUP_QUARTER_OPENS) string GROUP_QUARTER_DISPLAY = "Quarterly Opens / Display" int quarterlyMaxLevels = input.int(12, "Historical Levels", minval=1, maxval=100, group=GROUP_QUARTER_DISPLAY) int quarterlyLineWidth = input.int(2, "Line Width", minval=1, maxval=5, group=GROUP_QUARTER_DISPLAY) bool quarterlyShowLabels = input.bool(true, "Show Price Labels", group=GROUP_QUARTER_DISPLAY) int quarterlySundayHour = input.int(18, "Sunday Open Hour (NY)", minval=0, maxval=23, group=GROUP_QUARTER_DISPLAY) string LEVELS_TZ = "America/New_York" f_firstQuarterSunday(int y, int m) => int firstDay = timestamp(LEVELS_TZ, y, m, 1, 0, 0) int weekday = dayofweek(firstDay, LEVELS_TZ) int offset = (dayofweek.sunday - weekday + 7) % 7 timestamp(LEVELS_TZ, y, m, 1 + offset, quarterlySundayHour, 0) f_latestQuarterEvent() => int y = year(time, LEVELS_TZ) int latest = na int latestQuarter = 0 int latestYear = 0 for yearOffset = 0 to 1 int candidateYear = y - yearOffset for quarterNumber = 1 to 4 int candidateMonth = (quarterNumber - 1) * 3 + 1 int candidateTime = f_firstQuarterSunday(candidateYear, candidateMonth) if candidateTime <= time and (na(latest) or candidateTime > latest) latest := candidateTime latestQuarter := quarterNumber latestYear := candidateYear [latest, latestQuarter, latestYear] [quarterEventTime, quarterEventNumber, quarterEventYear] = f_latestQuarterEvent() bool newQuarterEvent = not na(quarterEventTime) and (na(quarterEventTime[1]) or quarterEventTime != quarterEventTime[1]) var array quarterlyLines = array.new() var array