The script
One strategy, five sizing modes chosen in the settings, each computing the quantity it sends, with the cash at risk shown in the Data Window. Paste it into a new strategy in the Pine Editor, then save and add it to the chart.
//@version=6
strategy("Position sizing modes", overlay = true, initial_capital = 10000, default_qty_type = strategy.percent_of_equity, default_qty_value = 10, commission_type = strategy.commission.percent, commission_value = 0.05, slippage = 2, margin_long = 100, margin_short = 100)
sizing = input.string("Risk % per trade", "Sizing", options = ["Properties (default qty)", "% of equity", "Fixed cash", "Fixed units", "Risk % per trade"])
equityPct = input.float(10, "% of equity", minval = 0.1, maxval = 100)
cashAmt = input.float(1000, "Cash per trade (account currency)", minval = 1)
units = input.float(1, "Units per trade", minval = 0.000001)
riskPct = input.float(1, "Risk per trade (% of equity)", minval = 0.1, maxval = 10, step = 0.1)
stopMult = input.float(2, "Stop distance (x ATR)", minval = 0.5, step = 0.5)
targetR = input.float(2, "Target (x stop distance)", minval = 0.5, step = 0.5)
wholeOnly = input.bool(false, "Round down to whole units")
atr = ta.atr(14)
fast = ta.sma(close, 20)
slow = ta.sma(close, 50)
longSignal = ta.crossover(fast, slow)
float stopDist = stopMult * atr
// Equity in the symbol's currency, and what one unit is worth
float equity = strategy.convert_to_symbol(strategy.equity)
float unitValue = close * syminfo.pointvalue
float rawQty = switch sizing
"% of equity" => equity * equityPct / 100 / unitValue
"Fixed cash" => strategy.convert_to_symbol(cashAmt) / unitValue
"Fixed units" => units
"Risk % per trade" => equity * riskPct / 100 / (stopDist * syminfo.pointvalue)
=> na
// Never more than the equity can pay for at 100% margin
float qty = math.min(rawQty, equity / unitValue)
qty := wholeOnly ? math.floor(qty) : qty
var float stopPrice = na
var float targetPrice = na
if longSignal and strategy.position_size == 0
stopPrice := close - stopDist
targetPrice := close + targetR * stopDist
if sizing == "Properties (default qty)"
// No qty: the order uses default_qty_type and default_qty_value
strategy.entry("Long", strategy.long)
else if qty > 0
strategy.entry("Long", strategy.long, qty = qty)
if not na(stopPrice)
strategy.exit("Exit", "Long", stop = stopPrice, limit = targetPrice)
// Cash lost if the stop is hit, in the symbol's currency, for the size this bar would send
float riskCash = qty * stopDist * syminfo.pointvalue
plot(strategy.position_size > 0 ? stopPrice : na, "Stop", color.red, style = plot.style_linebr)
plot(strategy.position_size > 0 ? targetPrice : na, "Target", color.teal, style = plot.style_linebr)
plot(qty, "Quantity this bar", display = display.data_window)
plot(riskCash, "Risk if stopped", display = display.data_window)
plot(strategy.position_size, "Position size", display = display.data_window)How it works
- Properties.
default_qty_type = strategy.percent_of_equity, default_qty_value = 10is whatstrategy.entry()uses when it gets noqty. It also fills the "Order size" field of the strategy's Properties tab, where the user can change it. The other types arestrategy.fixed(units) andstrategy.cash(an amount of money). - % of equity buys units worth that share of the equity:
equity × pct / 100 / (close × pointvalue). That sizes the position's value, not its risk. - Fixed cash and fixed units give the same exposure on every trade, whatever the account does.
- Risk % per trade divides the cash to lose (
equity × riskPct / 100) by what one unit loses if the stop is hit (stopDist × pointvalue). A wide stop gets a small position and a tight stop a large one, so a stop-out always costs about the same share of equity. syminfo.pointvalueis the value of one full point of price for one unit: 1 for most stocks, forex and crypto, larger for futures (50 for the E-mini S&P 500). Leaving it out sizes futures many times too large.strategy.convert_to_symbol()converts an amount from the strategy's account currency to the symbol's currency, for a strategy in USD trading a symbol quoted in EUR, for example.- The cap keeps any mode from buying more than the equity can pay for, which matters with tight stops in risk mode.
Pitfalls
- Percent of equity isn't risk. 10% of equity in a position with a stop 2% away risks 0.2% of equity; with a stop 10% away, 1%. If you meant "risk 1% per trade", use the risk formula, not
strategy.percent_of_equity. - Forgetting pointvalue. On a futures contract, one point is worth
syminfo.pointvaluein money. A risk formula without it treats the E-mini S&P 500 as if one point were worth 1, and sizes the position 50 times too large. - Margin. In v6
margin_longandmargin_shortdefault to 100%: a position worth more than the equity isn't funded, and the Strategy Tester simulates a margin call that closes part of it. Cap the size, as the script does, or set the margin your broker really requires. - Whole units. Stocks and futures trade in whole units.
math.floor(qty)rounds down, and on a small account it can round down to 0: the script then skips the trade instead of sending an order of zero. - qty overrides the Properties tab. Once
strategy.entry()gets aqty, the "Order size" in the Properties tab no longer does anything for that order, which surprises users who change it and see no difference.
Variations
Short trades. The formulas are the same with the stop above the entry: stopPrice := close + stopDist and strategy.entry("Short", strategy.short, qty = qty).
Risk from the equity at the start of the day. Sizing from strategy.equity includes the open trade's unrealized profit or loss. To size every trade of a day from the same base, store the equity when the day starts:
// Equity at the first bar of each day, used for every trade of that day
var float dayEquity = strategy.initial_capital
if timeframe.change("D")
dayEquity := strategy.equity
plot(dayEquity, "Equity at the day's start", display = display.data_window)
The strategy template wraps risk sizing, an exposure cap, filters and exits into one reusable strategy, and the free position size calculator gives the same numbers by hand.
Questions
What does default_qty_value do if I pass qty to strategy.entry?
Nothing for that order: an explicit qty always wins. default_qty_type and default_qty_value only size the entries that are called without a qty argument.
How do I risk 1% per trade in a Pine Script strategy?
Compute the stop distance first, then qty = strategy.equity * 0.01 / (stopDistance * syminfo.pointvalue), converting the equity with strategy.convert_to_symbol() if the account and symbol currencies differ. Pass it as strategy.entry(..., qty = qty) and use the same stop in strategy.exit().
Does strategy.equity include open profit?
Yes: it's the initial capital plus the closed trades' net profit plus the open position's unrealized profit or loss. Use strategy.initial_capital + strategy.netprofit to leave the open trade out.
Related pages
Every script on this page was checked with Edgoro Pine Check (Pine Script v6 rules). Pine Check is a static checker, not TradingView's compiler: TradingView compiles the script when you save it in the Pine Editor. The scripts are examples to learn from, not trading systems. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.