Code: Select all
//@version=6
strategy("Institutional Macro Engine v6", overlay=true, calc_on_every_tick=true, margin_long=100, margin_short=100)
// =========================================================================
// 1. INPUTS & RISK PARAMETERS
// =========================================================================
var grpTime = "Macro Event Target"
newsHour = input.int(8, "Hour (Exchange Time)", group=grpTime, minval=0, maxval=23)
newsMinute = input.int(30, "Minute", group=grpTime, minval=0, maxval=59)
var grpRisk = "Risk Management Engine"
riskPct = input.float(1.0, "Risk Per Trade (%)", group=grpRisk, step=0.1)
atrPeriod = input.int(14, "ATR Period (Stop Sizing)", group=grpRisk)
atrMult = input.float(1.5, "ATR Stop Multiplier", group=grpRisk)
maxSpread = input.float(1.5, "Max Spread (Pips)", group=grpRisk)
var grpExec = "Execution States"
preMins = input.int(10, "Kill-Switch Mins Before", group=grpExec)
digestMins = input.int(15, "Digest Window (Mins)", group=grpExec)
ttlMins = input.int(45, "Time-To-Live (Mins)", group=grpExec, tooltip="Cut trade if stagnant")
// =========================================================================
// 2. ENUMS & DATA STRUCTURES (v6 Features)
// =========================================================================
// Enums provide type-safe control over the state machine
enum MarketState
Active
PreNewsVacuum
Digest
PostNewsWindow
// UDT to encapsulate the structural impulse data
type MacroStructure
float impHigh
float impLow
float fib50
float fib618
bool isLocked
// =========================================================================
// 3. STATE MACHINE & TIME ENGINE
// =========================================================================
currMins = hour(time) * 60 + minute(time)
newsMins = newsHour * 60 + newsMinute
MarketState state = MarketState.Active
if (currMins >= (newsMins - preMins)) and (currMins < newsMins)
state := MarketState.PreNewsVacuum
else if (currMins >= newsMins) and (currMins < (newsMins + digestMins))
state := MarketState.Digest
else if (currMins >= (newsMins + digestMins)) and (currMins < (newsMins + 120))
state := MarketState.PostNewsWindow
// =========================================================================
// 4. LIQUIDITY & SPREAD VALIDATION
// =========================================================================
// Pine v6 introduces bid/ask. We use these in realtime; fallback to high/low for historical backtesting
float currentSpreadPips = barstate.isrealtime ? (ask - bid) / syminfo.mintick / 10 : (high - low) / syminfo.mintick / 10
// Strict booleans in v6: variables must evaluate to true/false, never 'na'
bool isSpreadTight = na(currentSpreadPips) ? false : (currentSpreadPips <= maxSpread)
// =========================================================================
// 5. STRUCTURAL IMPULSE MAPPING
// =========================================================================
var MacroStructure struct = MacroStructure.new(na, na, na, na, false)
var int tradeStartTime = na
if state == MarketState.Digest
// Reset structure on first tick of digest
if struct.isLocked
struct := MacroStructure.new(high, low, na, na, false)
else
struct.impHigh := math.max(nz(struct.impHigh, high), high)
struct.impLow := math.min(nz(struct.impLow, low), low)
else if state == MarketState.PostNewsWindow and not struct.isLocked
// Lock the structure and calculate institutional discount levels
struct.fib50 := struct.impHigh - ((struct.impHigh - struct.impLow) * 0.5)
struct.fib618 := struct.impHigh - ((struct.impHigh - struct.impLow) * 0.618)
struct.isLocked := true
// =========================================================================
// 6. VOLATILITY-ADJUSTED RISK MODEL
// =========================================================================
float currATR = ta.atr(atrPeriod)
float stopDist = currATR * atrMult
// Dynamic Sizing: (Account Equity * Risk %) / (Stop Distance in Account Currency)
float riskAmount = strategy.equity * (riskPct / 100)
float pointValue = syminfo.pointvalue
float lotSize = pointValue > 0 and stopDist > 0 ? (riskAmount / (stopDist / syminfo.mintick * pointValue)) : 0
// =========================================================================
// 7. EXECUTION & TIME EXITS
// =========================================================================
if state == MarketState.PreNewsVacuum
strategy.cancel_all()
strategy.close_all(comment="KILL: Vacuum")
// Entry Engine
if state == MarketState.PostNewsWindow and struct.isLocked and strategy.position_size == 0
// Setup: Price testing the 50%-61.8% discount array
bool inDiscountZone = close > struct.fib618 and low <= struct.fib50
if inDiscountZone and isSpreadTight and lotSize > 0
strategy.entry("Macro_Cont", strategy.long, qty=lotSize)
strategy.exit("Macro_Risk", "Macro_Cont", stop=close - stopDist, limit=struct.impHigh)
tradeStartTime := currMins
// Time-To-Live (TTL) Hard Exit
if strategy.position_size != 0
if (currMins - tradeStartTime) >= ttlMins
strategy.close("Macro_Cont", comment="TTL: Stagnant Flow")
// =========================================================================
// 8. VISUAL DIAGNOSTICS & DASHBOARD
// =========================================================================
bgcolor(state == MarketState.PreNewsVacuum ? color.new(color.red, 90) : na, title="Vacuum Zone")
bgcolor(state == MarketState.Digest ? color.new(color.orange, 90) : na, title="Digest Zone")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.impHigh : na, color=color.new(color.green, 50), style=plot.style_linebr, title="Impulse High")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.impLow : na, color=color.new(color.red, 50), style=plot.style_linebr, title="Impulse Low")
plot(struct.isLocked and state == MarketState.PostNewsWindow ? struct.fib50 : na, color=color.new(color.blue, 0), style=plot.style_cross, title="50% Retrace")
// v6 Professional Text Formatting on Dashboard
var table dash = table.new(position.bottom_right, 2, 2, border_width = 1)
if barstate.islast
table.cell(dash, 0, 0, "Market State:", text_color=color.gray)
table.cell(dash, 1, 0, str.tostring(state), text_color=color.white, text_formatting=text.format_bold)
table.cell(dash, 0, 1, "Top-of-Book Spread:", text_color=color.gray)
table.cell(dash, 1, 1, str.tostring(currentSpreadPips, "#.#") + " Pips", text_color=isSpreadTight ? color.green : color.red)