1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /*!
4 Copyright (C) 2006, 2007 StatPro Italia srl
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 showcases the CompositeInstrument class. Such class
21 is used to build a static replication of a down-and-out barrier
22 option, as outlined in Section 10.2 of Mark Joshi's "The Concepts
23 and Practice of Mathematical Finance" to which we refer the
24 reader.
25 */
26
27 #include <ql/qldefines.hpp>
28 #if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)
29 # include <ql/auto_link.hpp>
30 #endif
31 #include <ql/instruments/compositeinstrument.hpp>
32 #include <ql/instruments/barrieroption.hpp>
33 #include <ql/instruments/europeanoption.hpp>
34 #include <ql/pricingengines/barrier/analyticbarrierengine.hpp>
35 #include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
36 #include <ql/exercise.hpp>
37 #include <ql/termstructures/yield/flatforward.hpp>
38 #include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
39 #include <ql/quotes/simplequote.hpp>
40 #include <ql/time/calendars/nullcalendar.hpp>
41
42 #include <iostream>
43 #include <iomanip>
44
45 using namespace QuantLib;
46
47 int main(int, char* []) {
48
49 try {
50
51 std::cout << std::endl;
52
53 Date today(29, May, 2006);
54 Settings::instance().evaluationDate() = today;
55
56 // the option to replicate
57 Barrier::Type barrierType = Barrier::DownOut;
58 Real barrier = 70.0;
59 Real rebate = 0.0;
60 Option::Type type = Option::Put;
61 Real underlyingValue = 100.0;
62 auto underlying = ext::make_shared<SimpleQuote>(underlyingValue);
63 Real strike = 100.0;
64 auto riskFreeRate = ext::make_shared<SimpleQuote>(0.04);
65 auto volatility = ext::make_shared<SimpleQuote>(0.20);
66 Date maturity = today + 1*Years;
67
68 std::cout << std::endl ;
69
70 // write column headings
71 Size widths[] = { 45, 15, 15 };
72 Size totalWidth = widths[0]+widths[1]+widths[2];
73 std::string rule(totalWidth, '-'), dblrule(totalWidth, '=');
74
75 std::cout << dblrule << std::endl;
76 std::cout << "Initial market conditions" << std::endl;
77 std::cout << dblrule << std::endl;
78 std::cout << std::setw(widths[0]) << std::left << "Option"
79 << std::setw(widths[1]) << std::left << "NPV"
80 << std::setw(widths[2]) << std::left << "Error"
81 << std::endl;
82 std::cout << rule << std::endl;
83
84 // bootstrap the yield/vol curves
85 DayCounter dayCounter = Actual365Fixed();
86 Handle<Quote> h1(riskFreeRate);
87 Handle<Quote> h2(volatility);
88 Handle<YieldTermStructure> flatRate(
89 ext::make_shared<FlatForward>(0, NullCalendar(), h1, dayCounter));
90 Handle<BlackVolTermStructure> flatVol(
91 ext::make_shared<BlackConstantVol>(0, NullCalendar(), h2, dayCounter));
92
93 // instantiate the option
94 auto exercise = ext::make_shared<EuropeanExercise>(maturity);
95 auto payoff = ext::make_shared<PlainVanillaPayoff>(type, strike);
96
97 auto bsProcess = ext::make_shared<BlackScholesProcess>(
98 Handle<Quote>(underlying), flatRate, flatVol);
99
100 auto barrierEngine = ext::make_shared<AnalyticBarrierEngine>(bsProcess);
101 auto europeanEngine = ext::make_shared<AnalyticEuropeanEngine>(bsProcess);
102
103 BarrierOption referenceOption(barrierType, barrier, rebate,
104 payoff, exercise);
105 referenceOption.setPricingEngine(barrierEngine);
106
107 Real referenceValue = referenceOption.NPV();
108
109 std::cout << std::setw(widths[0]) << std::left
110 << "Original barrier option"
111 << std::fixed
112 << std::setw(widths[1]) << std::left << referenceValue
113 << std::setw(widths[2]) << std::left << "N/A"
114 << std::endl;
115
116 // Replicating portfolios
117 CompositeInstrument portfolio1, portfolio2, portfolio3;
118
119 // Final payoff first (the same for all portfolios):
120 // as shown in Joshi, a put struck at K...
121 auto put1 = ext::make_shared<EuropeanOption>(payoff, exercise);
122 put1->setPricingEngine(europeanEngine);
123 portfolio1.add(put1);
124 portfolio2.add(put1);
125 portfolio3.add(put1);
126 // ...minus a digital put struck at B of notional K-B...
127 auto digitalPayoff = ext::make_shared<CashOrNothingPayoff>(Option::Put, barrier, 1.0);
128 auto digitalPut = ext::make_shared<EuropeanOption>(digitalPayoff, exercise);
129 digitalPut->setPricingEngine(europeanEngine);
130 portfolio1.subtract(digitalPut, strike-barrier);
131 portfolio2.subtract(digitalPut, strike-barrier);
132 portfolio3.subtract(digitalPut, strike-barrier);
133 // ...minus a put option struck at B.
134 auto lowerPayoff = ext::make_shared<PlainVanillaPayoff>(Option::Put, barrier);
135 auto put2 = ext::make_shared<EuropeanOption>(lowerPayoff, exercise);
136 put2->setPricingEngine(europeanEngine);
137 portfolio1.subtract(put2);
138 portfolio2.subtract(put2);
139 portfolio3.subtract(put2);
140
141 // Now we use puts struck at B to kill the value of the
142 // portfolio on a number of points (B,t). For the first
143 // portfolio, we'll use 12 dates at one-month's distance.
144 Integer i;
145 for (i=12; i>=1; i--) {
146 // First, we instantiate the option...
147 Date innerMaturity = today + i*Months;
148 auto innerExercise = ext::make_shared<EuropeanExercise>(innerMaturity);
149 auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(Option::Put, barrier);
150 auto putn = ext::make_shared<EuropeanOption>(innerPayoff, innerExercise);
151 putn->setPricingEngine(europeanEngine);
152 // ...second, we evaluate the current portfolio and the
153 // latest put at (B,t)...
154 Date killDate = today + (i-1)*Months;
155 Settings::instance().evaluationDate() = killDate;
156 underlying->setValue(barrier);
157 Real portfolioValue = portfolio1.NPV();
158 Real putValue = putn->NPV();
159 // ...finally, we estimate the notional that kills the
160 // portfolio value at that point...
161 Real notional = portfolioValue/putValue;
162 // ...and we subtract from the portfolio a put with such
163 // notional.
164 portfolio1.subtract(putn, notional);
165 }
166 // The portfolio being complete, we return to today's market...
167 Settings::instance().evaluationDate() = today;
168 underlying->setValue(underlyingValue);
169 // ...and output the value.
170 Real portfolioValue = portfolio1.NPV();
171 Real error = portfolioValue - referenceValue;
172 std::cout << std::setw(widths[0]) << std::left
173 << "Replicating portfolio (12 dates)"
174 << std::fixed
175 << std::setw(widths[1]) << std::left << portfolioValue
176 << std::setw(widths[2]) << std::left << error
177 << std::endl;
178
179 // For the second portfolio, we'll use 26 dates at two-weeks'
180 // distance.
181 for (i=52; i>=2; i-=2) {
182 // Same as above.
183 Date innerMaturity = today + i*Weeks;
184 auto innerExercise = ext::make_shared<EuropeanExercise>(innerMaturity);
185 auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(Option::Put, barrier);
186 auto putn = ext::make_shared<EuropeanOption>(innerPayoff, innerExercise);
187 putn->setPricingEngine(europeanEngine);
188 Date killDate = today + (i-2)*Weeks;
189 Settings::instance().evaluationDate() = killDate;
190 underlying->setValue(barrier);
191 Real portfolioValue = portfolio2.NPV();
192 Real putValue = putn->NPV();
193 Real notional = portfolioValue/putValue;
194 portfolio2.subtract(putn, notional);
195 }
196 Settings::instance().evaluationDate() = today;
197 underlying->setValue(underlyingValue);
198 portfolioValue = portfolio2.NPV();
199 error = portfolioValue - referenceValue;
200 std::cout << std::setw(widths[0]) << std::left
201 << "Replicating portfolio (26 dates)"
202 << std::fixed
203 << std::setw(widths[1]) << std::left << portfolioValue
204 << std::setw(widths[2]) << std::left << error
205 << std::endl;
206
207 // For the third portfolio, we'll use 52 dates at one-week's
208 // distance.
209 for (i=52; i>=1; i--) {
210 // Same as above.
211 Date innerMaturity = today + i*Weeks;
212 auto innerExercise = ext::make_shared<EuropeanExercise>(innerMaturity);
213 auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(Option::Put, barrier);
214 auto putn = ext::make_shared<EuropeanOption>(innerPayoff, innerExercise);
215 putn->setPricingEngine(europeanEngine);
216 Date killDate = today + (i-1)*Weeks;
217 Settings::instance().evaluationDate() = killDate;
218 underlying->setValue(barrier);
219 Real portfolioValue = portfolio3.NPV();
220 Real putValue = putn->NPV();
221 Real notional = portfolioValue/putValue;
222 portfolio3.subtract(putn, notional);
223 }
224 Settings::instance().evaluationDate() = today;
225 underlying->setValue(underlyingValue);
226 portfolioValue = portfolio3.NPV();
227 error = portfolioValue - referenceValue;
228 std::cout << std::setw(widths[0]) << std::left
229 << "Replicating portfolio (52 dates)"
230 << std::fixed
231 << std::setw(widths[1]) << std::left << portfolioValue
232 << std::setw(widths[2]) << std::left << error
233 << std::endl;
234
235 // Now we modify the market condition to see whether the
236 // replication holds. First, we change the underlying value so
237 // that the option is out of the money.
238 std::cout << dblrule << std::endl;
239 std::cout << "Modified market conditions: out of the money"
240 << std::endl;
241 std::cout << dblrule << std::endl;
242 std::cout << std::setw(widths[0]) << std::left << "Option"
243 << std::setw(widths[1]) << std::left << "NPV"
244 << std::setw(widths[2]) << std::left << "Error"
245 << std::endl;
246 std::cout << rule << std::endl;
247
248 underlying->setValue(110.0);
249
250 referenceValue = referenceOption.NPV();
251 std::cout << std::setw(widths[0]) << std::left
252 << "Original barrier option"
253 << std::fixed
254 << std::setw(widths[1]) << std::left << referenceValue
255 << std::setw(widths[2]) << std::left << "N/A"
256 << std::endl;
257 portfolioValue = portfolio1.NPV();
258 error = portfolioValue - referenceValue;
259 std::cout << std::setw(widths[0]) << std::left
260 << "Replicating portfolio (12 dates)"
261 << std::fixed
262 << std::setw(widths[1]) << std::left << portfolioValue
263 << std::setw(widths[2]) << std::left << error
264 << std::endl;
265 portfolioValue = portfolio2.NPV();
266 error = portfolioValue - referenceValue;
267 std::cout << std::setw(widths[0]) << std::left
268 << "Replicating portfolio (26 dates)"
269 << std::fixed
270 << std::setw(widths[1]) << std::left << portfolioValue
271 << std::setw(widths[2]) << std::left << error
272 << std::endl;
273 portfolioValue = portfolio3.NPV();
274 error = portfolioValue - referenceValue;
275 std::cout << std::setw(widths[0]) << std::left
276 << "Replicating portfolio (52 dates)"
277 << std::fixed
278 << std::setw(widths[1]) << std::left << portfolioValue
279 << std::setw(widths[2]) << std::left << error
280 << std::endl;
281
282 // Next, we change the underlying value so that the option is
283 // in the money.
284 std::cout << dblrule << std::endl;
285 std::cout << "Modified market conditions: in the money" << std::endl;
286 std::cout << dblrule << std::endl;
287 std::cout << std::setw(widths[0]) << std::left << "Option"
288 << std::setw(widths[1]) << std::left << "NPV"
289 << std::setw(widths[2]) << std::left << "Error"
290 << std::endl;
291 std::cout << rule << std::endl;
292
293 underlying->setValue(90.0);
294
295 referenceValue = referenceOption.NPV();
296 std::cout << std::setw(widths[0]) << std::left
297 << "Original barrier option"
298 << std::fixed
299 << std::setw(widths[1]) << std::left << referenceValue
300 << std::setw(widths[2]) << std::left << "N/A"
301 << std::endl;
302 portfolioValue = portfolio1.NPV();
303 error = portfolioValue - referenceValue;
304 std::cout << std::setw(widths[0]) << std::left
305 << "Replicating portfolio (12 dates)"
306 << std::fixed
307 << std::setw(widths[1]) << std::left << portfolioValue
308 << std::setw(widths[2]) << std::left << error
309 << std::endl;
310 portfolioValue = portfolio2.NPV();
311 error = portfolioValue - referenceValue;
312 std::cout << std::setw(widths[0]) << std::left
313 << "Replicating portfolio (26 dates)"
314 << std::fixed
315 << std::setw(widths[1]) << std::left << portfolioValue
316 << std::setw(widths[2]) << std::left << error
317 << std::endl;
318 portfolioValue = portfolio3.NPV();
319 error = portfolioValue - referenceValue;
320 std::cout << std::setw(widths[0]) << std::left
321 << "Replicating portfolio (52 dates)"
322 << std::fixed
323 << std::setw(widths[1]) << std::left << portfolioValue
324 << std::setw(widths[2]) << std::left << error
325 << std::endl;
326
327 // Finally, a word of warning for those (shame on them) who
328 // run the example but do not read the code.
329 std::cout << dblrule << std::endl;
330 std::cout
331 << std::endl
332 << "The replication seems to be less robust when volatility and \n"
333 << "risk-free rate are changed. Feel free to experiment with \n"
334 << "the example and contribute a patch if you spot any errors."
335 << std::endl;
336
337 return 0;
338 } catch (std::exception& e) {
339 std::cerr << e.what() << std::endl;
340 return 1;
341 } catch (...) {
342 std::cerr << "unknown error" << std::endl;
343 return 1;
344 }
345 }