FlowscopeDocs

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.

Flowscope Script
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:

Flowscope Script
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:

Flowscope Script
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:

Flowscope Script
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 %:

Flowscope Script
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:

Flowscope Script
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.