1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /*!
4 Copyright (C) 2008 Allen Kuo
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 /* This example sets up a callable fixed rate bond with a Hull White pricing
21 engine and compares to Bloomberg's Hull White price/yield calculations.
22 */
23
24 #include <ql/qldefines.hpp>
25 #if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)
26 # include <ql/auto_link.hpp>
27 #endif
28 #include <ql/experimental/callablebonds/callablebond.hpp>
29 #include <ql/experimental/callablebonds/treecallablebondengine.hpp>
30 #include <ql/models/shortrate/onefactormodels/hullwhite.hpp>
31 #include <ql/termstructures/yield/flatforward.hpp>
32 #include <ql/time/calendars/unitedstates.hpp>
33 #include <ql/time/daycounters/actualactual.hpp>
34
35 #include <vector>
36 #include <cmath>
37 #include <iomanip>
38 #include <iostream>
39
40 using namespace std;
41 using namespace QuantLib;
42
43 ext::shared_ptr<YieldTermStructure>
44 flatRate(const Date& today,
45 const ext::shared_ptr<Quote>& forward,
46 const DayCounter& dc,
47 const Compounding& compounding,
48 const Frequency& frequency) {
49 return ext::make_shared<FlatForward>(today,
50 Handle<Quote>(forward),
51 dc,
52 compounding,
53 frequency);
54 }
55
56
57 ext::shared_ptr<YieldTermStructure>
58 flatRate(const Date& today,
59 Rate forward,
60 const DayCounter& dc,
61 const Compounding &compounding,
62 const Frequency &frequency) {
63 return flatRate(today,
64 ext::make_shared<SimpleQuote>(forward),
65 dc,
66 compounding,
67 frequency);
68 }
69
70
71 int main(int, char* [])
72 {
73 try {
74
75
76 Date today = Date(16,October,2007);
77 Settings::instance().evaluationDate() = today;
78
79 cout << endl;
80 cout << "Pricing a callable fixed rate bond using" << endl;
81 cout << "Hull White model w/ reversion parameter = 0.03" << endl;
82 cout << "BAC4.65 09/15/12 ISIN: US06060WBJ36" << endl;
83 cout << "roughly five year tenor, ";
84 cout << "quarterly coupon and call dates" << endl;
85 cout << "reference date is : " << today << endl << endl;
86
87 /* Bloomberg OAS1: "N" model (Hull White)
88 varying volatility parameter
89
90 The curve entered into Bloomberg OAS1 is a flat curve,
91 at constant yield = 5.5%, semiannual compounding.
92 Assume here OAS1 curve uses an ACT/ACT day counter,
93 as documented in PFC1 as a "default" in the latter case.
94 */
95
96 // set up a flat curve corresponding to Bloomberg flat curve
97
98 Rate bbCurveRate = 0.055;
99 DayCounter bbDayCounter = ActualActual(ActualActual::Bond);
100 InterestRate bbIR(bbCurveRate,bbDayCounter,Compounded,Semiannual);
101
102 Handle<YieldTermStructure> termStructure(flatRate(today,
103 bbIR.rate(),
104 bbIR.dayCounter(),
105 bbIR.compounding(),
106 bbIR.frequency()));
107
108 // set up the call schedule
109
110 CallabilitySchedule callSchedule;
111 Real callPrice = 100.;
112 Size numberOfCallDates = 24;
113 Date callDate = Date(15,September,2006);
114
115 for (Size i=0; i< numberOfCallDates; i++) {
116 Calendar nullCalendar = NullCalendar();
117
118 Bond::Price myPrice(callPrice, Bond::Price::Clean);
119 callSchedule.push_back(
120 ext::make_shared<Callability>(
121 myPrice,
122 Callability::Call,
123 callDate ));
124 callDate = nullCalendar.advance(callDate, 3, Months);
125 }
126
127
128 // set up the callable bond
129
130 Date dated = Date(16,September,2004);
131 Date issue = dated;
132 Date maturity = Date(15,September,2012);
133 Natural settlementDays = 3; // Bloomberg OAS1 settle is Oct 19, 2007
134 Calendar bondCalendar = UnitedStates(UnitedStates::GovernmentBond);
135 Real coupon = .0465;
136 Frequency frequency = Quarterly;
137 Real redemption = 100.0;
138 Real faceAmount = 100.0;
139
140 /* The 30/360 day counter Bloomberg uses for this bond cannot
141 reproduce the US Bond/ISMA (constant) cashflows used in PFC1.
142 Therefore use ActAct(Bond)
143 */
144 DayCounter bondDayCounter = ActualActual(ActualActual::Bond);
145
146 // PFC1 shows no indication dates are being adjusted
147 // for weekends/holidays for vanilla bonds
148 BusinessDayConvention accrualConvention = Unadjusted;
149 BusinessDayConvention paymentConvention = Unadjusted;
150
151 Schedule sch(dated, maturity, Period(frequency), bondCalendar,
152 accrualConvention, accrualConvention,
153 DateGeneration::Backward, false);
154
155 Size maxIterations = 1000;
156 Real accuracy = 1e-8;
157 Integer gridIntervals = 40;
158 Real reversionParameter = .03;
159
160 // output price/yield results for varying volatility parameter
161
162 Real sigma = QL_EPSILON; // core dumps if zero on Cygwin
163
164 auto hw0 = ext::make_shared<HullWhite>(termStructure,reversionParameter,sigma);
165
166 auto engine0 = ext::make_shared<TreeCallableFixedRateBondEngine>(hw0,gridIntervals);
167
168 CallableFixedRateBond callableBond(settlementDays, faceAmount, sch,
169 vector<Rate>(1, coupon),
170 bondDayCounter, paymentConvention,
171 redemption, issue, callSchedule);
172 callableBond.setPricingEngine(engine0);
173
174 cout << setprecision(2)
175 << showpoint
176 << fixed
177 << "sigma/vol (%) = "
178 << 100.*sigma
179 << endl;
180
181 cout << "QuantLib price/yld (%) ";
182 cout << callableBond.cleanPrice() << " / "
183 << 100. * callableBond.yield(bondDayCounter,
184 Compounded,
185 frequency,
186 accuracy,
187 maxIterations)
188 << endl;
189
190 cout << "Bloomberg price/yld (%) ";
191 cout << "96.50 / 5.47"
192 << endl
193 << endl;
194
195 sigma = .01;
196
197 cout << "sigma/vol (%) = " << 100.*sigma << endl;
198
199 auto hw1 = ext::make_shared<HullWhite>(termStructure,reversionParameter,sigma);
200
201 auto engine1 = ext::make_shared<TreeCallableFixedRateBondEngine>(hw1,gridIntervals);
202
203 callableBond.setPricingEngine(engine1);
204
205 cout << "QuantLib price/yld (%) ";
206 cout << callableBond.cleanPrice() << " / "
207 << 100.* callableBond.yield(bondDayCounter,
208 Compounded,
209 frequency,
210 accuracy,
211 maxIterations)
212 << endl;
213
214 cout << "Bloomberg price/yld (%) ";
215 cout << "95.68 / 5.66"
216 << endl
217 << endl;
218
219 ////////////////////
220
221 sigma = .03;
222
223 auto hw2 = ext::make_shared<HullWhite>(termStructure, reversionParameter, sigma);
224
225 auto engine2 = ext::make_shared<TreeCallableFixedRateBondEngine>(hw2,gridIntervals);
226
227 callableBond.setPricingEngine(engine2);
228
229 cout << "sigma/vol (%) = "
230 << 100.*sigma
231 << endl;
232
233 cout << "QuantLib price/yld (%) ";
234 cout << callableBond.cleanPrice() << " / "
235 << 100. * callableBond.yield(bondDayCounter,
236 Compounded,
237 frequency,
238 accuracy,
239 maxIterations)
240 << endl;
241
242 cout << "Bloomberg price/yld (%) ";
243 cout << "92.34 / 6.49"
244 << endl
245 << endl;
246
247 ////////////////////////////
248
249 sigma = .06;
250
251 auto hw3 = ext::make_shared<HullWhite>(termStructure, reversionParameter, sigma);
252
253 auto engine3 = ext::make_shared<TreeCallableFixedRateBondEngine>(hw3,gridIntervals);
254
255 callableBond.setPricingEngine(engine3);
256
257 cout << "sigma/vol (%) = "
258 << 100.*sigma
259 << endl;
260
261 cout << "QuantLib price/yld (%) ";
262 cout << callableBond.cleanPrice() << " / "
263 << 100. * callableBond.yield(bondDayCounter,
264 Compounded,
265 frequency,
266 accuracy,
267 maxIterations)
268 << endl;
269
270 cout << "Bloomberg price/yld (%) ";
271 cout << "87.16 / 7.83"
272 << endl
273 << endl;
274
275 /////////////////////////
276
277 sigma = .12;
278
279 auto hw4 = ext::make_shared<HullWhite>(termStructure, reversionParameter, sigma);
280
281 auto engine4 = ext::make_shared<TreeCallableFixedRateBondEngine>(hw4,gridIntervals);
282
283 callableBond.setPricingEngine(engine4);
284
285 cout << "sigma/vol (%) = "
286 << 100.*sigma
287 << endl;
288
289 cout << "QuantLib price/yld (%) ";
290 cout << callableBond.cleanPrice() << " / "
291 << 100.* callableBond.yield(bondDayCounter,
292 Compounded,
293 frequency,
294 accuracy,
295 maxIterations)
296 << endl;
297
298 cout << "Bloomberg price/yld (%) ";
299 cout << "77.31 / 10.65"
300 << endl
301 << endl;
302
303 return 0;
304
305 } catch (std::exception& e) {
306 std::cerr << e.what() << std::endl;
307 return 1;
308 } catch (...) {
309 std::cerr << "unknown error" << std::endl;
310 return 1;
311 }
312 }