Back to TradingView PineScript for CFD Traders
Part 3

Position sizing and risk in code

CFD leverage makes position sizing the highest-leverage decision in your strategy. Encoded correctly, it is also the easiest to test.

Advanced11 min readBeginner → AdvancedLesson 13 / 17
Try it in TradingView
Size so a stop-hit costs exactly 1% of equity

Drag Risk % and watch position size change: it shrinks when ATR expands. That's the whole point — constant dollar risk, not constant contracts.

//@version=5
strategy("Lesson 13 - Risk-based sizing", overlay=true,
     initial_capital=10000,
     default_qty_type=strategy.fixed,
     default_qty_value=1,
     slippage=2)

riskPct = input.float(1.0, "Risk % per trade", minval=0.1, step=0.1)
atrLen  = input.int(14, "ATR length", minval=1)
atrMult = input.float(2.0, "ATR stop multiplier", minval=0.5, step=0.1)

atrVal    = ta.atr(atrLen)
// Clamp so a tiny ATR can't blow up the division.
stopDist  = math.max(atrVal * atrMult, syminfo.mintick * 10)
riskMoney = strategy.equity * riskPct / 100
contracts = riskMoney / (stopDist * syminfo.pointvalue)

fast = ta.ema(close, 9)
slow = ta.ema(close, 21)

strategy.entry("Long", strategy.long, qty=contracts, when=ta.crossover(fast, slow))
strategy.exit("Long TP/SL", "Long",
     stop  = strategy.position_avg_price - stopDist,
     limit = strategy.position_avg_price + stopDist * 2)
strategy.close("Long", when=ta.crossunder(fast, slow))

plot(fast, "Fast EMA", color=color.blue, linewidth=2)
plot(slow, "Slow EMA", color=color.orange, linewidth=2)
plot(strategy.position_avg_price, "Entry", color=color.gray, style=plot.style_linebr)
To run it
  1. 1. Open any chart in TradingView and click Pine Editor at the bottom.
  2. 2. Select everything in the editor and paste this over it, then click Save.
  3. 3. Click Add to chart. Open the Strategy Tester tab if it is a strategy.

The sizing problem

On a CFD account, the question "how many contracts do I trade" is the highest-leverage decision in your strategy. A 1% account risk on a 10x leveraged instrument with a 2% stop means you can take a position equal to 50% of your equity — that's fine. The same risk with a 50x instrument and a 1% stop means 200% of equity — and a single tick against you wipes the account.

Risk-based sizing in Pine

riskPct   = input.float(1.0, "Risk %", minval=0.1)
stopDist  = ta.atr(14) * 1.5
riskMoney = strategy.equity * riskPct / 100
contracts = riskMoney / (stopDist * syminfo.pointvalue)

strategy.entry("Long", strategy.long, qty=contracts)
strategy.exit("X", "Long", stop=close - stopDist, limit=close + stopDist * 2)

For a CFD, syminfo.pointvalue is the value of a one-point move per contract. Combined with stop distance in price, this gives you a contract size that risks exactly riskPct percent of equity if the stop is hit.

Why this is better than fixed-contract sizing

If you size to "always 1 contract", your risk scales with volatility. In calm markets you under-trade; in volatile markets a single tick can blow the account. Risk-based sizing keeps your dollar risk constant — your position shrinks when volatility expands, which is exactly when you want it to.

Edge cases

If stopDist is too small (syminfo.mintick rounds), you'll get division blowups or weird fractions. Clamp with max(stopDist, syminfo.mintick * 10).

Testing sizing

The strategy tester reports PnL accurately when sizing is dynamic — that is one of the few areas where Pine's backtest engine handles complexity well. What it does not model: spread widening during your stop, requotes, slippage during news. Subtract at least 1 tick per side to be conservative.

What you should not do

Do not let Pine use default_qty_type=strategy.cash with a hard-coded number — it will over-size in calm markets and under-size in volatile ones, the opposite of what you want.

What you just did

Lesson 13 of 17 in TradingView PineScript for CFD Traders. When you have run the examples or read the section, tick it off and move to the next lesson.