title: Phase System description: Understanding Backtrader’s execution phases
Phase System¶
Backtrader executes strategies through distinct phases to handle the minimum period requirements of indicators and data feeds.
Execution Phases¶
stateDiagram-v2
[*] --> Prenext: Start
Prenext --> Prenext: minperiod not reached
Prenext --> Nextstart: minperiod == bars
Nextstart --> Next: Single bar transition
Next --> Next: Normal execution
Next --> [*]: End of data
```bash
## 1. Prenext Phase
The **prenext** phase runs before enough data bars have been accumulated for indicators to produce valid values.
```python
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = bt.indicators.SMA(period=20)
# minperiod = 20
def prenext(self):
# Called when len(self.data) < self.sma.minperiod
print(f"Bar {len(self)}: Accumulating data...")
```bash
- *Characteristics:**
- Runs from bar 0 until `minperiod - 1`
- Indicators may not have valid values
- Use for initialization and warm-up
## 2. Nextstart Phase
The **nextstart** phase runs exactly once when `minperiod` is first reached.
```python
def nextstart(self):
# Called once when len(self.data) == self.sma.minperiod
print(f"Bar {len(self)}: First valid bar!")
# Default implementation calls next() automatically
```bash
- *Characteristics:**
- Runs exactly once at bar `minperiod`
- Transition point between prenext and next
- Override for special first-bar logic
## 3. Next Phase
The **next** phase is the main execution loop.
```python
def next(self):
# Called for each bar after minperiod is satisfied
if self.sma[0] > self.data.close[0]:
self.sell()
```bash
- *Characteristics:**
- Runs from bar `minperiod` to end of data
- All indicators have valid values
- Main strategy logic goes here
## Minimum Period System
Each component has a `minperiod` attribute that indicates how many bars it needs before producing valid output.
```python
# Indicator minimum periods
SMA(period=20).minperiod # 20
EMA(period=12).minperiod # 12 (adjusted internally)
RSI(period=14).minperiod # 15 (14 + 1 for calculation)
# Strategy minperiod is the maximum of its indicators
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma20 = bt.indicators.SMA(period=20)
self.sma50 = bt.indicators.SMA(period=50)
# Strategy.minperiod = 50 (maximum)
```bash
## Practical Example
```python
import backtrader as bt
class PhaseExample(bt.Strategy):
params = (('period', 20),)
def __init__(self):
self.sma = bt.indicators.SMA(period=self.params.period)
self.prenext_count = 0
def prenext(self):
self.prenext_count += 1
# Logging not recommended during backtest
# Use observer instead
def nextstart(self):
# This is the first bar with valid SMA value
print(f"First valid bar: {len(self)}")
def next(self):
# Normal execution
if len(self) == self.params.period + 1:
print(f"Second valid bar: SMA = {self.sma[0]:.2f}")
cerebro = bt.Cerebro()
data = bt.feeds.YahooFinanceData(dataname='AAPL',
fromdate=datetime(2020, 1, 1),
todate=datetime(2020, 12, 31))
cerebro.adddata(data)
cerebro.addstrategy(PhaseExample)
cerebro.run()
```bash
## Execution Order
```mermaid
sequenceDiagram
participant Data
participant Cerebro
participant Strategy
participant Indicators
Data->>Cerebro: New bar
Cerebro->>Indicators: Calculate (always)
Indicators-->>Cerebro: Values updated
alt minperiod not reached
Cerebro->>Strategy: prenext()
else minperiod just reached
Cerebro->>Strategy: nextstart()
else minperiod satisfied
Cerebro->>Strategy: next()
end
Strategy->>Cerebro: Orders (optional)
Cerebro->>Cerebro: Process orders
```bash
## Key Points
1. **Indicators update every bar**- Even during prenext
2.**Strategy phases control execution**- Different logic for each phase
3.**Minimum period is automatic**- Calculated from components
4.**Observers follow same pattern** - Also have prenext/nextstart/next
## Best Practices
```python
# DO: Use minperiod for warm-up
class GoodStrategy(bt.Strategy):
def __init__(self):
self.warmup = 50 # Extra warm-up bars
def next(self):
if len(self) < self.warmup:
return # Skip warm-up period
# Main logic here
# DON'T: Assume indicators are valid in prenext
class BadStrategy(bt.Strategy):
def prenext(self):
value = self.sma[0] # May be NaN or invalid!
```bash