Risk
Risk-based sizing, position caps, drawdown stops, daily loss limits and cooldowns, written in the strategy script.
Risk rules are part of the script: compute the size, check the account and decide whether to trade. The examples below combine the order functions with the values from Position and trades.
Sizing by risk
Risk a fixed share of equity per trade: the size is the money at risk divided by the distance to the stop.
script "Risk 0.5 % per trade"
strategy(initialCapital: 10000.0, leverage: 5.0)
input (
riskPct = input.float(0.5, title: "Risk per trade (% equity)", min: 0.05, max: 5.0)
stopAtr = input.float(1.5, title: "Stop distance (ATR)", min: 0.2)
)
data chart = subscribe(data.ohlcv)
on chart.close {
let atr = chart.atr(14)
let fast = ta.ema(chart.close, 10)
let slow = ta.ema(chart.close, 30)
if atr != null && atr > 0.0 && ta.crossover(fast, slow) {
let stopDistance = atr * stopAtr
let qty = strategy.equity * riskPct / 100.0 / stopDistance
strategy.entry("Long", strategy.long, qty)
strategy.exit("Long SL", "Long", loss: math.round(stopDistance / context.tickSize))
}
}A tight stop can ask for more than the margin allows; the emulator then skips the entry rather than shrink it. Cap the size by what the account can carry:
script "Risk sizing with a cap"
strategy(initialCapital: 10000.0, leverage: 3.0)
data chart = subscribe(data.ohlcv)
on chart.close {
let atr = chart.atr(14)
let close = chart.close
if atr != null && close != null && atr > 0.0 && ta.crossover(close, ta.sma(chart.close, 50)) {
let byRisk = strategy.equity * 0.01 / (atr * 2.0)
// 3x leverage: at most three times equity in notional, with some room.
let byMargin = strategy.equity * 3.0 * 0.95 / close
strategy.entry("Long", strategy.long, math.min(byRisk, byMargin))
strategy.exit("Long SL", "Long", stop: close - atr * 2.0)
}
}Position cap
Only add while the position is below a limit:
script "Pyramid with a cap"
strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 0.5, pyramiding: 4)
input maxSize = input.float(1.5, title: "Largest position", min: 0.1)
data chart = subscribe(data.ohlcv)
on chart.close {
let ema = ta.ema(chart.close, 20)
if ema != null && chart.close > ema && strategy.positionSize + 0.5 <= maxSize {
strategy.entry("Add", strategy.long)
}
if ema != null && chart.close < ema {
strategy.closeAll()
}
}Drawdown stop
Track the equity peak and stop trading after a drawdown:
script "Halt after 10 % drawdown"
strategy(initialCapital: 10000.0, defaultQtyType: strategy.percentOfEquity, defaultQtyValue: 50.0)
data chart = subscribe(data.ohlcv)
state peak = 0.0
state halted = false
on chart.close {
peak = math.max(peak, strategy.equity)
if !halted && strategy.equity < peak * 0.9 {
halted = true
strategy.closeAll(comment: "Drawdown halt")
strategy.cancelAll()
}
if !halted && ta.crossover(ta.ema(chart.close, 9), ta.ema(chart.close, 21)) {
strategy.entry("Long", strategy.long)
}
if ta.crossunder(ta.ema(chart.close, 9), ta.ema(chart.close, 21)) {
strategy.close("Long")
}
}Daily loss limit
Remember equity at the start of each UTC day and stand aside for the rest of the day after losing 2 %:
script "Daily loss limit"
strategy(initialCapital: 10000.0, defaultQtyType: strategy.percentOfEquity, defaultQtyValue: 30.0)
data chart = subscribe(data.ohlcv)
state day: time? = null
state dayEquity = 0.0
on chart.close {
let t = chart.time
if t != null {
let today = time.dayStart(t)
if day != today {
day = today
dayEquity = strategy.equity
}
}
let stopped = strategy.equity < dayEquity * 0.98
if stopped && strategy.positionSize != 0.0 {
strategy.closeAll(comment: "Daily limit")
}
if !stopped && ta.crossover(chart.close, ta.ema(chart.close, 50)) {
strategy.entry("Long", strategy.long)
strategy.exit("Long SL", "Long", loss: 200.0)
}
}Cooldown after a loss
Wait a number of bars after a losing trade:
script "Cooldown"
strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)
input cooldownBars = input.int(12, title: "Bars to wait after a loss", min: 1)
data chart = subscribe(data.ohlcv)
state seen = 0
state wait = 0
on chart.close {
if strategy.closedTrades > seen {
let last = strategy.closedTrade(strategy.closedTrades - 1)
if last != null && last.profit < 0.0 {
wait = cooldownBars
}
seen = strategy.closedTrades
}
if wait > 0 {
wait -= 1
} else if ta.crossover(chart.close, ta.sma(chart.close, 20)) {
strategy.entry("Long", strategy.long)
strategy.exit("Exit", "Long", profit: 250.0, loss: 120.0)
}
}Choosing limits
- Size from the stop distance, not from a fixed share of equity, so every trade risks the same.
- Test limits on a period you did not tune on, for example another market.
- Commission and slippage grow with trade count: set them before judging a high-frequency strategy.