title: 执行阶段系统 description: 理解 Backtrader 的执行阶段


执行阶段系统

Backtrader 通过不同的阶段来执行策略,以处理指标和数据源的最小周期要求。

执行阶段

stateDiagram-v2
    [*] --> Prenext: 开始
    Prenext --> Prenext: 未达到最小周期
    Prenext --> Nextstart: 达到最小周期
    Nextstart --> Next: 单根 K 线过渡
    Next --> Next: 正常执行
    Next --> [*]: 数据结束

```bash

## 1. Prenext 阶段

- *prenext** 阶段在累积足够的数据 K 线之前运行,此时指标可能还没有产生有效值。

```python
class MyStrategy(bt.Strategy):
    def __init__(self):
        self.sma = bt.indicators.SMA(period=20)

# minperiod = 20

    def prenext(self):

# 当 len(self.data) < self.sma.minperiod 时调用
        print(f"K 线 {len(self)}: 正在累积数据...")

```bash

- *特点:**
- 从第 0 根 K 线运行到 `minperiod - 1`
- 指标可能没有有效值
- 用于初始化和预热

## 2. Nextstart 阶段

- *nextstart** 阶段在首次达到 `minperiod` 时运行一次。

```python
def nextstart(self):

# 当 len(self.data) == self.sma.minperiod 时调用一次
    print(f"K 线 {len(self)}: 第一根有效 K 线!")

# 默认实现会自动调用 next()

```bash

- *特点:**
- 在第 `minperiod` 根 K 线时运行一次
- prenext 和 next 之间的过渡点
- 可重写以实现特殊的首根 K 线逻辑

## 3. Next 阶段

- *next** 阶段是主执行循环。

```python
def next(self):

# 在满足 minperiod 后的每根 K 线调用
    if self.sma[0] > self.data.close[0]:
        self.sell()

```bash

- *特点:**
- 从第 `minperiod` 根 K 线运行到数据结束
- 所有指标都有有效值
- 主要策略逻辑写在这里

## 最小周期系统

每个组件都有一个 `minperiod` 属性,表示产生有效输出前需要多少根 K 线。

```python

# 指标的最小周期

SMA(period=20).minperiod  # 20

EMA(period=12).minperiod  # 12 (内部调整)

RSI(period=14).minperiod  # 15 (14 + 1 用于计算)

# 策略的 minperiod 是其指标的最大值

class MyStrategy(bt.Strategy):
    def __init__(self):
        self.sma20 = bt.indicators.SMA(period=20)
        self.sma50 = bt.indicators.SMA(period=50)

# Strategy.minperiod = 50 (最大值)

```bash

## 实际示例

```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

# 回测期间不建议使用日志输出

# 请改用 observer

    def nextstart(self):

# 这是第一根有有效 SMA 值的 K 线
        print(f"第一根有效 K 线: {len(self)}")

    def next(self):

# 正常执行
        if len(self) == self.params.period + 1:
            print(f"第二根有效 K 线: 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

## 执行顺序

```mermaid
sequenceDiagram
    participant Data as 数据
    participant Cerebro as Cerebro 引擎
    participant Strategy as 策略
    participant Indicators as 指标

    Data->>Cerebro: 新 K 线
    Cerebro->>Indicators: 计算指标 (始终)
    Indicators-->>Cerebro: 值已更新

    alt 未达到最小周期
        Cerebro->>Strategy: prenext()
    else 刚达到最小周期
        Cerebro->>Strategy: nextstart()
    else 满足最小周期
        Cerebro->>Strategy: next()
    end

    Strategy->>Cerebro: 订单 (可选)
    Cerebro->>Cerebro: 处理订单

```bash

## 关键要点

1. **指标每根 K 线都会更新**- 即使在 prenext 阶段

2.**策略阶段控制执行**- 每个阶段有不同的逻辑
3.**最小周期自动计算**- 从组件中得出
4.**观察器遵循相同模式** - 也有 prenext/nextstart/next

## 最佳实践

```python

# 推荐:使用 minperiod 进行预热

class GoodStrategy(bt.Strategy):
    def __init__(self):
        self.warmup = 50  # 额外的预热 K 线数

    def next(self):
        if len(self) < self.warmup:
            return  # 跳过预热期

# 主逻辑在这里

# 不推荐:假设 prenext 中的指标有效

class BadStrategy(bt.Strategy):
    def prenext(self):
        value = self.sma[0]  # 可能是 NaN 或无效值!

```bash