背景¶
backtrader 已经比较完善了,我想要借鉴量化投资框架中其他项目的优势,继续改进优化 backtrader。
任务¶
阅读研究分析 backtrader 这个项目的源代码,了解这个项目。
阅读研究分析/Users/yunjinqi/Documents/量化交易框架/BackTest-Cpp
借鉴这个新项目的优点和功能,给 backtrader 优化改进提供新的建议
写需规文档和设计文档放到这个文档的最下面,方便后续借鉴
BackTest-Cpp 项目简介¶
BackTest-Cpp 是一个 C++实现的高性能回测框架,具有以下核心特点:
C++实现: 高性能 C++实现
CRTP 设计: 使用奇异递归模板模式实现编译期多态
批量处理: 支持批量数据读取和处理
列式存储: SoA (Structure of Arrays) 内存布局
Header-only: 纯头文件设计,易于集成
零开销抽象: 无虚函数调用开销
重点借鉴方向¶
批量处理: 批量数据读取和处理模式
内存布局: SoA 列式存储布局
CRTP 设计: 编译期多态替代虚函数
增量计算: 技术指标的增量计算优化
预分配策略: 内存预分配和对象复用
noexcept 优化: 关键路径异常声明优化
一、项目对比分析¶
1.1 BackTest-Cpp 核心特性¶
| 特性 | 描述 |
|——|——|
| CRTP 模式| 奇异递归模板模式实现编译期多态 |
|批量处理| 默认 1024 条数据批量读取 |
|SoA 布局| OHLCBatch 列式存储结构 |
|增量计算| SMA/EMA/RSI 使用增量算法 |
|Header-only| 纯头文件设计 |
|noexcept| 关键函数异常声明 |
|内存预分配| reserve() 预分配内存 |
1.2 backtrader 现有能力对比¶
| 能力 | backtrader | BackTest-Cpp | 差距 |
|——|———–|————-|——|
|执行模式| 逐条处理 (next()) | 批量处理 | Cpp 更高效 |
|内存布局| AoS (Array of Structures) | SoA (Structure of Arrays) | Cpp 缓存友好 |
|多态| 虚函数 | CRTP 编译期多态 | Cpp 零开销 |
|指标计算| 逐条计算 | 增量计算 | Cpp 算法优化 |
|内存管理| 动态分配 | 预分配复用 | Cpp 更高效 |
1.3 差距分析¶
| 方面 | BackTest-Cpp | backtrader | 可借鉴点 |
|——|————-|———–|———|
|批量处理| 批量读取 CSV | 逐条处理 | backtrader 可添加批量模式 |
|内存布局| 列式存储 | 行式存储 | backtrader 可优化数据结构 |
|指标计算| 增量算法 | 完整计算 | backtrader 可优化指标 |
|异常保证| noexcept 关键路径 | 无特殊声明 | backtrader 可添加优化 |
二、需求规格文档¶
2.1 功能需求¶
FR1: 批量数据处理¶
支持批量数据读取和处理以提升性能:
FR1.1: BatchDataFeed - 批量数据源
FR1.2:
next_batch()- 批量获取数据FR1.3: 可配置批次大小
FR1.4: 批量模式策略执行
FR2: 内存布局优化¶
优化数据存储结构以提高缓存命中率:
FR2.1: SoA 数据结构设计
FR2.2: 列式存储实现
FR2.3: 向量化友好布局
FR2.4: 内存预分配策略
FR3: 增量指标计算¶
优化技术指标计算性能:
FR3.1: 增量 SMA 计算
FR3.2: 增量 EMA 计算
FR3.3: 增量 RSI 计算
FR3.4: 通用增量计算框架
FR4: 性能优化¶
C++ 性能优化技术移植:
FR4.1: noexcept 声明
FR4.2: 内存预分配
FR4.3: 对象复用
FR4.4: 缓存友好设计
2.2 非功能需求¶
NFR1: 性能 - 提升回测速度 20%+
NFR2: 兼容性 - 与现有 API 兼容
NFR3: 可选性 - 批量模式为可选
NFR4: 内存 - 内存使用不增加
2.3 用户故事¶
| ID | 故事描述 | 优先级 |
|—-|———|——–|
| US1 | 作为量化研究员,我希望使用批量处理加速回测,节省时间 | P0 |
| US2 | 作为系统开发者,我希望优化内存布局减少缓存未命中 | P1 |
| US3 | 作为策略开发者,我希望指标计算更快,提高回测效率 | P1 |
| US4 | 作为性能专家,我希望使用 noexcept 优化关键路径 | P2 |
三、设计文档¶
3.1 模块结构设计¶
backtrader/
├── core/
│ ├── batch.py # 批量处理模块
│ │ ├── batchfeed.py # 批量数据源
│ │ └── batchstrategy.py # 批量策略基类
├── utils/
│ ├── soa.py # SoA 数据结构
│ └── memory.py # 内存管理工具
└── indicators/
└── incremental.py # 增量指标计算
```bash
### 3.2 核心类设计
#### 3.2.1 批量数据源
```python
"""
Batch Data Feed for backtrader
参考:BackTest-Cpp/FileIterator.h
"""
import numpy as np
import pandas as pd
from backtrader import FeedBase
from backtrader.utils.py3 import iteritems
class SOAData:
"""
Structure of Arrays 数据结构
参考 BackTest-Cpp OHLCBatch 设计,使用列式存储提高缓存局部性
"""
def __init__(self, size=0, dtype=np.float64):
"""
Args:
size: 预分配大小
dtype: 数据类型
"""
self.size = 0
self.capacity = size
# 列式存储 - 每列是一个 numpy 数组
if size > 0:
self.date = np.empty(size, dtype='datetime64[ns]')
self.open = np.empty(size, dtype=dtype)
self.high = np.empty(size, dtype=dtype)
self.low = np.empty(size, dtype=dtype)
self.close = np.empty(size, dtype=dtype)
self.volume = np.empty(size, dtype=dtype)
self.openinterest = np.empty(size, dtype=dtype)
else:
self.date = None
self.open = None
self.high = None
self.low = None
self.close = None
self.volume = None
self.openinterest = None
def ensure_capacity(self, min_capacity):
"""确保足够的容量"""
if self.capacity >= min_capacity:
return
new_capacity = max(min_capacity, self.capacity *2)
self._resize(new_capacity)
def _resize(self, new_capacity):
"""调整数组大小"""
if self.size == 0:
self.date = np.empty(new_capacity, dtype='datetime64[ns]')
self.open = np.empty(new_capacity, dtype=np.float64)
self.high = np.empty(new_capacity, dtype=np.float64)
self.low = np.empty(new_capacity, dtype=np.float64)
self.close = np.empty(new_capacity, dtype=np.float64)
self.volume = np.empty(new_capacity, dtype=np.float64)
self.openinterest = np.empty(new_capacity, dtype=np.float64)
else:
self.date = np.resize(self.date, new_capacity)
self.open = np.resize(self.open, new_capacity)
self.high = np.resize(self.high, new_capacity)
self.low = np.resize(self.low, new_capacity)
self.close = np.resize(self.close, new_capacity)
self.volume = np.resize(self.volume, new_capacity)
self.openinterest = np.resize(self.openinterest, new_capacity)
self.capacity = new_capacity
def append(self, date, open_, high, low, close, volume, openinterest=0):
"""追加单条数据"""
if self.size >= self.capacity:
self.ensure_capacity(self.size + 1)
self.date[self.size] = date
self.open[self.size] = open_
self.high[self.size] = high
self.low[self.size] = low
self.close[self.size] = close
self.volume[self.size] = volume
self.openinterest[self.size] = openinterest
self.size += 1
def clear(self):
"""清空数据(保留内存)"""
self.size = 0
def to_dataframe(self):
"""转换为 pandas DataFrame"""
if self.size == 0:
return pd.DataFrame()
return pd.DataFrame({
'open': self.open[:self.size],
'high': self.high[:self.size],
'low': self.low[:self.size],
'close': self.close[:self.size],
'volume': self.volume[:self.size],
'openinterest': self.openinterest[:self.size],
}, index=self.date[:self.size])
def __len__(self):
return self.size
def __getitem__(self, key):
"""支持切片访问"""
if isinstance(key, slice):
# 返回新的 SOAData
result = SOAData()
slices = slice(0, self.size)[key]
result.size = min(self.size, slices.stop or self.size) - (slices.start or 0)
result.capacity = result.size
result.date = self.date[key]
result.open = self.open[key]
result.high = self.high[key]
result.low = self.low[key]
result.close = self.close[key]
result.volume = self.volume[key]
result.openinterest = self.openinterest[key]
return result
return SOADataSlice(self, key)
class SOADataSlice:
"""SOAData 的切片视图(零拷贝)"""
def __init__(self, soa_data, index):
self._soa = soa_data
self._idx = index
@property
def date(self):
return self._soa.date[self._idx]
@property
def open(self):
return self._soa.open[self._idx]
@property
def high(self):
return self._soa.high[self._idx]
@property
def low(self):
return self._soa.low[self._idx]
@property
def close(self):
return self._soa.close[self._idx]
@property
def volume(self):
return self._soa.volume[self._idx]
class BatchDataFeed:
"""
批量数据源
参考 BackTest-Cpp FileIterator,支持批量读取和处理
"""
def __init__(self, data_feed, batch_size=1024):
"""
Args:
data_feed: backtrader 数据源
batch_size: 批次大小
"""
self._data_feed = data_feed
self._batch_size = batch_size
self._current_batch = SOAData(size=batch_size)
self._batch_index = 0
self._total_index = 0
# 预加载所有数据到内存(如果数据量不大)
self._load_all_data()
def _load_all_data(self):
"""将所有数据加载到 SoA 结构"""
# 重置数据源
self._data_feed.reset()
# 收集所有数据
dates = []
opens = []
highs = []
lows = []
closes = []
volumes = []
openinterests = []
for i in range(len(self._data_feed)):
dates.append(self._data_feed.datetime.date(0))
opens.append(self._data_feed.open[0])
highs.append(self._data_feed.high[0])
lows.append(self._data_feed.low[0])
closes.append(self._data_feed.close[0])
volumes.append(self._data_feed.volume[0])
openinterests.append(self._data_feed.openinterest[0])
self._data_feed.advance()
# 存储到 numpy 数组
self._all_data = {
'date': np.array(dates),
'open': np.array(opens, dtype=np.float64),
'high': np.array(highs, dtype=np.float64),
'low': np.array(lows, dtype=np.float64),
'close': np.array(closes, dtype=np.float64),
'volume': np.array(volumes, dtype=np.float64),
'openinterest': np.array(openinterests, dtype=np.float64),
}
self._total_bars = len(dates)
def next_batch(self):
"""
获取下一批数据
Returns:
bool: 是否还有数据
"""
if self._batch_index >= self._total_bars:
return False
# 计算当前批次的结束位置
end_index = min(self._batch_index + self._batch_size, self._total_bars)
batch_length = end_index - self._batch_index
# 更新当前批次
self._current_batch.size = batch_length
self._current_batch.date = self._all_data['date'][self._batch_index:end_index]
self._current_batch.open = self._all_data['open'][self._batch_index:end_index]
self._current_batch.high = self._all_data['high'][self._batch_index:end_index]
self._current_batch.low = self._all_data['low'][self._batch_index:end_index]
self._current_batch.close = self._all_data['close'][self._batch_index:end_index]
self._current_batch.volume = self._all_data['volume'][self._batch_index:end_index]
self._current_batch.openinterest = self._all_data['openinterest'][self._batch_index:end_index]
self._batch_index = end_index
return True
@property
def current_batch(self):
"""获取当前批次"""
return self._current_batch
def reset(self):
"""重置数据源"""
self._batch_index = 0
```bash
#### 3.2.2 增量指标计算
```python
"""
Incremental Indicators
参考:BackTest-Cpp/Indicators.h
"""
import numpy as np
from backtrader import Indicator
class IncrementalSMA(Indicator):
"""
增量计算简单移动平均
参考 BackTest-Cpp 的 SMA 实现,使用增量算法避免重复求和
时间复杂度:O(n) 而非 O(n×period)
"""
lines = ('sma',)
params = (('period', 20),)
def __init__(self):
# 初始窗口期使用标准方法
self._sum = 0.0
self._window = []
def next(self):
price = self.data[0]
if len(self) <= self.p.period:
self._window.append(price)
self._sum += price
if len(self) == self.p.period:
self.lines.sma[0] = self._sum / self.p.period
else:
# 增量更新:新值进入,旧值移出
old_value = self._window.pop(0)
self._window.append(price)
self._sum += price - old_value
self.lines.sma[0] = self._sum / self.p.period
class IncrementalEMA(Indicator):
"""
增量计算指数移动平均
参考 BackTest-Cpp 的 EMA 实现,使用递归公式
EMA = α × (current - prev) + prev
其中 α = 2 / (period + 1)
"""
lines = ('ema',)
params = (('period', 20),)
def __init__(self):
self._alpha = 2.0 / (self.p.period + 1)
self._prev = None
self._initialized = False
def next(self):
price = self.data[0]
if not self._initialized:
# 前期使用 SMA 初始化
if len(self) == self.p.period:
# 计算初始 SMA
self._prev = sum(self.data.get(size=self.p.period)) / self.p.period
self.lines.ema[0] = self._prev
self._initialized = True
else:
# 增量更新:EMA = α × (current - prev) + prev
self._prev = self._alpha*(price - self._prev) + self._prev
self.lines.ema[0] = self._prev
class IncrementalRSI(Indicator):
"""
增量计算相对强弱指标
参考 BackTest-Cpp 的 RSI 实现,使用增量平均收益/损失
"""
lines = ('rsi',)
params = (('period', 14),)
def __init__(self):
self._avg_gain = 0.0
self._avg_loss = 0.0
self._initialized = False
self._prev_price = None
def next(self):
price = self.data[0]
if self._prev_price is None:
self._prev_price = price
return
delta = price - self._prev_price
self._prev_price = price
gain = delta if delta > 0 else 0.0
loss = -delta if delta < 0 else 0.0
if not self._initialized:
# 初始化阶段
if len(self) > self.p.period:
# 使用前期平均值初始化
self._avg_gain = gain / self.p.period
self._avg_loss = loss / self.p.period
self._initialized = True
self._calc_rsi()
else:
# 增量更新平均收益和损失
self._avg_gain = (self._avg_gain*(self.p.period - 1) + gain) / self.p.period
self._avg_loss = (self._avg_loss*(self.p.period - 1) + loss) / self.p.period
self._calc_rsi()
def _calc_rsi(self):
"""计算 RSI 值"""
if self._avg_loss == 0:
self.lines.rsi[0] = 100.0
else:
rs = self._avg_gain / self._avg_loss
self.lines.rsi[0] = 100.0 - 100.0 / (1.0 + rs)
class VectorizedSMA(Indicator):
"""
向量化 SMA - 使用 numpy 批量计算
适用于批量处理模式,一次性计算整个批次的 SMA
"""
lines = ('sma',)
params = (('period', 20),)
def __init__(self):
# 检查数据源是否是批量数据
if hasattr(self.data, 'batch_mode') and self.data.batch_mode:
self._batch_mode = True
else:
self._batch_mode = False
self._window = []
def next(self):
if self._batch_mode:
# 批量模式下,一次性计算整个批次
self._calc_batch()
else:
# 单条模式
self._calc_single()
def _calc_single(self):
"""单条计算"""
price = self.data[0]
self._window.append(price)
if len(self._window) > self.p.period:
self._window.pop(0)
if len(self._window) >= self.p.period:
self.lines.sma[0] = sum(self._window) / len(self._window)
def _calc_batch(self):
"""批量计算 - 利用 numpy 向量化"""
if not hasattr(self.data, 'close'):
return
close = self.data.close
if len(close) < self.p.period:
return
# 使用 numpy 的卷积计算移动平均
kernel = np.ones(self.p.period) / self.p.period
sma = np.convolve(close, kernel, mode='valid')
# 更新当前值
if len(sma) > 0:
self.lines.sma[0] = sma[-1]
```bash
#### 3.2.3 批量策略基类
```python
"""
Batch Strategy
参考:BackTest-Cpp/Strategy.h
"""
from backtrader import Strategy
class BatchStrategy(Strategy):
"""
批量策略基类
参考 BackTest-Cpp 的 Engine.run() 设计,支持批量处理
策略可以实现 on_batch 方法来处理整批数据
"""
params = (
('batch_size', 1024),
)
def __init__(self):
self._batch_data = []
self._batch_index = 0
def next(self):
"""
累积数据到批次大小,然后调用 on_batch
"""
# 收集当前 bar 数据
self._collect_bar()
# 检查是否达到批次大小
if len(self._batch_data) >= self.p.batch_size:
self._process_batch()
self._batch_data.clear()
def _collect_bar(self):
"""收集当前 bar 数据"""
bar = {
'datetime': self.data.datetime.datetime(0),
'open': self.data.open[0],
'high': self.data.high[0],
'low': self.data.low[0],
'close': self.data.close[0],
'volume': self.data.volume[0],
}
self._batch_data.append(bar)
def _process_batch(self):
"""
处理当前批次
将批量数据转换为 numpy 数组,然后调用 on_batch
"""
if not self._batch_data:
return
# 转换为 numpy 数组
batch = {
'datetime': [bar['datetime'] for bar in self._batch_data],
'open': np.array([bar['open'] for bar in self._batch_data], dtype=np.float64),
'high': np.array([bar['high'] for bar in self._batch_data], dtype=np.float64),
'low': np.array([bar['low'] for bar in self._batch_data], dtype=np.float64),
'close': np.array([bar['close'] for bar in self._batch_data], dtype=np.float64),
'volume': np.array([bar['volume'] for bar in self._batch_data], dtype=np.float64),
}
# 调用子类实现的 on_batch
self.on_batch(batch)
def on_batch(self, batch):
"""
子类实现此方法来处理批量数据
Args:
batch: 包含 'datetime', 'open', 'high', 'low', 'close', 'volume' 的字典
每个值(除 datetime 外)都是 numpy 数组
"""
pass
def stop(self):
"""处理剩余数据"""
if self._batch_data:
self._process_batch()
# 使用示例
class MAStrategy(BatchStrategy):
"""
移动平均策略 - 使用批量处理
"""
params = (
('fast_period', 20),
('slow_period', 50),
('batch_size', 256),
)
def __init__(self):
super().__init__()
def on_batch(self, batch):
"""
批量处理数据
"""
# 获取收盘价数组
close = batch['close']
if len(close) < self.p.slow_period:
return
# 使用 numpy 批量计算 SMA
fast_sma = self._calc_sma_numpy(close, self.p.fast_period)
slow_sma = self._calc_sma_numpy(close, self.p.slow_period)
# 检查金叉/死叉
if len(fast_sma) >= 2:
# 最近的交叉
if fast_sma[-1] > slow_sma[-1] and fast_sma[-2] <= slow_sma[-2]:
# 金叉 - 买入
if not self.position:
self.buy(size=100)
elif fast_sma[-1] < slow_sma[-1] and fast_sma[-2] >= slow_sma[-2]:
# 死叉 - 卖出
if self.position:
self.sell(size=self.position.size)
def _calc_sma_numpy(self, data, period):
"""使用 numpy 计算 SMA"""
kernel = np.ones(period) / period
return np.convolve(data, kernel, mode='valid')
```bash
#### 3.2.4 内存优化工具
```python
"""
Memory Optimization Tools
参考:BackTest-Cpp 内存管理策略
"""
import numpy as np
class MemoryPool:
"""
内存池 - 对象复用
参考 BackTest-Cpp 的 batch_.clear() 模式,复用内存而非频繁分配
"""
def __init__(self, dtype=np.float64, initial_capacity=1024):
"""
Args:
dtype: 数据类型
initial_capacity: 初始容量
"""
self.dtype = dtype
self._capacity = initial_capacity
self._size = 0
self._data = np.empty(initial_capacity, dtype=dtype)
def allocate(self, size):
"""分配指定大小的内存"""
if size > self._capacity:
# 扩容(两倍增长策略)
new_capacity = max(size, self._capacity* 2)
self._data = np.resize(self._data, new_capacity)
self._capacity = new_capacity
result = self._data[self._size:self._size + size]
self._size += size
return result
def reset(self):
"""重置内存池(保留内存)"""
self._size = 0
@property
def data(self):
"""获取当前有效数据"""
return self._data[:self._size]
class ArrayCache:
"""
数组缓存 - 用于缓存计算结果
避免重复计算相同的数据切片
"""
def __init__(self, max_size=100):
"""
Args:
max_size: 最大缓存条目数
"""
self._cache = {}
self._max_size = max_size
self._access_order = []
def get(self, key):
"""获取缓存值"""
if key in self._cache:
# 更新访问顺序
self._access_order.remove(key)
self._access_order.append(key)
return self._cache[key]
return None
def put(self, key, value):
"""存入缓存"""
if key in self._cache:
self._access_order.remove(key)
elif len(self._cache) >= self._max_size:
# 移除最久未使用的条目
oldest = self._access_order.pop(0)
del self._cache[oldest]
self._cache[key] = value
self._access_order.append(key)
def clear(self):
"""清空缓存"""
self._cache.clear()
self._access_order.clear()
def preallocate_arrays(size, columns=None):
"""
预分配数组
Args:
size: 数组大小
columns: 列名列表
Returns:
dict: 列名到数组的映射
"""
if columns is None:
columns = ['open', 'high', 'low', 'close', 'volume']
return {col: np.empty(size, dtype=np.float64) for col in columns}
```bash
- --
## 四、API 设计
### 4.1 批量数据 API
```python
from backtrader.feeds import BatchDataFeed
from backtrader import Cerebro
# 创建批量数据源
batch_feed = BatchDataFeed(data_feed, batch_size=1024)
# 批量处理
while batch_feed.next_batch():
batch = batch_feed.current_batch
# batch.open, batch.high 等都是 numpy 数组
sma = np.convolve(batch.close, np.ones(20)/20, mode='valid')
```bash
### 4.2 增量指标 API
```python
from backtrader.indicators import IncrementalSMA, IncrementalEMA, IncrementalRSI
class MyStrategy(bt.Strategy):
def __init__(self):
# 使用增量指标
self.sma = IncrementalSMA(self.data.close, period=20)
self.ema = IncrementalEMA(self.data.close, period=20)
self.rsi = IncrementalRSI(self.data.close, period=14)
```bash
### 4.3 批量策略 API
```python
from backtrader import BatchStrategy
class MyStrategy(BatchStrategy):
params = (
('batch_size', 512),
)
def on_batch(self, batch):
# batch 数据都是 numpy 数组
close = batch['close']
# 批量计算指标
sma_fast = self._calc_sma(close, 20)
sma_slow = self._calc_sma(close, 50)
# 批量处理信号
signals = np.where(sma_fast > sma_slow, 1, -1)
```bash
- --
## 五、实施计划
### 5.1 实施阶段
| 阶段 | 内容 | 时间 |
|------|------|------|
| Phase 1 | SOAData 数据结构实现 | 0.5 天 |
| Phase 2 | BatchDataFeed 批量数据源 | 1 天 |
| Phase 3 | 增量指标实现 | 1 天 |
| Phase 4 | BatchStrategy 批量策略 | 1 天 |
| Phase 5 | 内存优化工具 | 0.5 天 |
| Phase 6 | 测试和性能对比 | 1 天 |
### 5.2 优先级
1. **P0**: SOAData - 列式存储数据结构
2. **P0**: IncrementalSMA/EMA/RSI - 增量指标计算
3. **P1**: BatchDataFeed - 批量数据源
4. **P1**: BatchStrategy - 批量策略基类
5. **P2**: MemoryPool - 内存池
6. **P2**: ArrayCache - 数组缓存
- --
## 六、参考资料
### 6.1 关键参考代码
- BackTest-Cpp/include/backtest/Engine.h - CRTP 引擎设计
- BackTest-Cpp/include/backtest/FileIterator.h - 批量文件读取
- BackTest-Cpp/include/backtest/Indicators.h - 增量指标计算
- BackTest-Cpp/include/backtest/Data.h - 数据结构定义
- BackTest-Cpp/Examples/SupportResistance/SupportResistance.h - 策略示例
### 6.2 BackTest-Cpp 核心设计
```cpp
// CRTP 模式实现编译期多态
template <typename Derived, typename Feed>
class Engine {
void run() {
static_cast<Derived*>(this)->onStart();
while (feed_.nextBatch()) {
const Batch& batch = feed_.currentBatch();
for (std::size_t i = 0; i < batch.size(); ++i) {
static_cast<Derived*>(this)->onBar(batch, i);
}
}
static_cast<Derived*>(this)->onEnd();
}
};
// SoA 数据结构
struct OHLCBatch {
std::vector<std::string> date;
std::vector<double> open;
std::vector<double> high;
std::vector<double> low;
std::vector<double> close;
std::vector<double> volume;
};
// 增量 SMA 计算
inline std::vector<double> SMA(const std::vector<double>& data, std::size_t period) {
double sum = std::accumulate(data.begin(), data.begin() + period, 0.0);
for (std::size_t i = period; i < data.size(); ++i) {
sum += data[i] - data[i - period]; // 增量更新
sma[i] = sum / period;
}
}
```bash
### 6.3 性能优化技术总结
| 技术 | 描述 | backtrader 应用 |
|------|------|---------------|
| **批量处理**| 减少函数调用开销 | 批量数据源 |
|**SoA 布局**| 提高缓存局部性 | 列式数据结构 |
|**增量计算**| 减少重复计算 | 增量指标 |
|**内存预分配**| 减少分配次数 | 内存池 |
|**向量化**| 利用 SIMD | numpy 批量计算 |
|**noexcept** | 减少异常开销 | 关键路径声明 |
### 6.4 backtrader 可复用组件
- `backtrader/linebuffer.py` - 现有数据结构
- `backtrader/indicator.py` - 指标基类
- `backtrader/strategy.py` - 策略基类
- `backtrader/feeds/*` - 数据源接口