In [1]:
Copied!
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
np.random.seed(42)
print('Libraries loaded')
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
np.random.seed(42)
print('Libraries loaded')
Libraries loaded
In [2]:
Copied!
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
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}$ 是贴现因子向量。
In [3]:
Copied!
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()
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$。
In [4]:
Copied!
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%})')
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}$$
In [5]:
Copied!
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('久期越大 → 对利率变化越敏感(加息伤害越大)')
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
In [ ]:
Copied!