Guide

Position Size in Pine Script: How to Code Your Risk Rules

Pine Script offers three order-sizing methods plus per-order quantity. Here is how each works and how to turn a written rule into code you can verify.

13 min read

Most position-sizing problems in Pine Script are not disagreements about risk. They are a mismatch between a rule written in words and the sizing mechanism the code actually uses. A strategy sized in fixed contracts behaves nothing like one sized as a percentage of equity, and the difference shows up as trades that never appear or results you cannot reconcile.

Short answer

Pine Script controls position size in two places. The strategy() declaration sets a default through default_qty_type and default_qty_value, which accepts strategy.fixed for a contract quantity, strategy.cash for a cash amount, or strategy.percent_of_equity for a percentage of equity. Individual order calls can override that default with an explicit qty argument, which is how a rule that derives size from a stop distance is implemented. This article covers the mechanics of writing those rules in code; it does not recommend any particular risk level.

Key facts

  • default_qty_type accepts three values: strategy.fixed (a contract or share quantity), strategy.cash (a cash amount), and strategy.percent_of_equity (a percentage of current equity).
  • TradingView's default initial capital for a strategy is 1,000,000, and TradingView notes you may need to increase this value for trades to occur on certain symbols.
  • An explicit qty argument on strategy.entry() or strategy.order() overrides the default sizing for that order, which is what a stop-distance-based rule requires.
  • In Pine v6 the default long and short margin percentage is 100, and strategies enforce position-size limits by default rather than ignoring available funds.
  • If a strategy's equity falls far enough under v6 defaults, existing positions can be closed by margin calls, and v6 avoids attempting orders with negative quantity.
  • Chart-level Strategy Tester properties can override values set in the strategy() declaration, so the two must be checked together when results look wrong.
  • strategy.exit() defines the protective exit that a risk-based sizing rule assumes, so the stop used for sizing and the stop placed as an order must reference the same price.
Methoddefault_qty_typeSize expressed asBehavior as equity changes
Fixed quantitystrategy.fixedContracts or sharesConstant, ignores equity
Cash amountstrategy.cashCurrency per tradeConstant cash, varying units
Percent of equitystrategy.percent_of_equityPercent of equityScales with equity
Per-order overrideAny, plus qty argumentComputed per orderWhatever your formula defines

Write the rule in words before you write code

Every sizing bug I have seen starts with an unwritten rule. Before touching the script, state the rule as a sentence with every quantity named: what fraction of the account is at stake, what defines the stop, how the size is rounded, and what happens when the computed size is below the instrument's minimum. If any of those is missing, the code will fill it in with a default you did not choose.

A complete rule reads something like: risk a fixed fraction of current equity per trade, define the stop as a multiple of ATR below entry, derive quantity by dividing the risk amount by the stop distance, round down to the nearest whole unit, and skip the trade if the result is zero. That sentence maps line by line into Pine. An incomplete rule such as "risk one percent" does not, because it says nothing about the stop, which is the term the quantity actually depends on.

Note what this article is and is not doing. Choosing the fraction, the stop multiple, and the instrument is entirely your decision, and nothing here suggests any particular value is appropriate. What follows is purely the mechanics of expressing a rule you have already chosen in code that compiles and that you can verify.

The three default sizing methods

strategy.fixed sizes every order as the same number of contracts or shares. It is the easiest to reason about and the least portable, because a quantity that is unremarkable on a low-priced equity can exceed available capital on an index future. Under Pine v6, which enforces position-size limits by default, that is a common reason a migrated strategy suddenly places no orders.

strategy.cash sizes each order by a currency amount, letting Pine derive the unit count from price. This travels better across instruments at different price levels, though it still does not adapt as the account grows or shrinks, and it can round down to zero units on very high-priced instruments.

strategy.percent_of_equity sizes from current equity, so allocation scales as equity changes. This is the method most people mean when they describe sizing as a percentage, and it is also the most useful diagnostic setting: if a strategy that placed no trades starts placing them when you switch to a modest percentage of equity, the original problem was sizing rather than entry logic. Be aware that scaling with equity compounds in both directions, which changes the shape of a result curve compared with fixed sizing.

pine
//@version=6
// Default sizing lives on the declaration.
strategy("Percent of equity sizing",
     overlay = true,
     initial_capital = 10000,
     default_qty_type = strategy.percent_of_equity,
     default_qty_value = 10)

