Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
durationadjustedcmscoupon.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19// clang-format off
20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
22// clang-format on
23
27
28#include "toplevelfixture.hpp"
29
30#include <ql/time/date.hpp>
31#include <ql/cashflows/cmscoupon.hpp>
32#include <ql/cashflows/lineartsrpricer.hpp>
33#include <ql/termstructures/yieldtermstructure.hpp>
34#include <ql/termstructures/yield/flatforward.hpp>
35#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
36#include <ql/indexes/swap/euriborswap.hpp>
37
38using namespace QuantExt;
39using namespace QuantLib;
40
41BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
42
43BOOST_AUTO_TEST_SUITE(DurationAdjustedCouponTest)
44
45BOOST_AUTO_TEST_CASE(testAgainstCmsCoupon) {
46
47 BOOST_TEST_MESSAGE("Testing duration adjusted cms coupons vs. vanilla cms coupon...");
48
49 Date today(25, January, 2021);
50 Settings::instance().evaluationDate() = today;
51
52 Handle<YieldTermStructure> discountCurve(
53 QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.01, Actual365Fixed()));
54 Handle<YieldTermStructure> forwardCurve(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
55 Handle<SwaptionVolatilityStructure> swaptionVol(QuantLib::ext::make_shared<ConstantSwaptionVolatility>(
56 0, NullCalendar(), Unadjusted, 0.0050, Actual365Fixed(), Normal));
57 Handle<Quote> reversion(QuantLib::ext::make_shared<SimpleQuote>(0.01));
58
59 Date startDate = Date(25, January, 2025);
60 Date endDate = Date(25, January, 2026);
61 Date payDate = Date(27, January, 2026);
62 Size fixingDays = 2;
63 auto index = QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(10 * Years, forwardCurve, discountCurve);
64
65 CmsCoupon cmsCoupon(payDate, 1.0, startDate, endDate, fixingDays, index);
66 DurationAdjustedCmsCoupon durationAdjustedCmsCoupon(payDate, 1.0, startDate, endDate, fixingDays, index, 0);
67
68 auto cmsPricer = QuantLib::ext::make_shared<LinearTsrPricer>(swaptionVol, reversion, discountCurve,
69 LinearTsrPricer::Settings().withRateBound(-2.0, 2.0));
70
71 auto durationAdjustedCmsPricer = QuantLib::ext::make_shared<DurationAdjustedCmsCouponTsrPricer>(
72 swaptionVol, QuantLib::ext::make_shared<LinearAnnuityMappingBuilder>(reversion), -2.0, 2.0);
73
74 cmsCoupon.setPricer(cmsPricer);
75 durationAdjustedCmsCoupon.setPricer(durationAdjustedCmsPricer);
76
77 BOOST_TEST_MESSAGE("cms coupon rate = " << cmsCoupon.rate());
78 BOOST_TEST_MESSAGE("cms coupon convexity adj = " << cmsCoupon.convexityAdjustment());
79 BOOST_TEST_MESSAGE("duration adjusted cms coupon rate = " << durationAdjustedCmsCoupon.rate());
80 BOOST_TEST_MESSAGE("dur adj cms coupon convexity adj = " << durationAdjustedCmsCoupon.convexityAdjustment());
81
82 Real tol = 1E-6; // percentage tolerance, i.e. we have 1E-8 effectively
83
84 BOOST_CHECK_CLOSE(cmsCoupon.rate(), durationAdjustedCmsCoupon.rate(), tol);
85 BOOST_CHECK_CLOSE(cmsCoupon.convexityAdjustment(), durationAdjustedCmsCoupon.convexityAdjustment(), tol);
86}
87
88BOOST_AUTO_TEST_CASE(testHistoricalValues) {
89
90 BOOST_TEST_MESSAGE("Testing duration adjusted cms coupon historical rates...");
91
92 Date today(25, January, 2021);
93 Settings::instance().evaluationDate() = today;
94
95 Date startDate = Date(25, June, 2020);
96 Date endDate = Date(25, June, 2021);
97 Date payDate = Date(27, June, 2021);
98 Size fixingDays = 2;
99
100 Handle<YieldTermStructure> discountCurve(
101 QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.01, Actual365Fixed()));
102 Handle<YieldTermStructure> forwardCurve(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
103
104 auto index = QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(10 * Years, forwardCurve, discountCurve);
105
106 Date fixingDate = index->fixingCalendar().advance(startDate, -(fixingDays * Days), Preceding);
107 Real fixingValue = 0.01;
108 index->addFixing(fixingDate, fixingValue);
109
110 // we do not need a vol surface or an annuity mapping builder, since the coupon amount is deterministic
111 auto pricer =
112 QuantLib::ext::make_shared<DurationAdjustedCmsCouponTsrPricer>(Handle<SwaptionVolatilityStructure>(), nullptr);
113
114 DurationAdjustedCmsCoupon cpn0(payDate, 1.0, startDate, endDate, fixingDays, index, 0);
115 DurationAdjustedCmsCoupon cpn1(payDate, 1.0, startDate, endDate, fixingDays, index, 1);
116 DurationAdjustedCmsCoupon cpn10(payDate, 1.0, startDate, endDate, fixingDays, index, 10);
117
118 cpn0.setPricer(pricer);
119 cpn1.setPricer(pricer);
120 cpn10.setPricer(pricer);
121
122 BOOST_TEST_MESSAGE("duration = 0 : rate = " << cpn0.rate());
123 BOOST_TEST_MESSAGE("duration = 1 : rate = " << cpn1.rate());
124 BOOST_TEST_MESSAGE("duration = 10 : rate = " << cpn10.rate());
125
126 BOOST_TEST_MESSAGE("duration = 0 : indexFixing = " << cpn0.indexFixing());
127 BOOST_TEST_MESSAGE("duration = 1 : indexFixing = " << cpn1.indexFixing());
128 BOOST_TEST_MESSAGE("duration = 10 : indexFixing = " << cpn10.indexFixing());
129
130 auto durationAdjustment = [](const Real S, const Size i) {
131 if (i == 0)
132 return 1.0;
133 Real tmp = 0.0;
134 for (Size j = 0; j < i; ++j)
135 tmp += 1.0 / std::pow(1.0 + S, j + 1);
136 return tmp;
137 };
138
139 Real tol = 1.0E-6;
140
141 BOOST_CHECK_CLOSE(cpn0.rate(), fixingValue * durationAdjustment(fixingValue, 0), tol);
142 BOOST_CHECK_CLOSE(cpn1.rate(), fixingValue * durationAdjustment(fixingValue, 1), tol);
143 BOOST_CHECK_CLOSE(cpn10.rate(), fixingValue * durationAdjustment(fixingValue, 10), tol);
144
145 BOOST_CHECK_CLOSE(cpn0.indexFixing(), fixingValue * durationAdjustment(fixingValue, 0), tol);
146 BOOST_CHECK_CLOSE(cpn1.indexFixing(), fixingValue * durationAdjustment(fixingValue, 1), tol);
147 BOOST_CHECK_CLOSE(cpn10.indexFixing(), fixingValue * durationAdjustment(fixingValue, 10), tol);
148}
149
150BOOST_AUTO_TEST_SUITE_END()
151
152BOOST_AUTO_TEST_SUITE_END()
cms coupon scaled by a duration number
tsr coupon pricer for duration adjusted cms coupon
linear annuity mapping function f(S) = a*S+b
BOOST_AUTO_TEST_CASE(testAgainstCmsCoupon)
Fixture that can be used at top level.