Problem Description
Solution
To calculate position size based on the Average True Range (ATR), you typically use a Risk-Based Sizing model. The goal is to ensure that if your stop loss (based on ATR) is hit, you only lose a fixed percentage of your total account equity (e.g., 1% or 2%).
The formula is:
$$ \text{Shares} = \frac{\text{Total Account Value} \times \text{Risk %}}{\text{ATR} \times \text{Multiplier}} $$
Below is a complete, runnable QuantConnect strategy using the PEP8 (snake_case) API format.
ATR Position Sizing Strategy
#region imports
from AlgorithmImports import *
#endregion
class ATRPositionSizingAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2021, 1, 1)
self.set_end_date(2023, 1, 1)
self.set_cash(100000)
# 1. Add Security
self.symbol = self.add_equity("SPY", Resolution.DAILY).symbol
# 2. Initialize ATR Indicator
# 14-period ATR using Simple Moving Average
self.atr_indicator = self.atr(self.symbol, 14, MovingAverageType.SIMPLE, Resolution.DAILY)
# 3. Strategy Parameters
self.risk_per_trade = 0.02 # Risk 2% of total equity per trade
self.atr_multiplier = 2.0 # Stop loss is placed 2 ATRs away from entry
# Warm up the indicator so it is ready immediately when data starts
self.set_warm_up(14)
def on_data(self, data: Slice):
# Ensure indicator is ready and we are not warming up
if self.is_warming_up or not self.atr_indicator.is_ready:
return
# Check if data exists for the symbol
if not data.bars.contains_key(self.symbol):
return
# Simple Entry Logic: Enter if we have no holdings
if not self.portfolio.invested:
self.enter_position_with_atr_sizing()
def enter_position_with_atr_sizing(self):
# A. Get Current Data
current_price = self.securities[self.symbol].price
current_atr = self.atr_indicator.current.value
total_equity = self.portfolio.total_portfolio_value
# B. Calculate Risk Variables
# The maximum dollar amount we are willing to lose
risk_amount = total_equity * self.risk_per_trade
# The price distance from entry to stop loss
stop_loss_distance = current_atr * self.atr_multiplier
# Avoid division by zero
if stop_loss_distance == 0:
return
# C. Calculate Share Quantity
# Shares = Risk Amount / Risk per Share
quantity = int(risk_amount / stop_loss_distance)
# Ensure we have enough buying power (optional check, MarketOrder handles partial fills usually)
# But logically, if the stop is very tight, quantity might exceed leverage.
# Here we just ensure quantity > 0.
if quantity <= 0:
return
# D. Execute Orders
self.market_order(self.symbol, quantity)
# Place a Stop Loss Order based on the ATR calculation
stop_price = current_price - stop_loss_distance
self.stop_market_order(self.symbol, -quantity, stop_price)
self.log(f"Entry: {current_price} | ATR: {current_atr:.2f} | Size: {quantity} | Stop: {stop_price:.2f}")
Key Implementation Details
self.atr(...): This helper method creates anAverageTrueRangeindicator and automatically registers it to receive data updates.self.set_warm_up(14): This ensures the algorithm processes data before the start date so the ATR indicator is ready (is_ready) immediately upon the first trading day.- The Calculation:
- Risk Amount:
total_equity * 0.02(We are willing to lose $2,000 on a $100k account). - Stop Distance:
ATR * 2.0(If ATR is $5, the stop is $10 away). - Quantity:
$2,000 / $10 = 200 shares. - Result: If the price drops $10 (hitting the stop), you lose $10 * 200 shares = $2,000 (exactly 2% of your account).
- Risk Amount:
int(...): Share counts for equities must be integers.
Q&A
Q: Why use ATR for position sizing instead of a fixed percentage of capital?
A: ATR sizing normalizes volatility. It allows you to trade larger positions on stable stocks (low ATR) and smaller positions on volatile stocks (high ATR), keeping your dollar risk constant regardless of the asset's behavior.
Q: How do I handle "Insufficient Buying Power" errors with this method?
A: If the calculated quantity requires more cash than you have (or exceeds your leverage), QuantConnect will reject the order. You can add a check using self.portfolio.margin_remaining or self.calculate_order_quantity to cap the size at your maximum buying power if the ATR calculation suggests a position that is too large.
Q: Can I use this for Futures or Forex?
A: Yes, but the calculation changes slightly. For Forex/Futures, you must account for the SymbolProperties.ContractMultiplier. The formula becomes: Quantity = Risk Amount / (Stop Distance * Contract Multiplier).