fast = ta.sma(close, 10)
slow = ta.sma(close, 30)

if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)

Check these values in the Strategy Tester properties as well as in the declaration. Chart-level properties can override the declaration, and a silent mismatch between the two is a frequent source of results that cannot be reproduced.

Deriving quantity from a stop distance

The default methods size by account or price. A risk-based rule sizes by distance to the stop, which none of the defaults can express, so it requires computing the quantity yourself and passing it as the qty argument on the order call.

The arithmetic is a division: the amount you are willing to put at risk on the trade, divided by the per-unit distance between entry and stop. If the stop sits two dollars below entry and the risk amount is two hundred dollars, the quantity is one hundred units. Everything difficult about implementing it comes from the details around that division rather than the division itself.

Three details matter in code. First, the stop distance can be zero or na on early bars before an indicator like ATR has warmed up, and dividing by it will produce a runtime error or an absurd quantity. Guard it. Second, the result is fractional and most instruments require whole units, so round down deliberately with math.floor() rather than letting the platform decide. Third, the rounded result can be zero, and a zero-quantity order is not a trade; decide explicitly whether to skip it.

pine
//@version=6
strategy("Risk-based sizing", overlay = true, initial_capital = 10000)

riskPercent = input.float(1.0, "Risk % of equity", minval = 0.1, maxval = 100)
atrMult     = input.float(2.0, "Stop distance (ATR multiple)", minval = 0.1)

atr          = ta.atr(14)
stopDistance = atr * atrMult
riskAmount   = strategy.equity * riskPercent / 100

// Guard the division: ATR is na until it has warmed up.
validStop = not na(stopDistance) and stopDistance > 0
rawQty    = validStop ? riskAmount / stopDistance : 0
qty       = math.floor(rawQty)

longSignal = ta.crossover(ta.sma(close, 10), ta.sma(close, 30))

if longSignal and qty > 0
    strategy.entry("Long", strategy.long, qty = qty)
    // The stop used for sizing must be the stop actually placed.
    strategy.exit("Long exit", from_entry = "Long", stop = close - stopDistance)

The last line is the one people omit. If the quantity is derived from a stop distance but no stop order is placed at that distance, the sizing rule describes a risk the strategy is not actually enforcing. Sizing and the protective exit have to reference the same price.

Where sizing and the v6 defaults interact

Pine v6 changed two strategy defaults that directly affect sizing. The default long and short margin percentage is now 100, and strategies enforce position-size limits rather than ignoring available funds. The practical effect is that a strategy can no longer open a position it cannot afford, which is more realistic and which also means a v5 strategy can produce fewer or different trades after migration without any change to its entry rules.

The second effect is margin calls. Under v6 defaults, if equity falls far enough, existing positions can be liquidated, and TradingView documents that v6 avoids attempting orders with negative quantity in these conditions. A result curve that looks different after migration may be reflecting this rather than a mistranslation. Setting margin explicitly is a legitimate modelling choice, but it should be deliberate and documented, not a reflex to make old numbers reappear.

This is also where the default initial capital of 1,000,000 becomes relevant. It is deliberately large, and TradingView notes you may need to increase it further on certain symbols for trades to occur at all. Leaving it at the default while sizing as a percentage of equity produces position sizes that bear no relation to the account you are actually modelling, so set it to something meaningful for your test before drawing any conclusion from the output.

Verify the sizing, not just the compile

A sizing rule that compiles can still be wrong in ways the editor cannot detect, so verify it numerically. Take a single trade from the List of Trades, read its entry price and quantity, and check by hand that the quantity matches what your rule should have produced from the equity and stop distance at that bar. One trade checked properly is worth more than a plausible-looking equity curve.

Then test the boundaries deliberately. Look at the earliest bars, where ATR or any other stop input has not warmed up, and confirm your guard prevents a nonsensical size. Look at a high-priced instrument, where a rounded-down quantity may become zero. Look at a period of drawdown, where percentage-of-equity sizing shrinks and v6 margin behavior may intervene. Each of these is a real condition, not an edge case.

Use runtime logging rather than inference. log.info() can print the equity, stop distance, raw quantity, and rounded quantity at the moment of each entry, which turns sizing from something you deduce from results into something you can read directly. Our calculators are useful for checking the arithmetic independently: the position size calculator and the win rate calculator let you sanity-check a formula outside the script before you trust the script to apply it.

