21#include <boost/assign/std/vector.hpp>
25#include <ql/cashflows/capflooredcoupon.hpp>
26#include <ql/cashflows/cmscoupon.hpp>
27#include <ql/cashflows/iborcoupon.hpp>
28#include <ql/cashflows/lineartsrpricer.hpp>
29#include <ql/currencies/america.hpp>
30#include <ql/currencies/europe.hpp>
31#include <ql/experimental/coupons/lognormalcmsspreadpricer.hpp>
32#include <ql/indexes/ibor/euribor.hpp>
33#include <ql/indexes/swap/euriborswap.hpp>
34#include <ql/math/array.hpp>
35#include <ql/math/comparison.hpp>
36#include <ql/quotes/simplequote.hpp>
37#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
38#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
39#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
40#include <ql/termstructures/yield/flatforward.hpp>
41#include <ql/time/calendars/target.hpp>
42#include <ql/time/daycounters/actual360.hpp>
46#include <boost/make_shared.hpp>
47#include <boost/test/unit_test.hpp>
48#include <boost/timer/timer.hpp>
52using namespace boost::unit_test_framework;
58 refDate = Date(23, February, 2018);
60 Settings::instance().evaluationDate() = refDate;
62 yts2 = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(refDate, 0.02, Actual365Fixed()));
64 ovtLn = Handle<OptionletVolatilityStructure>(QuantLib::ext::make_shared<ConstantOptionletVolatility>(
65 refDate, TARGET(), Following, 0.20, Actual365Fixed(), ShiftedLognormal, 0.0));
66 ovtSln = Handle<OptionletVolatilityStructure>(QuantLib::ext::make_shared<ConstantOptionletVolatility>(
67 refDate, TARGET(), Following, 0.10, Actual365Fixed(), ShiftedLognormal, 0.01));
68 ovtN = Handle<OptionletVolatilityStructure>(QuantLib::ext::make_shared<ConstantOptionletVolatility>(
69 refDate, TARGET(), Following, 0.0075, Actual365Fixed(), Normal, 0.0));
71 swLn = Handle<SwaptionVolatilityStructure>(QuantLib::ext::make_shared<ConstantSwaptionVolatility>(
72 refDate, TARGET(), Following, 0.20, Actual365Fixed(), ShiftedLognormal, 0.0));
73 swSln = Handle<SwaptionVolatilityStructure>(QuantLib::ext::make_shared<ConstantSwaptionVolatility>(
74 refDate, TARGET(), Following, 0.10, Actual365Fixed(), ShiftedLognormal, 0.01));
75 swN = Handle<SwaptionVolatilityStructure>(QuantLib::ext::make_shared<ConstantSwaptionVolatility>(
76 refDate, TARGET(), Following, 0.0075, Actual365Fixed(), Normal, 0.01));
78 fxVol = Handle<BlackVolTermStructure>(
79 QuantLib::ext::make_shared<BlackConstantVol>(refDate, TARGET(), 0.20, Actual365Fixed()));
81 blackPricerLn = QuantLib::ext::make_shared<BlackIborCouponPricer>(ovtLn);
82 blackPricerSln = QuantLib::ext::make_shared<BlackIborCouponPricer>(ovtSln);
83 blackPricerN = QuantLib::ext::make_shared<BlackIborCouponPricer>(ovtN);
85 reversion = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.01));
86 cmsPricerLn = QuantLib::ext::make_shared<LinearTsrPricer>(swLn, reversion, yts2);
87 cmsPricerSln = QuantLib::ext::make_shared<LinearTsrPricer>(swSln, reversion, yts2);
88 cmsPricerN = QuantLib::ext::make_shared<LinearTsrPricer>(swN, reversion, yts2);
90 correlation = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6));
91 correlationTS = Handle<QuantExt::CorrelationTermStructure>(QuantLib::ext::make_shared<FlatCorrelation>(
92 refDate, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6)), Actual365Fixed()));
93 cmsspPricerLn = QuantLib::ext::make_shared<LognormalCmsSpreadPricer>(cmsPricerLn, correlation, yts2, 32);
94 cmsspPricerSln = QuantLib::ext::make_shared<LognormalCmsSpreadPricer>(cmsPricerSln, correlation, yts2, 32);
95 cmsspPricerN = QuantLib::ext::make_shared<LognormalCmsSpreadPricer>(cmsPricerN, correlation, yts2, 32);
97 quantoCorrelation = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.8));
98 quantoCorrelationTS = Handle<QuantExt::CorrelationTermStructure>(QuantLib::ext::make_shared<FlatCorrelation>(
99 refDate, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.8)), Actual365Fixed()));
100 blackQuantoPricerLn =
101 QuantLib::ext::make_shared<QuantExt::BlackIborQuantoCouponPricer>(fxVol, quantoCorrelation, ovtLn);
102 blackQuantoPricerSln =
103 QuantLib::ext::make_shared<QuantExt::BlackIborQuantoCouponPricer>(fxVol, quantoCorrelation, ovtSln);
104 blackQuantoPricerN = QuantLib::ext::make_shared<QuantExt::BlackIborQuantoCouponPricer>(fxVol, quantoCorrelation, ovtN);
106 std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>> indCorrelationTS;
107 indCorrelationTS[std::make_pair(
"EuriborSwapIsdaFixA2Y 30/360 (Bond Basis)",
108 "EuriborSwapIsdaFixA10Y 30/360 (Bond Basis)")] = correlationTS;
109 indCorrelationTS[std::make_pair(
"Euribor6M Actual/360",
"FX")] = quantoCorrelationTS;
110 std::map<std::string, QuantLib::ext::shared_ptr<IborCouponPricer>> iborPricers;
111 std::map<std::string, QuantLib::ext::shared_ptr<CmsCouponPricer>> cmsPricers;
112 std::map<std::string, Handle<BlackVolTermStructure>> fxVols, emptyFxVols;
113 fxVols[
"EUR"] = fxVol;
114 std::string key = QuantLib::ext::make_shared<Euribor>(6 * Months)->name();
115 iborPricers[key] = blackPricerLn;
116 cmsPricers[key] = cmsPricerLn;
117 formulaPricerLn = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
118 "EUR", iborPricers, cmsPricers, emptyFxVols, indCorrelationTS, yts2, samples);
119 formulaPricerUSDLn = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
120 "USD", iborPricers, cmsPricers, fxVols, indCorrelationTS, yts2, samples);
121 iborPricers[key] = blackPricerSln;
122 cmsPricers[key] = cmsPricerSln;
123 formulaPricerSln = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
124 "EUR", iborPricers, cmsPricers, emptyFxVols, indCorrelationTS, yts2, samples);
125 formulaPricerUSDSln = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
126 "USD", iborPricers, cmsPricers, fxVols, indCorrelationTS, yts2, samples);
127 iborPricers[key] = blackPricerN;
128 cmsPricers[key] = cmsPricerN;
129 formulaPricerN = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
130 "EUR", iborPricers, cmsPricers, emptyFxVols, indCorrelationTS, yts2, samples);
131 formulaPricerUSDN = QuantLib::ext::make_shared<MCGaussianFormulaBasedCouponPricer>(
132 "USD", iborPricers, cmsPricers, fxVols, indCorrelationTS, yts2, samples);
135 SavedSettings backup;
138 Handle<YieldTermStructure> yts2;
139 Handle<OptionletVolatilityStructure> ovtLn, ovtSln, ovtN;
140 Handle<SwaptionVolatilityStructure> swLn, swSln, swN;
141 Handle<BlackVolTermStructure> fxVol;
142 Handle<Quote> reversion, correlation, quantoCorrelation;
143 Handle<QuantExt::CorrelationTermStructure> correlationTS, quantoCorrelationTS;
144 QuantLib::ext::shared_ptr<IborCouponPricer> blackPricerLn, blackPricerSln, blackPricerN;
145 QuantLib::ext::shared_ptr<IborCouponPricer> blackQuantoPricerLn, blackQuantoPricerSln, blackQuantoPricerN;
146 QuantLib::ext::shared_ptr<CmsCouponPricer> cmsPricerLn, cmsPricerSln, cmsPricerN;
147 QuantLib::ext::shared_ptr<CmsSpreadCouponPricer> cmsspPricerLn, cmsspPricerSln, cmsspPricerN;
148 QuantLib::ext::shared_ptr<FormulaBasedCouponPricer> formulaPricerLn, formulaPricerSln, formulaPricerN;
149 QuantLib::ext::shared_ptr<FormulaBasedCouponPricer> formulaPricerUSDLn, formulaPricerUSDSln, formulaPricerUSDN;
152void runTest(QuantLib::ext::shared_ptr<FloatingRateCoupon> cpn, QuantLib::ext::shared_ptr<FloatingRateCoupon> cpnRef,
153 const QuantLib::ext::shared_ptr<FloatingRateCouponPricer>& pricer,
154 const QuantLib::ext::shared_ptr<FloatingRateCouponPricer>& pricerRef,
const std::string& testLabel,
157 cpnRef->setPricer(pricerRef);
158 cpn->setPricer(pricer);
161 auto cfRef = QuantLib::ext::dynamic_pointer_cast<CappedFlooredCoupon>(cpnRef);
163 cfRef->underlying()->setPricer(pricerRef);
164 auto cf = QuantLib::ext::dynamic_pointer_cast<CappedFlooredCoupon>(cpn);
166 cf->underlying()->setPricer(pricer);
168 boost::timer::cpu_timer timer;
169 Real aRef = cpnRef->amount();
171 Real tRef = timer.elapsed().wall * 1e-6;
173 Real a = cpn->amount();
175 Real t = timer.elapsed().wall * 1e-6;
176 BOOST_TEST_MESSAGE(testLabel <<
": amount = " << a <<
" (" << t <<
" ms), reference amount = " << aRef <<
" ("
178 BOOST_CHECK_SMALL(std::abs(a - aRef), tol);
185BOOST_AUTO_TEST_SUITE(FormulaBasedCouponTest)
189 BOOST_TEST_MESSAGE(
"Testing formula based coupons against capped Libor coupon...");
193 auto euribor6m = QuantLib::ext::make_shared<Euribor>(6 * Months, d.yts2);
198 auto indexPlain = QuantLib::ext::make_shared<FormulaBasedIndex>(
199 "libor-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{euribor6m}, formulaPlain,
200 euribor6m->fixingCalendar());
201 auto indexCapped = QuantLib::ext::make_shared<FormulaBasedIndex>(
202 "libor-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{euribor6m}, formulaCapped,
203 euribor6m->fixingCalendar());
205 auto undRef = QuantLib::ext::make_shared<IborCoupon>(Date(23, February, 2029), 10000.0, Date(23, February, 2028),
206 Date(23, February, 2029), 2, euribor6m, 1.0, 0.0, Date(), Date(),
208 auto cappedRef = QuantLib::ext::make_shared<CappedFlooredCoupon>(undRef, 0.03);
210 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> und(
212 Date(23, February, 2029), 2, indexPlain, Date(), Date(), Actual360(),
false));
213 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> capped(
215 Date(23, February, 2029), 2, indexCapped, Date(), Date(), Actual360(),
false));
217 runTest(und, undRef, d.formulaPricerLn, d.blackPricerLn,
"Plain Ibor Coupon, Lognormal", 0.05);
218 runTest(und, undRef, d.formulaPricerSln, d.blackPricerSln,
"Plain Ibor Coupon, ShiftedLN", 0.05);
219 runTest(und, undRef, d.formulaPricerN, d.blackPricerN,
"Plain Ibor Coupon, Normal", 0.05);
221 runTest(capped, cappedRef, d.formulaPricerLn, d.blackPricerLn,
"Capped Ibor Coupon, Lognormal", 0.05);
222 runTest(capped, cappedRef, d.formulaPricerSln, d.blackPricerSln,
"Capped Ibor Coupon, ShiftedLN", 0.05);
223 runTest(capped, cappedRef, d.formulaPricerN, d.blackPricerN,
"Capped Ibor Coupon, Normal", 0.05);
228 BOOST_TEST_MESSAGE(
"Testing formula based coupons against capped CMS coupon...");
232 auto cms10y = QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(10 * Years, d.yts2, d.yts2);
238 QuantLib::ext::make_shared<FormulaBasedIndex>(
"cms-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{cms10y},
239 formulaPlain, cms10y->fixingCalendar());
241 QuantLib::ext::make_shared<FormulaBasedIndex>(
"cms-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{cms10y},
242 formulaCapped, cms10y->fixingCalendar());
244 auto undRef = QuantLib::ext::make_shared<CmsCoupon>(Date(23, February, 2029), 10000.0, Date(23, February, 2028),
245 Date(23, February, 2029), 2, cms10y, 1.0, 0.0, Date(), Date(),
247 auto cappedRef = QuantLib::ext::make_shared<CappedFlooredCoupon>(undRef, 0.03);
249 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> und(
251 Date(23, February, 2029), 2, indexPlain, Date(), Date(), Actual360(),
false));
252 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> capped(
254 Date(23, February, 2029), 2, indexCapped, Date(), Date(), Actual360(),
false));
256 runTest(und, undRef, d.formulaPricerLn, d.cmsPricerLn,
"Plain CMS Coupon, Lognormal", 0.05);
257 runTest(und, undRef, d.formulaPricerSln, d.cmsPricerSln,
"Plain CMS Coupon, ShiftedLN", 0.05);
258 runTest(und, undRef, d.formulaPricerN, d.cmsPricerN,
"Plain CMS Coupon, Normal", 0.05);
263 runTest(capped, cappedRef, d.formulaPricerLn, d.cmsPricerLn,
"Capped CMS Coupon, Lognormal", 2.0);
264 runTest(capped, cappedRef, d.formulaPricerSln, d.cmsPricerSln,
"Capped CMS Coupon, ShiftedLN", 2.0);
265 runTest(capped, cappedRef, d.formulaPricerN, d.cmsPricerN,
"Capped CMS Coupon, Normal", 2.0);
270 BOOST_TEST_MESSAGE(
"Testing formula based coupons against capped CMS spread coupon...");
274 auto cms2y = QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(2 * Years, d.yts2, d.yts2);
275 auto cms10y = QuantLib::ext::make_shared<EuriborSwapIsdaFixA>(10 * Years, d.yts2, d.yts2);
276 auto cms10y2y = QuantLib::ext::make_shared<SwapSpreadIndex>(
"cms10y2y", cms10y, cms2y);
282 auto indexPlain = QuantLib::ext::make_shared<FormulaBasedIndex>(
283 "cmssp-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{cms2y, cms10y}, formulaPlain,
284 cms10y2y->fixingCalendar());
285 auto indexCapped = QuantLib::ext::make_shared<FormulaBasedIndex>(
286 "cmssp-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{cms2y, cms10y}, formulaCapped,
287 cms10y2y->fixingCalendar());
289 auto undRef = QuantLib::ext::make_shared<CmsSpreadCoupon>(Date(23, February, 2029), 10000.0, Date(23, February, 2028),
290 Date(23, February, 2029), 2, cms10y2y, 1.0, 0.0, Date(), Date(),
292 auto cappedRef = QuantLib::ext::make_shared<CappedFlooredCoupon>(undRef, 0.03);
294 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> und(
296 Date(23, February, 2029), 2, indexPlain, Date(), Date(), Actual360(),
false));
297 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> capped(
299 Date(23, February, 2029), 2, indexCapped, Date(), Date(), Actual360(),
false));
301 runTest(und, undRef, d.formulaPricerLn, d.cmsspPricerLn,
"Plain CmsSp Coupon, Lognormal", 0.05);
302 runTest(und, undRef, d.formulaPricerSln, d.cmsspPricerSln,
"Plain CmsSp Coupon, ShiftedLN", 0.05);
303 runTest(und, undRef, d.formulaPricerN, d.cmsspPricerN,
"Plain CmsSp Coupon, Normal", 0.05);
305 runTest(capped, cappedRef, d.formulaPricerLn, d.cmsspPricerLn,
"Capped CmsSp Coupon, Lognormal", 0.05);
306 runTest(capped, cappedRef, d.formulaPricerSln, d.cmsspPricerSln,
"Capped CmsSp Coupon, ShiftedLN", 0.05);
307 runTest(capped, cappedRef, d.formulaPricerN, d.cmsspPricerN,
"Capped CmsSp Coupon, Normal", 0.05);
312 BOOST_TEST_MESSAGE(
"Testing formula based coupons against (capped) Quanto Libor coupon...");
316 auto euribor6m = QuantLib::ext::make_shared<Euribor>(6 * Months, d.yts2);
321 auto indexPlain = QuantLib::ext::make_shared<FormulaBasedIndex>(
322 "libor-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{euribor6m}, formulaPlain,
323 euribor6m->fixingCalendar());
324 auto indexCapped = QuantLib::ext::make_shared<FormulaBasedIndex>(
325 "libor-family", std::vector<QuantLib::ext::shared_ptr<InterestRateIndex>>{euribor6m}, formulaCapped,
326 euribor6m->fixingCalendar());
328 auto undRef = QuantLib::ext::make_shared<IborCoupon>(Date(23, February, 2029), 10000.0, Date(23, February, 2028),
329 Date(23, February, 2029), 2, euribor6m, 1.0, 0.0, Date(), Date(),
331 auto cappedRef = QuantLib::ext::make_shared<CappedFlooredCoupon>(undRef, 0.03);
333 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> und(
335 Date(23, February, 2029), 2, indexPlain, Date(), Date(), Actual360(),
false));
336 QuantLib::ext::shared_ptr<QuantExt::FormulaBasedCoupon> capped(
338 Date(23, February, 2029), 2, indexCapped, Date(), Date(), Actual360(),
false));
340 runTest(und, undRef, d.formulaPricerUSDLn, d.blackQuantoPricerLn,
"Plain Quanto Ibor Coupon, Lognormal", 0.05);
341 runTest(und, undRef, d.formulaPricerUSDSln, d.blackQuantoPricerSln,
"Plain Quanto Ibor Coupon, ShiftedLN", 0.05);
342 runTest(und, undRef, d.formulaPricerUSDN, d.blackQuantoPricerN,
"Plain Quanto Ibor Coupon, Normal", 0.05);
344 runTest(capped, cappedRef, d.formulaPricerUSDLn, d.blackQuantoPricerLn,
"Capped Quanto Ibor Coupon, Lognormal",
346 runTest(capped, cappedRef, d.formulaPricerUSDSln, d.blackQuantoPricerSln,
"Capped Quanto Ibor Coupon, ShiftedLN",
348 runTest(capped, cappedRef, d.formulaPricerUSDN, d.blackQuantoPricerN,
"Capped Quanto Ibor Coupon, Normal", 0.05);
351BOOST_AUTO_TEST_SUITE_END()
353BOOST_AUTO_TEST_SUITE_END()
Term structure of flat correlations.
CompiledFormula min(CompiledFormula x, const CompiledFormula &y)
Fixture that can be used at top level.