46 {
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
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
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
82
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
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
94 discountTS, Handle<QuantLib::SwaptionVolatilityStructure>(svs), discountTS));
95
96
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
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
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
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
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
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
183
184 QuantLib::ext::shared_ptr<QuantLib::SwaptionVolatilityStructure> svs_normal(new QuantLib::ConstantSwaptionVolatility(
185 Settings::instance().evaluationDate(), NullCalendar(), ModifiedFollowing, 0.5, Actual365Fixed(), Normal));
186
187
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}
Black-formula bond option engine.