Keeping the rule readable a year later

Name the intermediate values. riskAmount, stopDistance, rawQty, and qty each say what they hold, so the division reads like the sentence you wrote at the start. A single compressed expression that computes quantity inline is shorter and much harder to audit when a number looks wrong.

Expose the parts of the rule you will want to change as typed inputs with bounds, and keep the parts you will not want to change as plain constants. A minval on a risk input prevents a zero or negative value producing a nonsensical size. This is not decoration; a bounded input removes a whole class of runtime error before it can occur.

Finally, write the rule in a comment at the top of the sizing block, in the same words you used before coding. When the strategy moves to a different instrument in three months and the sizes look strange, that comment is what tells you whether the code is wrong or the rule was never suited to the new instrument.

Where a Pine-focused workflow helps

Sizing code is a poor fit for the paste-and-regenerate loop. It sits at the intersection of the strategy declaration, the order call, and the exit order, so a general chat assistant asked to fix a sizing problem often returns a whole new strategy with a different declaration, different defaults, and a different exit. You then cannot tell whether the numbers changed because the sizing was fixed or because three other things moved with it.

PineScripter is the product we build, and this is the workflow it targets. It retrieves the Pine Script v5 and v6 manuals as context, so a proposal references documented behavior such as default_qty_type values or the v6 margin defaults rather than a plausible guess. Its edits target the specific lines and appear as a reviewable diff, which is what you want when a change touches the declaration, the entry, and the exit and each has to stay consistent with the others.

The boundary is worth stating plainly. No tool should choose your risk level, and nothing about a coding workflow makes a strategy perform better. What it can do is keep the translation from rule to code accurate and the edits small enough to audit. Verify the resulting quantities against your own arithmetic in the Strategy Tester before relying on them.

Describe a sizing rule in plain English, then review the generated code line by line

Frequently asked questions

How do I set position size in Pine Script?

Set a default on the strategy() declaration using default_qty_type and default_qty_value. default_qty_type accepts strategy.fixed for a contract or share quantity, strategy.cash for a cash amount, or strategy.percent_of_equity for a percentage of equity. To size an individual order differently, pass an explicit qty argument to strategy.entry() or strategy.order().

What is the difference between strategy.fixed, strategy.cash, and strategy.percent_of_equity?

strategy.fixed uses the same number of units on every order regardless of equity or price. strategy.cash allocates a fixed currency amount per trade, so the unit count varies with price. strategy.percent_of_equity sizes from current equity, so allocation scales up and down as equity changes.

How do I calculate position size from a stop loss in Pine Script?

Divide the amount you are risking on the trade by the per-unit distance between entry and stop, then pass the result as the qty argument on the order call. Guard the division because the stop distance can be na or zero before an indicator such as ATR warms up, round down with math.floor() because most instruments need whole units, and skip the order when the rounded quantity is zero.

Why does my Pine Script strategy place no trades after I change the sizing?

Usually the requested size cannot be afforded. Pine v6 enforces position-size limits by default with a 100 percent margin default, so an order larger than available capital is not placed. TradingView's default initial capital is 1,000,000 and may need increasing on certain symbols. Switching temporarily to a modest percentage of equity is a good way to confirm sizing is the cause rather than the entry logic.

Does the strategy() declaration or the Strategy Tester setting win?

Chart-level Strategy Tester properties can override values set in the strategy() declaration. If results do not match what the code appears to specify, check both places before editing the script, because a silent mismatch between them is a common reason a strategy cannot be reproduced on another chart.

Do I need to place a stop order if I size from a stop distance?

Yes. If quantity is derived from a stop distance but no protective order is placed at that distance, the sizing rule describes a risk the strategy is not enforcing. Use strategy.exit() with the same price the sizing calculation used so the two stay consistent.

The practical takeaway

Sizing is where a written rule meets the platform, so write the rule out in full first and then map it term by term. Use the default methods when a simple allocation is genuinely what you want, and compute qty yourself when the size depends on a stop distance. Guard the division, round deliberately, place the stop you sized against, and check one trade by hand before trusting the whole result.

Because a sizing change touches the declaration, the entry, and the exit at once, PineScripter is our product and shows all three as one reviewable diff, which is what keeps them consistent. It will not choose your risk level, and you should still check the quantities yourself.

Sources

Related reading: the position size calculator, the win rate calculator, why a strategy takes no trades, how TradingView backtesting works.

Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.