If market structure shifts and clips your profit buffer, stepping down incrementally just bleeds equity while your strategy is clearly out of phase with current volatility or order flow. The objective is absolute capital preservation. The moment equity drops back below the threshold (Initial Capital + $N Buffer), risk snaps back to base. You earn the right to scale; the market revokes it the second your edge softens.
Here is a production-grade Pine Script v5 implementation of this engine. I stripped out the basic indicators and built a modular risk function, paired with a raw liquidity sweep trigger (sweeping a fractal pivot and rejecting) to better reflect actual market microstructure.
Code: Select all
//@version=5
strategy("Dynamic Risk Scaling Engine", overlay=true, initial_capital=100000, commission_type=strategy.commission.cash_per_order, commission_value=2.5, slippage=1)
// =========================================================================
// MODULE: RISK & MONEY MANAGEMENT
// =========================================================================
grp_rm = "Risk Management Engine"
baseRiskPct = input.float(0.5, title="Base Risk (%)", step=0.1, group=grp_rm, tooltip="Capital risk when below profit threshold.")
scaledRiskPct = input.float(1.5, title="Scaled Risk (%)", step=0.1, group=grp_rm, tooltip="Aggressive risk applied only to house money.")
profitBuffer = input.float(5.0, title="Activation Threshold (%)", step=0.5, group=grp_rm, tooltip="Account growth required to unlock scaled risk.")
hardStopEq = input.float(10.0, title="Max Drawdown Killswitch (%)", step=1.0, group=grp_rm)
// =========================================================================
// MODULE: TRADE PARAMETERS
// =========================================================================
grp_ex = "Execution Parameters"
slPoints = input.int(150, title="Stop Loss (Points)", group=grp_ex)
tpPoints = input.int(350, title="Take Profit (Points)", group=grp_ex)
// =========================================================================
// METHOD: DYNAMIC POSITION SIZING
// =========================================================================
f_calculate_qty(sl_pts) =>
init_cap = strategy.initial_capital
curr_eq = strategy.equity
// Killswitch Check
if curr_eq <= (init_cap * (1 - (hardStopEq / 100)))
strategy.close_all(comment="HARD STOP: DD LIMIT")
0.0
else
// Immediate Reset Logic
target_eq = init_cap * (1 + (profitBuffer / 100))
active_risk = curr_eq >= target_eq ? scaledRiskPct : baseRiskPct
// Size calculation accounting for instrument tick values
risk_capital = curr_eq * (active_risk / 100)
tick_value = syminfo.pointvalue * syminfo.mintick
pos_size = risk_capital / (sl_pts * tick_value)
// Return tuple for UI monitoring
[pos_size, active_risk, target_eq]
[qty, currentRisk, requiredEq] = f_calculate_qty(slPoints)
// =========================================================================
// TRIGGER LOGIC: LIQUIDITY SWEEP (Microstructure Proxy)
// =========================================================================
// Detects a sweep of a 5-bar fractal high/low followed by an immediate structural rejection
lookback = 5
pivotHigh = ta.pivothigh(high, lookback, 1)
pivotLow = ta.pivotlow(low, lookback, 1)
var float lastPivotHigh = na
var float lastPivotLow = na
if not na(pivotHigh)
lastPivotHigh := pivotHigh
if not na(pivotLow)
lastPivotLow := pivotLow
// Sweep conditions: Wick pierces pivot, but body closes inside
sweepShort = high > lastPivotHigh and close < lastPivotHigh
sweepLong = low < lastPivotLow and close > lastPivotLow
// =========================================================================
// ORDER ROUTING
// =========================================================================
if sweepLong and strategy.position_size == 0 and qty > 0
strategy.entry("Long", strategy.long, qty=qty)
strategy.exit("Exit L", "Long", loss=slPoints, profit=tpPoints)
if sweepShort and strategy.position_size == 0 and qty > 0
strategy.entry("Short", strategy.short, qty=qty)
strategy.exit("Exit S", "Short", loss=slPoints, profit=tpPoints)
// =========================================================================
// HUD / TELEMETRY
// =========================================================================
var table hud = table.new(position.bottom_right, 2, 3, border_width=1, border_color=color.new(color.gray, 50))
if barstate.islast
isScaled = strategy.equity >= requiredEq
stateColor = isScaled ? color.new(color.teal, 30) : color.new(color.gray, 50)
table.cell(hud, 0, 0, "Equity:", text_color=color.white, bgcolor=color.black)
table.cell(hud, 1, 0, str.tostring(strategy.equity, "#.##"), text_color=color.white, bgcolor=color.black)
table.cell(hud, 0, 1, "Status:", text_color=color.white, bgcolor=stateColor)
table.cell(hud, 1, 1, isScaled ? "SCALED (House Money)" : "BASE (Capital Pres.)", text_color=color.white, bgcolor=stateColor)
table.cell(hud, 0, 2, "Active Risk:", text_color=color.white, bgcolor=stateColor)
table.cell(hud, 1, 2, str.tostring(currentRisk) + "%", text_color=color.white, bgcolor=stateColor)