1. 这节究竟解决什么实际问题?¶
1.10 现值、久期与资产定价基础 的具体任务是:把未来现金流折现到今天,并解释利率变化如何影响久期和资产价格。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
会使用百分比和基础代数;建议先完成环境配置。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
示例优先使用仓库固定的日频复权收盘价;它不是实时行情,也不代表完整市场。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 1.10 现值、久期与资产定价基础 的输入、计算步骤、输出和局限;尤其能说明:把未来现金流折现到今天,并解释利率变化如何影响久期和资产价格。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
价格表示资产在特定时点的水平,收益率表示两个时点之间的相对变化。多数跨资产比较使用收益率。
所有公式都先说明分子、分母、单位与时间尺度;年化前必须确认数据频率。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
NumPy/Pandas 帮你算数和对齐索引;统计库帮你估计参数,但不替你解释经济含义。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“把未来现金流折现到今天,并解释利率变化如何影响久期和资产价格。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把百分比和小数混用、忘记 dropna、把日频波动率直接叫作年化波动率、把相关性当因果。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
概念演示也要避免事后选择样本或只展示支持结论的时间段。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/01_financial_concepts/10_present_value_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/01_financial_concepts/10_present_value_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Investments(Bodie, Kane, Marcus)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 11_ar1_processes。本节的概念会成为理解它的输入,而不是孤立的名词。
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. 贴现现金流(DCF):现值的矩阵形式¶
未来现金流的现值: $$PV = \sum_{t=1}^{T} \frac{C_t}{(1+r)^t}$$
用矩阵表示(适合向量化计算): $$PV = \mathbf{d}^\top \mathbf{C}$$
其中 $d_t = \frac{1}{(1+r)^t}$ 是贴现因子向量。
r = 0.05 # 折现率 5%
T = 10 # 10 年期
# 贴现因子向量
periods = np.arange(1, T+1)
discount = (1/(1+r))**periods
# 场景 A:等额现金流(年金)
C_annuity = np.ones(T) * 100 # 每年 100
pv_annuity = np.dot(discount, C_annuity)
print(f'年金 PV (每年100元,折现率5%,10年): {pv_annuity:.2f} 元')
print(f' (解析公式验证: {100*(1-(1+r)**-T)/r:.2f})')
# 场景 B:增长型现金流(股票红利)
g = 0.03 # 红利增长率
C_div = 50 * (1+g)**np.arange(T) # 从50元开始每年增长
pv_div = np.dot(discount, C_div)
print(f'增长型红利 PV (g=3%, r=5%, 10年): {pv_div:.2f} 元')
# 可视化贴现因子
plt.figure(figsize=(10, 4))
plt.bar(periods, discount, color='steelblue', alpha=0.8, label='贴现因子 d_t')
plt.bar(periods, C_div*discount/100, color='darkorange', alpha=0.6,
bottom=0, label='红利现值(缩放)')
plt.title(f'贴现因子随时间递减(r={r:.0%})')
plt.xlabel('时间(年)'); plt.legend(); plt.grid(alpha=0.3); plt.show()
年金 PV (每年100元,折现率5%,10年): 772.17 元 (解析公式验证: 772.17) 增长型红利 PV (g=3%, r=5%, 10年): 437.38 元
2. Gordon 增长模型:股票内在价值¶
假设股票永续支付红利,且每年以固定速率 $g$ 增长:
$$P = \frac{D_1}{r - g} = \frac{D_0(1+g)}{r - g}$$
其中 $r$ 是要求回报率(折现率),$g$ 是红利增长率,必须 $r > g$。
D0 = 3.0 # 当前年红利(元)
g_values = [0.01, 0.02, 0.03, 0.04, 0.05]
r_values = np.linspace(0.05, 0.15, 100)
plt.figure(figsize=(10, 5))
for g in g_values:
# 仅绘制 r > g 的部分
r_valid = r_values[r_values > g + 0.001]
P = D0 * (1+g) / (r_valid - g)
plt.plot(r_valid*100, P, lw=2, label=f'g={g:.0%}')
plt.xlabel('要求回报率 r (%)')
plt.ylabel('股票理论价值 P (元)')
plt.title('Gordon 增长模型:要求回报率 vs 股票内在价值')
plt.legend(title='红利增长率 g'); plt.grid(alpha=0.3); plt.ylim(0, 300); plt.show()
# 利率上升对估值的冲击
r_base = 0.08; r_new = 0.10; g = 0.03
P_base = D0 * (1+g) / (r_base - g)
P_new = D0 * (1+g) / (r_new - g)
print(f'利率从 {r_base:.0%} 升至 {r_new:.0%}(加息 200bp):')
print(f' 股票理论价值: {P_base:.1f} → {P_new:.1f} 元 (跌幅 {(P_new-P_base)/P_base:.1%})')
利率从 8% 升至 10%(加息 200bp): 股票理论价值: 61.8 → 44.1 元 (跌幅 -28.6%)
3. 债券价格与久期¶
债券价格 = 所有现金流(票息+面值)的现值之和:
$$P = \sum_{t=1}^{T} \frac{C}{(1+y)^t} + \frac{F}{(1+y)^T}$$
**久期(Duration)**衡量利率变化对债券价格的敏感度: $$\text{Modified Duration} = -\frac{dP/dy}{P}$$
def bond_price(face, coupon_rate, y, T):
"""计算债券价格"""
C = face * coupon_rate
t = np.arange(1, T+1)
d = 1 / (1+y)**t
return C * d.sum() + face * d[-1]
def modified_duration(face, coupon_rate, y, T):
"""计算修正久期"""
C = face * coupon_rate
t = np.arange(1, T+1)
d = 1 / (1+y)**t
P = C * d.sum() + face * d[-1]
macaulay = (C * t * d).sum() / P + T * face * d[-1] / P
return macaulay / (1+y)
# 不同期限债券对利率变化的敏感性
F = 1000; cr = 0.05; y0 = 0.05
maturities = [1, 2, 5, 10, 20, 30]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
y_range = np.linspace(0.01, 0.15, 100)
for T in maturities:
prices = [bond_price(F, cr, y, T) for y in y_range]
ax1.plot(y_range*100, prices, lw=1.5, label=f'{T}年期')
dur = modified_duration(F, cr, y0, T)
ax2.bar(T, dur, color='steelblue', alpha=0.8)
ax2.text(T, dur+0.1, f'{dur:.1f}', ha='center', fontsize=9)
ax1.set_title('利率 vs 债券价格(票息率=5%)')
ax1.set_xlabel('到期收益率 (%)'); ax1.set_ylabel('债券价格(元)')
ax1.axvline(5, color='gray', linestyle='--', alpha=0.5); ax1.legend(fontsize=8)
ax2.set_title('不同期限债券的修正久期')
ax2.set_xlabel('债券期限(年)'); ax2.set_ylabel('修正久期(年)')
plt.tight_layout(); plt.show()
print('久期越大 → 对利率变化越敏感(加息伤害越大)')
久期越大 → 对利率变化越敏感(加息伤害越大)
练习¶
- 对于 10 年期债券(面值 1000,票息率 5%),计算利率从 3% 升至 8% 时债券价格的变化,验证久期公式的近似精度。
- 在加息时,哪些行业(高 PE、高成长性)的 Gordon 模型估值受冲击最大?用数值模拟说明。
- 用
scipy.optimize.brentq实现到期收益率(YTM)的数值求解,验证当债券以票面价格溢价交易时 YTM < 票息率。
下一节 → 11_ar1_processes.ipynb