Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bondoption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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#include "toplevelfixture.hpp"
20#include <boost/test/unit_test.hpp>
22#include <ql/cashflows/cashflows.hpp>
23#include <ql/cashflows/fixedratecoupon.hpp>
24#include <ql/instruments/bonds/fixedratebond.hpp>
25#include <ql/quotes/simplequote.hpp>
26#include <ql/termstructures/credit/flathazardrate.hpp>
27#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
28#include <ql/termstructures/yield/flatforward.hpp>
29#include <ql/time/calendars/weekendsonly.hpp>
30#include <ql/time/schedule.hpp>
32
33#include <boost/make_shared.hpp>
34#include <iomanip>
35#include <iostream>
36
37using namespace QuantLib;
38using namespace QuantExt;
39
40using boost::unit_test_framework::test_suite;
41
42BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
43
44BOOST_AUTO_TEST_SUITE(BondOptionTest)
45
46BOOST_AUTO_TEST_CASE(testBondOption) {
47
48 BOOST_TEST_MESSAGE("Testing QuantExt Bond Option pricing.");
49
50 SavedSettings backup;
51
52 Settings::instance().evaluationDate() = Date(5, Feb, 2016);
53 Date issueDate = Date(3, Nov, 2015);
54 Date today = Settings::instance().evaluationDate();
55 Settings::instance().includeReferenceDateEvents() = true;
56
57 // bond market data
58 Handle<Quote> rateQuote(QuantLib::ext::make_shared<SimpleQuote>(0.1));
59 Handle<Quote> issuerSpreadQuote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
60 DayCounter dc = Actual365Fixed();
61 Handle<YieldTermStructure> yts(QuantLib::ext::make_shared<FlatForward>(today, rateQuote, dc, Compounded, Semiannual));
62 Handle<DefaultProbabilityTermStructure> dpts(QuantLib::ext::make_shared<FlatHazardRate>(today, issuerSpreadQuote, dc));
63 Handle<Quote> bondSpecificSpread(QuantLib::ext::make_shared<SimpleQuote>(0.0));
64
65 // build the underlying fixed rate bond
66 Date startDate = today;
67 Date endDate = startDate + Period(2, Years);
68 Period tenor = 6 * Months;
69 Calendar calendar = WeekendsOnly();
70 BusinessDayConvention bdc = Following;
71 BusinessDayConvention bdcEnd = bdc;
72 DateGeneration::Rule rule = DateGeneration::Forward;
73 bool endOfMonth = false;
74 Date firstDate, lastDate;
75 Schedule schedule(startDate, endDate, tenor, calendar, bdc, bdcEnd, rule, endOfMonth, firstDate, lastDate);
76
77 Real redemption = 100.0;
78 Real notional = 1000;
79 Real couponRate = 0.1;
80
81 // bond option market data
82 // discount curve
83 Handle<Quote> discountQuote(QuantLib::ext::make_shared<SimpleQuote>(0.1));
84 Handle<YieldTermStructure> discountTS(
85 QuantLib::ext::make_shared<FlatForward>(today, discountQuote, dc, Compounded, Semiannual));
86
87 // lognormal yield vola
88 QuantLib::ext::shared_ptr<QuantLib::SwaptionVolatilityStructure> svs(
89 new QuantLib::ConstantSwaptionVolatility(Settings::instance().evaluationDate(), NullCalendar(),
90 ModifiedFollowing, 0.5, Actual365Fixed(), ShiftedLognormal, 0.0));
91
92 // pricing engine with lognormal yield vola
93 QuantLib::ext::shared_ptr<PricingEngine> bondOptionEngine(new QuantExt::BlackBondOptionEngine(
94 discountTS, Handle<QuantLib::SwaptionVolatilityStructure>(svs), discountTS));
95
96 // build bond option
97 Real strikePrice = notional;
98 Real faceAmount = notional;
99 Natural settlementDays = 2;
100 std::vector<Rate> rates = std::vector<Rate>(1, couponRate);
101 QuantLib::Bond::Price callabilityPrice = QuantLib::Bond::Price(strikePrice, QuantLib::Bond::Price::Dirty);
102 Callability::Type callabilityType = Callability::Call;
103 Date exerciseDate = Date(5, Dec, 2016);
104 QuantLib::ext::shared_ptr<Callability> callability(new Callability(callabilityPrice, callabilityType, exerciseDate));
105 CallabilitySchedule callabilitySchedule = std::vector<QuantLib::ext::shared_ptr<Callability>>(1, callability);
106
107 QuantLib::ext::shared_ptr<QuantLib::Bond> bond(
108 new QuantLib::FixedRateBond(settlementDays, faceAmount, schedule, rates, dc, bdc, redemption, issueDate));
109 QuantLib::ext::shared_ptr<QuantExt::BondOption> bondOption(new QuantExt::BondOption(bond, callabilitySchedule));
110 bondOption->setPricingEngine(bondOptionEngine);
111
112 // build tief OTM bond option
113 Real otmStrikePrice = notional * 2;
114 QuantLib::Bond::Price otmCallabilityPrice = QuantLib::Bond::Price(otmStrikePrice, QuantLib::Bond::Price::Dirty);
115 QuantLib::ext::shared_ptr<Callability> otmCallability(new Callability(otmCallabilityPrice, callabilityType, exerciseDate));
116 CallabilitySchedule otmCallabilitySchedule = std::vector<QuantLib::ext::shared_ptr<Callability>>(1, otmCallability);
117
118 QuantLib::ext::shared_ptr<QuantExt::BondOption> otmBondOption(new QuantExt::BondOption(bond, otmCallabilitySchedule));
119 otmBondOption->setPricingEngine(bondOptionEngine);
120
121 // build tief ITM bond option
122 Real itmStrikePrice = notional / 2;
123 QuantLib::Bond::Price itmCallabilityPrice = QuantLib::Bond::Price(itmStrikePrice, QuantLib::Bond::Price::Dirty);
124 QuantLib::ext::shared_ptr<Callability> itmCallability(new Callability(itmCallabilityPrice, callabilityType, exerciseDate));
125 CallabilitySchedule itmCallabilitySchedule = std::vector<QuantLib::ext::shared_ptr<Callability>>(1, itmCallability);
126
127 QuantLib::ext::shared_ptr<QuantExt::BondOption> itmBondOption(new QuantExt::BondOption(bond, itmCallabilitySchedule));
128 itmBondOption->setPricingEngine(bondOptionEngine);
129
130 // build zero bond option
131 QuantLib::ext::shared_ptr<QuantLib::Bond> zerobond(new QuantLib::FixedRateBond(
132 settlementDays, faceAmount,
133 Schedule(startDate, endDate, Period(Once), calendar, bdc, bdcEnd, DateGeneration::Backward, false),
134 std::vector<Rate>(1, 0.0), dc, bdc, redemption, issueDate));
135 QuantLib::ext::shared_ptr<QuantExt::BondOption> zeroBondOption(new QuantExt::BondOption(zerobond, callabilitySchedule));
136 zeroBondOption->setPricingEngine(bondOptionEngine);
137
138 BOOST_TEST_MESSAGE("normal bond option price = " << bondOption->NPV());
139 BOOST_CHECK_CLOSE(bondOption->NPV(), 36.807084355035521, 0.000001);
140
141 BOOST_TEST_MESSAGE("tief otm bond option price = " << otmBondOption->NPV());
142 BOOST_CHECK_CLOSE(otmBondOption->NPV(), 3.2657301416105546e-45, 0.000001);
143
144 BOOST_TEST_MESSAGE("tief itm bond option price = " << itmBondOption->NPV());
145 BOOST_CHECK_CLOSE(itmBondOption->NPV(), 491.52718033161705, 0.000001);
146
147 BOOST_TEST_MESSAGE("zero bond option price = " << zeroBondOption->NPV());
148 BOOST_CHECK_CLOSE(zeroBondOption->NPV(), 0.15813277744399326, 0.000001);
149
150 // test put-call parity
151 Real putCallStrikePrice = 1000;
152 QuantLib::Bond::Price putCallCallabilityPrice =
153 QuantLib::Bond::Price(putCallStrikePrice, QuantLib::Bond::Price::Dirty);
154
155 QuantLib::ext::shared_ptr<Callability> callCallability(
156 new Callability(putCallCallabilityPrice, Callability::Call, exerciseDate));
157 CallabilitySchedule callCallabilitySchedule = std::vector<QuantLib::ext::shared_ptr<Callability>>(1, callCallability);
158 QuantLib::ext::shared_ptr<QuantExt::BondOption> bondCallOption(new QuantExt::BondOption(bond, callCallabilitySchedule));
159 bondCallOption->setPricingEngine(bondOptionEngine);
160
161 QuantLib::ext::shared_ptr<Callability> putCallability(
162 new Callability(putCallCallabilityPrice, Callability::Put, exerciseDate));
163 CallabilitySchedule putCallabilitySchedule = std::vector<QuantLib::ext::shared_ptr<Callability>>(1, putCallability);
164 QuantLib::ext::shared_ptr<QuantExt::BondOption> bondPutOption(new QuantExt::BondOption(bond, putCallabilitySchedule));
165 bondPutOption->setPricingEngine(bondOptionEngine);
166
167 Real discount = discountTS->discount(exerciseDate);
168 // forward bond price, read from the pricing engine
169 Real fwdbond = bondCallOption->result<Real>("FwdCashPrice");
170 BOOST_TEST_MESSAGE("bond forward price = " << fwdbond);
171
172 BOOST_TEST_MESSAGE("bond call option price = " << bondCallOption->NPV());
173 BOOST_TEST_MESSAGE("bond put option price = " << bondPutOption->NPV());
174
175 Real right = bondCallOption->NPV() + putCallStrikePrice * discount;
176 Real left = bondPutOption->NPV() + fwdbond * discount;
177
178 BOOST_TEST_MESSAGE("right side of put-call parity (call + K*df) = " << right);
179 BOOST_TEST_MESSAGE("left side of put-call parity (put + Fwd*df) = " << left);
180 BOOST_CHECK_CLOSE(right, left, 0.000001);
181
182 // test put-call parity with normal yield vola
183 // normal yield vola
184 QuantLib::ext::shared_ptr<QuantLib::SwaptionVolatilityStructure> svs_normal(new QuantLib::ConstantSwaptionVolatility(
185 Settings::instance().evaluationDate(), NullCalendar(), ModifiedFollowing, 0.5, Actual365Fixed(), Normal));
186
187 // pricing engine with lognormal yield vola
188 QuantLib::ext::shared_ptr<PricingEngine> bondOptionEngineNormal(new QuantExt::BlackBondOptionEngine(
189 discountTS, Handle<QuantLib::SwaptionVolatilityStructure>(svs_normal), discountTS));
190
191 bondCallOption->setPricingEngine(bondOptionEngineNormal);
192 bondPutOption->setPricingEngine(bondOptionEngineNormal);
193
194 BOOST_TEST_MESSAGE("bond call option price = " << bondCallOption->NPV());
195 BOOST_TEST_MESSAGE("bond put option price = " << bondPutOption->NPV());
196
197 right = bondCallOption->NPV() + putCallStrikePrice * discount;
198 left = bondPutOption->NPV() + fwdbond * discount;
199
200 BOOST_TEST_MESSAGE("right side of put-call parity (call + K*df) = " << right);
201 BOOST_TEST_MESSAGE("left side of put-call parity (put + Fwd*df) = " << left);
202 BOOST_CHECK_CLOSE(right, left, 0.000001);
203}
204
205BOOST_AUTO_TEST_SUITE_END()
206BOOST_AUTO_TEST_SUITE_END()
Black bond option engine.
Black-formula bond option engine.
Bond option class.
Definition: bondoption.hpp:34
BOOST_AUTO_TEST_CASE(testBondOption)
Definition: bondoption.cpp:46
Fixture that can be used at top level.