Position Sizing (Sizers)
Relevant source files
The following files were used as context for generating this wiki page:
Position Sizing in the backtrader framework is managed through the Sizer module. Sizers automate the calculation of the quantity (number of shares, contracts, or units) for an order when a strategy issues a buy() or sell() signal without explicitly defining a size parameter. This decoupling allows traders to change their money management rules without modifying the underlying strategy logic.
Sizers have access to the current state of the broker (available cash and portfolio value) and the data feed properties. This enables complex sizing logic, such as risking a fixed percentage of capital or adjusting size based on volatility.
Sources: backtrader/sizer.py:25-45, strategies/moving_average_crossover.py:330-350
Architecture and Core Components
The sizing system is built around a base Sizer class that maintains references to the strategy and the broker.
The Sizer Base Class
The base class in backtrader/sizer.py defines the interface used by Cerebro and Strategy objects. It includes a setinfo method to link the sizer to the specific strategy and broker instance it will serve.
The most critical methods are:
getsizing(comminfo, cash, data, isbuy): The public interface that prepares the sizing request._getsizing(comminfo, cash, data, isbuy): The internal method that subclasses must override to implement specific sizing logic.
Sources: backtrader/sizer.py:29-57
Relationship with Strategy and Broker
When a strategy calls self.buy(), if no size is provided, the strategy uses its attached sizer to determine the volume.
This diagram shows the decision flow when an order is initiated within a strategy and how the Sizer intervenes when the size is omitted.
Sources: backtrader/sizer.py:48-70, samples/multi-example/mult-values.py:100-110
Built-in Sizers
The project provides standard implementations for common money management strategies.
FixedSize Sizer
The FixedSize sizer is the simplest implementation. it returns a constant value regardless of account equity or market conditions.
- Parameter:
stake(default: 1). - Logic: Always returns the value of
stake.
Sources: backtrader/sizers/fixedsize.py:25-35
PercentSizer
The PercentSizer calculates the position size as a percentage of the available cash in the broker.
- Parameter:
percents(default: 10). - Logic:
(available_cash * percents / 100) / current_price.
Sources: backtrader/sizers/percents_sizer.py:24-45
Summary Table of Standard Sizers
| Sizer Class | Main Parameters | Description | Implementation Logic |
|---|---|---|---|
FixedSize | stake (int) | Fixed unit sizing. | Returns self.p.stake. |
PercentSizer | percents (float) | Percentage of cash. | (cash * percents / 100) / price. |
AllInSizer | None | Uses all available cash. | Variant of PercentSizer at 100%. |
Sources: backtrader/sizers/fixedsize.py, backtrader/sizers/percents_sizer.py
Custom Sizer Implementation
Developers can create custom sizers by inheriting from bt.Sizer and overriding the _getsizing method. This is useful for implementing advanced techniques like Kelly Criterion or ATR-based sizing.
Implementation Pattern
A custom sizer has access to self.strategy and self.broker.
Sources: backtrader/sizer.py:35-45, samples/multi-example/mult-values.py:35-60
Code Example: Conditional Sizing
In samples/multi-example/mult-values.py, a TestSizer is implemented to double the stake for sell orders compared to buy orders:
class TestSizer(bt.Sizer):
params = dict(stake=1)
def _getsizing(self, comminfo, cash, data, isbuy):
# Double the stake if it is not a buy (is a sell)
s = self.p.stake * (1 + (not isbuy))
return s
Sources: samples/multi-example/mult-values.py:38-57
Manual Position Sizing in Strategies
While Sizers automate the process, many strategies implement sizing logic directly within their next() method for fine-grained control over risk management parameters like Stop Loss distance.
Risk-Based Sizing Logic
Strategies often calculate size based on a "Risk per Share" calculation, where the amount risked is a percentage of total equity divided by the distance to the stop loss.
| Component | Description |
|---|---|
risk_amount | Total Equity * risk_percent |
risk_per_share | Entry Price - Stop Loss Price |
size | risk_amount / risk_per_share |
Sources: strategies/moving_average_crossover.py:330-345, strategies/risk_adverse.py:440-460
The class hierarchy shows how specific sizer implementations inherit the core functionality and references of the base Sizer class.
Sources: backtrader/sizer.py:29-35, backtrader/sizers/fixedsize.py:25-30
Configuration and Usage
Sizers can be added to the backtesting engine (Cerebro) globally or to specific strategies.
- Global Sizer:
cerebro.addsizer(bt.sizers.FixedSize, stake=10) - Strategy Sizer: Within the strategy, a sizer can be replaced or defined, although standard practice is to use the
Cerebrointerface.
Once added, the strategy's buy() and sell() methods will use the sizer's output if the size argument is not provided.
Sources: samples/multi-example/mult-values.py:195-200, backtrader/sizer.py:48-55
Summary
Position Sizing (Sizers) provide a modular way to handle money management in backtrader. By separating the "what to trade" (Strategy) from "how much to trade" (Sizer), the framework allows for robust testing of different equity allocation models. Built-in options like FixedSize and PercentSizer cover basic needs, while the extensible Sizer base class permits the development of complex, risk-adjusted sizing algorithms.