1. 这节究竟解决什么实际问题?¶
1.9 大数定律与中心极限定理(金融视角) 的具体任务是:解释为什么平均值会稳定、单日收益却仍可能高度不稳定。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
会使用百分比和基础代数;建议先完成环境配置。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
示例优先使用仓库固定的日频复权收盘价;它不是实时行情,也不代表完整市场。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 1.9 大数定律与中心极限定理(金融视角) 的输入、计算步骤、输出和局限;尤其能说明:解释为什么平均值会稳定、单日收益却仍可能高度不稳定。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
价格表示资产在特定时点的水平,收益率表示两个时点之间的相对变化。多数跨资产比较使用收益率。
所有公式都先说明分子、分母、单位与时间尺度;年化前必须确认数据频率。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
NumPy/Pandas 帮你算数和对齐索引;统计库帮你估计参数,但不替你解释经济含义。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“解释为什么平均值会稳定、单日收益却仍可能高度不稳定。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把百分比和小数混用、忘记 dropna、把日频波动率直接叫作年化波动率、把相关性当因果。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
概念演示也要避免事后选择样本或只展示支持结论的时间段。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/01_financial_concepts/09_lln_clt_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/01_financial_concepts/09_lln_clt_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Investments(Bodie, Kane, Marcus)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 10_present_value。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42)
print('Libraries loaded')
Libraries loaded
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. 大数定律(LLN):为什么回测样本越大越可靠?¶
大数定律:随着观测次数增加,样本均值收敛到总体均值。
在量化交易中的含义:
- 策略的夏普比率估计需要足够的样本才能可靠
- 回测使用的历史数据越长,估计越稳定
- 但要小心:市场是非平稳的,早期数据可能已经失效
# LLN 演示:样本均值收敛
true_mean = 0.0005 # 真实日均收益率
true_std = 0.015 # 真实波动率
trial_sizes = np.arange(10, 2001, 10)
sample_means = [np.random.normal(true_mean, true_std, n).mean() for n in trial_sizes]
plt.figure(figsize=(12, 5))
plt.plot(trial_sizes, np.array(sample_means)*100, color='steelblue', lw=1.2, label='样本均值')
plt.axhline(true_mean*100, color='red', linestyle='--', lw=2, label=f'真实均值 = {true_mean*100:.3f}%')
plt.fill_between(trial_sizes,
(true_mean - 2*true_std/np.sqrt(trial_sizes))*100,
(true_mean + 2*true_std/np.sqrt(trial_sizes))*100,
alpha=0.2, color='red', label='±2σ/√n 置信带')
plt.title('大数定律:样本均值收敛到真实均值')
plt.xlabel('样本量(交易日数)'); plt.ylabel('样本均值 (%/日)')
plt.legend(); plt.grid(alpha=0.3); plt.show()
print(f' 10天样本: 均值误差约 ±{true_std/np.sqrt(10)*100:.3f}%')
print(f'252天样本: 均值误差约 ±{true_std/np.sqrt(252)*100:.3f}%')
print(f' 2520天(10年): 均值误差约 ±{true_std/np.sqrt(2520)*100:.3f}%')
10天样本: 均值误差约 ±0.474% 252天样本: 均值误差约 ±0.094% 2520天(10年): 均值误差约 ±0.030%
2. 中心极限定理(CLT):分散化的数学基础¶
CLT:$n$ 个独立同分布随机变量之和(或均值),当 $n$ 足够大时,近似服从正态分布:
$$\bar{X}_n = \frac{1}{n}\sum_{i=1}^n X_i \xrightarrow{d} N\left(\mu, \frac{\sigma^2}{n}\right)$$
投资组合含义:持有 $n$ 只独立同波动率股票的等权组合,其波动率约为单股的 $\frac{1}{\sqrt{n}}$。
这就是分散化的数学基础——但前提是资产相互独立(或低相关)。
# 演示 CLT:从非正态分布的股票收益合成组合
# 单只股票收益服从偏态分布(模拟肥尾)
def generate_stock_returns(n_stocks, n_days, corr=0.0):
# 生成独立的 t 分布收益
returns = np.random.standard_t(df=4, size=(n_days, n_stocks)) * 0.01
return returns
n_days = 252
fig, axes = plt.subplots(2, 3, figsize=(15, 8))
for ax, n_stocks in zip(axes.flat, [1, 5, 20, 50, 100, 300]):
ret = generate_stock_returns(n_stocks, n_days)
portfolio_ret = ret.mean(axis=1) # 等权
ax.hist(portfolio_ret, bins=40, density=True, alpha=0.7,
color='steelblue', edgecolor='none')
x = np.linspace(portfolio_ret.min(), portfolio_ret.max(), 200)
ax.plot(x, stats.norm.pdf(x, portfolio_ret.mean(), portfolio_ret.std()),
'r-', lw=2, label=f'正态拟合')
ax.set_title(f'N={n_stocks} 只股票\n峰度={stats.kurtosis(portfolio_ret):.2f}')
ax.legend(fontsize=8)
plt.suptitle('CLT 分散化:股票数量越多,组合收益率越接近正态分布', fontsize=12)
plt.tight_layout(); plt.show()
3. CLT 的局限:相关性破坏分散化¶
# 演示:高相关性下,CLT 失效
def portfolio_std_vs_correlation(corr, n_stocks=50, sigma=0.01):
"""计算高相关情形下等权组合的波动率"""
# 组合方差 = sigma^2 * (1/n + corr*(n-1)/n)
return sigma * np.sqrt(1/n_stocks + corr * (n_stocks-1)/n_stocks)
corr_values = np.linspace(0, 1, 50)
port_stds = [portfolio_std_vs_correlation(c) for c in corr_values]
plt.figure(figsize=(9, 5))
plt.plot(corr_values, np.array(port_stds)*100, 'b-', lw=2)
plt.axhline(0.01*100/np.sqrt(50), color='green', linestyle='--',
label=f'独立情形(corr=0): {0.01/np.sqrt(50)*100:.3f}%')
plt.axhline(0.01*100, color='red', linestyle='--',
label=f'完全相关(corr=1): {0.01*100:.2f}%')
plt.xlabel('资产间相关系数 ρ'); plt.ylabel('等权组合波动率 (%/日)')
plt.title(f'相关性对分散化的影响(50 只股票,单股波动率=1%)')
plt.legend(); plt.grid(alpha=0.3); plt.show()
print('结论:相关性越高(如金融危机时),分散化效果越差。这就是为什么危机时所有资产同跌。')
结论:相关性越高(如金融危机时),分散化效果越差。这就是为什么危机时所有资产同跌。
4. Bootstrap 重采样:小样本下的推断工具¶
当历史数据不足时(如只有 2 年日频数据),如何估计夏普比率的置信区间? Bootstrap:有放回地重复抽样,构建统计量的经验分布。
np.random.seed(42)
returns_true = np.random.normal(0.0005, 0.012, 252*2) # 2年模拟数据
# Bootstrap 夏普比率置信区间
n_boot = 2000
boot_sharpes = []
for _ in range(n_boot):
sample = np.random.choice(returns_true, size=len(returns_true), replace=True)
sharpe = sample.mean() / sample.std() * np.sqrt(252)
boot_sharpes.append(sharpe)
boot_sharpes = np.array(boot_sharpes)
point_estimate = returns_true.mean() / returns_true.std() * np.sqrt(252)
ci_low, ci_high = np.percentile(boot_sharpes, [2.5, 97.5])
plt.figure(figsize=(9, 4))
plt.hist(boot_sharpes, bins=60, density=True, color='steelblue', alpha=0.7, edgecolor='none')
plt.axvline(point_estimate, color='red', lw=2, label=f'点估计 SR={point_estimate:.3f}')
plt.axvline(ci_low, color='orange', lw=2, linestyle='--', label=f'95% CI: [{ci_low:.3f}, {ci_high:.3f}]')
plt.axvline(ci_high, color='orange', lw=2, linestyle='--')
plt.title('Bootstrap 估计夏普比率的 95% 置信区间(2年样本)')
plt.xlabel('Sharpe Ratio'); plt.legend(); plt.show()
print(f'夏普比率: {point_estimate:.3f}')
print(f'95% CI: [{ci_low:.3f}, {ci_high:.3f}]')
夏普比率: 0.846 95% CI: [-0.492, 2.281]
练习¶
- 将股票数量从 50 改为 500,但保持相关系数 ρ=0.4,验证组合波动率是否继续下降。
- 用 Bootstrap 对比 1 年、5 年、10 年三个样本长度的夏普比率估计区间宽度。
- 研究「Block Bootstrap」(保留时间序列自相关的重采样),相比简单 Bootstrap 有什么优势?
下一节 → 10_present_value.ipynb