Position sizing, margin, and leverage

Entry size sets the real market exposure, while margin determines the available leverage and the liquidation threshold. This page explains how the engine calculates them and how they interact.

Key concepts

Before getting into the engine rules, it helps to clarify a few important concepts.

Position sizing, margin, and leverage all come into play as soon as a position is opened. To understand what they do, start with a simple example.

Imagine a portfolio with 1,000 USDT. This 1,000 USDT is your starting capital: the money available in the account before you open a position.

You then decide to buy 0.08 BTC at 50,000 USDT. The calculation is direct: 0.08 × 50,000 = 4,000 USDT.

Your position therefore has a value of 4,000 USDT. This value is called the notional value. Your gains and losses are calculated on that 4,000 USDT, not on your 1,000 USDT of capital. For example, if Bitcoin rises by 1%, your position gains 40 USDT, because 1% of 4,000 USDT = 40 USDT. Conversely, if Bitcoin falls by 1%, your position loses 40 USDT.

This shows that the open position is larger than the available capital. You have 1,000 USDT in the account, but you are exposed to 4,000 USDT on the market. This is possible because the engine does not ask you to reserve the full value of the position. It only asks for collateral: the required margin.

Suppose margin is configured at 20%. This means the engine requires collateral equal to 20% of the position value. The position is worth 4,000 USDT, so the required margin is: 20% × 4,000 = 800 USDT.

The engine therefore reserves 800 USDT to keep the position open. Since your starting capital is 1,000 USDT, 200 USDT remains available in the account. This available cash can be used to pay fees, open other positions, or absorb part of the unrealized losses.

At all times, the engine also tracks account equity. Equity represents the real value of the account, taking into account available cash, reserved margin, and the unrealized profit or loss of the position. At entry, the position has not gained or lost anything yet. Equity is therefore still 1,000 USDT: 800 USDT of reserved margin and 200 USDT of available cash.

If Bitcoin rises by 1%, the position gains 40 USDT. Equity then moves from 1,000 USDT to 1,040 USDT. If Bitcoin falls by 1%, the position loses 40 USDT. Equity then drops to 960 USDT.

The engine checks that equity remains sufficient to keep the position open. In this example, the required margin is 800 USDT. As long as equity stays above that level, the position holds. If equity falls to that threshold, the engine considers that the account no longer has enough buffer to carry the position and liquidates it.

Leverage measures this exposure. It compares the size of the position with the capital in the account. Here, you have a 4,000 USDT position with 1,000 USDT of capital: 4,000 / 1,000 = 4. You are therefore using 4x leverage.

With margin configured at 20%, the maximum theoretical leverage is 5x, because: 100 / 20 = 5. This means that with 1,000 USDT of equity, the engine could allow up to 5,000 USDT of exposure. But this is only a maximum. You do not have to use all available leverage. In this example, you open a 4,000 USDT position, so you use 4x, even though the allowed ceiling is 5x.

Finally, distinguish the price used to size the order from the price at which the order is actually executed. For example, a signal can be triggered at the close of a candle when Bitcoin is worth 50,000 USDT. The engine uses that price to calculate the quantity to buy: this is the sizing price.

But if the order is a market order, it may be executed at the open of the next candle, for example at 50,200 USDT. In that case, the 50,200 USDT price becomes the actual entry price of the position.

The execution price is always the price used afterwards to calculate the notional actually opened, fees, gains or losses, and the final account solvency check. The sizing price is used to prepare the order; the execution price is used to calculate what actually happened.

Sizing an entry

When an entry is created, the engine sizes it in three steps: it determines where the size instruction comes from, it interprets it through a sizing mode, and it converts it into a quantity using the sizing price.

The instruction can come from two places. The [backtest] block sets a global sizing that applies to every entry by default. An [[entry]] or [[order]] block can override that global sizing by setting qty or qty_percent directly. In that case, the engine uses the value defined on the block for that entry. If the order defines neither qty nor qty_percent, the engine applies the global sizing from [backtest].

The mode then decides how the value is interpreted. percent_of_equity reads it as a percentage of equity, fixed as an absolute asset quantity, cash as a monetary budget. Global sizing selects its mode with default_qty_type; local sizing selects its own through the key used, qty for an absolute quantity and qty_percent for a percentage of equity.

When no global sizing is set, the engine follows TradingView’s default: default_qty_type = "fixed" with default_qty_value = 1. Setting only default_qty_value therefore still uses the implicit fixed mode. To express an equity percentage, also set default_qty_type = "percent_of_equity".

