标题: 量化回测中的常见陷阱及规避方法
链接: https://www.joinquant.com/view/community/detail/3
分类: 回测
================================================================================

# 量化回测中的常见陷阱及规避方法

## 一、过拟合陷阱

### 1.1 什么是过拟合

过拟合是指策略在历史数据上表现优异，但在实盘或样本外数据上表现糟糕的现象。

**表现特征**：
- 历史回测收益率很高，但样本外测试急剧下降
- 参数微调对结果影响巨大
- 交易次数过多，过度优化

### 1.2 规避方法

**方法1：样本外验证**

```
三段式回测验证：
- 训练集：60%数据（用于策略开发和参数优化）
- 验证集：20%数据（用于参数验证和策略选择）
- 测试集：20%数据（最终验证，只能用一次）
```

**方法2：参数敏感性分析**

不要只看最优参数，要分析参数周围的表现：

```python
# 参数敏感性测试
for param in range(5, 30, 5):
    # 测试不同参数下的策略表现
    result = backtest(strategy, param=param)
    print(f'参数{param}: 收益率{result['return']}, 夏普比率{result['sharpe']}')
```

**方法3：简化策略逻辑**

- 避免过多的条件判断
- 减少参数数量
- 策略逻辑应该有经济学或行为金融学解释

## 二、幸存者偏差

### 2.1 什么是幸存者偏差

幸存者偏差是指回测时只使用了当前还在上市的股票，而忽略了已经退市的股票，导致回测结果过于乐观。

**影响**：
- 高估策略收益率
- 低估策略风险
- 实盘表现远不如回测

### 2.2 规避方法

**在聚宽平台上的正确做法**：

```python
# 使用包含退市股票的完整数据集
from jqdata import *

def get_stock_pool(date):
    # 获取指定日期的指数成分股（包含当时的成分股，而不是当前的）
    return get_index_stocks('000300.XSHG', date=date)

# 或者使用all_securities获取所有股票（包含退市的）
all_stocks = list(all_securities)
```

**注意事项**：
- 不要使用当前的股票池回测历史数据
- 使用`get_index_stocks`时要指定日期参数
- 考虑ST股票、停牌股票的处理

## 三、未来函数陷阱

### 3.1 什么是未来函数

未来函数是指在策略中使用了当时还无法获得的数据。

**常见类型**：
1. 使用未来的财务数据
2. 知道未来的最高价、最低价进行交易
3. 使用停牌后的数据

### 3.2 规避方法

**检查清单**：

```
1. 数据获取日期检查
   - 确保使用的数据在交易决策时间点之前

2. 避免"先知先觉"
   - 不要使用未来才能知道的信息

3. 模拟真实决策流程
   - 按照实际交易时的信息获取顺序编写策略
```

**示例对比**：

```python
# 错误示例（使用未来数据）
def handle_data(context, data):
    for stock in g.stock_pool:
        # 获取未来一天的数据（这在实际交易中是不可能的）
        future_price = get_price(stock, count=1, end_date=context.current_date + timedelta(days=1))
        if future_price > data[stock].close:
            order(stock, 100)

# 正确示例
def handle_data(context, data):
    for stock in g.stock_pool:
        # 只使用当前及之前的数据
        hist = history(20, '1d', 'close', [stock])
        current_price = data[stock].close
        ma20 = hist.mean()
        if current_price > ma20:
            order(stock, 100)
```

## 四、交易成本低估

### 4.1 常见问题

- 忽略交易手续费
- 低估滑点影响
- 不考虑市场冲击成本

### 4.2 规避方法

**合理设置交易成本**：

```python
def initialize(context):
    # 设置手续费
    set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    # 设置滑点
    set_slippage(FixedSlippage(0.002))  # 0.2%的固定滑点
```

**成本参考**：
- 买入佣金：万分之三
- 卖出佣金：万分之三 + 千分之一印花税
- 滑点：0.1% - 0.5%（根据股票流动性调整）

## 五、实践案例

某投资者的教训：

**回测表现**：
- 年化收益率：60%
- 最大回撤：15%
- 夏普比率：2.5

**实盘表现**（3个月）：
- 实际收益率：-5%
- 最大回撤：20%

**问题诊断**：
1. 过拟合：参数过度优化，只在特定历史时期表现好
2. 幸存者偏差：回测时只用了当前股票，没考虑退市股票
3. 交易成本低估：回测时设置的成本过低

**改进措施**：
1. 采用三段式回测验证
2. 使用包含退市股票的完整数据集
3. 提高交易成本设置
4. 简化策略逻辑，减少参数

---

**总结**：回测陷阱是量化交易中最常见的问题，需要从数据质量、策略逻辑、回测设置等多方面进行严格把关。记住："回测表现只是起点，实盘验证才是关键"。
