Code: Select all
//@version=5
indicator("Asian Range Microstructure [PRO]", overlay=true, max_boxes_count=100, max_lines_count=200, max_labels_count=50)
// =========================================================================
// INPUTS & CONFIGURATION
// =========================================================================
var string G_TIME = "Session Parameters"
sessionTime = input.session("0000-0800", title="Asian Session (Exchange TZ)", group=G_TIME)
extendHours = input.int(6, title="London Projection Length (Hours)", minval=1, maxval=12, group=G_TIME)
var string G_CALC = "Expansion Logic"
lookback = input.int(15, title="Average Range Lookback (Days)", minval=5, maxval=50, group=G_CALC)
multiplier = input.float(1.5, title="Expansion Multiplier", step=0.1, group=G_CALC)
var string G_STYLE = "Aesthetics & HUD"
colorNormal = input.color(color.new(#2962FF, 85), title="Normal Range", group=G_STYLE)
colorExpand = input.color(color.new(#FF5252, 85), title="Expansion Range", group=G_STYLE)
colorLine = input.color(color.new(#787B86, 30), title="Liquidity & Equilibrium Lines", group=G_STYLE)
showHUD = input.bool(true, title="Show Data Dashboard", group=G_STYLE)
// =========================================================================
// STATE VARIABLES & SESSION LOGIC
// =========================================================================
inSession = not na(time(timeframe.period, sessionTime))
newSession = inSession and not inSession[1]
endSession = not inSession and inSession[1]
var float sessionHigh = na
var float sessionLow = na
var int sessionStartBar = na
if newSession
sessionHigh := high
sessionLow := low
sessionStartBar := bar_index
else if inSession
sessionHigh := math.max(sessionHigh, high)
sessionLow := math.min(sessionLow, low)
currentRange = sessionHigh - sessionLow
sessionEq = sessionLow + (currentRange / 2)
// =========================================================================
// HISTORICAL AVERAGE CALCULATION (AAR)
// =========================================================================
var float[] pastRanges = array.new_float(0)
if endSession
array.unshift(pastRanges, currentRange)
if array.size(pastRanges) > lookback
array.pop(pastRanges)
avgRange = array.size(pastRanges) > 0 ? array.avg(pastRanges) : na
isExpansion = inSession and not na(avgRange) and (currentRange > (avgRange * multiplier))
// =========================================================================
// DRAWING: BOXES, EQUILIBRIUM & LIQUIDITY EXTENSIONS
// =========================================================================
var box sessionBox = na
var line eqLine = na
var line[] liqLines = array.new_line(0)
if newSession
sessionBox := box.new(left=bar_index, top=sessionHigh, right=bar_index, bottom=sessionLow,
border_color=color.new(colorNormal, 30),
bgcolor=colorNormal)
eqLine := line.new(x1=bar_index, y1=sessionEq, x2=bar_index, y2=sessionEq,
color=colorLine, style=line.style_dashed)
else if inSession
currentColor = isExpansion ? colorExpand : colorNormal
// Update Box
box.set_top(sessionBox, sessionHigh)
box.set_bottom(sessionBox, sessionLow)
box.set_right(sessionBox, bar_index)
box.set_bgcolor(sessionBox, currentColor)
box.set_border_color(sessionBox, color.new(currentColor, 30))
// Update Equilibrium Line
line.set_y1(eqLine, sessionEq)
line.set_y2(eqLine, sessionEq)
line.set_x2(eqLine, bar_index)
if endSession
// Project liquidity pools forward into London
barsToExtend = (60 / timeframe.multiplier) * extendHours
endBar = bar_index + math.round(barsToExtend)
line.new(x1=bar_index, y1=sessionHigh, x2=endBar, y2=sessionHigh, color=colorLine, style=line.style_dotted)
line.new(x1=bar_index, y1=sessionLow, x2=endBar, y2=sessionLow, color=colorLine, style=line.style_dotted)
// =========================================================================
// ALERTS
// =========================================================================
// Trigger alert exactly when the threshold is crossed during the session
expansionTrigger = isExpansion and not isExpansion[1]
if expansionTrigger
alert("Asian Expansion Triggered on " + syminfo.ticker + ". London fades invalidated.", alert.freq_once_per_bar)
// =========================================================================
// HEADS UP DISPLAY (HUD)
// =========================================================================
var table hud = table.new(position.bottom_right, columns=2, rows=4, bgcolor=color.new(#131722, 10), border_width=1, border_color=color.new(#363A45, 50))
if showHUD and barstate.islast
rangeTicks = currentRange / syminfo.mintick
avgTicks = na(avgRange) ? 0 : (avgRange / syminfo.mintick)
pctOfAvg = na(avgRange) ? 0 : (currentRange / avgRange) * 100
statusColor = isExpansion ? color.red : color.blue
statusText = isExpansion ? "EXPANSION (NO FADE)" : "NORMAL"
// Header
table.cell(hud, 0, 0, "TOKYO MICROSTRUCTURE", text_color=color.white, text_size=size.small, bgcolor=color.new(#2A2E39, 0), span=2)
// Metrics
table.cell(hud, 0, 1, "Current Range:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
table.cell(hud, 1, 1, str.tostring(rangeTicks, "#.0") + " ticks", text_color=color.white, text_size=size.small, text_halign=text.align_right)
table.cell(hud, 0, 2, "15D Average:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
table.cell(hud, 1, 2, str.tostring(avgTicks, "#.0") + " ticks (" + str.tostring(pctOfAvg, "#") + "%)", text_color=color.white, text_size=size.small, text_halign=text.align_right)
// Status
table.cell(hud, 0, 3, "London Setup:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
table.cell(hud, 1, 3, statusText, text_color=statusColor, text_size=size.small, text_halign=text.align_right)