percent_of_equity

default_qty_type = "percent_of_equity" reads default_qty_value as a base expressed as a percentage of equity. This is not the implicit [backtest] mode: define default_qty_type explicitly to use it. If default_qty_value is omitted in this mode, the engine uses a base of 1.

[backtest]
default_qty_type  = "percent_of_equity"
default_qty_value = 40

With this configuration, the target entry is close to 40% of equity.

long_size_multiplier and short_size_multiplier adjust that base separately for longs and shorts. They default to 1 and act only on global percent_of_equity sizing, when the order does not already define qty or qty_percent. They change neither the required margin nor leverage.

[backtest]
default_qty_type      = "percent_of_equity"
default_qty_value     = 40
long_size_multiplier  = 1.5
short_size_multiplier = 0.5

Here, the base of 40% becomes 40 × 1.5 = 60 for longs and 40 × 0.5 = 20 for shorts. The target exposure is therefore close to 60% of equity on longs and 20% on shorts.

Sizing always starts from total equity, not from available cash alone. The quantity that is actually opened can stay slightly below the displayed target, because the engine subtracts fees and then rounds the result to the minimum contract size.

This base works well with grid search. The next example varies exposure while keeping margins fixed.

[backtest]
symbol                  = "BINANCE:BTCUSDT"
timeframe               = "240"
start_date              = 2024-01-01
end_date                = 2025-01-01
initial_capital         = 1000
margin_long             = 50
margin_short            = 100
default_qty_type        = "percent_of_equity"
default_qty_value.start = 20
default_qty_value.stop  = 80
default_qty_value.step  = 10

For the range syntax, see Hyperparameter combinations.

fixed

default_qty_type = "fixed" reads default_qty_value as an absolute asset quantity.

[backtest]
default_qty_type  = "fixed"
default_qty_value = 0.01

[[entry]]
id       = "buy"
order_id = "main"

With this configuration, the target entry is 0.01 asset units. If an order does not define its own local sizing, the engine uses default_qty_value; if that value is omitted, it uses 1 asset unit. The side multipliers have no effect in this mode, since the size is already an absolute quantity.

cash

default_qty_type = "cash" reads default_qty_value as a nominal monetary budget.

[backtest]
default_qty_type  = "cash"
default_qty_value = 500

[[entry]]
id       = "buy"
order_id = "main"

With this configuration, 500 units of the quote currency are converted into an asset quantity using the sizing price, then filled at the actual execution price. The budget stays nominal: if the fill price differs from the sizing price, the executed notional can differ from the initial budget. As in fixed mode, the side multipliers have no effect. If default_qty_value is omitted in global cash mode, the implicit budget is 1 unit of the quote currency.

qty and qty_percent in [[entry]] and [[order]]

In [[entry]] and [[order]], qty always means an absolute asset quantity and qty_percent always means a percentage of equity, never a percentage of an existing position. These two ways of sizing the order do not combine within the same block. qty_percent is not capped at 100 here: a higher value targets higher exposure, subject to margin rules and final order acceptance.

[backtest]
default_qty_type     = "percent_of_equity"
default_qty_value    = 40
long_size_multiplier = 1.5

[[entry]]
id       = "buy_fixed_qty"
order_id = "main"
qty      = 0.25

[[entry]]
id          = "buy_local_pct"
order_id    = "main"
qty_percent = 25

In this example, the first order opens 0.25 asset units and the second targets 25% of equity. Neither uses the global sizing: qty treats the order as a fixed-quantity entry, qty_percent as a percent_of_equity entry, even when the global mode is fixed or cash. These keys replace global sizing, they do not extend it.

In [[close]] and [[exit]], qty_percent changes meaning: it refers to a percentage of the position already open. If a position is 2 BTC, qty_percent = 25 targets a close of about 0.5 BTC before rounding. See the documentation for the [[close]] and [[exit]] blocks for the rules specific to them.

Sizing price and execution price

Quantity is not always calculated from the fill price. This mainly matters for entries whose size depends on a budget or on a percentage of equity.

For a market order that fills on the close, the order is sized and executed at the same price. For a market order that fills on the next bar open, the engine keeps the signal-bar sizing price and then fills at the open. For a limit order, the sizing price is the limit price; for a stop market order, the stop price; for a stop-limit order, the limit price.

[backtest]
default_qty_type  = "cash"
default_qty_value = 500

[[entry]]
id       = "buy_limit"
order_id = "main"
limit    = "close * 0.99"

