| 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>(args&: underlyingValue); |
| 63 | Real strike = 100.0; |
| 64 | auto riskFreeRate = ext::make_shared<SimpleQuote>(args: 0.04); |
| 65 | auto volatility = ext::make_shared<SimpleQuote>(args: 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>(args: 0, args: NullCalendar(), args&: h1, args&: dayCounter)); |
| 90 | Handle<BlackVolTermStructure> flatVol( |
| 91 | ext::make_shared<BlackConstantVol>(args: 0, args: NullCalendar(), args&: h2, args&: dayCounter)); |
| 92 | |
| 93 | // instantiate the option |
| 94 | auto exercise = ext::make_shared<EuropeanExercise>(args&: maturity); |
| 95 | auto payoff = ext::make_shared<PlainVanillaPayoff>(args&: type, args&: strike); |
| 96 | |
| 97 | auto bsProcess = ext::make_shared<BlackScholesProcess>( |
| 98 | args: Handle<Quote>(underlying), args&: flatRate, args&: flatVol); |
| 99 | |
| 100 | auto barrierEngine = ext::make_shared<AnalyticBarrierEngine>(args&: bsProcess); |
| 101 | auto europeanEngine = ext::make_shared<AnalyticEuropeanEngine>(args&: 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>(args&: payoff, args&: exercise); |
| 122 | put1->setPricingEngine(europeanEngine); |
| 123 | portfolio1.add(instrument: put1); |
| 124 | portfolio2.add(instrument: put1); |
| 125 | portfolio3.add(instrument: put1); |
| 126 | // ...minus a digital put struck at B of notional K-B... |
| 127 | auto digitalPayoff = ext::make_shared<CashOrNothingPayoff>(args: Option::Put, args&: barrier, args: 1.0); |
| 128 | auto digitalPut = ext::make_shared<EuropeanOption>(args&: digitalPayoff, args&: exercise); |
| 129 | digitalPut->setPricingEngine(europeanEngine); |
| 130 | portfolio1.subtract(instrument: digitalPut, multiplier: strike-barrier); |
| 131 | portfolio2.subtract(instrument: digitalPut, multiplier: strike-barrier); |
| 132 | portfolio3.subtract(instrument: digitalPut, multiplier: strike-barrier); |
| 133 | // ...minus a put option struck at B. |
| 134 | auto lowerPayoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: barrier); |
| 135 | auto put2 = ext::make_shared<EuropeanOption>(args&: lowerPayoff, args&: exercise); |
| 136 | put2->setPricingEngine(europeanEngine); |
| 137 | portfolio1.subtract(instrument: put2); |
| 138 | portfolio2.subtract(instrument: put2); |
| 139 | portfolio3.subtract(instrument: 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>(args&: innerMaturity); |
| 149 | auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: barrier); |
| 150 | auto putn = ext::make_shared<EuropeanOption>(args&: innerPayoff, args&: 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(instrument: putn, multiplier: 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>(args&: innerMaturity); |
| 185 | auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: barrier); |
| 186 | auto putn = ext::make_shared<EuropeanOption>(args&: innerPayoff, args&: 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(instrument: putn, multiplier: 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>(args&: innerMaturity); |
| 213 | auto innerPayoff = ext::make_shared<PlainVanillaPayoff>(args: Option::Put, args&: barrier); |
| 214 | auto putn = ext::make_shared<EuropeanOption>(args&: innerPayoff, args&: 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(instrument: putn, multiplier: 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 | } |
| 346 | |