143 lines
3.9 KiB
Plaintext
143 lines
3.9 KiB
Plaintext
标题: 聚宽策略性能优化实战指南
|
||
链接: https://www.joinquant.com/view/community/detail/2
|
||
分类: 回测
|
||
================================================================================
|
||
|
||
# 聚宽策略性能优化实战指南
|
||
|
||
## 一、代码结构优化
|
||
|
||
### 1.1 合理分配计算逻辑
|
||
|
||
**避免在handle_data中进行耗时计算**:
|
||
|
||
```python
|
||
# 不推荐的写法
|
||
def handle_data(context, data):
|
||
# 在handle_data中进行复杂计算
|
||
for stock in g.stock_pool:
|
||
# 计算技术指标
|
||
prices = history(60, '1d', 'close', [stock])
|
||
ma20 = prices.mean()
|
||
ma60 = prices.mean()
|
||
std = prices.std()
|
||
# ... 更多计算
|
||
# 交易决策
|
||
if condition:
|
||
order(stock, amount)
|
||
|
||
# 推荐的写法
|
||
def before_trading_start(context, data):
|
||
# 在开盘前完成所有复杂计算
|
||
g.signals = {}
|
||
prices = history(60, '1d', 'close', g.stock_pool)
|
||
for stock in g.stock_pool:
|
||
ma20 = prices[stock][-20:].mean()
|
||
ma60 = prices[stock].mean()
|
||
std = prices[stock].std()
|
||
g.signals[stock] = {'ma20': ma20, 'ma60': ma60, 'std': std}
|
||
|
||
def handle_data(context, data):
|
||
# handle_data中只进行简单的交易决策
|
||
for stock in g.stock_pool:
|
||
signal = g.signals[stock]
|
||
if signal['ma20'] > signal['ma60']:
|
||
order_target_percent(stock, 0.01)
|
||
```
|
||
|
||
### 1.2 使用全局变量缓存
|
||
|
||
```python
|
||
def initialize(context):
|
||
g.stock_pool = get_index_stocks('000300.XSHG')
|
||
# 初始化缓存字典
|
||
g.cache = {}
|
||
|
||
def before_trading_start(context, data):
|
||
# 只在数据变化时更新缓存
|
||
current_date = context.current_date.strftime('%Y-%m-%d')
|
||
if current_date not in g.cache:
|
||
g.cache[current_date] = {
|
||
'prices': get_price(g.stock_pool, count=60, end_date=context.previous_date),
|
||
'factors': calculate_factors(g.stock_pool, context.previous_date)
|
||
}
|
||
```
|
||
|
||
## 二、向量化操作替代循环
|
||
|
||
### 2.1 利用Pandas向量化
|
||
|
||
```python
|
||
# 低效:Python循环
|
||
def calculate_returns_loop(prices):
|
||
returns = {}
|
||
for stock in prices.columns:
|
||
returns[stock] = prices[stock].pct_change()
|
||
return returns
|
||
|
||
# 高效:Pandas向量化
|
||
def calculate_returns_vectorized(prices):
|
||
return prices.pct_change()
|
||
```
|
||
|
||
### 2.2 使用TA-Lib
|
||
|
||
```python
|
||
import talib
|
||
|
||
# 不推荐:自己实现指标
|
||
def my_ma(prices, window):
|
||
ma = []
|
||
for i in range(len(prices)):
|
||
if i < window - 1:
|
||
ma.append(None)
|
||
else:
|
||
ma.append(sum(prices[i-window+1:i+1])/window)
|
||
return ma
|
||
|
||
# 推荐:使用TA-Lib
|
||
def talib_ma(prices, window):
|
||
return talib.SMA(prices, timeperiod=window)
|
||
```
|
||
|
||
## 三、减少不必要的输出
|
||
|
||
### 3.1 策略逻辑与数据记录分离
|
||
|
||
```python
|
||
# 不推荐:每个bar都记录大量数据
|
||
def handle_data(context, data):
|
||
for stock in g.stock_pool:
|
||
record(**{f'{stock}_price': data[stock].close})
|
||
record(**{f'{stock}_position': context.portfolio.positions[stock].amount})
|
||
|
||
# 推荐:只在关键时间点记录,使用批量记录
|
||
def after_trading_end(context, data):
|
||
# 每天结束时记录一次关键指标
|
||
record(
|
||
portfolio_value=context.portfolio.total_value,
|
||
cash=context.portfolio.cash,
|
||
leverage=context.account.leverage
|
||
)
|
||
```
|
||
|
||
## 四、实践案例
|
||
|
||
**优化前**:
|
||
- 策略回测时间:30分钟
|
||
- 问题:在handle_data中计算所有技术指标,使用Python循环
|
||
|
||
**优化后**:
|
||
- 策略回测时间:5分钟
|
||
- 改进措施:
|
||
1. 将技术指标计算移到before_trading_start
|
||
2. 使用TA-Lib替代自行实现的指标
|
||
3. 利用Pandas向量化操作
|
||
4. 减少记录频率,只在每天结束时记录
|
||
|
||
**性能提升**:6倍速度提升
|
||
|
||
---
|
||
|
||
**总结**:策略性能优化的核心是"减少重复计算、利用向量化操作、合理分配计算时机"。通过这些优化,可以显著提升策略回测和实盘运行效率。
|