标签:false frequency 组合 osi 比较 code art github ext
QuantLib 中涉及利率互换的功能大致分为两大类:
这两类功能是紧密联系的,根据最新报价推算出的期限结构通常可以用来对存续合约进行估值。
本文接下来介绍如何根据合约报价推算出隐含的利率期限结构,并以建信金科的技术文档 《利率互换贴现因子曲线的构造模型》 中图 16 的结果作为比较基准。
图 16 的结果:
通过合约报价推算期限结构的过程称为“bootstrap”,其思想和实践非常类似于理论证明中用到的“数学归纳法”,大体过程如下:
其中 \(TS\) 可以是即期利率、远期利率、贴现因子三者中的任意一个,而可用的插值方法更是五花八门,例如线性插值、样条插值、对数线性插值和常数插值等等。两个维度相互搭配可以产生非常多的组合,QuantLib 通过模板技术实现两个维度的自由搭配,具体选择哪种组合要视业务需要而定。
需要注意的是,利率互换的估值对合约条款比较敏感。
示例中的合约均是 Shibor 3M 的利率互换,条款细则如下:
完整的代码请见 QuantLibEx 项目的 example.cpp 文件。
using namespace QuantLib;
using namespace std;
Calendar calendar = China(China::IB);
Date today(15, January, 2020);
Settings::instance().evaluationDate() = today;
Natural delayDays = 1;
Date settlementDate = calendar.advance(
today, delayDays, Days);
// must be a business day
settlementDate = calendar.adjust(settlementDate);
cout << "Today: " << today << endl;
cout << "Settlement date: " << settlementDate << endl;
Today: January 15th, 2020
Settlement date: January 16th, 2020
RateHelper
QuantLib 中 bootstrap 计算的核心是为 PiecewiseYieldCurve
模板类配置 RateHelper
对象,不同的金融工具要使用对应的派生类。对于已知利率通常用 DepositRateHelper
类,而普通互换则用 SwapRateHelper
类。
示例没有使用 QuantLib 提供的 Shibor
类,而是自己根据合约重新配置了一个对象。(查看源代码的话,这实际上正是 QuantLib 中 IborIndex
派生类的普遍构造方式)
另外,Actual365_25
是 QuantLib 中未提供的,要自己实现,几乎就是 Actual365Fixed
的翻版。
DayCounter termStrcDayCounter = Actual365_25();
Period mn1(1, Months), mn3(3, Months), mn6(6, Months), mn9(9, Months),
yr1(1, Years), yr2(2, Years), yr3(3, Years), yr4(4, Years),
yr5(5, Years), yr7(7, Years), yr10(10, Years);
ext::shared_ptr<Quote>
m1Rate(new SimpleQuote(2.7990 / 100.0)),
m3Rate(new SimpleQuote(2.8650 / 100.0)),
s6mRate(new SimpleQuote(2.8975 / 100.0)),
s9mRate(new SimpleQuote(2.9125 / 100.0)),
s1yRate(new SimpleQuote(2.9338 / 100.0)),
s2yRate(new SimpleQuote(3.0438 / 100.0)),
s3yRate(new SimpleQuote(3.1639 / 100.0)),
s4yRate(new SimpleQuote(3.2805 / 100.0)),
s5yRate(new SimpleQuote(3.3876 / 100.0)),
s7yRate(new SimpleQuote(3.5575 / 100.0)),
s10yRate(new SimpleQuote(3.7188 / 100.0));
Handle<Quote>
m1RateHandle(m1Rate),
m3RateHandle(m3Rate),
s6mRateHandle(s6mRate),
s9mRateHandle(s9mRate),
s1yRateHandle(s1yRate),
s2yRateHandle(s2yRate),
s3yRateHandle(s3yRate),
s4yRateHandle(s4yRate),
s5yRateHandle(s5yRate),
s7yRateHandle(s7yRate),
s10yRateHandle(s10yRate);
DayCounter depositDayCounter = Actual360();
ext::shared_ptr<RateHelper>
m1(new DepositRateHelper(
m1RateHandle, mn1, delayDays, calendar,
ModifiedFollowing, false, depositDayCounter)),
m3(new DepositRateHelper(
m3RateHandle, mn3, delayDays, calendar,
ModifiedFollowing, false, depositDayCounter));
Frequency fixedLegFreq = Quarterly;
BusinessDayConvention fixedLegConv = ModifiedFollowing;
DayCounter fixedLegDayCounter = Actual365Fixed();
ext::shared_ptr<IborIndex> shiborIndex(
new IborIndex(
"Shibor", mn3, delayDays, CNYCurrency(),
calendar, Unadjusted, false, Actual360()));
ext::shared_ptr<RateHelper>
s6m(new SwapRateHelper(
s6mRateHandle, mn6, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s9m(new SwapRateHelper(
s9mRateHandle, mn9, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s1y(new SwapRateHelper(
s1yRateHandle, yr1, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s2y(new SwapRateHelper(
s2yRateHandle, yr2, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s3y(new SwapRateHelper(
s3yRateHandle, yr3, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s4y(new SwapRateHelper(
s4yRateHandle, yr4, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s5y(new SwapRateHelper(
s5yRateHandle, yr5, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s7y(new SwapRateHelper(
s7yRateHandle, yr7, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex)),
s10y(new SwapRateHelper(
s10yRateHandle, yr10, calendar, fixedLegFreq, fixedLegConv,
fixedLegDayCounter, shiborIndex));
vector<ext::shared_ptr<RateHelper>> instruments;
instruments.push_back(m1);
instruments.push_back(m3);
instruments.push_back(s6m);
instruments.push_back(s9m);
instruments.push_back(s1y);
instruments.push_back(s2y);
instruments.push_back(s3y);
instruments.push_back(s4y);
instruments.push_back(s5y);
instruments.push_back(s7y);
instruments.push_back(s10y);
Bootstrap 的过程很简单,这里选用 PiecewiseYieldCurve<ForwardRate, BackwardFlat>
,与示例一致,将得到一个“阶梯状”的远期期限结构。
ext::shared_ptr<YieldTermStructure> termStrc(
new PiecewiseYieldCurve<ForwardRate, BackwardFlat>(
today,
instruments,
termStrcDayCounter));
Date curveNodeDate = calendar.adjust(settlementDate + mn1);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + mn3);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + mn6);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + mn9);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr1);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr2);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr3);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr4);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr5);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr7);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
curveNodeDate = calendar.adjust(settlementDate + yr10);
cout << setw(4) << curveNodeDate - today << ", "
<< termStrc->discount(curveNodeDate) << ", "
<< termStrc->zeroRate(curveNodeDate, termStrcDayCounter, Continuous).rate() * 100.0
<< endl;
33, 0.997441, 2.83629
92, 0.992733, 2.89565
183, 0.985631, 2.88875
275, 0.978375, 2.90377
369, 0.970721, 2.94142
733, 0.940822, 3.03969
1097, 0.909515, 3.15787
1462, 0.877015, 3.27853
1828, 0.843917, 3.39077
2560, 0.778373, 3.57473
3654, 0.687352, 3.74755
与建信金科专家们的模型结果非常接近了,只有一个日期出现了不一致。
由于工作日转换规则是 MFL,对假期比较敏感,QuantLib 中包含中国假期的日历类是 China
,它所记录的假期可能和建信金科系统的假期不一致。
标签:false frequency 组合 osi 比较 code art github ext
原文地址:https://www.cnblogs.com/xuruilong100/p/12940396.html