Here, the quantity is calculated from the limit price and then filled when the order is executed. If the final fill happens at another price, executed notional, debited fees, and the final solvency check all follow the actual execution price.

Margin and leverage

Margin does not define entry size. That size depends on sizing: default_qty_type, default_qty_value, the side multipliers, then qty or qty_percent. margin_long and margin_short come next and set the required margin, the maximum theoretical leverage, and the liquidation conditions. A lower margin setting therefore does not, by itself, create a larger entry: it changes the solvency conditions under which the entry is accepted, maintained, or liquidated.

margin_long and margin_short

margin_long and margin_short are margin percentages, written directly as percentages in the TOML file. margin_long = 50 means 50% margin, margin_long = 0.5 means 0.5%. Values above 100 are accepted and represent a margin requirement that is stricter than a 1x setup.

Required margin is calculated from notional value: notional value × (margin_long / 100) for a long, notional value × (margin_short / 100) for a short. Maximum theoretical leverage is read the same way: 100 / margin_long for a long, 100 / margin_short for a short.

So a margin of 100 corresponds to a setup close to 1x, a margin of 50 to about 2x, a margin of 25 to about 4x, and a margin of 0.5 to about 200x. Conversely, a margin of 200 requires twice the notional value as margin and limits the sustainable exposure to roughly half the equity.

[backtest]
symbol          = "BINANCE:BTCUSDT"
timeframe       = "240"
start_date      = 2024-01-01
end_date        = 2025-01-01
initial_capital = 1000
margin_long     = 50
margin_short    = 100

This example sets a theoretical long ceiling close to 2x with long margin at 50%, and a short setup close to 1x with short margin at 100%. The actual entry size still comes from sizing.

Disabling margin

When a margin value is 0, margin logic is disabled on that side: the engine reserves no margin and applies no margin liquidation there. If only one margin is 0, the other side keeps its own logic and the overall behavior stays mixed.

With margin_long = 0 and margin_short = 0, the engine runs without margin on both sides. Entry size is still driven by the sizing rules: default_qty_type, default_qty_value, the multipliers, qty, and qty_percent.

[backtest]
symbol          = "BINANCE:BTCUSDT"
timeframe       = "240"
start_date      = 2024-01-01
end_date        = 2025-01-01
initial_capital = 1000
margin_long     = 0
margin_short    = 0

Entry solvency

Once the size has been calculated, the engine checks whether the entry remains solvent. When margin is active, it compares equity with the required margin after the entry. For a margin other than 100, this check happens before the fill. For a margin equal to 100, the final decision can be made after the fill, from the actual execution price and the actual fees debited. An entry can therefore be filled and then liquidated immediately on the same bar: quantity is not reduced automatically to make it fit within the margin.

Margin liquidation

A margin liquidation happens when equity becomes less than or equal to the required margin. It exists only on sides where margin is active, meaning margin_long > 0 for long positions and margin_short > 0 for short positions.

The check is not limited to the bar close: the engine also runs it inside the bar. A liquidation can therefore happen before the close, even if price later moves back. When the threshold is crossed, part of the position is reduced to restore the required margin; depending on rounding and minimum contract size, that partial reduction can become a full close.

Even with a margin of 100, an immediate margin call can appear when an entry targets almost all of equity and the actual execution price plus fees tip the final check.

Worked margin liquidation example

Take a simple case, intentionally without fees or rounding to keep the numbers readable: an initial equity of 1,000 USDT, a long Bitcoin exposure of 4,000 USDT, and margin_long = 20. A 20% margin corresponds to a theoretical setup close to 5x, because 100 / 20 = 5. At entry, the required margin is 4,000 × 20% = 800 USDT.

The open exposure is 4,000 USDT, but that full value does not need to be covered by equity. As long as equity stays strictly above the required margin, the position remains open.

If price rises by 2%, unrealized profit is 80 USDT. Equity moves to 1,080 USDT, notional value to 4,080 USDT, and required margin to 816 USDT: equity stays above. If price falls by 2%, equity drops to 920 USDT and required margin falls to 784 USDT (3,920 × 20%): equity still stays above.

The critical point appears when equity meets the required margin. Here, that happens around a 6.25% decline: the loss then reaches 250 USDT, equity drops to 750 USDT, and notional value to 3,750 USDT, whose required margin is 3,750 × 20% = 750 USDT. At that point, a margin liquidation can begin. Liquidation therefore depends on the relationship between equity and required margin, not only on the percentage decline in price.