market-structure-dashboard-flux-charts.pine
Copy
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fluxchart
//@version=6
indicator("Market Structure Dashboard | Flux Charts", overlay = true, max_boxes_count = 500, max_labels_count = 500, max_bars_back = 1000)
//#region CONSTANTS
// Base colors
GRAY = #787b86
ORANGE = #f97316
YELLOW = #fbbf24
// Trend colors (defaults)
BULL_COLOR = #089981
BEAR_COLOR = #f23645
NEUT_COLOR = #b8b8b8
// Session colors (defaults)
SESSION_ASIAN = #3b82f6
SESSION_LONDON = #22c55e
SESSION_NY = #f97316
SESSION_LUNCH = #6b7280
SESSION_PM = #a855f7
// Dashboard theme colors
BG_DARK_DARK = color.rgb(30, 30, 30)
BG_DARK_LIGHT = color.rgb(245, 245, 245)
BG_HEADER_DARK = color.rgb(30, 30, 40)
BG_HEADER_LIGHT = color.rgb(230, 230, 240)
BG_ROW_DARK = #1a2332
BG_ROW_LIGHT = #e8edf5
TEXT_DARK = color.white
TEXT_LIGHT = color.rgb(30, 30, 30)
// Group Headers
gTF = "Timeframes"
gMsd = "Market Structure Dashboard"
gCtfd = "Current Timeframe Dashboard"
gMcd = "Market Context Dashboard"
gHtf = "HTF Levels Dashboard"
gBias = "Trend Bias Settings"
gVO = "Visual Overlays"
gSettings = "Settings"
//#endregion CONSTANTS
//#region INPUTS
// Line style enum
enum lineStyle
solid = "────"
dashed = "- - -"
dotted = "····"
// Timeframes
bool tf1Enabled = input.bool(true, "", group = gTF, inline = "tf1", display = display.none)
string tf1 = input.timeframe("1", "", group = gTF, inline = "tf1", active = tf1Enabled, display = display.none)
int tf1Weight = input.int(1, "Trend Weight", group = gTF, inline = "tf1", minval = 0, maxval = 10, active = tf1Enabled, display = display.none)
bool tf2Enabled = input.bool(true, "", group = gTF, inline = "tf2", display = display.none)
string tf2 = input.timeframe("5", "", group = gTF, inline = "tf2", active = tf2Enabled, display = display.none)
int tf2Weight = input.int(1, "Trend Weight", group = gTF, inline = "tf2", minval = 0, maxval = 10, active = tf2Enabled, display = display.none)
bool tf3Enabled = input.bool(true, "", group = gTF, inline = "tf3", display = display.none)
string tf3 = input.timeframe("15", "", group = gTF, inline = "tf3", active = tf3Enabled, display = display.none)
int tf3Weight = input.int(2, "Trend Weight", group = gTF, inline = "tf3", minval = 0, maxval = 10, active = tf3Enabled, display = display.none)
bool tf4Enabled = input.bool(true, "", group = gTF, inline = "tf4", display = display.none)
string tf4 = input.timeframe("60", "", group = gTF, inline = "tf4", active = tf4Enabled, display = display.none)
int tf4Weight = input.int(2, "Trend Weight", group = gTF, inline = "tf4", minval = 0, maxval = 10, active = tf4Enabled, display = display.none)
bool tf5Enabled = input.bool(true, "", group = gTF, inline = "tf5", display = display.none)
string tf5 = input.timeframe("240", "", group = gTF, inline = "tf5", active = tf5Enabled, display = display.none)
int tf5Weight = input.int(2, "Trend Weight", group = gTF, inline = "tf5", minval = 0, maxval = 10, active = tf5Enabled, display = display.none)
bool tf6Enabled = input.bool(true, "", group = gTF, inline = "tf6", display = display.none)
string tf6 = input.timeframe("D", "", group = gTF, inline = "tf6", active = tf6Enabled, display = display.none)
int tf6Weight = input.int(3, "Trend Weight", group = gTF, inline = "tf6", minval = 0, maxval = 10, active = tf6Enabled, display = display.none)
bool tf7Enabled = input.bool(true, "", group = gTF, inline = "tf7", display = display.none)
string tf7 = input.timeframe("W", "", group = gTF, inline = "tf7", active = tf7Enabled, display = display.none)
int tf7Weight = input.int(4, "Trend Weight", group = gTF, inline = "tf7", minval = 0, maxval = 10, active = tf7Enabled, display = display.none)
// Market Structure Dashboard
bool showMSD = input.bool(true, "Enable", group = gMsd, display = display.none)
bool showColEma = input.bool(true, "EMA Trend", group = gMsd, inline = "msdCols1", active = showMSD, display = display.none)
bool showColSwing = input.bool(true, "Swing H/L", group = gMsd, inline = "msdCols1", active = showMSD, display = display.none)
bool showColStruct = input.bool(true, "Structure", group = gMsd, inline = "msdCols1", active = showMSD, display = display.none)
bool showColOB = input.bool(true, "Order Block", group = gMsd, inline = "msdCols2", active = showMSD, display = display.none)
bool showColFVG = input.bool(true, "FVG", group = gMsd, inline = "msdCols2", active = showMSD, display = display.none)
// Current Timeframe Dashboard
bool showCurrTF = input.bool(true, "Enable", group = gCtfd, display = display.none)
bool showCurrSwing = input.bool(true, "Swing H/L", group = gCtfd, inline = "currCols", active = showCurrTF, display = display.none)
bool showCurrVol = input.bool(true, "Volume", group = gCtfd, inline = "currCols", active = showCurrTF, display = display.none)
bool showCurrAtr = input.bool(true, "Volatility", group = gCtfd, inline = "currCols", active = showCurrTF, display = display.none)
// Market Context Dashboard
bool showContext = input.bool(true, "Enable", group = gMcd, display = display.none)
bool showSession = input.bool(true, "Session", group = gMcd, inline = "ctxCols", active = showContext, display = display.none)
bool showKillzone = input.bool(true, "Killzone", group = gMcd, inline = "ctxCols", active = showContext, display = display.none)
bool showBias = input.bool(true, "Trend Bias", group = gMcd, inline = "ctxCols", active = showContext, display = display.none)
// HTF Levels Dashboard
bool showHTFLevels = input.bool(false, "Enable", group = gHtf, display = display.none)
bool showPDHL = input.bool(true, "PDH/L", group = gHtf, inline = "htfCols", active = showHTFLevels, display = display.none)
bool showPWHL = input.bool(true, "PWH/L", group = gHtf, inline = "htfCols", active = showHTFLevels, display = display.none)
bool showPMHL = input.bool(false, "PMH/L", group = gHtf, inline = "htfCols", active = showHTFLevels, display = display.none)
// Trend Bias Settings
bool biasUseStruct = input.bool(true, "Structure", group = gBias, inline = "biasFactors1", display = display.none)
bool biasUseOB = input.bool(true, "Order Block", group = gBias, inline = "biasFactors1", display = display.none)
bool biasUseFVG = input.bool(true, "FVG", group = gBias, inline = "biasFactors1", display = display.none)
bool biasUseEMA = input.bool(false, "EMA Trend", group = gBias, inline = "biasFactors2", display = display.none)
bool biasUseSwing = input.bool(true, "Swing H/L", group = gBias, inline = "biasFactors2", display = display.none)
// Settings
int emaLength = input.int(9, "EMA Length", group = gSettings, minval = 1, display = display.none)
int swingLength = input.int(5, "Swing Length", group = gSettings, minval = 1, tooltip = "Used for Swing H/L detection, Order Blocks, and Structure", display = display.none)
int atrAvgLength = input.int(20, "Volatility Lookback", group = gSettings, minval = 1, tooltip = "Period for averaging ATR to determine volatility state", display = display.none)
string distanceMode = input.string("Percent", "Distance Display", group = gSettings, options = ["Percent", "Price", "Pips"], tooltip = "Show distances as percentage, price, or pips (forex)", display = display.none)
string dashboardPos = input.string("Top Right", "Dashboard Position", group = gSettings, options = ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], display = display.none)
string dashboardTheme = input.string("Dark Mode", "Dashboard Colors", group = gSettings, options = ["Dark Mode", "Light Mode"], display = display.none)
color bullColor = input.color(BULL_COLOR, "Trend Colors ", group = gSettings, inline = "trendColors", display = display.none)
color bearColor = input.color(BEAR_COLOR, "", group = gSettings, inline = "trendColors", display = display.none)
color neutColor = input.color(NEUT_COLOR, "", group = gSettings, inline = "trendColors", display = display.none)
color sessAsianColor = input.color(SESSION_ASIAN, "Session Colors ", group = gSettings, inline = "sessionColors", tooltip = "Colors for Asian, London, NY sessions", display = display.none)
color sessLondonColor = input.color(SESSION_LONDON, "", group = gSettings, inline = "sessionColors", display = display.none)
color sessNyColor = input.color(SESSION_NY, "", group = gSettings, inline = "sessionColors", display = display.none)
color kzAsianColor = input.color(SESSION_ASIAN, "Killzone Colors ", group = gSettings, inline = "kzColors", tooltip = "Colors for Asian, London, NY AM, NY Lunch, NY PM killzones", display = display.none)
color kzLondonColor = input.color(SESSION_LONDON, "", group = gSettings, inline = "kzColors", display = display.none)
color kzNyAmColor = input.color(SESSION_NY, "", group = gSettings, inline = "kzColors", display = display.none)
color kzLunchColor = input.color(SESSION_LUNCH, "", group = gSettings, inline = "kzColors", display = display.none)
color kzPmColor = input.color(SESSION_PM, "", group = gSettings, inline = "kzColors", display = display.none)
string tblTextSize = size.small
// Dashboard position
tablePos = switch dashboardPos
"Top Left" => position.top_left
"Top Center" => position.top_center
"Top Right" => position.top_right
"Middle Left" => position.middle_left
"Middle Center" => position.middle_center
"Middle Right" => position.middle_right
"Bottom Left" => position.bottom_left
"Bottom Center" => position.bottom_center
"Bottom Right" => position.bottom_right
// Dashboard theme colors
color bgDark = dashboardTheme == "Dark Mode" ? BG_DARK_DARK : BG_DARK_LIGHT
color bgHeader = dashboardTheme == "Dark Mode" ? BG_HEADER_DARK : BG_HEADER_LIGHT
color bgRow = dashboardTheme == "Dark Mode" ? BG_ROW_DARK : BG_ROW_LIGHT
color textCol = dashboardTheme == "Dark Mode" ? TEXT_DARK : TEXT_LIGHT
// Visual Overlays
bool showOB = input.bool(false, "Order Blocks", group = gVO, inline = "ob", tooltip = "Display Order Block zones on chart", display = display.none)
color obBullColor = input.color(BULL_COLOR, "", group = gVO, inline = "ob", active = showOB, display = display.none)
color obBearColor = input.color(BEAR_COLOR, "", group = gVO, inline = "ob", active = showOB, display = display.none)
int obLookback = input.int(6, "└ Show Last", group = gVO, inline = "obOpts", minval = 1, maxval = 20, active = showOB, tooltip = "Number of recent Order Blocks to display", display = display.none)
int obExtend = input.int(20, "Extend", group = gVO, inline = "obOpts", minval = 1, active = showOB, tooltip = "Bars to extend Order Block zones into the future", display = display.none)
bool showOBLabels = input.bool(false, "└ Labels", group = gVO, active = showOB, tooltip = "Show OB labels inside Order Block zones", display = display.none)
bool showFVG = input.bool(false, "FVGs ", group = gVO, inline = "fvg", tooltip = "Display Fair Value Gap zones on chart", display = display.none)
color fvgBullColor = input.color(BULL_COLOR, "", group = gVO, inline = "fvg", active = showFVG, display = display.none)
color fvgBearColor = input.color(BEAR_COLOR, "", group = gVO, inline = "fvg", active = showFVG, display = display.none)
int fvgLookback = input.int(6, "└ Show Last", group = gVO, inline = "fvgOpts", minval = 1, maxval = 20, active = showFVG, tooltip = "Number of recent FVGs to display", display = display.none)
int fvgExtend = input.int(20, "Extend", group = gVO, inline = "fvgOpts", minval = 1, active = showFVG, tooltip = "Bars to extend FVG zones into the future", display = display.none)
bool showFVGLabels = input.bool(false, "└ Labels", group = gVO, active = showFVG, tooltip = "Show FVG labels inside Fair Value Gap zones", display = display.none)
bool showSwingLabels = input.bool(false, "Swing Labels", group = gVO, inline = "swingLabels", tooltip = "Display HH/HL/LH/LL labels at swing points", display = display.none)
color swingBullColor = input.color(BULL_COLOR, "", group = gVO, inline = "swingLabels", active = showSwingLabels, display = display.none)
color swingBearColor = input.color(BEAR_COLOR, "", group = gVO, inline = "swingLabels", active = showSwingLabels, display = display.none)
int swingLabelLookback = input.int(10, "└ Show Last", group = gVO, minval = 1, maxval = 500, active = showSwingLabels, tooltip = "Number of recent swing labels to display", display = display.none)
bool showSwingLines = input.bool(false, "Swing Lines ", group = gVO, inline = "swingLines", tooltip = "Draw horizontal lines at recent swing high/low levels", display = display.none)
color swingLineLowColor = input.color(BULL_COLOR, "", group = gVO, inline = "swingLines", active = showSwingLines, display = display.none)
color swingLineHighColor = input.color(BEAR_COLOR, "", group = gVO, inline = "swingLines", active = showSwingLines, display = display.none)
lineStyle swingLineStyle = input.enum(lineStyle.dashed, "", group = gVO, inline = "swingLines", active = showSwingLines, display = display.none)
int swingLinesExtend = input.int(20, "└ Extend", group = gVO, minval = 1, active = showSwingLines, tooltip = "Bars to extend swing lines into the future", display = display.none)
bool plotPDHL = input.bool(false, "Previous Day High/Low ", group = gVO, inline = "pdhl", display = display.none)
color pdlColor = input.color(BULL_COLOR, "", group = gVO, inline = "pdhl", active = plotPDHL, display = display.none)
color pdhColor = input.color(BEAR_COLOR, "", group = gVO, inline = "pdhl", active = plotPDHL, display = display.none)
lineStyle pdhlLineStyle = input.enum(lineStyle.dashed, "", group = gVO, inline = "pdhl", active = plotPDHL, display = display.none)
bool plotPWHL = input.bool(false, "Previous Week High/Low ", group = gVO, inline = "pwhl", display = display.none)
color pwlColor = input.color(BULL_COLOR, "", group = gVO, inline = "pwhl", active = plotPWHL, display = display.none)
color pwhColor = input.color(BEAR_COLOR, "", group = gVO, inline = "pwhl", active = plotPWHL, display = display.none)
lineStyle pwhlLineStyle = input.enum(lineStyle.dashed, "", group = gVO, inline = "pwhl", active = plotPWHL, display = display.none)
bool plotPMHL = input.bool(false, "Previous Month High/Low", group = gVO, inline = "pmhl", tooltip = "Plot previous month high and low levels", display = display.none)
color pmlColor = input.color(BULL_COLOR, "", group = gVO, inline = "pmhl", active = plotPMHL, display = display.none)
color pmhColor = input.color(BEAR_COLOR, "", group = gVO, inline = "pmhl", active = plotPMHL, display = display.none)
lineStyle pmhlLineStyle = input.enum(lineStyle.dashed, "", group = gVO, inline = "pmhl", active = plotPMHL, display = display.none)
int htfLevelExtend = input.int(20, "└ HTF Extend", group = gVO, minval = 1, active = plotPDHL or plotPWHL or plotPMHL, tooltip = "Bars to extend HTF level lines into the future. Labels auto-combine when levels are close.", display = display.none)
bool showEMA = input.bool(false, "EMA", group = gVO, inline = "ema", display = display.none)
color emaColor = input.color(ORANGE, "", group = gVO, inline = "ema", active = showEMA, display = display.none)
//#endregion INPUTS
//#region GLOBALS
atrLength = 14
emaValue = ta.ema(close, emaLength)
pivotHigh = ta.pivothigh(high, swingLength, swingLength)
pivotLow = ta.pivotlow(low, swingLength, swingLength)
_green = close > open or close > close[1]
_red = close < open or close < close[1]
bullFVG = low[1] > high[3] and _green[2] and low[1] < high[2] and low[2] < high[3]
bearFVG = high[1] < low[3] and _red[2] and high[1] > low[2] and high[2] > low[3]
//#endregion GLOBALS
//#region TYPES
type ZoneBlock
int dir
float top
float bottom
int barIdx
type SwingData
float prevH
float currH
float prevL
float currL
type PivotData
float lastPH
int lastPHBar
float lastPL
int lastPLBar
type SwingLineData
line highLine
line lowLine
int lastHBar
int lastLBar
type CandleSearch
float price
float otherPrice
int barIdx
int barsBack
type ZoneResult
int dir
float top
float bottom
int barIdx
bool isNew
type SwingLabel
int x
float y
string txt
bool isHigh
color clr
color textClr
//#endregion TYPES
//#region FUNCTIONS
getLineStyle(lineStyle ls) =>
switch ls
lineStyle.solid => line.style_solid
lineStyle.dashed => line.style_dashed
lineStyle.dotted => line.style_dotted
pct(value, base) => (value / base) * 100
priceToPct(priceVal) => (priceVal / close) * 100
getPipSize() => syminfo.mintick * (str.contains(syminfo.ticker, "JPY") ? 100 : 10)
priceToPips(priceVal) => priceVal / getPipSize()
isBreakAbove(level) => not na(level) and close > level and close[1] <= level
isBreakBelow(level) => not na(level) and close < level and close[1] >= level
calcTrend() =>
trendDist = close - emaValue
dir = close > emaValue ? 1 : -1
[dir, trendDist]
calcSwingHL() =>
var SwingData swing = SwingData.new()
var bool lowBroken = false
var bool highBroken = false
newSwingH = not na(pivotHigh)
newSwingL = not na(pivotLow)
if newSwingH
swing.prevH := swing.currH
swing.currH := pivotHigh
highBroken := false // Reset on new swing
if newSwingL
swing.prevL := swing.currL
swing.currL := pivotLow
lowBroken := false // Reset on new swing
// Track breaks
if low < swing.currL
lowBroken := true
if high > swing.currH
highBroken := true
// Detect reclaims (broke and came back inside)
reclaimedLow = lowBroken and close > swing.currL
reclaimedHigh = highBroken and close < swing.currH
var int swingBias = 0
if newSwingH
swingBias := 1
if newSwingL
swingBias := -1
[swing.currH, swing.currL, swingBias, newSwingH, newSwingL, swing.prevH, swing.prevL, reclaimedLow, reclaimedHigh]
// 3. Market Structure - tracks last 3 HH/HL/H/LL sequence
calcStructure() =>
var SwingData swing = SwingData.new()
// Track last 3 structure labels
var string struct1 = "--"
var string struct2 = "--"
var string struct3 = "--"
var int highType = 0
var int lowType = 0
if not na(pivotHigh)
swing.prevH := swing.currH
swing.currH := pivotHigh
if not na(swing.prevH)
highType := swing.currH > swing.prevH ? 1 : -1
struct3 := struct2
struct2 := struct1
struct1 := highType > 0 ? "HH" : "LH"
if not na(pivotLow)
swing.prevL := swing.currL
swing.currL := pivotLow
if not na(swing.prevL)
lowType := swing.currL > swing.prevL ? 1 : -1
struct3 := struct2
struct2 := struct1
struct1 := lowType > 0 ? "HL" : "LL"
structureBias = 0
if highType == 1 and lowType == 1
structureBias := 1
else if highType == -1 and lowType == -1
structureBias := -1
[highType, lowType, structureBias, struct1, struct2, struct3]
findNearestZone(array<ZoneBlock> zones) =>
nearestBullDist = 100000.0
nearestBearDist = 100000.0
zoneCount = zones.size()
if zoneCount > 0
for i = 0 to zoneCount - 1
zone = zones.get(i)
if zone.dir == 1
dist = zone.top - close
if math.abs(dist) < math.abs(nearestBullDist)
nearestBullDist := dist
else
dist = zone.bottom - close
if math.abs(dist) < math.abs(nearestBearDist)
nearestBearDist := dist
nearestDir = 0
nearestDist = 0.0
if math.abs(nearestBullDist) < math.abs(nearestBearDist) and math.abs(nearestBullDist) < 100000.0
nearestDir := 1
nearestDist := nearestBullDist
else if math.abs(nearestBearDist) < 100000.0
nearestDir := -1
nearestDist := nearestBearDist
[nearestDir, nearestDist]
mitigateOBs(array<ZoneBlock> obs) =>
obCount = obs.size()
if obCount > 0
for i = obCount - 1 to 0
ob = obs.get(i)
if ob.dir == 1 and close < ob.bottom
obs.remove(i)
else if ob.dir == -1 and close > ob.top
obs.remove(i)
mitigateFVGs(array<ZoneBlock> fvgs) =>
fvgCount = fvgs.size()
if fvgCount > 0
for i = fvgCount - 1 to 0
fvg = fvgs.get(i)
if fvg.dir == 1 and low < fvg.bottom
fvgs.remove(i)
else if fvg.dir == -1 and high > fvg.top
fvgs.remove(i)
limitArraySize(array<ZoneBlock> arr, int maxSize) =>
if arr.size() > maxSize
arr.pop()
limitSwingLabels(array<SwingLabel> arr, int maxSize) =>
if arr.size() > maxSize
arr.shift()
findLowestCandle(int barsBack) =>
search = CandleSearch.new(low, high, bar_index, barsBack)
for i = 0 to math.max(0, math.min(barsBack, 50))
if low[i] < search.price
search.price := low[i]
search.otherPrice := high[i]
search.barIdx := bar_index - i
search
findHighestCandle(int barsBack) =>
search = CandleSearch.new(high, low, bar_index, barsBack)
for i = 0 to math.max(0, math.min(barsBack, 50))
if high[i] > search.price
search.price := high[i]
search.otherPrice := low[i]
search.barIdx := bar_index - i
search
hasOverlappingOB(int dir, float top, float bottom, array<ZoneBlock> obs) =>
overlaps = false
for ob in obs
if ob.dir == dir and top >= ob.bottom and bottom <= ob.top
overlaps := true
break
overlaps
detectOB(int dir, int pivotBar, array<ZoneBlock> obs, int maxOBs, ZoneResult result) =>
barsBack = bar_index - pivotBar - 1
search = dir > 0 ? findLowestCandle(barsBack) : findHighestCandle(barsBack)
newTop = dir > 0 ? search.otherPrice : search.price
newBottom = dir > 0 ? search.price : search.otherPrice
if not hasOverlappingOB(dir, newTop, newBottom, obs)
result.dir := dir
result.top := newTop
result.bottom := newBottom
result.barIdx := search.barIdx
result.isNew := true
obs.unshift(ZoneBlock.new(dir, newTop, newBottom, search.barIdx))
limitArraySize(obs, maxOBs)
detectOBSimple(int dir, int pivotBar, array<ZoneBlock> obs, int maxOBs) =>
barsBack = bar_index - pivotBar - 1
search = dir > 0 ? findLowestCandle(barsBack) : findHighestCandle(barsBack)
top = dir > 0 ? search.otherPrice : search.price
bottom = dir > 0 ? search.price : search.otherPrice
if not hasOverlappingOB(dir, top, bottom, obs)
obs.unshift(ZoneBlock.new(dir, top, bottom, search.barIdx))
limitArraySize(obs, maxOBs)
trackPivotBreaks(PivotData pivot) =>
bullBreak = false
bearBreak = false
bullBar = 0
bearBar = 0
// Track new pivots
if not na(pivotHigh)
pivot.lastPH := pivotHigh
pivot.lastPHBar := bar_index - swingLength
if not na(pivotLow)
pivot.lastPL := pivotLow
pivot.lastPLBar := bar_index - swingLength
if isBreakAbove(pivot.lastPH) and not na(pivot.lastPHBar)
bullBreak := true
bullBar := pivot.lastPHBar
pivot.lastPH := na
pivot.lastPHBar := na
if isBreakBelow(pivot.lastPL) and not na(pivot.lastPLBar)
bearBreak := true
bearBar := pivot.lastPLBar
pivot.lastPL := na
pivot.lastPLBar := na
[bullBreak, bullBar, bearBreak, bearBar]
calcOrderBlock(maxOBs) =>
var PivotData pivot = PivotData.new()
var array<ZoneBlock> obs = array.new<ZoneBlock>()
var ZoneResult result = ZoneResult.new()
[bullBreak, bullBar, bearBreak, bearBar] = trackPivotBreaks(pivot)
result.isNew := false
if bullBreak
detectOB(1, bullBar, obs, maxOBs, result)
if bearBreak
detectOB(-1, bearBar, obs, maxOBs, result)
mitigateOBs(obs)
[nearestDir, nearestDist] = findNearestZone(obs)
[result.dir, result.top, result.bottom, result.isNew, nearestDir, nearestDist]
detectFVGSignal() =>
fvgDir = 0
fvgTop = 0.0
fvgBottom = 0.0
fvgBar = 0
if bullFVG
fvgDir := 1
fvgTop := low[1]
fvgBottom := high[3]
fvgBar := bar_index - 3
// Bear FVG: gap down
if bearFVG
fvgDir := -1
fvgTop := low[3]
fvgBottom := high[1]
fvgBar := bar_index - 3
[fvgDir, fvgTop, fvgBottom, fvgBar]
calcFVG(maxFVGs) =>
var array<ZoneBlock> fvgs = array.new<ZoneBlock>()
[fvgDir, fvgTop, fvgBottom, fvgBar] = detectFVGSignal()
if fvgDir != 0
fvgs.unshift(ZoneBlock.new(fvgDir, fvgTop, fvgBottom, fvgBar))
limitArraySize(fvgs, maxFVGs)
mitigateFVGs(fvgs)
[nearestDir, nearestDist] = findNearestZone(fvgs)
[fvgDir, fvgTop, fvgBottom, fvgDir != 0, nearestDir, nearestDist]
[currSwingH, currSwingL, _currSwingBias, currNewSwingH, currNewSwingL, currPrevSwingH, currPrevSwingL, currReclaimLow, currReclaimHigh ] = calcSwingHL()
[currObDir, currObTop, currObBottom, currNewOB, _currObNearDir, _currObNearDist ] = calcOrderBlock(obLookback)
[currFvgDir, currFvgTop, currFvgBottom, currNewFVG, _currFvgNearDir, _currFvgNearDist] = calcFVG(fvgLookback)
var array<ZoneBlock> currTfOBs = array.new<ZoneBlock>()
var array<box> obBoxes = array.new<box>()
var PivotData currTfPivot = PivotData.new()
[currBullBreak, currBullBar, currBearBreak, currBearBar] = trackPivotBreaks(currTfPivot)
if currBullBreak
detectOBSimple(1, currBullBar, currTfOBs, obLookback)
if currBearBreak
detectOBSimple(-1, currBearBar, currTfOBs, obLookback)
mitigateOBs(currTfOBs)
var array<ZoneBlock> currTfFvgs = array.new<ZoneBlock>()
var array<box> fvgBoxes = array.new<box>()
[currFvgSigDir, currFvgSigTop, currFvgSigBottom, currFvgSigBar] = detectFVGSignal()
if currFvgSigDir != 0
currTfFvgs.unshift(ZoneBlock.new(currFvgSigDir, currFvgSigTop, currFvgSigBottom, currFvgSigBar))
limitArraySize(currTfFvgs, fvgLookback)
mitigateFVGs(currTfFvgs)
// Track swing labels
var SwingLineData swingLines = SwingLineData.new()
var array<SwingLabel> swingLabels = array.new<SwingLabel>()
if currNewSwingH
swingLines.lastHBar := bar_index - swingLength
labelText = "HH"
labelColor = color.new(swingBullColor, 80)
labelTextColor = swingBullColor
if not na(currPrevSwingH) and currSwingH < currPrevSwingH
labelText := "LH"
labelColor := color.new(swingBearColor, 80)
labelTextColor := swingBearColor
swingLabels.push(SwingLabel.new(bar_index - swingLength, currSwingH, labelText, true, labelColor, labelTextColor))
limitSwingLabels(swingLabels, swingLabelLookback)
if currNewSwingL
swingLines.lastLBar := bar_index - swingLength
labelText = "HL"
labelColor = color.new(swingBullColor, 80)
labelTextColor = swingBullColor
if not na(currPrevSwingL) and currSwingL < currPrevSwingL
labelText := "LL"
labelColor := color.new(swingBearColor, 80)
labelTextColor := swingBearColor
swingLabels.push(SwingLabel.new(bar_index - swingLength, currSwingL, labelText, false, labelColor, labelTextColor))
limitSwingLabels(swingLabels, swingLabelLookback)
calcAll() =>
[trendDir, trendDist] = calcTrend()
[swingH, swingL, _swingBias, _newSwingH, _newSwingL, _prevSwingH, _prevSwingL, reclaimLow, reclaimHigh] = calcSwingHL()
[_highType, _lowType, structBias, struct1, struct2, struct3] = calcStructure()
[_obDir, _obTop, _obBottom, _newOB, obNearDir, obNearDist] = calcOrderBlock(obLookback)
[_fvgDir, _fvgTop, _fvgBottom, _newFVG, fvgNearDir, fvgNearDist] = calcFVG(fvgLookback)
[trendDir, trendDist, swingH, swingL, structBias, struct1, struct2, struct3, obNearDir, obNearDist, fvgNearDir, fvgNearDist, reclaimLow, reclaimHigh]
tfActive(enabled, tf) => enabled and timeframe.in_seconds() <= timeframe.in_seconds(tf)
// MTF data requests
[t1_trendDir, t1_trendDist, t1_swingH, t1_swingL, t1_structBias, t1_struct1, t1_struct2, t1_struct3, t1_obNearDir, t1_obNearDist, t1_fvgNearDir, t1_fvgNearDist, t1_reclaimLow, t1_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf1Enabled, tf1) ? tf1 : na, calcAll())
[t2_trendDir, t2_trendDist, t2_swingH, t2_swingL, t2_structBias, t2_struct1, t2_struct2, t2_struct3, t2_obNearDir, t2_obNearDist, t2_fvgNearDir, t2_fvgNearDist, t2_reclaimLow, t2_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf2Enabled, tf2) ? tf2 : na, calcAll())
[t3_trendDir, t3_trendDist, t3_swingH, t3_swingL, t3_structBias, t3_struct1, t3_struct2, t3_struct3, t3_obNearDir, t3_obNearDist, t3_fvgNearDir, t3_fvgNearDist, t3_reclaimLow, t3_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf3Enabled, tf3) ? tf3 : na, calcAll())
[t4_trendDir, t4_trendDist, t4_swingH, t4_swingL, t4_structBias, t4_struct1, t4_struct2, t4_struct3, t4_obNearDir, t4_obNearDist, t4_fvgNearDir, t4_fvgNearDist, t4_reclaimLow, t4_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf4Enabled, tf4) ? tf4 : na, calcAll())
[t5_trendDir, t5_trendDist, t5_swingH, t5_swingL, t5_structBias, t5_struct1, t5_struct2, t5_struct3, t5_obNearDir, t5_obNearDist, t5_fvgNearDir, t5_fvgNearDist, t5_reclaimLow, t5_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf5Enabled, tf5) ? tf5 : na, calcAll())
[t6_trendDir, t6_trendDist, t6_swingH, t6_swingL, t6_structBias, t6_struct1, t6_struct2, t6_struct3, t6_obNearDir, t6_obNearDist, t6_fvgNearDir, t6_fvgNearDist, t6_reclaimLow, t6_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf6Enabled, tf6) ? tf6 : na, calcAll())
[t7_trendDir, t7_trendDist, t7_swingH, t7_swingL, t7_structBias, t7_struct1, t7_struct2, t7_struct3, t7_obNearDir, t7_obNearDist, t7_fvgNearDir, t7_fvgNearDist, t7_reclaimLow, t7_reclaimHigh] = request.security(syminfo.tickerid, tfActive(tf7Enabled, tf7) ? tf7 : na, calcAll())
dirScore(dir) => dir > 0 ? 1 : dir < 0 ? -1 : 0
swingBiasDir(swingH, swingL) =>
swingRange = math.max(swingH - swingL, syminfo.mintick)
swingPct = (close - swingL) / swingRange * 100
swingPct < 30 ? 1 : swingPct > 70 ? -1 : 0
calcBiasForTf(structBias, obDir, fvgDir, trendDir, swingDir, weight) =>
score = 0
if biasUseStruct
score += dirScore(structBias)
if biasUseOB
score += dirScore(obDir)
if biasUseFVG
score += dirScore(fvgDir)
if biasUseEMA
score += dirScore(trendDir)
if biasUseSwing
score += swingDir
score * weight
// Count active factors for max score calculation
biasFactorCount() =>
count = 0
if biasUseStruct
count += 1
if biasUseOB
count += 1
if biasUseFVG
count += 1
if biasUseEMA
count += 1
if biasUseSwing
count += 1
count
dirColor(dir) => dir > 0 ? bullColor : dir < 0 ? bearColor : neutColor
fmtTF(tf) =>
switch tf
"1" => "1M"
"3" => "3M"
"5" => "5M"
"15" => "15M"
"30" => "30M"
"45" => "45M"
"60" => "1H"
"120" => "2H"
"180" => "3H"
"240" => "4H"
"D" => "D"
"W" => "W"
"M" => "M"
=> tf
fmtPct(dir, priceDist) =>
arrow = dir > 0 ? " ↑" : " ↓"
switch distanceMode
"Price" => str.tostring(math.abs(priceDist), "#.##") + arrow
"Pips" => str.tostring(math.abs(priceToPips(priceDist)), "#.#") + " pips" + arrow
=> str.tostring(priceToPct(priceDist), "#.##") + "%" + arrow
fmtSwingBar(swingH, swingL, reclaimLow, reclaimHigh) =>
swingRange = math.max(swingH - swingL, syminfo.mintick)
rawPct = pct(close - swingL, swingRange)
bars = 9
barText = ""
barColor = neutColor
if rawPct < 0
// Price below swing low - broke swing low
barText := "↓ L " + str.repeat("─", bars) + " H "
barColor := bearColor
else if rawPct > 100
// Price above swing high - broke swing high
barText := " L " + str.repeat("─", bars) + " H ↑"
barColor := bullColor
else if reclaimLow
// Swept low, back inside - bullish signal (liquidity grab)
pos = math.round(rawPct / 100 * (bars - 1))
left = str.repeat("─", pos)
right = str.repeat("─", bars - 1 - pos)
barText := "⤴ L " + left + "⬤" + right + " H "
barColor := bullColor
else if reclaimHigh
// Swept high, back inside - bearish signal (liquidity grab)
pos = math.round(rawPct / 100 * (bars - 1))
left = str.repeat("─", pos)
right = str.repeat("─", bars - 1 - pos)
barText := " L " + left + "⬤" + right + " H ⤵"
barColor := bearColor
else
// Price inside range
pos = math.round(rawPct / 100 * (bars - 1))
left = str.repeat("─", pos)
right = str.repeat("─", bars - 1 - pos)
barText := " L " + left + "⬤" + right + " H "
barColor := rawPct >= 50 ? bearColor : bullColor
[barText, barColor, rawPct]
fmtStructure(struct1, struct2, struct3, structBias) =>
arrow = structBias == 1 ? " ↑" : structBias == -1 ? " ↓" : " →"
struct3 + "-" + struct2 + "-" + struct1 + arrow
fmtZone(zoneType, nearestDir, nearestDist) =>
if nearestDir == 0
"NONE"
else if nearestDir == 1
if nearestDist >= 0
"IN BULL " + zoneType + " ↑"
else
distStr = switch distanceMode
"Price" => str.tostring(math.abs(nearestDist), "#.##")
"Pips" => str.tostring(math.abs(priceToPips(nearestDist)), "#.#") + " pips"
=> str.tostring(math.abs(priceToPct(nearestDist)), "#.#") + "%"
"BULL " + zoneType + " (" + distStr + ") ↑"
else
if nearestDist <= 0
"IN BEAR " + zoneType + " ↓"
else
distStr = switch distanceMode
"Price" => str.tostring(nearestDist, "#.##")
"Pips" => str.tostring(priceToPips(nearestDist), "#.#") + " pips"
=> str.tostring(priceToPct(nearestDist), "#.#") + "%"
"BEAR " + zoneType + " (+" + distStr + ") ↓"
fmtOB(nearestDir, nearestDist) => fmtZone("OB", nearestDir, nearestDist)
fmtFVG(nearestDir, nearestDist) => fmtZone("FVG", nearestDir, nearestDist)
tipZone(zoneType, nearestDir, nearestDist, tf) =>
tfStr = fmtTF(tf)
distStr = switch distanceMode
"Price" => str.tostring(math.abs(nearestDist), "#.##")
"Pips" => str.tostring(math.abs(priceToPips(nearestDist)), "#.#") + " pips"
=> str.tostring(math.abs(priceToPct(nearestDist)), "#.##") + "%"
if nearestDir == 0
"No unmitigated " + zoneType + "s on " + tfStr
else if nearestDir == 1
if nearestDist >= 0
"Price is currently in bullish " + zoneType + " on " + tfStr
else
"Nearest " + zoneType + " is bullish, " + distStr + " away on " + tfStr
else
if nearestDist <= 0
"Price is currently in bearish " + zoneType + " on " + tfStr
else
"Nearest " + zoneType + " is bearish, " + distStr + " away on " + tfStr
tipOB(nearestDir, nearestDist, tf) => tipZone("OB", nearestDir, nearestDist, tf)
tipFVG(nearestDir, nearestDist, tf) => tipZone("FVG", nearestDir, nearestDist, tf)
tipEma(trendDir, trendDist, tf) =>
tfStr = fmtTF(tf)
distStr = switch distanceMode
"Price" => str.tostring(math.abs(trendDist), "#.##")
"Pips" => str.tostring(math.abs(priceToPips(trendDist)), "#.#") + " pips"
=> str.tostring(math.abs(priceToPct(trendDist)), "#.##") + "%"
if trendDir > 0
"Price is " + distStr + " above EMA on " + tfStr
else if trendDir < 0
"Price is " + distStr + " below EMA on " + tfStr
else
"Price is at EMA on " + tfStr
tipSwing(swingPct, reclaimLow, reclaimHigh, tf) =>
tfStr = fmtTF(tf)
if reclaimLow
"Liquidity sweep - price swept swing low and reclaimed on " + tfStr
else if reclaimHigh
"Liquidity sweep - price swept swing high and reclaimed on " + tfStr
else if swingPct < 0
"Price below swing low on " + tfStr + " - swing low broken"
else if swingPct > 100
"Price above swing high on " + tfStr + " - swing high broken"
else if swingPct <= 30
"Price nearing swing low on " + tfStr
else if swingPct >= 70
"Price nearing swing high on " + tfStr
else
"Price in mid-range on " + tfStr
tipStructure(structBias, tf) =>
tfStr = fmtTF(tf)
if structBias == 1
"Bullish market structure on " + tfStr
else if structBias == -1
"Bearish market structure on " + tfStr
else
"Mixed market structure on " + tfStr
getSession(int hr) =>
sessName = ""
sessColor = GRAY
sessStart = 0
sessDur = 0
if hr >= 19 or hr < 3
sessName := "ASIAN"
sessColor := sessAsianColor
sessStart := 19
sessDur := 8
else if hr >= 8 and hr < 17
sessName := "NEW YORK"
sessColor := sessNyColor
sessStart := 8
sessDur := 9
else if hr >= 3 and hr < 8
sessName := "LONDON"
sessColor := sessLondonColor
sessStart := 3
sessDur := 9//9 for visual purpose only
else
sessName := "OFF HOURS"
[sessName, sessColor, sessStart, sessDur]
getKillzone(int hr, int mins, color neutralColor) =>
kzName = ""
kzColor = neutralColor
nextText = ""
nextColor = neutralColor
currentMins = hr * 60 + mins
// ICT Killzones
asianKzStart = 20 * 60 // 20:00
asianKzEnd = 24 * 60 // 00:00
londonKzStart = 2 * 60 // 02:00
londonKzEnd = 5 * 60 // 05:00
nyAmKzStart = 9 * 60 + 30 // 09:30
nyAmKzEnd = 11 * 60 // 11:00
nyLunchStart = 12 * 60 // 12:00
nyLunchEnd = 13 * 60 // 13:00
nyPmKzStart = 13 * 60 + 30 // 13:30
nyPmKzEnd = 16 * 60 // 16:00
if currentMins >= asianKzStart or currentMins < 0
kzName := "ASIAN KZ"
kzColor := kzAsianColor
nextColor := kzAsianColor
else if currentMins >= londonKzStart and currentMins < londonKzEnd
kzName := "LONDON KZ"
kzColor := kzLondonColor
nextColor := kzLondonColor
else if currentMins >= nyAmKzStart and currentMins < nyAmKzEnd
kzName := "NY AM KZ"
kzColor := kzNyAmColor
nextColor := kzNyAmColor
else if currentMins >= nyLunchStart and currentMins < nyLunchEnd
kzName := "NY LUNCH"
kzColor := kzLunchColor
nextColor := kzLunchColor
else if currentMins >= nyPmKzStart and currentMins < nyPmKzEnd
kzName := "NY PM KZ"
kzColor := kzPmColor
nextColor := kzPmColor
else
kzName := "NO KILLZONE"
kzColor := neutColor
nextKzName = ""
minutesToNext = 0
if currentMins < londonKzStart
nextKzName := "LONDON KZ"
minutesToNext := londonKzStart - currentMins
nextColor := kzLondonColor
else if currentMins < nyAmKzStart
nextKzName := "NY AM KZ"
minutesToNext := nyAmKzStart - currentMins
nextColor := kzNyAmColor
else if currentMins < nyLunchStart
nextKzName := "NY LUNCH"
minutesToNext := nyLunchStart - currentMins
nextColor := kzLunchColor
else if currentMins < nyPmKzStart
nextKzName := "NY PM KZ"
minutesToNext := nyPmKzStart - currentMins
nextColor := kzPmColor
else if currentMins < asianKzStart
nextKzName := "ASIAN KZ"
minutesToNext := asianKzStart - currentMins
nextColor := kzAsianColor
nextKzHours = math.floor(minutesToNext / 60)
nextKzMins = minutesToNext % 60
nextText := nextKzName + " in " + str.tostring(nextKzHours) + "h:" + str.tostring(nextKzMins, "00") + "m"
[kzName, kzColor, nextText, nextColor]
drawMsdRow(table tbl, int row, string tfStr, int trendDir, float trendDist, string swingBar, color swingColor, float swingPct, bool reclaimLow, bool reclaimHigh, int structBias, string struct1, string struct2, string struct3, int obNearDir, float obNearDist, int fvgNearDir, float fvgNearDist, string tf) =>
col = 0
table.cell(tbl, col, row, tfStr, text_color = textCol, text_size = tblTextSize, bgcolor = color.new(bgRow, 15))
if showColSwing
col += 1
table.cell(tbl, col, row, swingBar, text_color = swingColor, text_size = tblTextSize, tooltip = tipSwing(swingPct, reclaimLow, reclaimHigh, tf))
if showColStruct
col += 1
table.cell(tbl, col, row, fmtStructure(struct1, struct2, struct3, structBias), text_color = dirColor(structBias), text_size = tblTextSize, tooltip = tipStructure(structBias, tf))
if showColOB
col += 1
table.cell(tbl, col, row, fmtOB(obNearDir, obNearDist), text_color = dirColor(obNearDir), text_size = tblTextSize, tooltip = tipOB(obNearDir, obNearDist, tf))
if showColFVG
col += 1
table.cell(tbl, col, row, fmtFVG(fvgNearDir, fvgNearDist), text_color = dirColor(fvgNearDir), text_size = tblTextSize, tooltip = tipFVG(fvgNearDir, fvgNearDist, tf))
if showColEma
col += 1
table.cell(tbl, col, row, fmtPct(trendDir, trendDist), text_color = dirColor(trendDir), text_size = tblTextSize, tooltip = tipEma(trendDir, trendDist, tf))
drawBottomHeaders(table tbl, int row) =>
col = 0
if showCurrTF
if showCurrVol
table.cell(tbl, col, row, "VOLUME", text_color = textCol, text_size = size.normal, tooltip = "Current volume")
col += 1
if showCurrSwing
table.cell(tbl, col, row, "SWING H/L", text_color = textCol, text_size = size.normal, tooltip = "Price position within current swing range")
col += 1
if showCurrAtr
table.cell(tbl, col, row, "VOLATILITY", text_color = textCol, text_size = size.normal, tooltip = "Current volatility")
col += 1
if showContext
if showSession
table.cell(tbl, col, row, "SESSION", text_color = textCol, text_size = size.normal, tooltip = "Current trading session")
col += 1
if showKillzone
table.cell(tbl, col, row, "KILLZONE", text_color = textCol, text_size = size.normal, tooltip = "Current killzone")
col += 1
if showBias
table.cell(tbl, col, row, "TREND BIAS", text_color = textCol, text_size = size.normal, tooltip = "Current trend bias")
col += 1
drawBottomData1(table tbl, int row, string swingText, color swingColor, string tipSwing, string volBar, color volColor, string tipVol, string volState, color volStateColor, string tipVolState, string sessName, color sessColor, string tipSess, string kzName, color kzColor, string tipKz, string biasLbl, color biasClr, string tipBias) =>
col = 0
if showCurrTF
if showCurrVol
table.cell(tbl, col, row, volBar, text_color = volColor, text_size = tblTextSize, tooltip = tipVol)
col += 1
if showCurrSwing
table.cell(tbl, col, row, swingText, text_color = swingColor, text_size = tblTextSize, tooltip = tipSwing)
col += 1
if showCurrAtr
table.cell(tbl, col, row, volState, text_color = volStateColor, text_size = tblTextSize, tooltip = tipVolState)
col += 1
if showContext
if showSession
table.cell(tbl, col, row, sessName, text_color = sessColor, text_size = tblTextSize, tooltip = tipSess)
col += 1
if showKillzone
table.cell(tbl, col, row, kzName, text_color = kzColor, text_size = tblTextSize, tooltip = tipKz)
col += 1
if showBias
table.cell(tbl, col, row, biasLbl, text_color = biasClr, text_size = tblTextSize, tooltip = tipBias)
col += 1
drawBottomData2(table tbl, int row, color swingColor, string volState, color volColor, string sessProgress, color sessColor, string tipSessProg, string nextKz, color nextKzClr, string tipNextKz, string biasScr, color biasClr) =>
col = 0
if showCurrTF
if showCurrVol
table.cell(tbl, col, row, volState, text_color = volColor, text_size = tblTextSize)
col += 1
if showCurrSwing
table.cell(tbl, col, row, "", text_color = swingColor, text_size = tblTextSize)
col += 1
if showCurrAtr
table.cell(tbl, col, row, "", text_color = volColor, text_size = tblTextSize)
col += 1
if showContext
if showSession
table.cell(tbl, col, row, sessProgress, text_color = sessColor, text_size = tblTextSize, tooltip = tipSessProg)
col += 1
if showKillzone
table.cell(tbl, col, row, nextKz, text_color = nextKzClr, text_size = tblTextSize, tooltip = tipNextKz)
col += 1
if showBias
table.cell(tbl, col, row, biasScr, text_color = biasClr, text_size = tblTextSize)
col += 1
drawHTFRows(table tbl, int startRow, string pdBar, color pdColor, string tipPD, string pwBar, color pwColor, string tipPW, string pmBar, color pmColor, string tipPM) =>
row = startRow
if showHTFLevels
if showPDHL
table.cell(tbl, 0, row, "PDH/L", text_color = textCol, text_size = tblTextSize, bgcolor = color.new(bgRow, 15))
table.cell(tbl, 1, row, pdBar, text_color = pdColor, text_size = tblTextSize, tooltip = tipPD)
row += 1
if showPWHL
table.cell(tbl, 0, row, "PWH/L", text_color = textCol, text_size = tblTextSize, bgcolor = color.new(bgRow, 15))
table.cell(tbl, 1, row, pwBar, text_color = pwColor, text_size = tblTextSize, tooltip = tipPW)
row += 1
if showPMHL
table.cell(tbl, 0, row, "PMH/L", text_color = textCol, text_size = tblTextSize, bgcolor = color.new(bgRow, 15))
table.cell(tbl, 1, row, pmBar, text_color = pmColor, text_size = tblTextSize, tooltip = tipPM)
//#endregion FUNCTIONS
//#region CALCULATIONS
[t1_swingBar, t1_swingColor, t1_swingPct] = fmtSwingBar(t1_swingH, t1_swingL, t1_reclaimLow, t1_reclaimHigh)
[t2_swingBar, t2_swingColor, t2_swingPct] = fmtSwingBar(t2_swingH, t2_swingL, t2_reclaimLow, t2_reclaimHigh)
[t3_swingBar, t3_swingColor, t3_swingPct] = fmtSwingBar(t3_swingH, t3_swingL, t3_reclaimLow, t3_reclaimHigh)
[t4_swingBar, t4_swingColor, t4_swingPct] = fmtSwingBar(t4_swingH, t4_swingL, t4_reclaimLow, t4_reclaimHigh)
[t5_swingBar, t5_swingColor, t5_swingPct] = fmtSwingBar(t5_swingH, t5_swingL, t5_reclaimLow, t5_reclaimHigh)
[t6_swingBar, t6_swingColor, t6_swingPct] = fmtSwingBar(t6_swingH, t6_swingL, t6_reclaimLow, t6_reclaimHigh)
[t7_swingBar, t7_swingColor, t7_swingPct] = fmtSwingBar(t7_swingH, t7_swingL, t7_reclaimLow, t7_reclaimHigh)
// Current TF swing bar
currSwingRange = math.max(currSwingH - currSwingL, syminfo.mintick)
currSwingPct = pct(close - currSwingL, currSwingRange)
currBars = 9
currSwingBarText = ""
currSwingBarColor = neutColor
if currSwingPct < 0
// Price below swing low - swing low broken
currSwingBarText := "↓ L " + str.repeat("─", currBars) + " H "
currSwingBarColor := bearColor
else if currSwingPct > 100
// Price above swing high - swing high broken
currSwingBarText := " L " + str.repeat("─", currBars) + " H ↑"
currSwingBarColor := bullColor
else if currReclaimLow
// Swept low, back inside - bullish signal (liquidity grab)
currPos = math.round(currSwingPct / 100 * (currBars - 1))
currLeft = str.repeat("─", currPos)
currRight = str.repeat("─", currBars - 1 - currPos)
currSwingBarText := "⤴ L " + currLeft + "⬤" + currRight + " H "
currSwingBarColor := bullColor
else if currReclaimHigh
// Swept high, back inside - bearish signal (liquidity grab)
currPos = math.round(currSwingPct / 100 * (currBars - 1))
currLeft = str.repeat("─", currPos)
currRight = str.repeat("─", currBars - 1 - currPos)
currSwingBarText := " L " + currLeft + "⬤" + currRight + " H ⤵"
currSwingBarColor := bearColor
else
// Price inside range
currPos = math.round(currSwingPct / 100 * (currBars - 1))
currLeft = str.repeat("─", currPos)
currRight = str.repeat("─", currBars - 1 - currPos)
currSwingBarText := " L " + currLeft + "⬤" + currRight + " H "
currSwingBarColor := currSwingPct >= 50 ? bearColor : bullColor
// Volume calculations
volumeLength = 20
avgVolume = ta.sma(volume, volumeLength)
volumePct = pct(volume, avgVolume)
volumeState = volumePct > 200 ? "EXTREME" : volumePct > 120 ? "HIGH" : volumePct > 80 ? "NORMAL" : volumePct > 50 ? "LOW" : "VERY LOW"
volumeBars = volumePct > 200 ? 5 : volumePct > 120 ? 4 : volumePct > 80 ? 3 : volumePct > 50 ? 2 : 1
volumeBarString = str.repeat("█", volumeBars) + str.repeat("░", math.max(0, 5 - volumeBars))
volumeColor = volumePct > 200 ? YELLOW : volumePct > 120 ? ORANGE : neutColor
// Volatility calculations
currentATR = ta.atr(atrLength)
avgATR = ta.sma(currentATR, atrAvgLength)
atrPct = pct(currentATR, avgATR)
volatilityState = atrPct > 130 ? "HIGH" : atrPct > 70 ? "NORMAL" : "LOW"
volatilityColor = atrPct > 130 ? bearColor : atrPct > 70 ? neutColor : bullColor
// HTF Levels (PDH/L, PWH/L, PMH/L)
[pdHigh, pdLow] = request.security(syminfo.tickerid, "D", [high[1], low[1]], lookahead = barmerge.lookahead_on)
[pwHigh, pwLow] = request.security(syminfo.tickerid, "W", [high[1], low[1]], lookahead = barmerge.lookahead_on)
[pmHigh, pmLow] = request.security(syminfo.tickerid, "M", [high[1], low[1]], lookahead = barmerge.lookahead_on)
// HTF Levels reclaim tracking
// Track HTF level breaks and reclaims
trackHTFLevel(float levelHigh, float levelLow, simple string tf) =>
var bool lowBroken = false
var bool highBroken = false
if timeframe.change(tf)
lowBroken := false
highBroken := false
if low < levelLow
lowBroken := true
if high > levelHigh
highBroken := true
reclaimLow = lowBroken and close > levelLow
reclaimHigh = highBroken and close < levelHigh
[reclaimLow, reclaimHigh]
// HTF Levels reclaim detection
[pdReclaimLow, pdReclaimHigh] = trackHTFLevel(pdHigh, pdLow, "D")
[pwReclaimLow, pwReclaimHigh] = trackHTFLevel(pwHigh, pwLow, "W")
[pmReclaimLow, pmReclaimHigh] = trackHTFLevel(pmHigh, pmLow, "M")
// Format HTF level bars
[pdSwingBar, pdSwingColor, pdSwingPct] = fmtSwingBar(pdHigh, pdLow, pdReclaimLow, pdReclaimHigh)
[pwSwingBar, pwSwingColor, pwSwingPct] = fmtSwingBar(pwHigh, pwLow, pwReclaimLow, pwReclaimHigh)
[pmSwingBar, pmSwingColor, pmSwingPct] = fmtSwingBar(pmHigh, pmLow, pmReclaimLow, pmReclaimHigh)
// HTF Levels tooltips
tipPD = pdReclaimLow ? "Liquidity sweep - price swept PDL and reclaimed" : pdReclaimHigh ? "Liquidity sweep - price swept PDH and reclaimed" : pdSwingPct < 0 ? "Price below PDL - previous day low broken" : pdSwingPct > 100 ? "Price above PDH - previous day high broken" : pdSwingPct <= 30 ? "Price nearing PDL" : pdSwingPct >= 70 ? "Price nearing PDH" : "Price in previous day range"
tipPW = pwReclaimLow ? "Liquidity sweep - price swept PWL and reclaimed" : pwReclaimHigh ? "Liquidity sweep - price swept PWH and reclaimed" : pwSwingPct < 0 ? "Price below PWL - previous week low broken" : pwSwingPct > 100 ? "Price above PWH - previous week high broken" : pwSwingPct <= 30 ? "Price nearing PWL" : pwSwingPct >= 70 ? "Price nearing PWH" : "Price in previous week range"
tipPM = pmReclaimLow ? "Liquidity sweep - price swept PML and reclaimed" : pmReclaimHigh ? "Liquidity sweep - price swept PMH and reclaimed" : pmSwingPct < 0 ? "Price below PML - previous month low broken" : pmSwingPct > 100 ? "Price above PMH - previous month high broken" : pmSwingPct <= 30 ? "Price nearing PML" : pmSwingPct >= 70 ? "Price nearing PMH" : "Price in previous month range"
// Session and killzone detection (with replay mode support)
isReplay = (timenow - time) > timeframe.in_seconds() * 1000 and barstate.islast
sessionTime = isReplay ? time : timenow
currentHour = hour(sessionTime, "America/New_York")
currentMinute = minute(sessionTime, "America/New_York")
[sessionName, sessionColor, sessionStartHour, sessionDuration] = getSession(currentHour)
[killzoneName, killzoneColor, nextKzText, nextKzColor] = getKillzone(currentHour, currentMinute, neutColor)
// Session progress bar (adds bar at 30 min mark)
hoursElapsed = 0
sessionProgressBar = ""
if sessionName != "OFF HOURS"
minsIntoSession = sessionName == "ASIAN" and currentHour < 3 ? ((currentHour + 24 - sessionStartHour) * 60 + currentMinute) : ((currentHour - sessionStartHour) * 60 + currentMinute)
hoursElapsed := math.floor((minsIntoSession + 30) / 60)
hoursElapsed := math.min(hoursElapsed, sessionDuration)
sessionProgressBar := str.repeat("█", hoursElapsed) + str.repeat("░", math.max(0, sessionDuration - hoursElapsed))
// Trend Bias Calculation (User Weighted)
tfWeights = array.from(tf1Weight, tf2Weight, tf3Weight, tf4Weight, tf5Weight, tf6Weight, tf7Weight)
tfActives = array.from(tfActive(tf1Enabled, tf1), tfActive(tf2Enabled, tf2), tfActive(tf3Enabled, tf3), tfActive(tf4Enabled, tf4), tfActive(tf5Enabled, tf5), tfActive(tf6Enabled, tf6), tfActive(tf7Enabled, tf7))
structBiases = array.from(t1_structBias, t2_structBias, t3_structBias, t4_structBias, t5_structBias, t6_structBias, t7_structBias)
obDirs = array.from(t1_obNearDir, t2_obNearDir, t3_obNearDir, t4_obNearDir, t5_obNearDir, t6_obNearDir, t7_obNearDir)
fvgDirs = array.from(t1_fvgNearDir, t2_fvgNearDir, t3_fvgNearDir, t4_fvgNearDir, t5_fvgNearDir, t6_fvgNearDir, t7_fvgNearDir)
trendDirs = array.from(t1_trendDir, t2_trendDir, t3_trendDir, t4_trendDir, t5_trendDir, t6_trendDir, t7_trendDir)
swingDirs = array.from(swingBiasDir(t1_swingH, t1_swingL), swingBiasDir(t2_swingH, t2_swingL), swingBiasDir(t3_swingH, t3_swingL), swingBiasDir(t4_swingH, t4_swingL), swingBiasDir(t5_swingH, t5_swingL), swingBiasDir(t6_swingH, t6_swingL), swingBiasDir(t7_swingH, t7_swingL))
// Calculate max possible score and bias points
factorCount = biasFactorCount()
maxScore = 0
biasPoints = 0
for i = 0 to 6
weight = tfWeights.get(i)
if tfActives.get(i) and weight > 0
maxScore += weight * factorCount
biasPoints += calcBiasForTf(structBiases.get(i), obDirs.get(i), fvgDirs.get(i), trendDirs.get(i), swingDirs.get(i), weight)
// Format bias display
biasPct = maxScore > 0 ? (biasPoints / maxScore) * 100 : 0
biasLabel = maxScore == 0 ? "N/A" : biasPct > 50 ? "BULLISH ↑" : biasPct > 20 ? "LEAN BULL ↑" : biasPct < -50 ? "BEARISH ↓" : biasPct < -20 ? "LEAN BEAR ↓" : "NEUTRAL →"
biasScore = maxScore == 0 ? "--" : (biasPoints >= 0 ? "+" : "") + str.tostring(biasPoints) + "/" + str.tostring(maxScore)
biasColor = maxScore == 0 ? neutColor : biasPct > 20 ? bullColor : biasPct < -20 ? bearColor : neutColor
// Dynamic tooltips
tipKillzone = killzoneName == "NO KILLZONE" ? "No active killzone" : "Currently in " + killzoneName
tipNextKz = killzoneName == "NO KILLZONE" ? "Next killzone: " + nextKzText : "Currently in " + killzoneName
tipSession = sessionName == "OFF HOURS" ? "Market is currently not in any session" : "Currently in " + sessionName + " session"
tipSessionProg = sessionName == "OFF HOURS" ? "Market is currently not in any session" : str.tostring(hoursElapsed) + " of " + str.tostring(sessionDuration) + " hours elapsed in " + sessionName + " session"
tipBiasLabel = "Current Trend Bias: " + (biasPct > 50 ? "Bullish" : biasPct > 20 ? "Leaning Bullish" : biasPct < -50 ? "Bearish" : biasPct < -20 ? "Leaning Bearish" : "Neutral") + " based on HTF analysis"
tipVolume = "Volume is currently at " + str.tostring(volumePct, "#") + "% of average"
tipVolatility = "Volatility is currently " + (atrPct > 130 ? "high" : atrPct > 70 ? "normal" : "low")
tipCurrSwing = currReclaimLow ? "Liquidity sweep - price swept swing low and reclaimed" : currReclaimHigh ? "Liquidity sweep - price swept swing high and reclaimed" : currSwingPct < 0 ? "Price below swing low - swing low broken" : currSwingPct > 100 ? "Price above swing high - swing high broken" : currSwingPct <= 30 ? "Price is nearing swing low" : currSwingPct >= 70 ? "Price is nearing swing high" : "Price is in mid-range"
//#endregion CALCULATIONS
//#region VISUALIZATION
var table dashboard = table.new(tablePos, 7, 18,
bgcolor = bgDark,
border_color = #3d3d3d,
border_width = 1)
if barstate.islast
// Market Structure Dashboard section
if showMSD
// Calculate total columns for MSD
msdCols = 1 + (showColEma ? 1 : 0) + (showColSwing ? 1 : 0) + (showColStruct ? 1 : 0) + (showColOB ? 1 : 0) + (showColFVG ? 1 : 0)
// Header row
table.cell(dashboard, 0, 0, "MARKET STRUCTURE DASHBOARD", text_color = textCol, text_size = size.normal, bgcolor = bgHeader, text_halign = text.align_center)
if msdCols > 1
table.merge_cells(dashboard, 0, 0, msdCols - 1, 0)
// Column headers (row 1) - EMA moved to last
col = 0
table.cell(dashboard, col, 1, "TF", text_color = textCol, text_size = size.normal, width = 3)
if showColSwing
col += 1
table.cell(dashboard, col, 1, "SWING H/L", text_color = textCol, text_size = size.normal, tooltip = "Price position within swing range")
if showColStruct
col += 1
table.cell(dashboard, col, 1, "STRUCTURE", text_color = textCol, text_size = size.normal, tooltip = "Last 3 swing patterns (HH/HL/LH/LL)")
if showColOB
col += 1
table.cell(dashboard, col, 1, "ORDER BLOCK", text_color = textCol, text_size = size.normal, tooltip = "Nearest unmitigated order block")
if showColFVG
col += 1
table.cell(dashboard, col, 1, "FVG", text_color = textCol, text_size = size.normal, tooltip = "Nearest unmitigated fair value gap")
if showColEma
col += 1
table.cell(dashboard, col, 1, "EMA-" + str.tostring(emaLength) + " TREND", text_color = textCol, text_size = size.normal, tooltip = "Distance from EMA as percentage")
// TF Rows
if tfActive(tf1Enabled, tf1)
drawMsdRow(dashboard, 2, fmtTF(tf1), t1_trendDir, t1_trendDist, t1_swingBar, t1_swingColor, t1_swingPct, t1_reclaimLow, t1_reclaimHigh, t1_structBias, t1_struct1, t1_struct2, t1_struct3, t1_obNearDir, t1_obNearDist, t1_fvgNearDir, t1_fvgNearDist, tf1)
if tfActive(tf2Enabled, tf2)
drawMsdRow(dashboard, 3, fmtTF(tf2), t2_trendDir, t2_trendDist, t2_swingBar, t2_swingColor, t2_swingPct, t2_reclaimLow, t2_reclaimHigh, t2_structBias, t2_struct1, t2_struct2, t2_struct3, t2_obNearDir, t2_obNearDist, t2_fvgNearDir, t2_fvgNearDist, tf2)
if tfActive(tf3Enabled, tf3)
drawMsdRow(dashboard, 4, fmtTF(tf3), t3_trendDir, t3_trendDist, t3_swingBar, t3_swingColor, t3_swingPct, t3_reclaimLow, t3_reclaimHigh, t3_structBias, t3_struct1, t3_struct2, t3_struct3, t3_obNearDir, t3_obNearDist, t3_fvgNearDir, t3_fvgNearDist, tf3)
if tfActive(tf4Enabled, tf4)
drawMsdRow(dashboard, 5, fmtTF(tf4), t4_trendDir, t4_trendDist, t4_swingBar, t4_swingColor, t4_swingPct, t4_reclaimLow, t4_reclaimHigh, t4_structBias, t4_struct1, t4_struct2, t4_struct3, t4_obNearDir, t4_obNearDist, t4_fvgNearDir, t4_fvgNearDist, tf4)
if tfActive(tf5Enabled, tf5)
drawMsdRow(dashboard, 6, fmtTF(tf5), t5_trendDir, t5_trendDist, t5_swingBar, t5_swingColor, t5_swingPct, t5_reclaimLow, t5_reclaimHigh, t5_structBias, t5_struct1, t5_struct2, t5_struct3, t5_obNearDir, t5_obNearDist, t5_fvgNearDir, t5_fvgNearDist, tf5)
if tfActive(tf6Enabled, tf6)
drawMsdRow(dashboard, 7, fmtTF(tf6), t6_trendDir, t6_trendDist, t6_swingBar, t6_swingColor, t6_swingPct, t6_reclaimLow, t6_reclaimHigh, t6_structBias, t6_struct1, t6_struct2, t6_struct3, t6_obNearDir, t6_obNearDist, t6_fvgNearDir, t6_fvgNearDist, tf6)
if tfActive(tf7Enabled, tf7)
drawMsdRow(dashboard, 8, fmtTF(tf7), t7_trendDir, t7_trendDist, t7_swingBar, t7_swingColor, t7_swingPct, t7_reclaimLow, t7_reclaimHigh, t7_structBias, t7_struct1, t7_struct2, t7_struct3, t7_obNearDir, t7_obNearDist, t7_fvgNearDir, t7_fvgNearDist, tf7)
// Current TF & Market Context sections
if showCurrTF or showContext
// Calculate column counts
ctfCols = (showCurrVol ? 1 : 0) + (showCurrSwing ? 1 : 0) + (showCurrAtr ? 1 : 0)
ctxCols = (showSession ? 1 : 0) + (showKillzone ? 1 : 0) + (showBias ? 1 : 0)
bottomCols = (showCurrTF ? ctfCols : 0) + (showContext ? ctxCols : 0)
// Row 9: Empty separator (only show if MSD is also visible)
if showMSD
table.cell(dashboard, 0, 9, "", bgcolor = bgDark)
if bottomCols > 0
table.merge_cells(dashboard, 0, 9, bottomCols - 1, 9)
// Row 10: Section headers (start at col 0)
col = 0
if showCurrTF and ctfCols > 0
table.cell(dashboard, col, 10, "CURRENT TIMEFRAME", text_color = textCol, text_size = size.normal, bgcolor = bgHeader, text_halign = text.align_center)
if ctfCols > 1
table.merge_cells(dashboard, col, 10, col + ctfCols - 1, 10)
col += ctfCols
if showContext and ctxCols > 0
table.cell(dashboard, col, 10, "MARKET CONTEXT", text_color = textCol, text_size = size.normal, bgcolor = bgHeader, text_halign = text.align_center)
if ctxCols > 1
table.merge_cells(dashboard, col, 10, col + ctxCols - 1, 10)
// Row 11-13: Column headers and data rows
drawBottomHeaders(dashboard, 11)
drawBottomData1(dashboard, 12, currSwingBarText, currSwingBarColor, tipCurrSwing, volumeBarString, volumeColor, tipVolume, volatilityState, volatilityColor, tipVolatility, sessionName, sessionColor, tipSession, killzoneName, killzoneColor, tipKillzone, biasLabel, biasColor, tipBiasLabel)
drawBottomData2(dashboard, 13, currSwingBarColor, volumeState, volumeColor, sessionProgressBar, sessionColor, tipSessionProg, nextKzText, nextKzColor, tipNextKz, biasScore, biasColor)
// HTF Levels section
if showHTFLevels
htfRows = (showPDHL ? 1 : 0) + (showPWHL ? 1 : 0) + (showPMHL ? 1 : 0)
if htfRows > 0
// Row 14: Section header
table.cell(dashboard, 0, 14, "HTF LEVELS", text_color = textCol, text_size = size.normal, bgcolor = bgHeader, text_halign = text.align_center)
table.merge_cells(dashboard, 0, 14, 1, 14)
// Rows 15+: HTF data
drawHTFRows(dashboard, 15, pdSwingBar, pdSwingColor, tipPD, pwSwingBar, pwSwingColor, tipPW, pmSwingBar, pmSwingColor, tipPM)
// Plot EMA
plot(showEMA ? emaValue : na, "EMA", color = emaColor, linewidth = 2)
// Draw HTF Level Lines
var line pdhLine = na, var line pdlLine = na
var line pwhLine = na, var line pwlLine = na
var line pmhLine = na, var line pmlLine = na
var label pdhLabel = na, var label pdlLabel = na
var label pwhLabel = na, var label pwlLabel = na
var label pmhLabel = na, var label pmlLabel = na
// Segment-based threshold for combining labels
htfMaxHigh = math.max(plotPDHL ? pdHigh : 0, plotPWHL ? pwHigh : 0, plotPMHL ? pmHigh : 0)
htfMinLow = math.min(plotPDHL ? pdLow : 10e10, plotPWHL ? pwLow : 10e10, plotPMHL ? pmLow : 10e10)
segmentSize = (htfMaxHigh - htfMinLow) / 50
getSegment(price) => segmentSize > 0 ? math.floor((price - htfMinLow) / segmentSize) : 0
// Get segments for all levels (-1 if disabled)
pdhSeg = plotPDHL ? getSegment(pdHigh) : -1, pdlSeg = plotPDHL ? getSegment(pdLow) : -1
pwhSeg = plotPWHL ? getSegment(pwHigh) : -2, pwlSeg = plotPWHL ? getSegment(pwLow) : -2
pmhSeg = plotPMHL ? getSegment(pmHigh) : -3, pmlSeg = plotPMHL ? getSegment(pmLow) : -3
// Build combined labels - highs
pdhInPwh = pdhSeg == pwhSeg and plotPDHL and plotPWHL
pdhInPmh = pdhSeg == pmhSeg and plotPDHL and plotPMHL
pwhInPmh = pwhSeg == pmhSeg and plotPWHL and plotPMHL
pmhLbl = (pdhInPmh ? "PDH/" : "") + (pwhInPmh ? "PWH/" : "") + "PMH"
pwhLbl = (pdhInPwh and not pdhInPmh ? "PDH/" : "") + "PWH"
pdhLbl = "PDH"
// Build combined labels - lows
pdlInPwl = pdlSeg == pwlSeg and plotPDHL and plotPWHL
pdlInPml = pdlSeg == pmlSeg and plotPDHL and plotPMHL
pwlInPml = pwlSeg == pmlSeg and plotPWHL and plotPMHL
pmlLbl = (pdlInPml ? "PDL/" : "") + (pwlInPml ? "PWL/" : "") + "PML"
pwlLbl = (pdlInPwl and not pdlInPml ? "PDL/" : "") + "PWL"
pdlLbl = "PDL"
if barstate.islast
// Delete all existing
pdhLine.delete(), pdlLine.delete(), pwhLine.delete(), pwlLine.delete(), pmhLine.delete(), pmlLine.delete()
pdhLabel.delete(), pdlLabel.delete(), pwhLabel.delete(), pwlLabel.delete(), pmhLabel.delete(), pmlLabel.delete()
// Draw all lines at their actual prices (no merging)
if plotPMHL
pmhLine := line.new(bar_index - 1, pmHigh, bar_index + htfLevelExtend, pmHigh, color = pmhColor, style = getLineStyle(pmhlLineStyle), width = 1)
pmlLine := line.new(bar_index - 1, pmLow, bar_index + htfLevelExtend, pmLow, color = pmlColor, style = getLineStyle(pmhlLineStyle), width = 1)
if plotPWHL
pwhLine := line.new(bar_index - 1, pwHigh, bar_index + htfLevelExtend, pwHigh, color = pwhColor, style = getLineStyle(pwhlLineStyle), width = 1)
pwlLine := line.new(bar_index - 1, pwLow, bar_index + htfLevelExtend, pwLow, color = pwlColor, style = getLineStyle(pwhlLineStyle), width = 1)
if plotPDHL
pdhLine := line.new(bar_index - 1, pdHigh, bar_index + htfLevelExtend, pdHigh, color = pdhColor, style = getLineStyle(pdhlLineStyle), width = 1)
pdlLine := line.new(bar_index - 1, pdLow, bar_index + htfLevelExtend, pdLow, color = pdlColor, style = getLineStyle(pdhlLineStyle), width = 1)
// Draw labels (combined when in same segment, highest TF gets the label)
if plotPMHL
pmhLabel := label.new(bar_index + htfLevelExtend, pmHigh, pmhLbl, color = color.new(pmhColor, 100), textcolor = pmhColor, style = label.style_label_left, size = size.small)
pmlLabel := label.new(bar_index + htfLevelExtend, pmLow, pmlLbl, color = color.new(pmlColor, 100), textcolor = pmlColor, style = label.style_label_left, size = size.small)
if plotPWHL and not pwhInPmh
pwhLabel := label.new(bar_index + htfLevelExtend, pwHigh, pwhLbl, color = color.new(pwhColor, 100), textcolor = pwhColor, style = label.style_label_left, size = size.small)
if plotPWHL and not pwlInPml
pwlLabel := label.new(bar_index + htfLevelExtend, pwLow, pwlLbl, color = color.new(pwlColor, 100), textcolor = pwlColor, style = label.style_label_left, size = size.small)
if plotPDHL and not pdhInPmh and not pdhInPwh
pdhLabel := label.new(bar_index + htfLevelExtend, pdHigh, pdhLbl, color = color.new(pdhColor, 100), textcolor = pdhColor, style = label.style_label_left, size = size.small)
if plotPDHL and not pdlInPml and not pdlInPwl
pdlLabel := label.new(bar_index + htfLevelExtend, pdLow, pdlLbl, color = color.new(pdlColor, 100), textcolor = pdlColor, style = label.style_label_left, size = size.small)
// Draw Order Blocks
if showOB and barstate.islast
for b in obBoxes
b.delete()
obBoxes.clear()
obCount = currTfOBs.size()
for i = 0 to obCount - 1
ob = currTfOBs.get(i)
obColor = ob.dir > 0 ? color.new(obBullColor, 80) : color.new(obBearColor, 80)
obBorder = ob.dir > 0 ? obBullColor : obBearColor
newBox = box.new(
left = ob.barIdx,
top = ob.top,
right = bar_index + obExtend,
bottom = ob.bottom,
border_color = obBorder,
border_width = 1,
bgcolor = obColor,
text = showOBLabels ? (ob.dir > 0 ? "OB ↑" : "OB ↓") : "",
text_color = obBorder,
text_halign = text.align_right,
text_valign = text.align_center,
text_size = size.small
)
obBoxes.unshift(newBox)
// Draw FVGs
if showFVG and barstate.islast
for b in fvgBoxes
b.delete()
fvgBoxes.clear()
fvgCount = currTfFvgs.size()
for i = 0 to fvgCount - 1
fvg = currTfFvgs.get(i)
fvgColorBg = fvg.dir > 0 ? color.new(fvgBullColor, 85) : color.new(fvgBearColor, 85)
fvgBorderClr = fvg.dir > 0 ? color.new(fvgBullColor, 60) : color.new(fvgBearColor, 60)
fvgTextClr = fvg.dir > 0 ? fvgBullColor : fvgBearColor
newBox = box.new(
left = fvg.barIdx,
top = fvg.top,
right = bar_index + fvgExtend,
bottom = fvg.bottom,
bgcolor = fvgColorBg,
border_width = 1,
border_style = line.style_dashed,
border_color = fvgBorderClr,
text = showFVGLabels ? (fvg.dir > 0 ? "FVG ↑" : "FVG ↓") : "",
text_color = fvgTextClr,
text_halign = text.align_right,
text_valign = text.align_center,
text_size = size.small
)
fvgBoxes.unshift(newBox)
// Draw Swing Labels (from array on last bar)
var array<label> swingLabelObjs = array.new<label>()
if showSwingLabels and barstate.islast
// Clear old labels
for lbl in swingLabelObjs
lbl.delete()
swingLabelObjs.clear()
// Draw labels from array
for lbl in swingLabels
newLabel = label.new(
x = lbl.x,
y = lbl.y,
text = lbl.txt,
yloc = lbl.isHigh ? yloc.abovebar : yloc.belowbar,
color = lbl.clr,
style = lbl.isHigh ? label.style_label_down : label.style_label_up,
textcolor = lbl.textClr,
size = size.small
)
swingLabelObjs.push(newLabel)
// Draw Swing Lines
if showSwingLines and barstate.islast
swingLines.highLine.delete()
swingLines.lowLine.delete()
if not na(currSwingH) and not na(swingLines.lastHBar)
swingLines.highLine := line.new(
x1 = swingLines.lastHBar,
y1 = currSwingH,
x2 = bar_index + swingLinesExtend,
y2 = currSwingH,
color = color.new(swingLineHighColor, 30),
style = getLineStyle(swingLineStyle),
width = 1
)
if not na(currSwingL) and not na(swingLines.lastLBar)
swingLines.lowLine := line.new(
x1 = swingLines.lastLBar,
y1 = currSwingL,
x2 = bar_index + swingLinesExtend,
y2 = currSwingL,
color = color.new(swingLineLowColor, 30),
style = getLineStyle(swingLineStyle),
width = 1
)
//#endregion VISUALIZATION
Show all 1,468 lines