Here is an enterprise-grade Pine Script v5 architecture. It uses Pine's object-oriented type and method structures to encapsulate state management—keeping the global scope clean—and relies strictly on raw price action structure (BoS) and volume footprint proxies.
Code: Select all
//@version=5
indicator("Microstructure & Imbalance Filter [Pro]", overlay=true, max_lines_count=50)
// =========================================================================
// INPUTS & CONFIGURATION
// =========================================================================
grp_struct = "Market Structure (Raw PA)"
mtfRes = input.timeframe("15", title="HTF Structure Resolution", group=grp_struct)
pivotLeft = input.int(5, title="Pivot Left Legs", group=grp_struct)
pivotRight = input.int(2, title="Pivot Right Legs", group=grp_struct)
grp_flow = "Order Flow / Imbalance Proxy"
volSpike = input.float(1.5, title="Volume Spike Multiplier", tooltip="Requires volume to be X times the SMA to register as institutional footprint", group=grp_flow)
wickAbsorb = input.float(0.4, title="Sweep/Absorption Wick %", tooltip="If price closes within this % of the opposite end on high volume, it signals absorption.", group=grp_flow)
grp_env = "Execution Environment"
maxSpread = input.float(1.2, title="Max Allowed Spread (Pips)", group=grp_env)
sessWindow = input.session("0800-1700", title="Active Liquidity Window", group=grp_env)
// =========================================================================
// TYPES & STATE MANAGEMENT (OOP Architecture)
// =========================================================================
// Encapsulates the execution environment state
type ExecutionEnvironment
bool isInSession
float currentSpread
bool isTradable
method update(ExecutionEnvironment this) =>
this.isInSession := not na(time(timeframe.period, sessWindow))
// Spread calculation (defaults to 0 historically, tracks realtime accurately)
rtSpread = (syminfo.ask - syminfo.bid) * 10000
this.currentSpread := na(rtSpread) ? 0.0 : rtSpread
this.isTradable := this.isInSession and (this.currentSpread <= maxSpread or this.currentSpread == 0.0)
// Encapsulates HTF market structure (BoS / Trend)
type MarketStructure
int bias // 1 = Bullish, -1 = Bearish
float lastSwingH
float lastSwingL
method updateStructure(MarketStructure this, float ph, float pl, float c) =>
if not na(ph)
this.lastSwingH := ph
if not na(pl)
this.lastSwingL := pl
// Break of Structure Logic (Close outside established swings)
if c > this.lastSwingH
this.bias := 1
else if c < this.lastSwingL
this.bias := -1
// Encapsulates Bar-by-Bar Microstructure (Initiation vs Absorption)
type OrderFlow
bool isBullImbalance
bool isBearImbalance
method mapFootprint(OrderFlow this, float h, float l, float c, float v, float avgV) =>
range_hl = h - l == 0 ? syminfo.mintick : h - l
isHighVol = v > (avgV * volSpike)
// Close proximity to extremes (0.0 = Low, 1.0 = High)
closePos = (c - l) / range_hl
// Bull Imbalance: High vol pushing price to close near highs (Initiation) OR
// High vol sweeping lows but closing aggressively higher (Absorption)
this.isBullImbalance := isHighVol and (closePos >= (1.0 - wickAbsorb))
// Bear Imbalance: High vol pushing price near lows OR sweeping highs and rejecting
this.isBearImbalance := isHighVol and (closePos <= wickAbsorb)
// =========================================================================
// INSTANTIATION & EXECUTION LOOP
// =========================================================================
// 1. Initialize State Objects
var env = ExecutionEnvironment.new(false, 0.0, false)
var ms = MarketStructure.new(0, high, low)
var of = OrderFlow.new(false, false)
// 2. Fetch HTF Data (Raw PA only, no smoothing)
[htf_ph, htf_pl, htf_c] = request.security(syminfo.tickerid, mtfRes, [ta.pivothigh(high, pivotLeft, pivotRight), ta.pivotlow(low, pivotLeft, pivotRight), close], lookahead=barmerge.lookahead_on)
// 3. Update States
env.update()
ms.updateStructure(htf_ph, htf_pl, htf_c)
avgVolume = ta.sma(volume, 20)
of.mapFootprint(high, low, close, volume, avgVolume)
// =========================================================================
// FILTER LOGIC (THE GATEKEEPER)
// =========================================================================
// Bias is ONLY granted when HTF structure and LTF order flow align in a valid environment.
// The filter says NO to everything else.
bool permitLong = env.isTradable and (ms.bias == 1) and of.isBullImbalance
bool permitShort = env.isTradable and (ms.bias == -1) and of.isBearImbalance
// =========================================================================
// VISUALIZATION (Clean & Unobtrusive)
// =========================================================================
// Background color highlights specific bars where all conditions are met for stalking
color longFilterColor = permitLong ? color.new(color.teal, 80) : na
color shortFilterColor = permitShort ? color.new(color.maroon, 80) : na
bgcolor(longFilterColor, title="Stalk Long Bias")
bgcolor(shortFilterColor, title="Stalk Short Bias")
// Optional: Plot HTF Structure lines for visual confirmation (uncomment if desired)
// plot(ms.lastSwingH, color=color.new(color.red, 50), style=plot.style_stepline, title="HTF Resistance")
// plot(ms.lastSwingL, color=color.new(color.green, 50), style=plot.style_stepline, title="HTF Support")