
在金融市场中,收益率曲线是理解和预测未来利率走势的关键工具。QuantLib是一个功能强大的开源量化金融库,提供了丰富的工具来构建和分析收益率曲线。本节将介绍如何使用QuantLib从一系列债券数据中引导(bootstrap)出一条零利率曲线。
首先,我们需要导入必要的库并设置QuantLib的评估日期、日历和日计数约定。
import pandas as pd import matplotlib.pyplot as plt import QuantLib as ql # 设置评估日期 today = ql.Date(21, ql.November, 2023) ql.Settings.instance().evaluationDate = today # 定义日历和日计数约定 calendar = ql.NullCalendar() # 使用空日历,表示所有日期都是工作日 day_count = ql.Actual365Fixed() # 实际天数/365固定日计数 # 债券参数 faceAmount = 100 # 面值
我们将使用零息债券和附息债券的混合数据来构建收益率曲线。QuantLib通过FixedRateBondHelper等辅助类来简化债券数据的处理。
# 债券数据:(发行日期, 到期日期, 票息率, 市场价格, 结算天数)
data = [
('11-09-2023', '11-12-2023', 0, 99.524, 4), # 零息债券
('11-09-2023', '11-03-2024', 0, 96.539, 4), # 零息债券
('11-09-2023', '10-06-2024', 0, 93.552, 4), # 零息债券
('11-09-2023', '09-09-2024', 0, 89.510, 4), # 零息债券
('22-08-2022', '22-08-2024', 9.0, 96.406933, 3), # 附息债券
('27-06-2022', '27-06-2025', 10.0, 88.567570, 3), # 附息债券
('27-06-2022', '27-06-2027', 11.0, 71.363073, 3), # 附息债券
('22-08-2022', '22-08-2029', 12.0, 62.911623, 3), # 附息债券
('27-06-2022', '27-06-2032', 13.0, 55.976845, 3), # 附息债券
('22-08-2022', '22-08-2037', 14.0, 52.656596, 3) # 附息债券
]
helpers = []
for issue_date_str, maturity_str, coupon, price, settlement_days in data:
price_handle = ql.QuoteHandle(ql.SimpleQuote(price))
issue_date = ql.Date(issue_date_str, '%d-%m-%Y')
maturity = ql.Date(maturity_str, '%d-%m-%Y')
# 构建债券支付时间表
# schedule的第一个参数通常是有效日期,此处使用today作为基准
schedule = ql.Schedule(today, maturity, ql.Period(ql.Semiannual), calendar,
ql.DateGeneration.Backward, ql.Following,
ql.DateGeneration.Backward, False)
# 创建债券辅助工具
helper = ql.FixedRateBondHelper(price_handle, settlement_days, faceAmount,
schedule, [coupon / 100], day_count, False)
helpers.append(helper)使用PiecewiseCubicZero类通过辅助工具引导零利率曲线。该方法通过插值从离散的债券数据点构建连续的收益率曲线。
立即学习“Python免费学习笔记(深入)”;
# 引导零利率曲线
curve = ql.PiecewiseCubicZero(today, helpers, day_count)
# 启用外推,以便计算超出观测数据范围的利率
curve.enableExtrapolation()
print("收益率曲线已成功构建。")在处理零息债券时,到期收益率(YTM)和零利率(Spot Rate)的概念常常引起混淆,尤其是在涉及结算日时。本节将详细解释这些概念的差异及其在QuantLib中的正确处理方式。
核心差异:零利率通常以评估日期为起点,而YTM则以结算日期为起点。如果评估日期与结算日期不同,则两者会存在差异。
结算日(Settlement Days)表示交易发生后,资金和证券实际交割所需的工作日数。例如,T+4表示交易后第四个工作日进行结算。
例如,一个面值100的零息债券,到期日为M,结算日为S,其价格应为 100 / (1 + YTM)^(T_M - T_S),其中 T_M - T_S 是从结算日到到期日的天数。如果settlement_date比today晚4天,那么从today到maturity的整个期间,我们需要将这4天从折现周期中剔除,因为这4天内的利率风险已经由买方承担,折现计算应从结算日开始。
为了使零息债券的YTM和零利率保持一致(即从同一时间点开始计算),我们需要进行以下调整:
以下是结合上述修正和建议的完整QuantLib代码,用于计算债券的YTM、零利率和折现因子,并输出到DataFrame。
# 创建一个空的DataFrame来存储结果
bond_results = {
'Issue Date': [],
'Maturity Date': [],
'Coupon Rate': [],
'Price': [],
'Settlement Days': [],
'Yield (YTM)': [],
'Zero Rate (Settlement to Maturity)': [], # 修正后的零利率
'Zero Rate (Evaluation to Maturity)': [], # 评估日到期日零利率
'Discount Factor': [],
'Clean Price': [],
'Dirty Price': []
}
# 计算债券价格和收益率
for issue_date_str, maturity_str, coupon, price, settlement_days in data:
price_handle = ql.QuoteHandle(ql.SimpleQuote(price))
issue_date = ql.Date(issue_date_str, '%d-%m-%Y')
maturity = ql.Date(maturity_str, '%d-%m-%Y')
# 债券支付时间表 (与helper中使用的schedule保持一致)
schedule = ql.Schedule(today, maturity, ql.Period(ql.Semiannual), calendar,
ql.DateGeneration.Backward, ql.Following,
ql.DateGeneration.Backward, False)
# 创建债券对象
bond = ql.FixedRateBond(settlement_days, faceAmount, schedule,
[coupon / 100], day_count)
# 设置定价引擎,使用已引导的收益率曲线
bondEngine = ql.DiscountingBondEngine(ql.YieldTermStructureHandle(curve))
bond.setPricingEngine(bondEngine)
# 计算债券YTM、净价和全价
bondYield = bond.bondYield(day_count, ql.Compounded, ql.Annual)
bondCleanPrice = bond.cleanPrice()
bondDirtyPrice = bond.dirtyPrice()
# 修正后的零利率:从结算日期到到期日期的远期零利率
# 对于零息债券,这应该与YTM非常接近
zero_rate_settlement_to_maturity = curve.forwardRate(
bond.settlementDate(), maturity, day_count, ql.Compounded, ql.Annual
).rate()
# 从评估日期到到期日期的零利率 (用于比较)
zero_rate_evaluation_to_maturity = curve.zeroRate(
maturity, day_count, ql.Compounded, ql.Annual
).rate()
# 从评估日期到到期日期的折现因子
discount_factor = curve.discount(maturity)
# 将结果添加到DataFrame
bond_results['Issue Date'].append(issue_date)
bond_results['Maturity Date'].append(maturity)
bond_results['Coupon Rate'].append(coupon)
bond_results['Price'].append(price_handle.value())
bond_results['Settlement Days'].append(settlement_days)
bond_results['Yield (YTM)'].append(bondYield)
bond_results['Zero Rate (Settlement to Maturity)'].append(zero_rate_settlement_to_maturity)
bond_results['Zero Rate (Evaluation to Maturity)'].append(zero_rate_evaluation_to_maturity)
bond_results['Discount Factor'].append(discount_factor)
bond_results['Clean Price'].append(bondCleanPrice)
bond_results['Dirty Price'].append(bondDirtyPrice)
# 从结果创建DataFrame
bond_results_df = pd.DataFrame(bond_results)
# 打印结果
print("\n债券定价与收益率分析结果:")
print(bond_results_df)
# 输出到Excel
bond_results_df.to_excel('BondResults.xlsx', index=False)
# 打印曲线节点上的零利率、远期利率和折现因子
print("\n收益率曲线节点数据:")
node_data = {'Date': [],
'Zero Rates (Evaluation to Date)': [],
'Forward Rates (1Y)': [],
'Discount Factors': []}
for dt in curve.dates():
node_data['Date'].append(dt)
node_data['Zero Rates (Evaluation to Date)'].append(
curve.zeroRate(dt, day_count, ql.Compounded, ql.Annual).rate()
)
node_data['Forward Rates (1Y)'].append(
curve.forwardRate(dt, dt + ql.Period(1, ql.Years), day_count, ql.Compounded, ql.Annual).rate()
)
node_data['Discount Factors'].append(curve.discount(dt))
node_dataframe = pd.DataFrame(node_data)
print(node_dataframe)
node_dataframe.to_excel('NodeRates.xlsx', index=False)观察BondResults.xlsx中的数据,特别是前四只零息债券:
关键修正点总结:
本教程详细阐述了在QuantLib Python中构建收益率曲线、计算零息债券收益率以及处理结算日影响的关键概念和实践。理解YTM与零利率的计算起点差异(结算日 vs 评估日)以及结算日对折现周期的实际影响,对于确保金融模型计算的准确性至关重要。通过遵循本文提供的代码示例和注意事项,用户可以更精确地使用QuantLib进行债券定价和收益率分析。
以上就是QuantLib Python实战:零息债券收益率、零利率与结算日折扣的精确处理的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号