1. 这节究竟解决什么实际问题?¶
10.3 马尔可夫链与金融市场状态建模 的具体任务是:用状态转移描述市场情景,并区分转移概率和预测确定性。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
掌握基础代数、概率和 Python 数组操作。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
使用模拟或固定教学数据;重点是说明模型假设、估计误差和数值误差。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 10.3 马尔可夫链与金融市场状态建模 的输入、计算步骤、输出和局限;尤其能说明:用状态转移描述市场情景,并区分转移概率和预测确定性。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
数学方法给出明确的计算规则;结论是否有效仍取决于研究对象、输入数据和模型假设。
先给出符号、单位、假设和可识别条件,再把公式翻译为逐步代码。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
数值库帮你稳定求解;理解目标函数、约束和诊断仍不可省略。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“用状态转移描述市场情景,并区分转移概率和预测确定性。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
随机种子未固定、矩阵维度错配、优化约束遗漏、把数值收敛当成模型正确。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
数学模型的简化假设也会引入偏差;需要做参数敏感性与极端情景检查。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/10_math_methods/03_markov_chains_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/10_math_methods/03_markov_chains_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:QuantEcon Python Lectures。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 04_mle_estimation。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
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. 马尔可夫链基础¶
马尔可夫性质:未来状态只依赖于当前状态,与历史无关: $$P(X_{t+1} = j | X_t = i, X_{t-1}, ...) = P_{ij}$$
转移矩阵 $P$:$P_{ij}$ = 从状态 $i$ 转移到状态 $j$ 的概率
- 每行之和 = 1
- 所有元素 ≥ 0
稳态分布 $\pi$:满足 $\pi P = \pi$ 的分布(长期各状态概率)
# 示例:市场三状态马尔可夫链
# 状态:0=熊市, 1=震荡, 2=牛市
P = np.array([
[0.70, 0.20, 0.10], # 熊市: 70%继续熊, 20%震荡, 10%牛市
[0.15, 0.70, 0.15], # 震荡: 对称
[0.05, 0.25, 0.70], # 牛市: 70%继续牛
])
states = ['熊市', '震荡', '牛市']
P_df = pd.DataFrame(P, index=states, columns=states)
print('市场状态转移矩阵 P:')
print(P_df.round(3).to_string())
# 计算稳态分布(求解 πP = π, sum(π) = 1)
eigenvalues, eigenvectors = np.linalg.eig(P.T)
pi = eigenvectors[:, np.argmin(np.abs(eigenvalues - 1))].real
pi = np.abs(pi) / np.abs(pi).sum()
print(f'\n稳态分布 π:')
for s, p in zip(states, pi):
print(f' {s}: {p:.3f} ({p:.1%})')
市场状态转移矩阵 P:
熊市 震荡 牛市
熊市 0.70 0.20 0.10
震荡 0.15 0.70 0.15
牛市 0.05 0.25 0.70
稳态分布 π:
熊市: 0.266 (26.6%)
震荡: 0.430 (43.0%)
牛市: 0.304 (30.4%)
# 模拟马尔可夫链状态序列
state = 0 # 从熊市开始
state_seq = [state]
for _ in range(252 * 5 - 1):
state = np.random.choice(3, p=P[state])
state_seq.append(state)
state_seq = np.array(state_seq)
# 对应各状态的市场收益率参数
state_params = {0: (-0.0008, 0.02), 1: (0.0002, 0.01), 2: (0.001, 0.008)}
returns = np.array([np.random.normal(*state_params[s]) for s in state_seq])
cum_returns = (1 + pd.Series(returns)).cumprod()
fig, axes = plt.subplots(2, 1, figsize=(13, 7), sharex=True)
cum_returns.plot(ax=axes[0], color='steelblue', lw=1)
cmap = {0: 'red', 1: 'yellow', 2: 'green'}
for t in range(len(state_seq)):
axes[0].axvspan(t, t+1, alpha=0.15, color=cmap[state_seq[t]])
axes[0].set_title('马尔可夫市场状态模拟(红=熊, 黄=震荡, 绿=牛)'); axes[0].set_ylabel('累积净值')
pd.Series(state_seq).plot(ax=axes[1], color='black', lw=0.8, drawstyle='steps-post')
axes[1].set_yticks([0,1,2]); axes[1].set_yticklabels(['熊市','震荡','牛市'])
axes[1].set_title('状态序列'); axes[1].set_xlabel('交易日')
plt.tight_layout(); plt.show()
2. 信用评级迁移矩阵(标准普尔实证)¶
马尔可夫链最重要的金融应用之一:信用评级迁移矩阵。 描述了债券从一个信用等级迁移到另一个等级(包括违约 D)的年概率。
# 标准普尔简化年度信用迁移矩阵(近似历史数据)
ratings = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B', 'D']
P_credit = np.array([
[0.9111, 0.0788, 0.0071, 0.0006, 0.0000, 0.0000, 0.0000+0.0024], # AAA
[0.0070, 0.9065, 0.0779, 0.0064, 0.0006, 0.0011, 0.0005], # AA
[0.0009, 0.0227, 0.9105, 0.0530, 0.0074, 0.0026, 0.0029], # A
[0.0002, 0.0033, 0.0595, 0.8693, 0.0530, 0.0117, 0.0030], # BBB
[0.0003, 0.0014, 0.0067, 0.0773, 0.8053, 0.0862, 0.0228], # BB
[0.0000, 0.0011, 0.0024, 0.0043, 0.0648, 0.8346, 0.0928], # B
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000], # D(违约吸收态)
])
# 调整行归一化
for i in range(len(ratings)-1):
P_credit[i] = P_credit[i] / P_credit[i].sum()
P_credit_df = pd.DataFrame(P_credit, index=ratings, columns=ratings)
print('信用评级年度迁移矩阵 (%):' )
print((P_credit_df * 100).round(2).to_string())
# 5 年后的违约概率(矩阵乘幂)
P5 = np.linalg.matrix_power(P_credit, 5)
print(f'\n5 年后各级别累积违约概率:')
for r, d in zip(ratings, P5[:, -1]):
print(f' {r}: {d:.4f} ({d:.2%})')
信用评级年度迁移矩阵 (%):
AAA AA A BBB BB B D
AAA 91.11 7.88 0.71 0.06 0.00 0.00 0.24
AA 0.70 90.65 7.79 0.64 0.06 0.11 0.05
A 0.09 2.27 91.05 5.30 0.74 0.26 0.29
BBB 0.02 0.33 5.95 86.93 5.30 1.17 0.30
BB 0.03 0.14 0.67 7.73 80.53 8.62 2.28
B 0.00 0.11 0.24 0.43 6.48 83.46 9.28
D 0.00 0.00 0.00 0.00 0.00 0.00 100.00
5 年后各级别累积违约概率:
AAA: 0.0109 (1.09%)
AA: 0.0058 (0.58%)
A: 0.0184 (1.84%)
BBB: 0.0335 (3.35%)
BB: 0.1370 (13.70%)
B: 0.3484 (34.84%)
D: 1.0000 (100.00%)
练习¶
- 计算 A 级债券从现在开始,在未来 3 年内升到 AAA 的累积概率(用矩阵幂次)。
- 基于信用迁移矩阵,设计一个信用策略:买入 BB 级债券(若升至 BBB 则获得价差收益),计算期望收益。
- 研究 hmmlearn 的 GaussianHMM,用真实 VIX 数据拟合 2 状态和 3 状态 HMM 的对数似然,哪个更合适?
下一节 → 04_mle_estimation.ipynb