第 03 课:数学与统计基础
量化的本质是用数学语言描述市场。如果你的数学假设错了,模型越精确,亏得越快。
LTCM:诺贝尔奖得主的致命假设
1998 年,长期资本管理公司(LTCM)拥有两位诺贝尔经济学奖得主、华尔街最顶尖的数学家、以及 1250 亿美元的资产。
他们的模型精妙绝伦,基于一个核心假设:市场收益服从正态分布。
按正态分布计算,他们的策略一天亏损超过 1% 的概率是 10 万分之一。一年 252 个交易日,理论上要 400 年才会遇到一次。
然后俄罗斯债务违约了。
1998 年 8 月 21 日一天,LTCM 亏损 5.5 亿美元。接下来的一个月,他们亏损了 46 亿美元——几乎全部资本。按正态分布,这种事件发生的概率是 10 的负 27 次方,大约是宇宙诞生以来每秒发生一次都不可能遇到。
但它发生了。
问题出在哪里?
- 市场不是正态分布:真实市场是"厚尾分布",极端事件的频率远超正态分布预测
- 相关性会突变:正常时期资产相关性低,危机时所有资产相关性飙升到 1
- 波动率会聚集:大跌之后往往还是大跌,不是独立的"抛硬币"
LTCM 的教训:不是模型不够精确,是数学假设从根本上错了。
这一课的目标:让你理解金融数据的真实特性,避免用"抛硬币"的数学去分析市场。
3.1 时间序列基础
序列 vs IID 假设
传统统计学假设数据是 IID (Independent and Identically Distributed):独立同分布。
- 独立:今天的数据和昨天无关
- 同分布:每天的数据来自同一个分布
金融数据几乎从不满足 IID 假设:
import numpy as np
# IID 数据:抛硬币
coin_flips = np.random.choice([0, 1], size=100) # 每次独立,概率恒定
# 金融数据:今天的价格依赖昨天
prices = [100]
for _ in range(99):
# 今天的价格 = 昨天的价格 + 随机变动
prices.append(prices[-1] * (1 + np.random.normal(0, 0.02)))
为什么这很重要?
- 如果你用 IID 假设建模,会低估连续亏损的概率
- 真实市场有"惯性":涨了容易继续涨,跌了容易继续跌
滞后 (Lag) 与自相关
自相关 (Autocorrelation):当前值与过去值的相关性。
纸上计算示例:
假设连续 5 天的收益率是:+2%, -1%, +3%, +2%, -2%
| 今天 (t) | 昨天 (t-1) |
|---|---|
| -1% | +2% |
| +3% | -1% |
| +2% | +3% |
| -2% | +2% |
观察规律:
- 昨天涨,今天有时涨有时跌 → 自相关接近 0(随机)
- 如果昨天涨今天大概率涨 → 自相关为正(动量)
- 如果昨天涨今天大概率跌 → 自相关为负(均值回归)
SPY(标普 500 ETF)的日收益自相关通常接近 0(例如 2010-2020 年约为 0.02)——几乎为 0,说明美股日收益接近随机游走,短期难以预测。具体数值因样本期间而异。
解读:
- 自相关接近 0 → 过去对未来预测价值低(弱式有效市场)
- 自相关显著为正 → 动量效应(趋势跟随可能有效)
- 自相关显著为负 → 均值回归效应
💻 代码实现(工程师参考)
import pandas as pd
def calculate_autocorrelation(series, lag=1):
"""计算滞后 lag 期的自相关系数"""
return series.autocorr(lag=lag)
# 示例:SPY (S&P 500 ETF) 日收益率的自相关
returns = prices_series.pct_change().dropna()
print(f"滞后1期自相关: {calculate_autocorrelation(returns, 1):.3f}")
print(f"滞后5期自相关: {calculate_autocorrelation(returns, 5):.3f}")平稳性检验
平稳性:统计特性(均值、方差)不随时间变化。
from statsmodels.tsa.stattools import adfuller
def check_stationarity(series, name="Series"):
"""ADF 检验:判断序列是否平稳"""
result = adfuller(series.dropna())
print(f"{name}:")
print(f" ADF 统计量: {result[0]:.4f}")
print(f" p-value: {result[1]:.4f}")
print(f" 结论: {'平稳 ✓' if result[1] < 0.05 else '非平稳 ✗'}")
# 价格序列通常非平稳
check_stationarity(prices_series, "价格序列")
# 收益率序列通常平稳
check_stationarity(returns_series, "收益率序列")
为什么要检验平稳性?
- 大多数统计模型假设输入是平稳的
- 对非平稳序列建模会产生"伪回归"
- 解决方案:用收益率(差分)代替价格
3.2 收益的数学定义
简单收益 vs 对数收益
📝 纸上计算:
假设 AAPL 从 $100 涨到 $110:
简单收益 = (110 - 100) / 100 = 10%
对数收益 = ln(110/100) = ln(1.1) ≈ 9.53%
为什么量化常用对数收益? 看下面的例子:
可加性问题(用计算器验证):
| Day 1 | Day 2 | 总收益 | |
|---|---|---|---|
| 价格 | $100 → $110 | $110 → $100 | $100 → $100 |
| 简单收益 | +10% | -9.09% | 0% |
| 直接相加 | 10% - 9.09% = 0.91% ❌ | ||
| 对数收益 | +9.53% | -9.53% | 0% |
| 直接相加 | 9.53% - 9.53% = 0% ✅ |
关键洞察:涨 10% 再跌回来,简单收益不等于 0(因为基数变了),但对数收益等于 0。
结论:对数收益可以直接相加,简单收益必须连乘。当你需要计算 100 天的累计收益时,对数收益只需 100 次加法,简单收益需要 100 次乘法。
| 特性 | 简单收益 | 对数收益 |
|---|---|---|
| 可加性 | ✗ 多期收益不能直接相加 | ✓ 多期收益可以直接相加 |
| 对称性 | ✗ 涨10%再跌10%≠0 | ✓ 更接近对称 |
| 正态假设 | 较难满足 | 更接近正态 |
累计收益计算
📝 纸上计算:
连续 5 天的日收益率:+1%, -2%, +3%, +1%, -1%
简单收益累计(连乘):
(1+0.01) × (1-0.02) × (1+0.03) × (1+0.01) × (1-0.01) - 1
= 1.01 × 0.98 × 1.03 × 1.01 × 0.99 - 1
= 1.0194 - 1 ≈ 1.94%
对数收益累计(直接相加):
0.01 + (-0.02) + 0.03 + 0.01 + (-0.01) = 2%
两种方法结果接近,但对数收益计算更简单。
年化收益率
📝 纸上计算:
假设 60 个交易日赚了 5%,年化是多少?
年化收益 = (1 + 5%)^(252/60) - 1
= 1.05^4.2 - 1
= 1.227 - 1
≈ 22.7%
为什么是 252? 美股一年约 252 个交易日(365 天 - 周末 - 节假日)。
常见错误:
- ❌ 直接把月收益 × 12 当年化(忽略复利)
- ❌ 用日历天数而非交易日(应该用 252 不是 365)
- ❌ 忽略手续费后再年化
💻 代码实现(工程师参考)
def cumulative_return(returns, method='simple'):
"""计算累计收益"""
if method == 'simple':
return (1 + returns).prod() - 1 # 连乘
else:
return returns.sum() # 直接相加
def annualize_return(total_return, days, trading_days_per_year=252):
"""年化收益率"""
years = days / trading_days_per_year
return (1 + total_return) ** (1 / years) - 1
# 示例:60 天赚了 5%
ann_return = annualize_return(0.05, 60)
print(f"年化收益率: {ann_return:.2%}") # ≈ 23.5%3.3 风险的数学定义
方差与标准差
📝 纸上计算:
假设 5 天的日收益率:+1%, -2%, +3%, 0%, -1%
Step 1:计算均值
均值 = (1 - 2 + 3 + 0 - 1) / 5 = 0.2%
Step 2:计算方差(每个值与均值的差的平方的平均)
方差 = [(1-0.2)² + (-2-0.2)² + (3-0.2)² + (0-0.2)² + (-1-0.2)²] / 5
= [0.64 + 4.84 + 7.84 + 0.04 + 1.44] / 5
= 14.8 / 5 = 2.96 (%²)
Step 3:计算标准差(波动率)
日波动率 = √2.96 ≈ 1.72%
Step 4:年化
年化波动率 = 日波动率 × √252 ≈ 1.72% × 15.87 ≈ 27.3%
波动率的直觉含义:
| 年化波动率 | 典型资产 | 一年可能的波动范围(68%概率) |
|---|---|---|
| 15% | 大盘股(SPY) | -15% 到 +15% |
| 30% | 科技股(TSLA) | -30% 到 +30% |
| 80% | 加密货币(BTC) | -80% 到 +80% |
记住这个公式:年化波动率 = 日波动率 × √252 ≈ 日波动率 × 16
💻 代码实现(工程师参考)
def calculate_volatility(returns, annualize=True, trading_days=252):
"""计算波动率(标准差)"""
daily_vol = returns.std()
if annualize:
return daily_vol * np.sqrt(trading_days)
return daily_vol
# 示例
daily_returns = pd.Series([0.01, -0.02, 0.03, 0, -0.01])
annual_vol = calculate_volatility(daily_returns)
print(f"年化波动率: {annual_vol:.2%}")协方差与相关性
直觉理解:
相关系数衡量两个资产"是否同涨同跌":
| 相关系数 | 含义 | 例子 |
|---|---|---|
| +1 | 完全同步:A 涨 B 必涨 | 同行业股票(AAPL vs MSFT) |
| 0 | 无关:A 涨跌与 B 无关 | 黄金 vs 科技股 |
| -1 | 完全相反:A 涨 B 必跌 | 股票 vs VIX(恐慌指数) |
实际市场中的相关性(参考值):
| 资产对 | 正常时期相关性 | 危机时期相关性 |
|---|---|---|
| AAPL vs MSFT | +0.7 | +0.9 |
| SPY vs TLT (债券) | -0.3 | -0.5 或 +0.8 |
| SPY vs GLD (黄金) | +0.1 | -0.2 |
危机时期的"相关性飙升":正常时期不相关的资产,危机时可能突然相关性飙到 0.9。这就是 LTCM 栽跟头的原因——他们假设相关性稳定。
多智能体视角:Portfolio Agent 的核心工作就是寻找低相关资产构建组合,但 Risk Agent 必须考虑"危机时相关性飙升"的风险。
💻 代码实现(工程师参考)
def analyze_correlation(returns_a, returns_b):
"""分析两个资产的相关性"""
corr = returns_a.corr(returns_b)
print(f"相关系数: {corr:.3f}")
if corr > 0.7:
print("→ 高度正相关:涨跌同步,分散效果差")
elif corr < -0.3:
print("→ 负相关:天然对冲,适合组合")
else:
print("→ 低相关:有分散风险的价值")分布假设的危险性
正态分布假设会害死你。 这是 LTCM 事件的核心教训。
📝 纸上计算:
假设 SPY 年化波动率 20%,日波动率约 1.26%(= 20% / √252)。
在正态分布下:
- 日跌幅超过 1σ (1.26%) 的概率 = 16%(正常)
- 日跌幅超过 2σ (2.52%) 的概率 = 2.3%(每月 0.5 次)
- 日跌幅超过 3σ (3.78%) 的概率 = 0.13%(每 3 年 1 次)
- 日跌幅超过 4σ (5.04%) 的概率 = 0.003%(每 125 年 1 次)
但实际发生了什么?
| 日期 | 事件 | SPY 单日跌幅 | 按正态分布的"几σ事件" | 理论上多少年一遇 |
|---|---|---|---|---|
| 2020-03-16 | COVID崩盘 | -12.0% | 9.5σ | 10^20 年 |
| 2008-10-15 | 金融危机 | -9.0% | 7.1σ | 10^12 年 |
| 2011-08-08 | 美国降级 | -6.7% | 5.3σ | 1500万年 |
| 2018-02-05 | 波动率崩盘 | -4.1% | 3.3σ | 4年 |
结论:正态分布说"10^20 年一遇"的事件,实际上每隔几年就发生一次。
这就是"厚尾分布"的威力——极端事件发生的频率远超正态分布预测。如果你的风控模型假设正态分布,你必定会在某个"黑天鹅"事件中爆仓。
3.4 金融时间序列的特殊性
厚尾分布 (Fat Tails)
两个关键指标:
-
偏度 (Skewness):分布是否对称
- 偏度 = 0 → 对称(涨跌幅度差不多)
- 偏度 < 0 → 左偏(暴跌比暴涨多)
- 股票市场偏度通常是 -0.5 到 -1,说明暴跌风险大于暴涨机会
-
峰度 (Kurtosis):尾部有多"厚"
- 峰度 = 3 → 正态分布
- 峰度 > 3 → 厚尾(极端事件比正态分布预测的多)
- 股票市场峰度通常是 5-10,远超正态分布的 3
| 指标 | 正态分布 | SPY 实际 | 含义 |
|---|---|---|---|
| 偏度 | 0 | -0.7 | 暴跌更剧烈 |
| 峰度 | 3 | 8 | 极端事件频繁 |
厚尾对策略的影响:
- 止损必须考虑跳空风险(隔夜暴跌可能跳过你的止损价)
- VaR 模型会严重低估风险
- 需要用 Expected Shortfall (CVaR) 替代 VaR
💻 代码实现(工程师参考)
def analyze_tail_risk(returns):
"""分析尾部风险"""
skew = returns.skew() # 偏度
kurt = returns.kurtosis() # 峰度(减3后的)
print(f"偏度: {skew:.3f} {'(负偏,小心暴跌)' if skew < -0.5 else ''}")
print(f"峰度: {kurt+3:.3f} {'(厚尾!)' if kurt > 0 else ''}")波动聚集 (Volatility Clustering)
GARCH 效应:大波动之后往往还是大波动,小波动之后往往还是小波动。
直觉理解:
回想 2020 年 3 月的 COVID 暴跌:
- 3月9日:SPY 跌 7.6%
- 3月12日:SPY 跌 9.5%
- 3月16日:SPY 跌 12.0%
暴跌不是独立的"抛硬币"——一旦开始暴跌,往往会连续暴跌。这就是波动聚集。
波动率的自相关高达 0.7-0.9,远高于收益率的自相关(约 0)。这意味着:
- 收益率几乎不可预测(随机游走)
- 波动率是可预测的(明天的波动率很可能和今天相近)
实战意义:
- 波动率可预测性 > 收益率可预测性
- 趋势 Agent 应该在低波动期建仓,高波动期减仓
- Risk Agent 应该根据近期波动率动态调整止损
💻 代码实现(工程师参考)
def detect_volatility_clustering(returns, window=20):
"""检测波动聚集"""
rolling_vol = returns.rolling(window).std()
vol_autocorr = rolling_vol.autocorr(lag=1)
print(f"波动率的滞后1期自相关: {vol_autocorr:.3f}")非平稳性与结构性断裂 (Regime Shift)
市场会在不同"状态"之间切换,每个状态有不同的统计特性:
| Regime | 特征 | 适合策略 |
|---|---|---|
| 牛市 | 低波动、正收益、低相关 | 动量、买入持有 |
| 震荡 | 中波动、收益接近0 | 均值回归 |
| 危机 | 高波动、负收益、高相关 | 减仓、对冲 |
结构性断裂的典型案例(近似值):
注:下表为常见量级示意,具体数值会随口径、窗口与数据源变化。
| 时间 | 事件 | 波动率变化 | 相关性变化 |
|---|---|---|---|
| 2008.09 | 雷曼倒闭 | 15% → 80% | 0.3 → 0.95 |
| 2020.03 | COVID爆发 | 12% → 85% | 0.4 → 0.90 |
| 2022.01 | 美联储加息 | 15% → 30% | 0.5 → 0.70 |
Regime Shift 对策略的影响:
- 在旧 Regime 下训练的模型,在新 Regime 下会失效
- 相关性突变意味着分散投资突然失效
- 波动率突变意味着止损位置需要重新计算
这就是为什么需要多智能体——不同 Regime 需要不同策略,一个模型搞不定。Meta Agent 的核心任务就是识别当前 Regime,并将任务分发给对应的专家 Agent。
💻 代码实现(工程师参考)
def detect_regime_change(returns, window=60):
"""简单的 Regime 变化检测"""
rolling_vol = returns.rolling(window).std()
vol_change = rolling_vol.diff().abs()
threshold = vol_change.quantile(0.95)
regime_changes = vol_change > threshold
print(f"检测到 {regime_changes.sum()} 个潜在的 Regime 变化点")
return regime_changes3.5 常见误区
误区一:正态分布足够描述市场
不对。真实市场是厚尾分布,极端事件发生频率远超正态分布预测。LTCM 假设正态分布,结果"百万年一遇"的事件在几周内发生。
误区二:波动率越低越安全
不完全对。低波动可能是暴风雨前的平静。更危险的是,低波动期往往伴随杠杆增加,一旦波动率突然飙升,损失会被放大。
误区三:相关性稳定可预测
危险的假设。正常时期相关性 0.3 的资产,危机时可能飙升到 0.9。分散投资在你最需要它的时候可能失效。
误区四:历史数据能预测未来
只能部分预测。市场存在 Regime Shift(结构性断裂),过去的统计规律可能突然失效。2008 年、2020 年的市场与之前完全不同。
3.6 多智能体视角
不同 Agent 可以使用不同的统计假设:
| Agent | 统计假设 | 适用场景 |
|---|---|---|
| Trend Agent | 动量存在(正自相关) | 趋势市场 |
| Mean Reversion Agent | 均值回归(负自相关) | 震荡市场 |
| Risk Agent | 厚尾分布 + 波动聚集 | 所有场景 |
| Regime Agent | 非平稳 + 结构断裂 | 状态识别 |
关键洞察:
- 不要让所有 Agent 用同一套统计假设
- Risk Agent 必须用最保守的假设(厚尾、极端事件频繁)
- Meta Agent 的职责是识别当前 Regime,分发给合适的 Agent
本课交付物
完成本课后,你将获得:
- 对金融数据特性的正确认知 - 知道为什么"抛硬币"模型在市场中不适用
- 收益率计算能力 - 掌握对数收益、年化收益的正确计算方法
- 风险度量的直觉 - 理解波动率、相关性、厚尾分布的实际含义
- 对统计假设的警惕 - 知道正态分布假设如何导致致命错误
✅ 验收标准
| 检查项 | 验收标准 | 自测方法 |
|---|---|---|
| 收益率计算 | 能手算对数收益和年化收益 | 给定 $100→$110→$99,计算两日对数收益和累计 |
| 波动率计算 | 能手算标准差并年化 | 给定 5 个日收益率,计算年化波动率 |
| 厚尾理解 | 能解释为什么正态分布假设危险 | 不看笔记,说出 LTCM 失败的统计原因 |
| Regime 认知 | 能列举 3 种市场状态及对应策略 | 画出 Regime 切换图,标注每种状态的特征 |
📝 综合练习(用计算器完成):
某日内策略:
- 10 天收益率:+1%, -0.5%, +2%, -1%, +0.5%, -2%, +1.5%, 0%, -0.5%, +1%
- 计算:(1) 累计收益 (2) 日波动率 (3) 年化波动率 (4) 这是高波动还是低波动策略?
点击查看答案
- 累计收益(简单法)≈ 2.0%
- 日波动率 ≈ 1.2%
- 年化波动率 = 1.2% × √252 ≈ 19%
- 年化波动率 19% 属于中等波动,接近 SPY 的波动水平
本课要点回顾
- 理解金融数据不满足 IID 假设,存在自相关和非平稳性
- 掌握对数收益 vs 简单收益的区别,以及年化计算方法
- 认识正态分布假设的危险性:厚尾、波动聚集、极端事件
- 理解 Regime Shift 对策略的影响,以及多智能体如何应对
延伸阅读
- 背景知识:Alpha 与 Beta - 收益分解的数学基础
- 背景知识:历史著名量化事故 - 忽略厚尾的代价
- 背景知识:夏普比率的统计陷阱 - 估计误差、多重检验与 Deflated Sharpe
下一课预告
第 04 课:技术指标的真实角色
MACD、RSI、布林带……这些指标到底有没有用?答案是:它们不是"买卖信号",而是特征工程。下一课我们揭开技术指标的真面目。