QuantLib 金融计算——案例之普通利率互换分析(3):FR007 互换分析
QuantLib 金融计算——案例之普通利率互换分析(3)
基本解决了一直想解决的问题,牛年开局良好 😃
哈皮牛耶
概述
下面尝试实现 FR007 互换的分析。
相关组件
对比 OvernightIndexedSwap
,分析 FR007 互换用到了如下几个类:
ChinaFixingRepo
(对应OvernightIndex
),存储定盘回购利率的相关信息,包括名称、期限、计息规则等,这些信息在构造互换对象内部也会直接或间接地用到。ChinaFixingRepoCoupon
(对应OvernightIndexedCoupon
),用来表示某一期的浮动现金流。内部会持有一个ChinaFixingRepoCouponPricer
对象。ChinaFixingRepoCouponPricer
(对应OvernightIndexedCouponPricer
),为ChinaFixingRepoCoupon
对象确定浮动利率。ChinaFixingRepoLeg
(对应OvernightLeg
),根据日期表生成浮动现金流,也就是一个ChinaFixingRepoCoupon
的向量,存储在ChinaFixingRepoSwap
对象中。ChinaFixingRepoSwap
(对应OvernightIndexedSwap
),表示定盘回购利率相关的互换,是OvernightIndexedSwap
和VanillaSwap
的兄弟类,同属Swap
的派生类,考虑到 FR007 互换的经济含义,ChinaFixingRepoSwap
成员函数更接近VanillaSwap
。MakeChinaFixingRepoSwap
(对应MakeOIS
),是创建ChinaFixingRepoSwap
的工厂类。ChinaFixingRepoSwapRateHelper
(对应OISRateHelper
),用于根据 FR007 互换的报价进行 bootstrap。
以上各个组件的实现可以查看 QuantLibEx 中的同名文件。
测试组件
测试 ChinaFixingRepoCouponPricer
下面以文档《人民币利率互换的定价模型》中图 9 的结果作基准,测试 ChinaFixingRepoCouponPricer
能否正确的推算出浮动利率。完整的实现请查看 TestChinaFixingRepoCoupon
函数。
图 9 的结果
测试代码中用到了 QuantLib 中的另一个“单体”——IndexManager
,用户可以根据它统一管理各种利率的历史数据,因为 fixing 的过程可能涉及到去数据库调用历史数据,特别是在 OIS 和 FR007 互换的语境下。
录入 FR007 的历史数据:
ext::shared_ptr<ChinaFixingRepo> fr(
new ChinaFixingRepo(
Period(7, Days),
fixingDays));
vector<Date> dates = {
Date(27, July, 2018),
Date(3, August, 2018),
Date(10, August, 2018),
Date(17, August, 2018),
Date(24, August, 2018),
Date(31, August, 2018),
Date(7, September, 2018),
Date(14, September, 2018),
Date(21, September, 2018),
Date(30, September, 2018),
Date(12, October, 2018),
Date(19, October, 2018),
Date(26, October, 2018)};
vector<Rate> fwds = {
2.8 / 100.0,
2.4 / 100.0,
2.29 / 100.0,
2.65 / 100.0,
2.5 / 100.0,
2.66 / 100.0,
2.69 / 100.0,
2.6 / 100.0,
2.65 / 100.0,
2.76 / 100.0,
2.6 / 100.0,
2.61 / 100.0,
2.65 / 100.0};
TimeSeries<Real> ts(dates.begin(), dates.end(), fwds.begin());
IndexManager::instance().setHistory(fr->name(), ts);
输出浮动利率和日期:
cout << coupon.rate() << endl;
size_t n = coupon.fixingDates().size();
for (size_t i = 0; i < n; ++i) {
cout << setw(15) << coupon.fixingDates()[i]
<< setw(15) << coupon.valueDates()[i] << endl;
}
0.0262145
July 27th, 2018 July 30th, 2018
August 3rd, 2018 August 6th, 2018
August 10th, 2018 August 13th, 2018
August 17th, 2018 August 20th, 2018
August 24th, 2018 August 27th, 2018
August 31st, 2018 September 3rd, 2018
September 7th, 2018 September 10th, 2018
September 14th, 2018 September 17th, 2018
September 21st, 2018 September 24th, 2018
September 30th, 2018 October 1st, 2018
September 30th, 2018 October 8th, 2018
October 12th, 2018 October 15th, 2018
October 19th, 2018 October 22nd, 2018
October 26th, 2018 October 29th, 2018
结果完全符合预期,无论是利率还是日期,结果都是对的。
测试 ChinaFixingRepoSwap
下面以文档《利率互换贴现因子曲线的构造模型》中图 12 的结果作基准,测试 ChinaFixingRepoSwap
的运行是否正确。完整的实现请查看 TestChinaFixingRepoSwap
函数。
图 12 的结果
以 10 年期互换为例(CNYFR_S10Y),若采用图中的期限结构定价互换合约,其 NPV 将会等于零或者极为接近零(相对于互换的本金而言)。
cout << swap.NPV() / 10000000.0 * 100.0 << '%' << endl;
cout << swap.fixedLegNPV() / 10000000.0 * 100.0 << '%' << endl;
cout << swap.floatingLegNPV() / 10000000.0 * 100.0 << '%' << endl;
0.00340699%
-27.3602%
27.3636%
NPV 并未等于零,但相对于合约面值而言已经很接近于零。差异原因稍候解释。
测试 Bootstrap
依然是以文档《利率互换贴现因子曲线的构造模型》中图 12 的结果作基准,测试 ChinaFixingRepoSwapRateHelper
的运行是否正确。完整的实现请查看 ChinaFixingRepoSwapCurve
函数,代码实现与 ShiborIRScurve
几乎一致。
运行结果:
Today: January 21st, 2020
Settlement date: January 22nd, 2020
13, 0.99907852, 2.5884391
34, 0.99759776, 2.5819802
92, 0.99355973, 2.5633687
183, 0.98719415, 2.570667
275, 0.98071798, 2.5842461
367, 0.9742452, 2.5950071
734, 0.94794334, 2.6584605
1098, 0.92102612, 2.7347369
1462, 0.89302652, 2.8246057
1828, 0.86428623, 2.9122383
2558, 0.80737256, 3.0531303
3654, 0.72642071, 3.1927605
结果与基准相比并不吻合,特别是日期的计算,但数值值已经非常接近。差异原因稍候解释。
差异可能的来源
相比于 ShiborIRScurve
的测试结果,这次 ChinaFixingRepoSwapCurve
的结果与基准差异较大。究其原因,可能是因为工作日转换规则是 MFL,对假期比较敏感,而这次的估值日期安排 在公历 1 月下旬,更容易受到农历春节七天长假的影响。
QuantLib 中包含中国假期的日历类是 China
,不过它对中国农历假期的维护并不完善,所以它所记录的假期很可能和建信金科系统的假期不一致。
另一个可能的原因是 bootstrap 中缺少 FR001 和 FR014 的报价。