629 lines
25 KiB
Markdown
629 lines
25 KiB
Markdown
# 量化策略开发与向量化回测详解
|
||
## `quant_strategy_backtest_demo.py` 学习文档
|
||
|
||
> **目标读者**:零基础量化入门者
|
||
> **配套文件**:`quant_strategy_backtest_demo.py`
|
||
> **前置知识**:建议先阅读 `doc_01_data_pipeline.md`
|
||
> **系列位置**:第 2 篇 — 策略开发与回测篇
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [本篇概览](#1-本篇概览)
|
||
2. [合成价格数据:几何布朗运动](#2-合成价格数据几何布朗运动-geometric-brownian-motion)
|
||
3. [技术指标详解](#3-技术指标详解-technical-indicators)
|
||
- [3.1 SMA 简单移动平均线](#31-sma-简单移动平均线-simple-moving-average)
|
||
- [3.2 EMA 指数移动平均线](#32-ema-指数移动平均线-exponential-moving-average)
|
||
- [3.3 RSI 相对强弱指数](#33-rsi-相对强弱指数-relative-strength-index)
|
||
- [3.4 MACD 指数平滑异同移动平均线](#34-macd-指数平滑异同移动平均线)
|
||
- [3.5 布林带 Bollinger Bands](#35-布林带-bollinger-bands)
|
||
4. [策略 A:双均线金叉/死叉](#4-策略-a双均线金叉死叉-ma-crossover)
|
||
5. [策略 B:RSI 均值回归](#5-策略-brsi-均值回归-rsi-mean-reversion)
|
||
6. [向量化回测引擎](#6-向量化回测引擎-vectorized-backtest-engine)
|
||
7. [绩效指标详解](#7-绩效指标详解-performance-metrics)
|
||
8. [滚动前向验证与参数优化](#8-滚动前向验证与参数优化-walk-forward-validation)
|
||
9. [收益率分布分析](#9-收益率分布分析-return-distribution-analysis)
|
||
10. [术语速查表](#10-术语速查表)
|
||
|
||
---
|
||
|
||
## 1. 本篇概览
|
||
|
||
这篇文档对应量化交易的第二个核心环节:**"有了干净的数据,怎么用它来赚钱?"**
|
||
|
||
整个流程可以归纳为:
|
||
|
||
```
|
||
干净的价格数据
|
||
│
|
||
▼
|
||
计算技术指标(均线、RSI、MACD、布林带)
|
||
│
|
||
▼
|
||
制定交易规则 → 生成交易信号(买/卖/空仓)
|
||
│
|
||
▼
|
||
向量化回测引擎(模拟历史交易,扣除成本)
|
||
│
|
||
▼
|
||
计算绩效指标(夏普、最大回撤、胜率…)
|
||
│
|
||
▼
|
||
前向验证(防止过拟合)→ 判断策略是否值得继续研究
|
||
```
|
||
|
||
**两种策略类型**,代码各实现一种:
|
||
|
||
| 类型 | 代表指标 | 逻辑 | 适合市场环境 |
|
||
|------|----------|------|-------------|
|
||
| **趋势跟随 (Trend Following)** | 移动均线、MACD | 跟着趋势走,买涨跟涨 | 有明显趋势的牛市/熊市 |
|
||
| **均值回归 (Mean Reversion)** | RSI、布林带 | 逆势操作,跌多了买、涨多了卖 | 震荡盘整市场 |
|
||
|
||
---
|
||
|
||
## 2. 合成价格数据:几何布朗运动 (Geometric Brownian Motion)
|
||
|
||
### 2.1 为什么用模拟数据?
|
||
|
||
- 真实数据需要付费数据源,这个 Demo 用合成数据让所有人都能直接运行
|
||
- 合成数据有"已知"的统计特性(我们自己设定的 μ 和 σ),便于验证代码正确性
|
||
- 通过调整参数可以模拟不同市场环境(牛市、熊市、震荡市)
|
||
|
||
### 2.2 GBM 公式直观理解
|
||
|
||
**几何布朗运动 (GBM)** 是描述股价随机游走的经典数学模型,也是期权定价公式(Black-Scholes)的基础。
|
||
|
||
离散形式(代码实现的):
|
||
|
||
$$P_t = P_{t-1} \times e^{\left(\mu - \frac{\sigma^2}{2}\right) \cdot \Delta t + \sigma \cdot \sqrt{\Delta t} \cdot \varepsilon_t}$$
|
||
|
||
用大白话解释这个公式:
|
||
|
||
- $P_t$:今天的价格
|
||
- $P_{t-1}$:昨天的价格
|
||
- $\mu$(mu):**年化漂移率 (Annual Drift)**,相当于股票的"长期趋势倾斜度"。$\mu = 0.10$ 表示每年平均上涨 10%
|
||
- $\sigma$(sigma):**年化波动率 (Annual Volatility)**,表示价格的"抖动剧烈程度"。$\sigma = 0.25$ 表示年化波动 25%
|
||
- $\Delta t = 1/252$:一个交易日是一年的 1/252
|
||
- $\varepsilon_t \sim N(0,1)$:每天的随机冲击,从标准正态分布中随机抽取
|
||
|
||
**直觉**:每天的价格变化 = 确定性漂移(趋势)+ 随机波动(噪声)
|
||
|
||
```python
|
||
# 代码中的实现
|
||
dt = 1.0 / 252
|
||
epsilon = np.random.randn(n_days) # 随机冲击
|
||
log_returns = (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * epsilon
|
||
prices = s0 * np.exp(np.cumsum(log_returns)) # 累积乘积 → 价格路径
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 技术指标详解 (Technical Indicators)
|
||
|
||
**技术分析 (Technical Analysis)** 的核心假设:过去的价格和成交量模式包含了预测未来走势的信息。技术指标就是从原始价格数据中提炼这些模式的数学工具。
|
||
|
||
> ⚠️ **注意**:技术分析在学术界存在争议,有效市场假说 (Efficient Market Hypothesis) 认为价格已经反映了所有信息,技术分析无法持续获得超额收益。但在实践中,许多量化基金仍然将技术指标作为信号的一部分。
|
||
|
||
### 3.1 SMA 简单移动平均线 (Simple Moving Average)
|
||
|
||
**公式**:
|
||
|
||
$$\text{SMA}_n(t) = \frac{P_t + P_{t-1} + \cdots + P_{t-n+1}}{n}$$
|
||
|
||
**直觉**:取最近 n 天收盘价的算术平均数,形成一条平滑的"趋势线"。
|
||
|
||
**关键参数 n 的选择**:
|
||
|
||
| 窗口 n | 代号 | 特性 |
|
||
|--------|------|------|
|
||
| 5、10 天 | 短期 (Short-term) | 贴近价格,反应快,但"噪声"多 |
|
||
| 20、30 天 | 中期 (Medium-term) | 月线级别的趋势 |
|
||
| 60、120 天 | 长期 (Long-term) | 季度级别的主要趋势,滞后但稳定 |
|
||
| 250 天 | 年线 (Annual) | 区分牛熊市的重要参考线 |
|
||
|
||
**代码**:
|
||
|
||
```python
|
||
def sma(prices, window):
|
||
return prices.rolling(window=window).mean()
|
||
# rolling(window) = 创建一个滑动窗口
|
||
# .mean() = 对窗口内的值取平均
|
||
```
|
||
|
||
**前 n-1 天为什么没有值?**
|
||
计算 20 日均线需要 20 天数据,第 1-19 天的均线值是 `NaN`(数据不够)。第 20 天才有第一个有效均线值。
|
||
|
||
### 3.2 EMA 指数移动平均线 (Exponential Moving Average)
|
||
|
||
**公式**:
|
||
|
||
$$\text{EMA}_t = \alpha \cdot P_t + (1 - \alpha) \cdot \text{EMA}_{t-1}, \quad \alpha = \frac{2}{n+1}$$
|
||
|
||
**与 SMA 的核心区别**:SMA 对窗口内所有日期权重相等;EMA 给**近期数据更高权重**,越新的数据越重要。
|
||
|
||
**形象比喻**:
|
||
SMA 像"民主投票"——每一天的发言权相等;
|
||
EMA 像"资历制"——越近的日期说话越算数。
|
||
|
||
**结果**:EMA 比 SMA 对最近的价格变化反应更快,但也更"神经质"(更多小幅震动)。
|
||
|
||
```python
|
||
def ema(prices, span):
|
||
return prices.ewm(span=span, adjust=False).mean()
|
||
# ewm = exponentially weighted moving (指数加权移动)
|
||
# span 对应公式中的 n,α = 2/(span+1)
|
||
```
|
||
|
||
### 3.3 RSI 相对强弱指数 (Relative Strength Index)
|
||
|
||
**RSI 解决的问题**:量化"最近一段时间,涨的力量和跌的力量哪个更强?"
|
||
|
||
**计算步骤**(以 14 天为例):
|
||
|
||
1. 计算每天的涨跌:`delta = close.diff()`
|
||
2. 上涨天的涨幅 (Gain):`delta > 0` 的部分
|
||
3. 下跌天的跌幅 (Loss):`delta < 0` 的部分(取正值)
|
||
4. 用指数平均分别平滑 Gain 和 Loss:`avg_gain`、`avg_loss`
|
||
5. 计算相对强弱 RS = avg_gain / avg_loss
|
||
6. 转换:`RSI = 100 - 100 / (1 + RS)`
|
||
|
||
**结果范围**:RSI 永远在 0 到 100 之间。
|
||
|
||
| RSI 值 | 解读 |
|
||
|--------|------|
|
||
| > 70 | **超买 (Overbought)**:涨势过强,可能即将回调 |
|
||
| 50~70 | 偏强,多头占优 |
|
||
| 50 | **中性 (Neutral)**:多空势均力敌 |
|
||
| 30~50 | 偏弱,空头占优 |
|
||
| < 30 | **超卖 (Oversold)**:跌势过强,可能即将反弹 |
|
||
|
||
**形象比喻**:RSI 就像一个"疲劳度量表"。跑得太快(涨太猛)= 超买,该休息了;跌得太惨 = 超卖,该反弹了。
|
||
|
||
```python
|
||
def rsi(prices, window=14):
|
||
delta = prices.diff()
|
||
gain = delta.clip(lower=0) # 只保留正值(涨的部分)
|
||
loss = -delta.clip(upper=0) # 只保留负值并取正(跌的部分)
|
||
avg_gain = gain.ewm(alpha=1.0/window, adjust=False).mean()
|
||
avg_loss = loss.ewm(alpha=1.0/window, adjust=False).mean()
|
||
rs = avg_gain / avg_loss # 相对强弱
|
||
return 100 - (100 / (1 + rs)) # 映射到 0~100
|
||
```
|
||
|
||
### 3.4 MACD 指数平滑异同移动平均线
|
||
|
||
**MACD 解决的问题**:用两条不同速度的 EMA 的"差距"来衡量趋势动能的变化。
|
||
|
||
**三条线**:
|
||
|
||
| 名称 | 计算 | 含义 |
|
||
|------|------|------|
|
||
| **MACD 线 (DIF)** | EMA(12) - EMA(26) | 快线(短期)减慢线(长期),反映近期动能 |
|
||
| **信号线 (DEA)** | EMA(9) of MACD 线 | MACD 线的 9 日平均,平滑后的趋势 |
|
||
| **柱状图 (Histogram)** | MACD 线 - 信号线 | 两线之差,放大了趋势变化 |
|
||
|
||
**交叉信号**:
|
||
|
||
| 信号 | 含义 |
|
||
|------|------|
|
||
| **金叉 (Golden Cross)**:MACD 线上穿信号线 | 买入信号,趋势转强 |
|
||
| **死叉 (Death Cross)**:MACD 线下穿信号线 | 卖出信号,趋势转弱 |
|
||
| 柱状图由负转正 | 多头动能开始占优 |
|
||
| 柱状图由正转负 | 空头动能开始占优 |
|
||
|
||
**形象比喻**:MACD 像赛跑中两个运动员的"领先差距"。快选手(短期均线)超过慢选手(长期均线)→ 金叉;被超过 → 死叉。
|
||
|
||
### 3.5 布林带 (Bollinger Bands)
|
||
|
||
**布林带解决的问题**:建立价格的"正常波动区间",判断当前价格是"正常"还是"过度偏离"。
|
||
|
||
**三条线**:
|
||
|
||
$$\text{中轨 (Middle Band)} = \text{SMA}(20)$$
|
||
|
||
$$\text{上轨 (Upper Band)} = \text{SMA}(20) + 2\sigma_{20}$$
|
||
|
||
$$\text{下轨 (Lower Band)} = \text{SMA}(20) - 2\sigma_{20}$$
|
||
|
||
其中 $\sigma_{20}$ 是 20 日滚动标准差 (Rolling Standard Deviation)。
|
||
|
||
**含义**:在正态分布假设下,价格有 95% 的概率在上下轨之间运动。
|
||
|
||
| 价格位置 | 信号 |
|
||
|----------|------|
|
||
| 触碰下轨 | 超卖区域,考虑买入 |
|
||
| 触碰上轨 | 超买区域,考虑卖出 |
|
||
| **带宽收窄 (Band Squeeze)** | 波动率降低,预示即将有大行情 |
|
||
| 带宽突然扩大 | 大行情开始,追方向 |
|
||
|
||
---
|
||
|
||
## 4. 策略 A:双均线金叉/死叉 (MA Crossover)
|
||
|
||
### 4.1 策略逻辑
|
||
|
||
这是最经典的趋势跟随策略,逻辑非常直观:
|
||
|
||
```
|
||
短期均线(20日)代表"近期走势"
|
||
长期均线(60日)代表"长期趋势基准"
|
||
|
||
金叉(Golden Cross):
|
||
短期均线 从下方穿过 长期均线 → 近期走势开始强于长期 → 买入信号 🟢
|
||
|
||
死叉(Death Cross):
|
||
短期均线 从上方穿过 长期均线 → 近期走势开始弱于长期 → 卖出平仓信号 🔴
|
||
```
|
||
|
||
### 4.2 为什么用"均线上穿"而不是"价格本身"?
|
||
|
||
直接看价格太噪声(每天都有小涨小跌),用均线平滑后只关注真正的趋势转折。这是"**降噪 (Noise Filtering)**"的思想。
|
||
|
||
### 4.3 信号生成代码详解
|
||
|
||
```python
|
||
# 计算两条均线
|
||
ma_short = sma(price, 20) # 短期均线(快线)
|
||
ma_long = sma(price, 60) # 长期均线(慢线)
|
||
|
||
# 原始信号:短线在长线上方为 1(看多),否则为 0
|
||
raw_signal = (ma_short > ma_long).astype(int)
|
||
|
||
# ⭐ 关键步骤:信号向后移动一天,避免前视偏差
|
||
# 今天收盘后判断方向,明天开盘才能交易
|
||
# 如果不 shift(1),等于"今天收盘价知道后立刻成交"——这在现实中做不到
|
||
ma_signal = raw_signal.shift(1).fillna(0)
|
||
```
|
||
|
||
**为什么一定要 `shift(1)`?**
|
||
|
||
假设均线在今天收盘后(16:00)产生了金叉信号。现实中,你看到这个信号后,最早也只能在**明天的开盘**(9:30)才能下单。如果不 shift,相当于"今天收盘价一出来就瞬间买入",这是不可能做到的,这叫**未来函数 (Future Function)** 或**前视偏差 (Lookahead Bias)**。
|
||
|
||
### 4.4 策略的适用条件与局限
|
||
|
||
✅ **适合**:
|
||
- 有明显趋势的市场(牛市单边上涨,熊市单边下跌)
|
||
- 日线、周线级别的中长线交易
|
||
|
||
❌ **不适合**:
|
||
- 震荡市(横盘整理):频繁金叉死叉,反复"买高卖低",交易成本侵蚀利润
|
||
- 高频交易:滞后性太大
|
||
|
||
---
|
||
|
||
## 5. 策略 B:RSI 均值回归 (RSI Mean Reversion)
|
||
|
||
### 5.1 策略逻辑
|
||
|
||
这是**逆势策略 (Contrarian Strategy)**,核心思想:
|
||
|
||
> "市场短期会过度反应,极端走势终将回归正常。"
|
||
> "Markets overreact in the short term, and extreme moves will eventually revert."
|
||
|
||
```
|
||
RSI < 30(超卖):市场跌过头了 → 做多(买入),等反弹 🟢
|
||
RSI > 70(超买):市场涨过头了 → 做空(卖出),等回落 🔴
|
||
|
||
出场规则(Exit Rules):
|
||
多仓持有中,RSI 回升到 50 以上 → 平仓多头(涨回去了,获利了结)
|
||
空仓持有中,RSI 回落到 50 以下 → 平仓空头(跌回去了,获利了结)
|
||
```
|
||
|
||
### 5.2 均值回归的理论基础
|
||
|
||
**均值回归 (Mean Reversion)** 是统计学中的一个概念:当一个量偏离其长期均值过远时,有向均值靠拢的倾向。
|
||
|
||
在金融市场中表现为:
|
||
- 市场恐慌性下跌后,往往会有技术性反弹
|
||
- 过度追涨后,往往会有获利回吐
|
||
- 短期波动会均值回归,但长期趋势可能持续(这就是趋势策略存在的理由)
|
||
|
||
### 5.3 为什么 RSI 策略在本 Demo 中表现差?
|
||
|
||
看本 Demo 的回测结果:RSI 策略的夏普比率为负数,而 Buy & Hold 为正。
|
||
|
||
原因很简单:我们的模拟数据用的是 GBM,设定了 `mu = 0.10`(年化 10% 的正漂移)。这意味着数据有**明显的上涨趋势**。均值回归策略在趋势市中会频繁做空(RSI 超买),而市场偏偏持续上涨,造成亏损。
|
||
|
||
**结论**:没有"万能"策略。策略的有效性依赖市场环境:
|
||
- 趋势市 → 趋势策略赢
|
||
- 震荡市 → 均值回归策略赢
|
||
- 如何判断当前市场状态?这正是更高级的量化研究课题(策略择时、机制识别)
|
||
|
||
---
|
||
|
||
## 6. 向量化回测引擎 (Vectorized Backtest Engine)
|
||
|
||
### 6.1 什么是向量化?
|
||
|
||
**向量化 (Vectorization)** 指用 numpy/pandas 的数组运算一次性计算所有日期的结果,而不是用 Python `for` 循环逐日计算。
|
||
|
||
```python
|
||
# 非向量化(慢)
|
||
results = []
|
||
for i in range(len(prices)):
|
||
r = signal[i] * daily_return[i] # 逐日计算
|
||
results.append(r)
|
||
|
||
# 向量化(快,pandas 内部是 C 语言实现)
|
||
strategy_returns = signal * daily_returns # 整个序列一次运算
|
||
```
|
||
|
||
**速度差异**:对于 1000 天的数据,向量化通常比 for 循环快 100-1000 倍。
|
||
|
||
### 6.2 回测核心逻辑
|
||
|
||
```python
|
||
# 1. 每日收益率
|
||
daily_ret = prices.pct_change().fillna(0)
|
||
|
||
# 2. 策略每日收益率(持仓方向 × 市场收益率)
|
||
# signal = 1 时,策略收益 = 市场收益(同向)
|
||
# signal = 0 时,策略收益 = 0(空仓不参与)
|
||
# signal = -1 时,策略收益 = -市场收益(反向做空)
|
||
strat_ret_gross = signal * daily_ret
|
||
|
||
# 3. 扣除交易成本
|
||
position_change = signal.diff().abs() # 仓位变化量(换手)
|
||
cost = position_change * cost_per_trade # 每次换手扣除成本
|
||
|
||
# 4. 净收益率
|
||
strat_ret_net = strat_ret_gross - cost
|
||
|
||
# 5. 净值曲线(每日净收益率连乘)
|
||
equity = initial_capital * (1 + strat_ret_net).cumprod()
|
||
```
|
||
|
||
### 6.3 交易成本的重要性
|
||
|
||
**成本模型 (Cost Model)**:
|
||
|
||
| 成本项 | 说明 | 典型值(A 股)|
|
||
|--------|------|--------------|
|
||
| **佣金 (Commission)** | 券商按交易金额收取 | 万分之三(0.03%)单边 |
|
||
| **印花税 (Stamp Duty)** | A 股卖出时收取 | 万分之五(0.05%),仅卖出 |
|
||
| **滑点 (Slippage)** | 信号价格与实际成交价的差 | 视流动性而定,通常 1-5 bp |
|
||
|
||
**一个典型的坑**:回测不计成本,结果漂亮,一上实盘就亏钱。
|
||
高频交易尤其明显:哪怕每次 0.1% 的成本,一天交易 10 次就是 1% 的消耗。
|
||
|
||
### 6.4 净值曲线 (Equity Curve) 的含义
|
||
|
||
```
|
||
初始资金 100万元
|
||
│ 每天乘以 (1 + 当天净收益率)
|
||
▼
|
||
...第 n 天的净值 = 100万 × (1 + r_1) × (1 + r_2) × ⋯ × (1 + r_n)
|
||
```
|
||
|
||
净值曲线是策略"假设从第一天以 100 万元开始运行"的**账户价值随时间变化曲线**。
|
||
|
||
---
|
||
|
||
## 7. 绩效指标详解 (Performance Metrics)
|
||
|
||
不能只看"总收益率"——一个策略可能靠运气在某段时间暴赚,但实际上风险极高。需要多维度评估。
|
||
|
||
### 7.1 总收益率 & 年化收益率 (CAGR)
|
||
|
||
**总收益率 (Total Return)**:
|
||
|
||
$$\text{Total Return} = \frac{V_{end} - V_{start}}{V_{start}}$$
|
||
|
||
**年化复合增长率 (CAGR, Compound Annual Growth Rate)**:
|
||
|
||
$$\text{CAGR} = \left(\frac{V_{end}}{V_{start}}\right)^{1/\text{years}} - 1$$
|
||
|
||
将总收益率折算成"每年平均增长多少",消除回测时间长短的影响,便于不同策略横向比较。
|
||
|
||
**例子**:5 年总收益 100%,CAGR = $(2)^{1/5} - 1 \approx 14.9\%$(不是 100%/5 = 20%,因为有复利效应)
|
||
|
||
### 7.2 夏普比率 (Sharpe Ratio)
|
||
|
||
$$\text{Sharpe} = \frac{\text{年化收益率} - r_f}{\text{年化波动率}} = \frac{\bar{r} - r_f}{\sigma} \times \sqrt{252}$$
|
||
|
||
- $r_f$:无风险利率 (Risk-Free Rate),通常用国债利率,代码中简化为 0
|
||
- **含义**:每承担 1 单位波动率,获得多少超额收益
|
||
|
||
| 夏普比率 | 评级 |
|
||
|----------|------|
|
||
| < 0 | 不如存钱(还有风险)|
|
||
| 0 ~ 1 | 可接受,但不优秀 |
|
||
| 1 ~ 2 | 良好,值得关注 |
|
||
| > 2 | 优秀(或者有数据问题,需检查)|
|
||
| > 3 | 通常意味着回测存在 bug 或严重过拟合 |
|
||
|
||
**局限**:夏普比率假设收益率服从正态分布。如果有"小涨长期 + 偶尔大跌"的特征(如卖期权策略),夏普比率会虚高。
|
||
|
||
### 7.3 索提诺比率 (Sortino Ratio)
|
||
|
||
$$\text{Sortino} = \frac{\text{CAGR} - r_f}{\text{下行波动率 (Downside Std)}}$$
|
||
|
||
**与夏普的区别**:夏普把所有波动(涨和跌)都视为"风险";索提诺**只把亏损日的波动**视为风险(正向波动是好事,不应被惩罚)。
|
||
|
||
对于有正偏态(不对称上涨)的策略,索提诺比夏普更公平。
|
||
|
||
### 7.4 最大回撤 (Maximum Drawdown)
|
||
|
||
$$\text{Drawdown}(t) = \frac{V_t - \max_{s \leq t} V_s}{\max_{s \leq t} V_s}$$
|
||
|
||
$$\text{Max Drawdown} = \min_t \text{Drawdown}(t)$$
|
||
|
||
**含义**:从账户价值的历史最高点,最大跌了多少百分比。
|
||
|
||
**为什么这是最重要的风险指标之一?**
|
||
|
||
- 假设你的策略总收益 200%,但中间经历了 -70% 的最大回撤
|
||
- 你有没有能力扛过 -70% 而不放弃?(心理和资金层面)
|
||
- 如果你在最大回撤开始前一年进场,可能等了很多年才回本
|
||
|
||
**经验规则**:最大回撤超过 30% 的策略,很少有人能真实执行。
|
||
|
||
### 7.5 卡玛比率 (Calmar Ratio)
|
||
|
||
$$\text{Calmar} = \frac{\text{CAGR}}{|\text{Max Drawdown}|}$$
|
||
|
||
每承担 1 单位的最大回撤,获得多少年化收益。适合用于比较不同策略的"收益/最大损失"性价比。
|
||
|
||
### 7.6 胜率 & 盈亏比 (Win Rate & Profit Factor)
|
||
|
||
**日胜率 (Daily Win Rate)**:有收益的交易日 / 总交易日
|
||
|
||
**盈亏比 (Profit Factor)**:
|
||
|
||
$$\text{Profit Factor} = \frac{\text{总盈利金额}}{\text{总亏损金额(绝对值)}}$$
|
||
|
||
> Profit Factor > 1 意味着策略是盈利的(赚的比亏的多)
|
||
|
||
**一个重要的直觉**:胜率和盈亏比是跷跷板关系。
|
||
|
||
| 策略类型 | 典型胜率 | 典型盈亏比 |
|
||
|----------|----------|------------|
|
||
| 趋势跟随(如本 Demo 的 MA 策略)| 30%-40%(多次小亏)| > 2(偶尔大赚)|
|
||
| 均值回归(高频做市)| 60%-80%(频繁小赚)| < 1.5(偶尔大亏)|
|
||
|
||
---
|
||
|
||
## 8. 滚动前向验证与参数优化 (Walk-Forward Validation)
|
||
|
||
### 8.1 过拟合的危害
|
||
|
||
**过拟合 (Overfitting)** 是量化回测最大的陷阱:
|
||
|
||
> 在相同的历史数据上测试足够多的参数组合,总能找到一组让历史表现完美的参数。
|
||
> 但这些参数可能只是"碰巧"匹配了历史噪声,在未来数据上会失效。
|
||
|
||
**例子**:如果你测试了 1000 种均线参数组合,在同一段历史数据上,必然有几组看起来"完美",纯属随机巧合。这叫**数据窥探偏差 (Data Snooping Bias)** 或 **p-hacking**。
|
||
|
||
### 8.2 前向验证的正确做法
|
||
|
||
```
|
||
完整历史数据(1500天)
|
||
├─ 训练集 / In-Sample (80%,1200天)
|
||
│ 用于:网格搜索最优参数
|
||
│ 报告:训练集夏普(仅供参考)
|
||
│
|
||
└─ 测试集 / Out-of-Sample (20%,300天)
|
||
用于:用找到的"最优参数"在这段数据上跑一遍
|
||
报告:测试集夏普(这才是真正的绩效评估)
|
||
原则:这 300 天的数据在优化参数时绝对不能碰过!
|
||
```
|
||
|
||
**夏普衰减 (Sharpe Decay)**:
|
||
|
||
通常,训练集夏普 > 测试集夏普。这是正常的,问题在于衰减多少:
|
||
- 训练集 2.0,测试集 1.5 → 轻微过拟合,可以接受
|
||
- 训练集 3.0,测试集 -0.5 → 严重过拟合,该策略不可用
|
||
|
||
### 8.3 网格搜索 (Grid Search)
|
||
|
||
```python
|
||
for sw in [5, 10, 15, 20, 30]: # 短窗口候选值
|
||
for lw in [30, 40, 50, 60, 80, 100]: # 长窗口候选值
|
||
if sw >= lw:
|
||
continue # 逻辑约束:短窗口必须 < 长窗口
|
||
# 在训练集上运行回测,记录夏普
|
||
...
|
||
# 选出夏普最高的参数组合
|
||
# 再在测试集上验证
|
||
```
|
||
|
||
**更严格的验证方法**(代码未实现,但应了解):
|
||
|
||
**滚动前向验证 (Rolling Walk-Forward)**:
|
||
```
|
||
[Train1] → [Test1]
|
||
[Train2] → [Test2]
|
||
[Train3] → [Test3]
|
||
...
|
||
汇报所有 Test 期的拼接结果
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 收益率分布分析 (Return Distribution Analysis)
|
||
|
||
### 9.1 为什么要分析收益率分布?
|
||
|
||
夏普比率的计算假设收益率服从正态分布。如果分布严重偏离正态,夏普比率就不可靠。
|
||
|
||
### 9.2 偏度 (Skewness)
|
||
|
||
$$\text{Skewness} = \frac{\frac{1}{n}\sum(r_i - \bar{r})^3}{\sigma^3}$$
|
||
|
||
| 偏度值 | 含义 |
|
||
|--------|------|
|
||
| 接近 0 | 左右对称(正态分布)|
|
||
| > 0 | 右偏(右尾长),有"意外大赚"的可能性 |
|
||
| < 0 | 左偏(左尾长),有"意外大亏"的可能性 ⚠️ |
|
||
|
||
**金融数据的现实**:大多数股票收益率分布是**负偏态**(崩盘比暴涨更极端)。
|
||
|
||
### 9.3 峰度 (Kurtosis / Excess Kurtosis)
|
||
|
||
- 正态分布的峰度 = 0(代码中 pandas 计算的是超额峰度)
|
||
- 金融收益率通常峰度 > 0(**厚尾,Fat Tails**)
|
||
|
||
**含义**:极端事件(±3σ 以外)发生的频率比正态分布预测的更高。这意味着基于正态分布计算的风险模型(如 VaR)会低估实际风险。
|
||
|
||
### 9.4 月度收益热力图 (Monthly Return Heatmap)
|
||
|
||
热力图直观展示每个月的盈亏情况:
|
||
- 🟢 绿色 → 盈利月份
|
||
- 🔴 红色 → 亏损月份
|
||
- 颜色越深 → 盈亏越大
|
||
|
||
**用途**:快速识别策略的"季节性"——某些策略在特定月份/季节表现特别好或特别差,这可能是利用了真实的市场规律,也可能是偶然的样本特征。
|
||
|
||
---
|
||
|
||
## 10. 术语速查表
|
||
|
||
| 中文 | English | 简要说明 |
|
||
|------|---------|----------|
|
||
| 技术分析 | Technical Analysis | 用历史价格/成交量预测未来走势 |
|
||
| 趋势跟随 | Trend Following | 顺势而为,买入上涨资产 |
|
||
| 均值回归 | Mean Reversion | 逆势操作,买入超跌资产 |
|
||
| 技术指标 | Technical Indicator | 从原始数据提炼交易信号的数学公式 |
|
||
| 简单移动平均线 | SMA (Simple Moving Average) | n 日收盘价的算术平均 |
|
||
| 指数移动平均线 | EMA (Exponential Moving Average) | 近期数据权重更高的加权平均 |
|
||
| 相对强弱指数 | RSI (Relative Strength Index) | 衡量涨跌动能的 0-100 震荡指标 |
|
||
| 超买 | Overbought | RSI > 70,涨势过强 |
|
||
| 超卖 | Oversold | RSI < 30,跌势过强 |
|
||
| 指数平滑异同移动平均 | MACD | 两条 EMA 之差,衡量趋势动能 |
|
||
| 布林带 | Bollinger Bands | 均线 ± 2 倍标准差的价格通道 |
|
||
| 带宽 | Band Width | 上轨与下轨之差,衡量波动率 |
|
||
| 金叉 | Golden Cross | 短线上穿长线,看涨信号 |
|
||
| 死叉 | Death Cross | 短线下穿长线,看跌信号 |
|
||
| 信号生成 | Signal Generation | 根据指标产生买卖指令的逻辑 |
|
||
| 前视偏差 | Lookahead Bias | 回测中使用了"当时无法知道"的未来数据 |
|
||
| 向量化回测 | Vectorized Backtest | 用数组运算一次性计算所有时间步 |
|
||
| 净值曲线 | Equity Curve | 账户价值随时间变化的曲线 |
|
||
| 无风险利率 | Risk-Free Rate | 国债等无风险资产的收益率 |
|
||
| 夏普比率 | Sharpe Ratio | 每单位总风险对应的超额收益 |
|
||
| 索提诺比率 | Sortino Ratio | 每单位下行风险对应的超额收益 |
|
||
| 最大回撤 | Maximum Drawdown (MDD) | 净值从峰值跌落的最大幅度 |
|
||
| 卡玛比率 | Calmar Ratio | CAGR 除以最大回撤 |
|
||
| 年化复合增长率 | CAGR | 考虑复利后的年均增长率 |
|
||
| 胜率 | Win Rate | 盈利次数 / 总次数 |
|
||
| 盈亏比 | Profit Factor | 总盈利 / 总亏损 |
|
||
| 过拟合 | Overfitting | 策略过度适配历史数据,泛化能力差 |
|
||
| 数据窥探偏差 | Data Snooping Bias | 在同一数据上测试太多参数导致的虚假优化 |
|
||
| 训练集 | In-Sample (IS) | 用于优化参数的历史数据段 |
|
||
| 测试集 | Out-of-Sample (OOS) | 用于验证的未见过的历史数据段 |
|
||
| 滚动前向验证 | Walk-Forward Validation | 用过去优化参数,在紧接着的未来上验证 |
|
||
| 网格搜索 | Grid Search | 枚举所有参数组合的暴力搜索法 |
|
||
| 正态分布 | Normal Distribution | 均值两侧对称的钟形分布 |
|
||
| 厚尾 | Fat Tails (Leptokurtosis) | 极端事件频率高于正态分布预测 |
|
||
| 偏度 | Skewness | 分布的左右不对称程度 |
|
||
| 峰度 | Kurtosis | 分布尾部的厚度(与正态分布对比)|
|
||
| 几何布朗运动 | GBM (Geometric Brownian Motion) | 描述股价随机游走的经典数学模型 |
|
||
| 年化波动率 | Annualized Volatility | 日波动率 × √252 |
|
||
| 滑点 | Slippage | 信号价格与实际成交价的差 |
|
||
| 佣金 | Commission | 券商收取的交易手续费 |
|
||
|
||
---
|
||
|
||
*上一篇:[数据管道](doc_01_data_pipeline.md)*
|
||
*下一篇:[事件驱动回测引擎](doc_03_event_driven_backtest.md)*
|