We have to solve a unique logic problem: the indicator naturally filters out C-grade setups, so it can't automatically know if you took a bad trade off-script.
The script acts as a virtual broker. When an A+ signal fires, it opens a "virtual trade" and tracks the price. If price hits the ATR stop-loss, it increments your daily loss count.
An input setting in the indicator properties. If you break your rules and take a C-grade trade (like chasing an extended move), you manually increment this input.
If either the daily A+ losses or the C-grade tilt counter hits your max threshold, the script triggers the circuit breaker. It halts all alerts, shades the dashboard red, and forces the "soft mental stop" mentioned in your forum rules.
Code: Select all
//@version=5
indicator("Institutional Execution Framework [PRO+]", shorttitle="A+ Exec PRO+", overlay=true, timeframe="", timeframe_gaps=true)
// ==============================================================================
// 1. INPUT GROUPS & PARAMETERS
// ==============================================================================
var string GRP_TIME = "1. Session & Timeframes"
var string GRP_MA = "2. Trend Alignment"
var string GRP_FILT = "3. Location & Anti-Chasing"
var string GRP_RISK = "4. Risk Pre-Sizing Engine"
var string GRP_CB = "5. Circuit Breakers (Tilt & Loss)"
i_sessTime = input.session("0800-1100", "A+ Session Window", group=GRP_TIME)
i_htf = input.timeframe("60", "Higher Timeframe", group=GRP_TIME)
i_ltf = input.timeframe("15", "Lower Timeframe", group=GRP_TIME)
i_emaFast = input.int(9, "Fast EMA", group=GRP_MA)
i_emaSlow = input.int(21, "Slow EMA", group=GRP_MA)
i_maxAtrExt = input.float(2.0, "Max ATR Extension", step=0.1, group=GRP_FILT, tooltip="Disqualifies trades if price is further than X ATRs from the Slow EMA.")
i_accSize = input.float(50000, "Account Balance ($)", group=GRP_RISK)
i_riskPct = input.float(1.0, "Risk Per Trade (%)", step=0.1, group=GRP_RISK)
i_slAtrMult = input.float(1.5, "Stop Loss (ATR Multiplier)", step=0.1, group=GRP_RISK)
i_maxLosses = input.int(2, "Max Daily A+ Losses", group=GRP_CB)
i_maxCGrade = input.int(2, "Max C-Grade Tilt Limit", group=GRP_CB)
i_cGrades = input.int(0, "Confessional: C-Grades Taken Today", group=GRP_CB, tooltip="Increment this manually if you took a FOMO trade off-script. Trips the breaker if it hits the limit.")
i_virtRR = input.float(1.5, "Virtual TP (For Tracker Reset)", step=0.1, group=GRP_CB, tooltip="Reward-to-Risk ratio used to clear winning virtual trades so the script can track the next signal.")
// ==============================================================================
// 2. DATA FETCHING (TUPLE OPTIMIZED)
// ==============================================================================
f_ema() => [ta.ema(close, i_emaFast), ta.ema(close, i_emaSlow)]
[htfF, htfS] = request.security(syminfo.tickerid, i_htf, f_ema(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_ignore)
[ltfF, ltfS] = request.security(syminfo.tickerid, i_ltf, f_ema(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_ignore)
[chartF, chartS] = f_ema()
// ==============================================================================
// 3. CORE LOGIC (ALIGNMENT, LOCATION, PULLBACK)
// ==============================================================================
inSess = not na(time(timeframe.period, i_sessTime, syminfo.timezone))
atr = ta.atr(14)
distToSlow = math.abs(close - chartS)
slDistance = atr * i_slAtrMult
htfBull = htfF > htfS, ltfBull = ltfF > ltfS, chartBull = chartF > chartS
htfBear = htfF < htfS, ltfBear = ltfF < ltfS, chartBear = chartF < chartS
bullAligned = htfBull and ltfBull and chartBull
bearAligned = htfBear and ltfBear and chartBear
isExtended = distToSlow > (atr * i_maxAtrExt)
inValueLong = low <= chartF and close > chartS
inValueShort = high >= chartF and close < chartS
// Base A+ Conditions
baseAPlusLong = inSess and bullAligned and not isExtended and inValueLong
baseAPlusShort = inSess and bearAligned and not isExtended and inValueShort
// ==============================================================================
// 4. CIRCUIT BREAKER & VIRTUAL TRADE MANAGER
// ==============================================================================
var int dailyLosses = 0
if ta.change(time("D"))
dailyLosses := 0 // Reset at midnight
var int vTradeDir = 0 // 1 = Long, -1 = Short, 0 = Flat
var float vSL = na
var float vTP = na
// Check Exits BEFORE Entries on current bar
if vTradeDir == 1
if low <= vSL
dailyLosses += 1
vTradeDir := 0 // Stopped out
else if high >= vTP
vTradeDir := 0 // Target hit, clear state
if vTradeDir == -1
if high >= vSL
dailyLosses += 1
vTradeDir := 0 // Stopped out
else if low <= vTP
vTradeDir := 0 // Target hit, clear state
// Circuit Breaker Evaluation
breakerTripped = (dailyLosses >= i_maxLosses) or (i_cGrades >= i_maxCGrade)
// Final Executable Signals
triggerLong = baseAPlusLong and not breakerTripped and vTradeDir == 0
triggerShort = baseAPlusShort and not breakerTripped and vTradeDir == 0
// Execute Virtual Entry
if triggerLong
vTradeDir := 1
vSL := close - slDistance
vTP := close + (slDistance * i_virtRR)
if triggerShort
vTradeDir := -1
vSL := close + slDistance
vTP := close - (slDistance * i_virtRR)
// ==============================================================================
// 5. RISK & POSITION SIZING CALCULATOR
// ==============================================================================
riskDollars = i_accSize * (i_riskPct / 100)
calcUnits = riskDollars / (slDistance * syminfo.pointvalue)
units = na(calcUnits) or calcUnits == 0 ? 0 : calcUnits
// ==============================================================================
// 6. VISUALS & HUD
// ==============================================================================
bgcolor(breakerTripped ? color.new(color.red, 95) : triggerLong ? color.new(color.teal, 85) : triggerShort ? color.new(color.maroon, 85) : na, title="A+ Zone Shading")
plot(chartF, "Fast EMA", color=color.new(color.aqua, 0), linewidth=2)
plot(chartS, "Slow EMA", color=color.new(color.orange, 0), linewidth=2)
var table hud = table.new(position.top_right, 2, 8, border_width=1, border_color=color.new(color.gray, 80), frame_color=color.new(color.gray, 80), frame_width=1)
if barstate.islast
// Headers
table.cell(hud, 0, 0, "EXECUTION METRIC", text_color=color.white, bgcolor=color.new(color.black, 20), text_size=size.small)
table.cell(hud, 1, 0, "LIVE STATUS", text_color=color.white, bgcolor=color.new(color.black, 20), text_size=size.small)
// Status Gates
table.cell(hud, 0, 1, "Session Window", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 1, inSess ? "ACTIVE" : "CLOSED", text_color=inSess ? color.lime : color.gray, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 0, 2, "MTF Alignment", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 2, bullAligned ? "BULLISH" : bearAligned ? "BEARISH" : "MIXED", text_color=bullAligned ? color.lime : bearAligned ? color.red : color.gray, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 0, 3, "Location Guard", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 3, isExtended ? "CHASING (EXTENDED)" : "WITHIN VALUE", text_color=isExtended ? color.red : color.lime, bgcolor=color.new(color.black, 60), text_size=size.small)
// Circuit Breakers Tracker
string cbStatus = "Loss: " + str.tostring(dailyLosses) + "/" + str.tostring(i_maxLosses) + " | C-Grade: " + str.tostring(i_cGrades) + "/" + str.tostring(i_maxCGrade)
table.cell(hud, 0, 4, "Circuit Breakers", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 4, cbStatus, text_color=breakerTripped ? color.red : color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
// Grade Output
string gradeTxt = breakerTripped ? "HALTED (BREAKER TRIPPED)" : vTradeDir != 0 ? "IN VIRTUAL TRADE" : (triggerLong or triggerShort) ? "A+ TRIGGER READY" : (bullAligned or bearAligned) and not isExtended ? "WAITING PULLBACK" : isExtended ? "BLOCKED (C-RISK)" : "NO SETUP"
color gradeClr = breakerTripped ? color.red : vTradeDir != 0 ? color.yellow : (triggerLong or triggerShort) ? color.lime : (bullAligned or bearAligned) and not isExtended ? color.orange : color.gray
table.cell(hud, 0, 5, "SYSTEM GRADE", text_color=color.white, bgcolor=breakerTripped ? color.new(color.red, 70) : color.new(color.blue, 70), text_size=size.small)
table.cell(hud, 1, 5, gradeTxt, text_color=gradeClr, bgcolor=breakerTripped ? color.new(color.red, 70) : color.new(color.blue, 70), text_size=size.small)
// Risk Parameters
table.cell(hud, 0, 6, "Risk Allocation", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 6, "$" + str.tostring(riskDollars, "#.##") + " | " + str.tostring(slDistance / syminfo.mintick, "#") + " ticks", text_color=color.white, bgcolor=color.new(color.black, 60), text_size=size.small)
// Final Sizing Output
table.cell(hud, 0, 7, "PRE-SIZED UNITS", text_color=color.silver, bgcolor=color.new(color.black, 60), text_size=size.small)
table.cell(hud, 1, 7, str.tostring(units, "#.##"), text_color=color.aqua, bgcolor=color.new(color.black, 60), text_size=size.normal)
// ==============================================================================
// 7. WEBHOOK ALERTS (Disabled Automatically if Breaker Trips)
// ==============================================================================
string jsonLong = '{"ticker": "' + syminfo.ticker + '", "action": "buy", "units": "' + str.tostring(units, "#.##") + '", "sl_dist": "' + str.tostring(slDistance, "#.#####") + '"}'
string jsonShort = '{"ticker": "' + syminfo.ticker + '", "action": "sell", "units": "' + str.tostring(units, "#.##") + '", "sl_dist": "' + str.tostring(slDistance, "#.#####") + '"}'
alertcondition(triggerLong, "A+ Long Trigger", message=jsonLong)
alertcondition(triggerShort, "A+ Short Trigger", message=jsonShort)