Skip to content

Position Sizing

qlcore provides multiple position sizing strategies to manage risk and optimize capital allocation.

Fixed Sizing

Fixed Quantity

Trade a fixed number of units:

import qlcore as qc
from decimal import Decimal

size = qc.fixed_quantity(quantity="0.1")
print(f"Size: {size}")  # 0.1

Fixed Notional

Trade a fixed dollar amount:

size = qc.fixed_notional(
    notional="5000",     # $5000
    price="45000",       # BTC price
)
print(f"Size: {size}")   # 0.111... BTC

Percentage-Based Sizing

Percent of Equity

Allocate a percentage of account equity:

size = qc.percent_of_equity(
    equity=10000,        # Account equity
    percent="0.1",       # 10% of equity
    price="45000",       # Entry price
)
print(f"Size: {size}")   # ~0.022 BTC ($1000 notional)

Common Allocations

  • Conservative: 2-5% per position
  • Moderate: 5-10% per position
  • Aggressive: 10-20% per position

Risk-Based Sizing

Risk Per Trade

Size position based on maximum acceptable loss:

size = qc.risk_per_trade(
    equity=10000,
    risk_percent="0.02",   # Risk 2% of equity
    entry_price="45000",
    stop_price="44000",    # Stop loss price
)
print(f"Size: {size}")     # Position sized so $1000 stop = 2% loss

The formula: size = (equity × risk%) / (entry - stop)

Example breakdown:

  • Equity: $10,000
  • Risk: 2% = $200 max loss
  • Stop distance: $45,000 - $44,000 = $1,000
  • Size: $200 / $1,000 = 0.2 BTC

With Leverage

For leveraged positions, account for margin:

size = qc.risk_per_trade(
    equity=10000,
    risk_percent="0.02",
    entry_price="45000",
    stop_price="44000",
    leverage=10,           # Optional leverage factor
)

Volatility-Based Sizing

ATR Position Size

Size based on Average True Range (volatility):

size = qc.atr_position_size(
    equity=10000,
    risk_percent="0.02",   # 2% risk
    atr="1500",            # Current ATR in price units
    atr_multiplier=2,      # Stop at 2x ATR
)
print(f"Size: {size}")

The formula: size = (equity × risk%) / (ATR × multiplier)

Example:

  • ATR = $1,500
  • Stop = 2 × ATR = $3,000
  • Size = $200 / $3,000 = 0.067 BTC

Kelly Criterion

Kelly Fraction

Optimal bet size based on win rate and payoff:

kelly = qc.kelly_fraction(
    win_rate=0.55,       # 55% win rate
    avg_win=100,         # Average winning trade
    avg_loss=80,         # Average losing trade
)
print(f"Kelly: {kelly:.2%}")  # Optimal fraction of capital

Full Kelly is Aggressive

Most practitioners use fractional Kelly (e.g., half-Kelly) to reduce volatility:

half_kelly = kelly * 0.5

Example calculation:

# Win rate: 55%, Avg win: $100, Avg loss: $80
# Edge = 0.55 × 100 - 0.45 × 80 = 55 - 36 = 19
# Kelly = Edge / Avg Win = 19 / 100 = 0.19 (19%)

kelly = qc.kelly_fraction(0.55, 100, 80)
print(f"Full Kelly: {kelly:.1%}")      # 19%
print(f"Half Kelly: {kelly*0.5:.1%}")  # 9.5%

Position Limits

Apply Constraints

Ensure positions respect limits:

size = qc.apply_position_limits(
    desired_size=Decimal("1.0"),
    max_position_size=Decimal("0.5"),    # Max allowed
    min_position_size=Decimal("0.01"),   # Min allowed
    lot_size=Decimal("0.001"),           # Round to lot size
)
print(f"Constrained size: {size}")       # 0.5 (capped at max)

Multiple Constraints

from decimal import Decimal

desired = Decimal("0.1")

# Check max position
if desired > max_position:
    desired = max_position

# Check max notional
notional = desired * price
if notional > max_notional:
    desired = max_notional / price

# Round to lot size
size = qc.apply_position_limits(
    desired_size=desired,
    lot_size=Decimal("0.001"),
)

Complete Sizing Example

import qlcore as qc
from decimal import Decimal

# Account state
equity = Decimal("10000")
price = Decimal("45000")
stop = Decimal("43500")
atr = Decimal("1200")

# Calculate sizes using different methods
sizes = {
    "Fixed 10%": qc.percent_of_equity(equity, "0.10", price),
    "2% Risk": qc.risk_per_trade(equity, "0.02", price, stop),
    "ATR 2x": qc.atr_position_size(equity, "0.02", atr, 2),
}

print("Position Sizing Comparison")
print("-" * 40)
for method, size in sizes.items():
    notional = size * price
    print(f"{method:15} {size:.6f} BTC (${notional:.0f})")

Output:

Position Sizing Comparison
----------------------------------------
Fixed 10%       0.022222 BTC ($1000)
2% Risk         0.133333 BTC ($6000)
ATR 2x          0.083333 BTC ($3750)


Using Namespace

import qlcore as qc

qc.sizing.fixed_quantity("0.1")
qc.sizing.fixed_notional("5000", "45000")
qc.sizing.percent_of_equity(10000, "0.1", "45000")
qc.sizing.risk_per_trade(10000, "0.02", "45000", "44000")
qc.sizing.atr_position_size(10000, "0.02", "1500", 2)
qc.sizing.kelly_fraction(0.55, 100, 80)
qc.sizing.apply_position_limits(size, max_size, min_size, lot_size)