19#include <boost/make_shared.hpp>
20#include <boost/test/unit_test.hpp>
30#include <oret/toplevelfixture.hpp>
31#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
32#include <ql/termstructures/yield/flatforward.hpp>
33#include <ql/time/calendars/target.hpp>
34#include <ql/time/daycounters/actualactual.hpp>
39using namespace boost::unit_test_framework;
50 asof_ = Date(3, Feb, 2016);
63 QuantLib::ext::shared_ptr<Conventions> conventions = QuantLib::ext::make_shared<Conventions>();
67 "EUR-6M-SWAP-CONVENTIONS",
"TARGET",
"Annual",
"MF",
"30/360",
"EUR-EURIBOR-6M"));
68 conventions->add(swapEURConv);
69 QuantLib::ext::shared_ptr<ore::data::Convention> swapIndexEURLongConv(
71 conventions->add(swapIndexEURLongConv);
73 InstrumentConventions::instance().setConventions(conventions);
79 Handle<YieldTermStructure> flatRateYts(Real forward) {
80 QuantLib::ext::shared_ptr<YieldTermStructure> yts(
new FlatForward(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
81 return Handle<YieldTermStructure>(yts);
83 Handle<SwaptionVolatilityStructure> flatRateSvs(Volatility forward) {
84 QuantLib::ext::shared_ptr<SwaptionVolatilityStructure> Svs(
85 new ConstantSwaptionVolatility(0, NullCalendar(), ModifiedFollowing, forward, ActualActual(ActualActual::ISDA)));
86 return Handle<SwaptionVolatilityStructure>(Svs);
110 vector<double> notionals;
111 vector<double> spread;
112 vector<string> spreadDates;
115 QuantLib::ext::shared_ptr<ore::data::Swap> makeSwap() {
120 LegData fixedLegData(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), !isPayer, ccy,
121 fixedSchedule, fixDC, notionals);
122 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread), isPayer, ccy,
123 cmsSchedule, fixDC, notionals);
126 QuantLib::ext::shared_ptr<ore::data::Swap> swap(
new ore::data::Swap(env, fixedLegData, cmsLegData));
130 QuantLib::ext::shared_ptr<ore::data::Swap> makeSwap(Real fixedRate_,
string fixtenor_) {
135 LegData fixedLegData(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate_)), !isPayer, ccy,
136 fixedSchedule, fixDC, notionals);
137 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread), isPayer, ccy,
138 cmsSchedule, fixDC, notionals);
141 QuantLib::ext::shared_ptr<ore::data::Swap> swap(
new ore::data::Swap(env, fixedLegData, cmsLegData));
145 QuantLib::ext::shared_ptr<ore::data::Swap> makeCmsLegSwap() {
148 vector<LegData> legData;
150 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread), isPayer, ccy,
151 cmsSchedule, fixDC, notionals);
152 legData.push_back(cmsLegData);
155 QuantLib::ext::shared_ptr<ore::data::Swap> swap(
new ore::data::Swap(env, legData));
159 QuantLib::ext::shared_ptr<ore::data::Swap> makeCappedCmsLegSwap(vector<double> caps,
160 vector<string> capDates = vector<string>()) {
163 vector<LegData> legData;
165 QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread, spreadDates, caps, capDates),
166 isPayer, ccy, cmsSchedule, fixDC, notionals);
167 legData.push_back(cmsLegData);
170 QuantLib::ext::shared_ptr<ore::data::Swap> swap(
new ore::data::Swap(env, legData));
174 QuantLib::ext::shared_ptr<ore::data::CapFloor> makeCap(vector<double> caps) {
177 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread, spreadDates), isPayer,
178 ccy, cmsSchedule, fixDC, notionals);
181 QuantLib::ext::shared_ptr<ore::data::CapFloor> capfloor(
186 QuantLib::ext::shared_ptr<ore::data::Swap> makeFlooredCmsLegSwap(vector<double> floors,
187 vector<string> floorDates = vector<string>()) {
190 vector<LegData> legData;
191 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread, spreadDates,
192 vector<double>(), vector<string>(), floors, floorDates),
193 isPayer, ccy, cmsSchedule, fixDC, notionals);
194 legData.push_back(cmsLegData);
197 QuantLib::ext::shared_ptr<ore::data::Swap> swap(
new ore::data::Swap(env, legData));
201 QuantLib::ext::shared_ptr<ore::data::CapFloor> makeFloor(vector<double> floors) {
204 LegData cmsLegData(QuantLib::ext::make_shared<CMSLegData>(index, fixingdays, isinarrears, spread, spreadDates), isPayer,
205 ccy, cmsSchedule, fixDC, notionals);
208 QuantLib::ext::shared_ptr<ore::data::CapFloor> capfloor(
213 CommonVars() : ccy(
"EUR"), isPayer(false), start(
"20160301"), end(
"20360301"), fixtenor(
"1Y"), cmstenor(
"6M") {
215 longShort = isPayer ?
"Short" :
"Long";
222 index =
"EUR-CMS-30Y";
226 notionals.push_back(10000000);
227 spread.push_back(0.0);
231void outputCoupons(QuantLib::ext::shared_ptr<ore::data::Swap> cmsSwap) {
232 Leg leg = cmsSwap->legs().at(1);
233 for (
auto cf : leg) {
234 QuantLib::ext::shared_ptr<FloatingRateCoupon> frc = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(cf);
235 BOOST_TEST_MESSAGE(
"Coupon Date: " << frc->date() <<
"; Rate: " << frc->rate()
236 <<
"; DayCount: " << frc->dayCounter());
241BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
243BOOST_AUTO_TEST_SUITE(CmsTests)
246 BOOST_TEST_MESSAGE(
"Testing CMS Analytic Hagan price...");
249 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
250 Settings::instance().evaluationDate() = market->asofDate();
252 QuantLib::ext::shared_ptr<ore::data::Swap> cmsSwap = vars.makeSwap();
255 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
256 engineData->model(
"CMS") =
"Hagan";
257 engineData->engine(
"CMS") =
"Analytic";
258 map<string, string> engineparams;
259 engineparams[
"YieldCurveModel"] =
"Standard";
260 engineparams[
"MeanReversion"] =
"0.0";
261 engineData->engineParameters(
"CMS") = engineparams;
263 engineData->model(
"Swap") =
"DiscountedCashflows";
264 engineData->engine(
"Swap") =
"DiscountingSwapEngineOptimised";
266 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
268 cmsSwap->build(engineFactory);
270 Real expectedNPV = 3440673.46;
271 Real npv = cmsSwap->instrument()->NPV();
273 BOOST_TEST_MESSAGE(
"Hagan Analytic price is " << npv);
274 outputCoupons(cmsSwap);
276 BOOST_CHECK_CLOSE(npv, expectedNPV, 1.0);
280 BOOST_TEST_MESSAGE(
"Testing CMS Numerical Hagan price...");
283 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
284 Settings::instance().evaluationDate() = market->asofDate();
287 QuantLib::ext::shared_ptr<ore::data::Swap> cmsSwap = vars.makeSwap();
290 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
291 engineData->model(
"CMS") =
"Hagan";
292 engineData->engine(
"CMS") =
"Numerical";
293 map<string, string> engineparams;
294 engineparams[
"YieldCurveModel"] =
"Standard";
295 engineparams[
"MeanReversion"] =
"0.0";
296 engineparams[
"UpperLimit"] =
"0.0";
297 engineparams[
"LowerLimit"] =
"1.0";
298 engineparams[
"Precision"] =
"0.000001";
299 engineData->engineParameters(
"CMS") = engineparams;
301 engineData->model(
"Swap") =
"DiscountedCashflows";
302 engineData->engine(
"Swap") =
"DiscountingSwapEngineOptimised";
304 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
306 cmsSwap->build(engineFactory);
308 Real expectedNPV = 3440673.46;
309 Real npv = cmsSwap->instrument()->NPV();
311 BOOST_TEST_MESSAGE(
"Hagan Numerical price is " << npv);
312 outputCoupons(cmsSwap);
314 BOOST_CHECK_CLOSE(npv, expectedNPV, 1.0);
318 BOOST_TEST_MESSAGE(
"Testing CMS Linear TSR price...");
321 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
322 Settings::instance().evaluationDate() = market->asofDate();
325 QuantLib::ext::shared_ptr<ore::data::Swap> cmsSwap = vars.makeSwap();
328 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
329 engineData->model(
"CMS") =
"LinearTSR";
330 engineData->engine(
"CMS") =
"LinearTSRPricer";
331 map<string, string> engineparams;
332 engineparams[
"MeanReversion"] =
"0.0";
333 engineparams[
"Policy"] =
"RateBound";
334 engineparams[
"LowerRateBoundLogNormal"] =
"0.0001";
335 engineparams[
"UpperRateBoundLogNormal"] =
"2.0000";
336 engineData->engineParameters(
"CMS") = engineparams;
338 engineData->model(
"Swap") =
"DiscountedCashflows";
339 engineData->engine(
"Swap") =
"DiscountingSwapEngineOptimised";
341 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
343 cmsSwap->build(engineFactory);
345 Real expectedNPV = 3440673.46;
346 Real npv = cmsSwap->instrument()->NPV();
348 BOOST_TEST_MESSAGE(
"Linear TSR price is " << npv);
349 outputCoupons(cmsSwap);
351 BOOST_CHECK_CLOSE(npv, expectedNPV, 1.0);
355 BOOST_TEST_MESSAGE(
"Testing CMS CapFloor price...");
358 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
359 Settings::instance().evaluationDate() = market->asofDate();
362 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
363 engineData->model(
"CMS") =
"Hagan";
364 engineData->engine(
"CMS") =
"Analytic";
365 map<string, string> engineparams;
366 engineparams[
"YieldCurveModel"] =
"Standard";
367 engineparams[
"MeanReversion"] =
"0.0";
368 engineData->engineParameters(
"CMS") = engineparams;
370 engineData->model(
"Swap") =
"DiscountedCashflows";
371 engineData->engine(
"Swap") =
"DiscountingSwapEngineOptimised";
373 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
376 "Comparing CMS Cap price to replication by a Single Legged CMS Swap and a Single Leg Capped CMS Swap...");
377 vector<double> capRate(1, 0.021);
378 QuantLib::ext::shared_ptr<ore::data::Swap> cmsLegSwap = vars.makeCmsLegSwap();
379 QuantLib::ext::shared_ptr<ore::data::Swap> cappedCmsLegSwap = vars.makeCappedCmsLegSwap(capRate);
380 QuantLib::ext::shared_ptr<ore::data::CapFloor> cap = vars.makeCap(capRate);
382 cmsLegSwap->build(engineFactory);
383 cappedCmsLegSwap->build(engineFactory);
384 cap->build(engineFactory);
386 Real cmsLegNpv = cmsLegSwap->instrument()->NPV();
387 Real cappedCmsLegNpv = cappedCmsLegSwap->instrument()->NPV();
388 Real capNpv = cap->instrument()->NPV();
390 Real capBySwaps = cmsLegNpv - cappedCmsLegNpv;
392 BOOST_TEST_MESSAGE(
"CMS Leg swap NPV is " << cmsLegNpv);
393 BOOST_TEST_MESSAGE(
"CMS Capped Leg swap NPV is " << cappedCmsLegNpv);
394 BOOST_TEST_MESSAGE(
"CMS Cap NPV is " << capNpv);
395 BOOST_TEST_MESSAGE(
"CMS Cap NPV from Swap replication is " << capBySwaps);
396 BOOST_CHECK_CLOSE(capNpv, capBySwaps, 1.0);
398 BOOST_TEST_MESSAGE(
"Checking CMS Cap with high Cap is zero...");
399 vector<double> capHigh(1, 1.0);
401 cap = vars.makeCap(capHigh);
402 cap->build(engineFactory);
403 capNpv = cap->instrument()->NPV();
404 BOOST_TEST_MESSAGE(
"CMS Cap (Cap of 100%) NPV is " << capNpv);
405 BOOST_CHECK_SMALL(capNpv, 0.01);
407 BOOST_TEST_MESSAGE(
"Checking CMS Cap with low Cap is equal to single leg swap...");
408 vector<double> capLow(1, -1.0);
410 cap = vars.makeCap(capLow);
411 cap->build(engineFactory);
412 capNpv = cap->instrument()->NPV();
413 BOOST_TEST_MESSAGE(
"CMS Cap (Cap of -100%) NPV is " << capNpv);
414 BOOST_CHECK_CLOSE(capNpv, cmsLegNpv, 1.0);
416 BOOST_TEST_MESSAGE(
"Checking CMS Floor with low Cap is equal to zero...");
417 vector<double> floorLow(1, -1.0);
419 QuantLib::ext::shared_ptr<ore::data::CapFloor> floor = vars.makeFloor(floorLow);
420 floor->build(engineFactory);
421 Real floorNpv = floor->instrument()->NPV();
422 BOOST_TEST_MESSAGE(
"CMS Floor (Floor of -100%) NPV is " << floorNpv);
423 BOOST_CHECK_SMALL(floorNpv, 0.01);
425 BOOST_TEST_MESSAGE(
"Checking CMS Cap + CMS Floor = Swap...");
426 vector<double> floorRate(1, 0.021);
428 cap = vars.makeCap(capRate);
429 floor = vars.makeFloor(floorRate);
430 QuantLib::ext::shared_ptr<ore::data::Swap> swap = vars.makeSwap(0.021,
"6M");
431 cap->build(engineFactory);
432 floor->build(engineFactory);
433 swap->build(engineFactory);
434 capNpv = cap->instrument()->NPV();
435 floorNpv = floor->instrument()->NPV();
436 Real swapNpv = swap->instrument()->NPV();
437 Real capFloorNpv = capNpv - floorNpv;
438 BOOST_TEST_MESSAGE(
"CMS Cap NPV is " << capNpv);
439 BOOST_TEST_MESSAGE(
"CMS Floor NPV is " << floorNpv);
440 BOOST_TEST_MESSAGE(
"CMS Cap + Floor NPV is " << capFloorNpv);
441 BOOST_TEST_MESSAGE(
"CMS Swap NPV is " << swapNpv);
442 BOOST_CHECK_CLOSE(capFloorNpv, swapNpv, 1.0);
445BOOST_AUTO_TEST_SUITE_END()
447BOOST_AUTO_TEST_SUITE_END()
Ibor cap, floor or collar trade data model and serialization.
Serializable CMS Leg Data.
Serializable cap, floor, collar.
Serializable object holding generic trade data, reporting dimensions.
Container for storing Interest Rate Swap conventions.
Serializable object holding leg data.
static const string defaultConfiguration
Default configuration label.
map< tuple< string, YieldCurveType, string >, Handle< YieldTermStructure > > yieldCurves_
map< pair< string, string >, Handle< IborIndex > > iborIndices_
map< pair< string, string >, Handle< QuantLib::SwaptionVolatilityStructure > > swaptionCurves_
void addSwapIndex(const string &swapindex, const string &discountIndex, const string &configuration=Market::defaultConfiguration) const
add a swap index to the market
Serializable schedule data.
Serializable object holding schedule Rules data.
Serializable Swap, Single and Cross Currency.
Container for storing Swap Index conventions.
builder that returns an engine to price capped floored ibor legs
A class to hold pricing engine parameters.
trade envelope data model and serialization
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Map text representations to QuantLib/QuantExt types.
leg data model and serialization
An implementation of the Market class that stores the required objects in maps.
trade schedule data model and serialization
Swap trade data model and serialization.
BOOST_AUTO_TEST_CASE(testCMSAnalyticHagan)