To enforce a shared risk budget, the script must calculate your total risk allowance in dollars, divide it by the number of correlated pairs you are actively trading, and then translate that allocated dollar amount into a specific lot size using an ATR-based stop loss.
This prevents you from accidentally doubling or tripling your risk exposure when taking setups on EURUSD, EURJPY, and EURGBP simultaneously.
Code: Select all
//@version=5
indicator("EURUSD Risk Desk & Dynamic Sizer", overlay=true)
// =========================================================================
// INPUTS & RULES
// =========================================================================
grp1 = "Session & News Lockouts (Exchange Time)"
newsWindow = input.session("1315-1345", "Tier-1 News (Zero Risk)", group=grp1)
londonOpen = input.session("0700-0830", "London Open (Sweep Risk)", group=grp1)
grp2 = "Discipline Breaker"
processBreaksHit = input.bool(false, "2 Process Breaks Hit? (LOCK CHART)", group=grp2)
grp3 = "Dynamic Position Sizing (Shared Budget)"
acctBalance = input.float(100000, "Account Balance ($)", group=grp3)
totalRiskPct = input.float(1.0, "Total Risk Budget (%)", group=grp3, step=0.1, tooltip="Max risk across ALL open EUR pairs combined.")
activePairs = input.int(1, "Active Correlated EUR Pairs", group=grp3, minval=1, maxval=5, tooltip="How many EUR pairs are you trading right now? (Divides the budget)")
atrLength = input.int(14, "ATR Length for Stop Loss", group=grp3)
atrMult = input.float(1.5, "ATR Multiplier", group=grp3, step=0.1)
// =========================================================================
// TIME LOGIC
// =========================================================================
inNews = not na(time(timeframe.period, newsWindow))
inLondon = not na(time(timeframe.period, londonOpen))
// =========================================================================
// POSITION SIZING MATH
// =========================================================================
// 1. Calculate the allocated risk budget in dollars
totalRiskUsd = acctBalance * (totalRiskPct / 100)
allocatedRiskUsd = totalRiskUsd / activePairs
// 2. Calculate the Stop Loss Distance in price terms using ATR
atrValue = ta.atr(atrLength)
stopLossPriceDist = atrValue * atrMult
// 3. Calculate Lot Size (Assuming Quote Currency = Account Currency, e.g., USD)
// 1 Standard Lot = 100,000 units
positionSizeUnits = allocatedRiskUsd / stopLossPriceDist
positionSizeLots = positionSizeUnits / 100000
// =========================================================================
// VISUAL ENFORCEMENT (BACKGROUNDS)
// =========================================================================
bgcolor(processBreaksHit ? color.new(color.maroon, 20) : na, title="Lockout Background")
bgcolor(inNews and not processBreaksHit ? color.new(color.red, 80) : na, title="News Window")
bgcolor(inLondon and not processBreaksHit ? color.new(color.orange, 90) : na, title="London Open")
// =========================================================================
// ON-CHART RISK DASHBOARD
// =========================================================================
var table riskDesk = table.new(position.top_right, 2, 8, bgcolor=color.new(color.black, 20), border_width=1, border_color=color.gray)
if barstate.islast
// Header
table.cell(riskDesk, 0, 0, "EURUSD RISK DESK", text_color=color.white, text_halign=text.align_left, bgcolor=color.new(color.blue, 60))
table.cell(riskDesk, 1, 0, "STATUS", text_color=color.white, text_halign=text.align_center, bgcolor=color.new(color.blue, 60))
// Rule 1: Sizing Outputs
table.cell(riskDesk, 0, 1, "Budget Split (" + str.tostring(activePairs) + " Pairs)", text_color=color.white, text_halign=text.align_left)
table.cell(riskDesk, 1, 1, "$" + str.tostring(allocatedRiskUsd, "#.##") + " per pair", text_color=color.aqua, text_halign=text.align_center)
table.cell(riskDesk, 0, 2, "Stop Distance (ATR x" + str.tostring(atrMult) + ")", text_color=color.white, text_halign=text.align_left)
table.cell(riskDesk, 1, 2, str.tostring(stopLossPriceDist * 10000, "#.##") + " pips", text_color=color.silver, text_halign=text.align_center)
table.cell(riskDesk, 0, 3, "MAX POSITION SIZE", text_color=color.white, text_halign=text.align_left, bgcolor=color.new(color.green, 70))
table.cell(riskDesk, 1, 3, str.tostring(positionSizeLots, "#.##") + " Lots", text_color=color.white, text_halign=text.align_center, bgcolor=color.new(color.green, 70))
// Rule 2: Tier-1 News
table.cell(riskDesk, 0, 4, "Tier-1 News Window", text_color=color.white, text_halign=text.align_left)
table.cell(riskDesk, 1, 4, inNews ? "FLAT / ZERO RISK" : "CLEAR", text_color=inNews ? color.red : color.green, text_halign=text.align_center)
// Rule 3: London Open
table.cell(riskDesk, 0, 5, "London Open", text_color=color.white, text_halign=text.align_left)
table.cell(riskDesk, 1, 5, inLondon ? "MAX R / SWEEP RISK" : "CLEAR", text_color=inLondon ? color.orange : color.green, text_halign=text.align_center)
// Rule 4: Process Breaks
table.cell(riskDesk, 0, 6, "2 Process Breaks Limit", text_color=color.white, text_halign=text.align_left)
table.cell(riskDesk, 1, 6, processBreaksHit ? "DONE FOR MORNING" : "ACTIVE", text_color=processBreaksHit ? color.red : color.green, text_halign=text.align_center)