[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2018 Sebastian Schlenkrich
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include "basismodels.hpp"
21#include "utilities.hpp"
22#include <ql/cashflows/iborcoupon.hpp>
23#include <ql/compounding.hpp>
24#include <ql/experimental/basismodels/swaptioncfs.hpp>
25#include <ql/experimental/basismodels/tenoroptionletvts.hpp>
26#include <ql/experimental/basismodels/tenorswaptionvts.hpp>
27#include <ql/indexes/ibor/euribor.hpp>
28#include <ql/instruments/swaption.hpp>
29#include <ql/pricingengines/swap/discountingswapengine.hpp>
30#include <ql/termstructures/volatility/optionlet/strippedoptionlet.hpp>
31#include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp>
32#include <ql/termstructures/volatility/swaption/swaptionvolmatrix.hpp>
33#include <ql/termstructures/yield/zerocurve.hpp>
34#include <ql/math/interpolations/cubicinterpolation.hpp>
35#include <ql/quotes/simplequote.hpp>
36#include <ql/time/calendars/target.hpp>
37#include <ql/time/daycounters/thirty360.hpp>
38
39using namespace QuantLib;
40using namespace boost::unit_test_framework;
41
42namespace {
43
44 // auxiliary data
45 Period termsData[] = {
46 Period(0, Days), Period(1, Years), Period(2, Years), Period(3, Years),
47 Period(5, Years), Period(7, Years), Period(10, Years), Period(15, Years),
48 Period(20, Years), Period(61, Years) // avoid extrapolation issues with 30y caplets
49 };
50 std::vector<Period> terms(termsData, termsData + 10);
51
52 Real discRatesData[] = {-0.00147407, -0.001761684, -0.001736745, -0.00119244, 0.000896055,
53 0.003537077, 0.007213824, 0.011391278, 0.013334611, 0.013982809};
54 std::vector<Real> discRates(discRatesData, discRatesData + 10);
55
56 Real proj3mRatesData[] = {-0.000483439, -0.000578569, -0.000383832, 0.000272656, 0.002478699,
57 0.005100113, 0.008750643, 0.012788095, 0.014534052, 0.014942896};
58 std::vector<Real> proj3mRates(proj3mRatesData, proj3mRatesData + 10);
59
60 Real proj6mRatesData[] = {0.000233608, 0.000218862, 0.000504018, 0.001240556, 0.003554415,
61 0.006153921, 0.009688264, 0.013521628, 0.015136391, 0.015377704};
62 std::vector<Real> proj6mRates(proj6mRatesData, proj6mRatesData + 10);
63
64 Handle<YieldTermStructure> getYTS(const std::vector<Period>& terms,
65 const std::vector<Real>& rates,
66 const Real spread = 0.0) {
67 Date today = Settings::instance().evaluationDate();
68 std::vector<Date> dates;
69 dates.reserve(n: terms.size());
70 for (auto term : terms)
71 dates.push_back(x: NullCalendar().advance(date: today, period: term, convention: Unadjusted));
72 std::vector<Real> ratesPlusSpread(rates);
73 for (Real& k : ratesPlusSpread)
74 k += spread;
75 ext::shared_ptr<YieldTermStructure> ts =
76 ext::shared_ptr<YieldTermStructure>(new InterpolatedZeroCurve<Cubic>(
77 dates, ratesPlusSpread, Actual365Fixed(), NullCalendar()));
78 return RelinkableHandle<YieldTermStructure>(ts);
79 }
80
81 Period capletTermsData[] = {Period(1, Years), Period(2, Years), Period(3, Years),
82 Period(5, Years), Period(7, Years), Period(10, Years),
83 Period(15, Years), Period(20, Years), Period(25, Years),
84 Period(30, Years)};
85 std::vector<Period> capletTerms(capletTermsData, capletTermsData + 10);
86
87 Real capletStrikesData[] = {-0.0050, 0.0000, 0.0050, 0.0100, 0.0150, 0.0200, 0.0300, 0.0500};
88 std::vector<Real> capletStrikes(capletStrikesData, capletStrikesData + 8);
89
90
91 Handle<OptionletVolatilityStructure> getOptionletTS() {
92 Date today = Settings::instance().evaluationDate();
93 std::vector<Date> dates;
94 dates.reserve(n: capletTerms.size());
95 for (auto& capletTerm : capletTerms)
96 dates.push_back(x: TARGET().advance(date: today, period: capletTerm, convention: Following));
97 // set up vol data manually
98 std::vector<std::vector<Real> > capletVols =
99 {
100 {0.003010094, 0.002628065, 0.00456118, 0.006731268, 0.008678572, 0.010570881, 0.014149552, 0.021000638},
101 {0.004173715, 0.003727039, 0.004180263, 0.005726083, 0.006905876, 0.008263514, 0.010555395, 0.014976523},
102 {0.005870143, 0.005334526, 0.005599775, 0.006633987, 0.007773317, 0.009036581, 0.011474391, 0.016277549},
103 {0.007458597, 0.007207522, 0.007263995, 0.007308727, 0.007813586, 0.008274858, 0.009743988, 0.012555171},
104 {0.007711531, 0.007608826, 0.007572816, 0.007684107, 0.007971932, 0.008283118, 0.009268828, 0.011574083},
105 {0.007619605, 0.007639059, 0.007719825, 0.007823373, 0.00800813, 0.008113384, 0.008616374, 0.009785436},
106 {0.007312199, 0.007352993, 0.007369116, 0.007468333, 0.007515657, 0.00767695, 0.008020447, 0.009072769},
107 {0.006905851, 0.006966315, 0.007056413, 0.007116494, 0.007259661, 0.00733308, 0.007667563, 0.008419696},
108 {0.006529553, 0.006630731, 0.006749022, 0.006858027, 0.007001959, 0.007139097, 0.007390404, 0.008036255},
109 {0.006225482, 0.006404012, 0.00651594, 0.006642273, 0.006640887, 0.006885713, 0.007093024, 0.00767373}
110 };
111 // create quotes
112 std::vector<std::vector<Handle<Quote> > > capletVolQuotes;
113 for (auto& capletVol : capletVols) {
114 std::vector<Handle<Quote> > row;
115 row.reserve(n: capletVol.size());
116 for (Real j : capletVol)
117 row.push_back(x: RelinkableHandle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(j))));
118 capletVolQuotes.push_back(x: row);
119 }
120 Handle<YieldTermStructure> curve3m = getYTS(terms, rates: proj3mRates);
121 ext::shared_ptr<IborIndex> index(new Euribor3M(curve3m));
122 ext::shared_ptr<StrippedOptionletBase> tmp1(
123 new StrippedOptionlet(2, TARGET(), Following, index, dates, capletStrikes,
124 capletVolQuotes, Actual365Fixed(), Normal, 0.0));
125 ext::shared_ptr<StrippedOptionletAdapter> tmp2(new StrippedOptionletAdapter(tmp1));
126 return RelinkableHandle<OptionletVolatilityStructure>(tmp2);
127 }
128
129 Period swaptionVTSTermsData[] = {
130 Period(1, Years), Period(5, Years), Period(10, Years), Period(20, Years), Period(30, Years),
131 };
132 std::vector<Period> swaptionVTSTerms(swaptionVTSTermsData, swaptionVTSTermsData + 5);
133
134 Handle<SwaptionVolatilityStructure> getSwaptionVTS() {
135 std::vector<std::vector<Real> > swaptionVols =
136 {
137 {0.002616, 0.00468, 0.0056, 0.005852, 0.005823},
138 {0.006213, 0.00643, 0.006622, 0.006124, 0.005958},
139 {0.006658, 0.006723, 0.006602, 0.005802, 0.005464},
140 {0.005728, 0.005814, 0.005663, 0.004689, 0.004276},
141 {0.005041, 0.005059, 0.004746, 0.003927, 0.003608}
142 };
143 std::vector<std::vector<Handle<Quote> > > swaptionVolQuotes;
144 for (auto& swaptionVol : swaptionVols) {
145 std::vector<Handle<Quote> > row;
146 row.reserve(n: swaptionVol.size());
147 for (Real j : swaptionVol)
148 row.push_back(x: RelinkableHandle<Quote>(ext::shared_ptr<Quote>(new SimpleQuote(j))));
149 swaptionVolQuotes.push_back(x: row);
150 }
151 ext::shared_ptr<SwaptionVolatilityStructure> tmp(
152 new SwaptionVolatilityMatrix(TARGET(), Following, swaptionVTSTerms, swaptionVTSTerms,
153 swaptionVolQuotes, Actual365Fixed(), true, Normal));
154 return RelinkableHandle<SwaptionVolatilityStructure>(tmp);
155 }
156
157 void testSwaptioncfs(bool contTenorSpread) {
158 bool usingAtParCoupons = IborCoupon::Settings::instance().usingAtParCoupons();
159 // market data and floating rate index
160 Handle<YieldTermStructure> discYTS = getYTS(terms, rates: discRates);
161 Handle<YieldTermStructure> proj6mYTS = getYTS(terms, rates: proj6mRates);
162 ext::shared_ptr<IborIndex> euribor6m(new Euribor6M(proj6mYTS));
163 // Vanilla swap details
164 Date today = Settings::instance().evaluationDate();
165 Date swapStart = TARGET().advance(date: today, period: Period(5, Years), convention: Following);
166 Date swapEnd = TARGET().advance(date: swapStart, period: Period(10, Years), convention: Following);
167 Date exerciseDate = TARGET().advance(date: swapStart, period: Period(-2, Days), convention: Preceding);
168 Schedule fixedSchedule(swapStart, swapEnd, Period(1, Years), TARGET(), ModifiedFollowing,
169 ModifiedFollowing, DateGeneration::Backward, false);
170 Schedule floatSchedule(swapStart, swapEnd, Period(6, Months), TARGET(), ModifiedFollowing,
171 ModifiedFollowing, DateGeneration::Backward, false);
172 ext::shared_ptr<VanillaSwap> swap(
173 new VanillaSwap(Swap::Payer, 10000.0, fixedSchedule, 0.03, Thirty360(Thirty360::BondBasis),
174 floatSchedule, euribor6m, 0.0, euribor6m->dayCounter()));
175 swap->setPricingEngine(ext::shared_ptr<PricingEngine>(new DiscountingSwapEngine(discYTS)));
176 // European exercise and swaption
177 ext::shared_ptr<Exercise> europeanExercise(new EuropeanExercise(exerciseDate));
178 ext::shared_ptr<Swaption> swaption(
179 new Swaption(swap, europeanExercise, Settlement::Physical));
180 // calculate basis model swaption cash flows, discount and conmpare with swap
181 SwaptionCashFlows cashFlows(swaption, discYTS, contTenorSpread);
182 // model time is always Act365Fixed
183 Time exerciseTime = Actual365Fixed().yearFraction(d1: discYTS->referenceDate(),
184 d2: swaption->exercise()->dates()[0]);
185 if (exerciseTime != cashFlows.exerciseTimes()[0])
186 BOOST_ERROR(
187 "Swaption cash flow exercise time does not coincide with manual calculation");
188 // there might be rounding errors
189 Real tol = 1.0e-8;
190 // (discounted) fixed leg coupons must match swap fixed leg NPV
191 Real fixedLeg = 0.0;
192 for (Size k = 0; k < cashFlows.fixedTimes().size(); ++k)
193 fixedLeg += cashFlows.fixedWeights()[k] * discYTS->discount(t: cashFlows.fixedTimes()[k]);
194 if (fabs(x: fixedLeg - (-swap->fixedLegNPV())) > tol) // note, '-1' because payer swap
195 BOOST_ERROR("Swaption cash flow fixed leg NPV does not match Vanillaswap fixed leg NPV"
196 << "SwaptionCashFlows: " << fixedLeg << "\n"
197 << "swap->fixedLegNPV: " << swap->fixedLegNPV() << "\n"
198 << "Variance: " << swap->fixedLegNPV() - fixedLeg << "\n");
199 // (discounted) floating leg coupons must match swap floating leg NPV
200 Real floatLeg = 0.0;
201 for (Size k = 0; k < cashFlows.floatTimes().size(); ++k)
202 floatLeg += cashFlows.floatWeights()[k] * discYTS->discount(t: cashFlows.floatTimes()[k]);
203 if (fabs(x: floatLeg - swap->floatingLegNPV()) > tol)
204 BOOST_ERROR(
205 "Swaption cash flow floating leg NPV does not match Vanillaswap floating leg NPV.\n"
206 << "SwaptionCashFlows: " << floatLeg << "\n"
207 << "swap->floatingLegNPV: " << swap->floatingLegNPV() << "\n"
208 << "Variance: " << swap->floatingLegNPV() - floatLeg << "\n");
209 // There should not be spread coupons in a single-curve setting.
210 // However, if indexed coupons are used the floating leg is not at par,
211 // so we need to relax the tolerance to a level at which it will only
212 // catch large errors.
213 Real tol2 = usingAtParCoupons ? tol : 0.02;
214
215 SwaptionCashFlows singleCurveCashFlows(swaption, proj6mYTS, contTenorSpread);
216 for (Size k = 1; k < singleCurveCashFlows.floatWeights().size() - 1; ++k) {
217 if (fabs(x: singleCurveCashFlows.floatWeights()[k]) > tol2)
218 BOOST_ERROR("Swaption cash flow floating leg spread does not vanish in "
219 "single-curve setting.\n"
220 << "Cash flow index k: " << k << ", floatWeights: "
221 << singleCurveCashFlows.floatWeights()[k] << "\n");
222 }
223 }
224
225}
226
227
228void BasismodelsTest::testSwaptioncfsContCompSpread() {
229 BOOST_TEST_MESSAGE(
230 "Testing deterministic tenor basis model with continuous compounded spreads...");
231 testSwaptioncfs(contTenorSpread: true);
232}
233
234void BasismodelsTest::testSwaptioncfsSimpleCompSpread() {
235 BOOST_TEST_MESSAGE("Testing deterministic tenor basis model with simple compounded spreads...");
236 testSwaptioncfs(contTenorSpread: false);
237}
238
239void BasismodelsTest::testTenoroptionletvts() {
240 BOOST_TEST_MESSAGE("Testing volatility transformation for caplets/floorlets...");
241 // market data and floating rate index
242 Real spread = 0.01;
243 Handle<YieldTermStructure> discYTS = getYTS(terms, rates: discRates);
244 Handle<YieldTermStructure> proj3mYTS = getYTS(terms, rates: proj3mRates);
245 Handle<YieldTermStructure> proj6mYTS = getYTS(terms, rates: proj3mRates, spread);
246 ext::shared_ptr<IborIndex> euribor3m(new Euribor6M(proj3mYTS));
247 ext::shared_ptr<IborIndex> euribor6m(new Euribor6M(proj6mYTS));
248 // 3m optionlet VTS
249 Handle<OptionletVolatilityStructure> optionletVTS3m = getOptionletTS();
250 {
251 // we need a correlation structure
252 Real corrTimesRaw[] = {0.0, 50.0};
253 Real rhoInfDataRaw[] = {0.3, 0.3};
254 Real betaDataRaw[] = {0.9, 0.9};
255 std::vector<Real> corrTimes(corrTimesRaw, corrTimesRaw + 2);
256 std::vector<Real> rhoInfData(rhoInfDataRaw, rhoInfDataRaw + 2);
257 std::vector<Real> betaData(betaDataRaw, betaDataRaw + 2);
258 ext::shared_ptr<Interpolation> rho(
259 new LinearInterpolation(corrTimes.begin(), corrTimes.end(), rhoInfData.begin()));
260 ext::shared_ptr<Interpolation> beta(
261 new LinearInterpolation(corrTimes.begin(), corrTimes.end(), betaData.begin()));
262 ext::shared_ptr<TenorOptionletVTS::CorrelationStructure> corr(
263 new TenorOptionletVTS::TwoParameterCorrelation(rho, beta));
264 // now we can set up the new volTS and calculate volatilities
265 ext::shared_ptr<OptionletVolatilityStructure> optionletVTS6m(
266 new TenorOptionletVTS(optionletVTS3m, euribor3m, euribor6m, corr));
267 for (auto& capletTerm : capletTerms) {
268 for (Real& capletStrike : capletStrikes) {
269 Real vol3m = optionletVTS3m->volatility(optionTenor: capletTerm, strike: capletStrike, extrapolate: true);
270 Real vol6m = optionletVTS6m->volatility(optionTenor: capletTerm, strike: capletStrike, extrapolate: true);
271 Real vol6mShifted =
272 optionletVTS6m->volatility(optionTenor: capletTerm, strike: capletStrike + spread, extrapolate: true);
273 // De-correlation yields that larger tenor shifted vols are smaller then shorter
274 // tenor vols
275 if (vol6mShifted - vol3m >
276 0.0001) // we leave 1bp tolerance due to simplified spread calculation
277 BOOST_ERROR("Shifted 6m vol significantly larger then 3m vol at\n"
278 << "expiry term: " << capletTerm << ", strike: " << capletStrike
279 << "\n"
280 << "vol3m: " << vol3m << ", vol6m: " << vol6m
281 << ", vol6mShifted: " << vol6mShifted << "\n");
282 }
283 }
284 }
285 {
286 // we need a correlation structure
287 Real corrTimesRaw[] = {0.0, 50.0};
288 Real rhoInfDataRaw[] = {0.0, 0.0};
289 Real betaDataRaw[] = {0.0, 0.0};
290 std::vector<Real> corrTimes(corrTimesRaw, corrTimesRaw + 2);
291 std::vector<Real> rhoInfData(rhoInfDataRaw, rhoInfDataRaw + 2);
292 std::vector<Real> betaData(betaDataRaw, betaDataRaw + 2);
293 ext::shared_ptr<Interpolation> rho(
294 new LinearInterpolation(corrTimes.begin(), corrTimes.end(), rhoInfData.begin()));
295 ext::shared_ptr<Interpolation> beta(
296 new LinearInterpolation(corrTimes.begin(), corrTimes.end(), betaData.begin()));
297 ext::shared_ptr<TenorOptionletVTS::CorrelationStructure> corr(
298 new TenorOptionletVTS::TwoParameterCorrelation(rho, beta));
299 // now we can set up the new volTS and calculate volatilities
300 ext::shared_ptr<OptionletVolatilityStructure> optionletVTS6m(
301 new TenorOptionletVTS(optionletVTS3m, euribor3m, euribor6m, corr));
302 for (Size i = 0; i < capletTerms.size(); ++i) {
303 for (Real& capletStrike : capletStrikes) {
304 Real vol3m = optionletVTS3m->volatility(optionTenor: capletTerms[i], strike: capletStrike, extrapolate: true);
305 Real vol6m = optionletVTS6m->volatility(optionTenor: capletTerms[i], strike: capletStrike, extrapolate: true);
306 Real vol6mShifted =
307 optionletVTS6m->volatility(optionTenor: capletTerms[i], strike: capletStrike + spread, extrapolate: true);
308 // for perfect correlation shifted 6m vols should coincide with 3m vols
309 Real tol =
310 (i < 3) ? (0.001) :
311 (0.0001); // 10bp tol for smaller tenors and 1bp tol for larger tenors
312 if (fabs(x: vol6mShifted - vol3m) > tol)
313 BOOST_ERROR("Shifted 6m vol does not match 3m vol for perfect correlation at\n"
314 << "expiry term: " << capletTerms[i] << ", strike: " << capletStrike
315 << "\n"
316 << "vol3m: " << vol3m << ", vol6m: " << vol6m
317 << ", vol6mShifted: " << vol6mShifted << "\n");
318 }
319 }
320 }
321}
322
323void BasismodelsTest::testTenorswaptionvts() {
324 BOOST_TEST_MESSAGE("Testing volatility transformation for swaptions...");
325 // market data and floating rate index
326 Real spread = 0.01;
327 Handle<YieldTermStructure> discYTS = getYTS(terms, rates: discRates);
328 Handle<YieldTermStructure> proj3mYTS = getYTS(terms, rates: proj3mRates);
329 Handle<YieldTermStructure> proj6mYTS = getYTS(terms, rates: proj3mRates, spread);
330 ext::shared_ptr<IborIndex> euribor3m(new Euribor6M(proj3mYTS));
331 ext::shared_ptr<IborIndex> euribor6m(new Euribor6M(proj6mYTS));
332 // Euribor6m ATM vols
333 Handle<SwaptionVolatilityStructure> euribor6mSwVTS = getSwaptionVTS();
334 {
335 ext::shared_ptr<TenorSwaptionVTS> euribor3mSwVTS(
336 new TenorSwaptionVTS(euribor6mSwVTS, discYTS, euribor6m, euribor3m, Period(1, Years),
337 Period(1, Years), Thirty360(Thirty360::BondBasis), Thirty360(Thirty360::BondBasis)));
338 // 6m vols should be slightly larger then 3m vols due to basis
339 for (Size i = 0; i < swaptionVTSTerms.size(); ++i) {
340 for (Size j = 0; j < swaptionVTSTerms.size(); ++j) {
341 Real vol6m = euribor6mSwVTS->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
342 strike: 0.01, extrapolate: true);
343 Real vol3m = euribor3mSwVTS->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
344 strike: 0.01, extrapolate: true);
345 if (vol3m > vol6m)
346 BOOST_ERROR("Euribor 6m must be larger than 3m vol at\n"
347 << "expiry term: " << swaptionVTSTerms[i]
348 << ", swap term: " << swaptionVTSTerms[j] << "\n"
349 << "vol3m: " << vol3m << ", vol6m: " << vol6m << "\n");
350 }
351 }
352 }
353 {
354 ext::shared_ptr<TenorSwaptionVTS> euribor6mSwVTS2(
355 new TenorSwaptionVTS(euribor6mSwVTS, discYTS, euribor6m, euribor6m, Period(1, Years),
356 Period(1, Years), Thirty360(Thirty360::BondBasis), Thirty360(Thirty360::BondBasis)));
357 // 6m vols to 6m vols should yield initiial vols
358 for (Size i = 0; i < swaptionVTSTerms.size(); ++i) {
359 for (Size j = 0; j < swaptionVTSTerms.size(); ++j) {
360 Real vol6m = euribor6mSwVTS->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
361 strike: 0.01, extrapolate: true);
362 Real vol6m2 = euribor6mSwVTS2->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
363 strike: 0.01, extrapolate: true);
364 Real tol = 1.0e-8;
365 if (fabs(x: vol6m2 - vol6m) > tol)
366 BOOST_ERROR("Euribor 6m to 6m vols should not change at\n"
367 << "expiry term: " << swaptionVTSTerms[i]
368 << ", swap term: " << swaptionVTSTerms[j] << "\n"
369 << "vol6m: " << vol6m << ", vol6m2: " << vol6m2
370 << ", variance: " << (vol6m2 - vol6m) << "\n");
371 }
372 }
373 }
374 {
375 ext::shared_ptr<TenorSwaptionVTS> euribor3mSwVTS(
376 new TenorSwaptionVTS(euribor6mSwVTS, discYTS, euribor6m, euribor3m, Period(1, Years),
377 Period(1, Years), Thirty360(Thirty360::BondBasis), Thirty360(Thirty360::BondBasis)));
378 ext::shared_ptr<TenorSwaptionVTS> euribor6mSwVTS2(new TenorSwaptionVTS(
379 RelinkableHandle<SwaptionVolatilityStructure>(euribor3mSwVTS), discYTS, euribor3m,
380 euribor6m, Period(1, Years), Period(1, Years), Thirty360(Thirty360::BondBasis), Thirty360(Thirty360::BondBasis)));
381 // 6m vols to 6m vols should yield initiial vols
382 for (Size i = 0; i < swaptionVTSTerms.size(); ++i) {
383 for (Size j = 0; j < swaptionVTSTerms.size(); ++j) {
384 Real vol6m = euribor6mSwVTS->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
385 strike: 0.01, extrapolate: true);
386 Real vol6m2 = euribor6mSwVTS2->volatility(optionTenor: swaptionVTSTerms[i], swapTenor: swaptionVTSTerms[j],
387 strike: 0.01, extrapolate: true);
388 Real tol = 1.0e-8;
389 if (fabs(x: vol6m2 - vol6m) > tol)
390 BOOST_ERROR("Euribor 6m to 3m to 6m vols should not change at\n"
391 << "expiry term: " << swaptionVTSTerms[i]
392 << ", swap term: " << swaptionVTSTerms[j] << "\n"
393 << "vol6m: " << vol6m << ", vol6m2: " << vol6m2
394 << ", variance: " << (vol6m2 - vol6m) << "\n");
395 }
396 }
397 }
398}
399
400
401test_suite* BasismodelsTest::suite() {
402 auto* suite = BOOST_TEST_SUITE("Basismodels tests");
403 suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testSwaptioncfsContCompSpread));
404 suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testSwaptioncfsSimpleCompSpread));
405 suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testTenoroptionletvts));
406 suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testTenorswaptionvts));
407 return suite;
408}
409

source code of quantlib/test-suite/basismodels.cpp