Replication.cpp
This example uses the CompositeInstrument class to perform static replication of a down-and-out barrier option.
00001 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 00002 00020 /* This example showcases the CompositeInstrument class. Such class 00021 is used to build a static replication of a down-and-out barrier 00022 option, as outlined in Section 10.2 of Mark Joshi's "The Concepts 00023 and Practice of Mathematical Finance" to which we refer the 00024 reader. 00025 */ 00026 00027 // the only header you need to use QuantLib 00028 #define BOOST_LIB_DIAGNOSTIC 00029 # include <ql/quantlib.hpp> 00030 #undef BOOST_LIB_DIAGNOSTIC 00031 00032 #ifdef BOOST_MSVC 00033 /* Uncomment the following lines to unmask floating-point 00034 exceptions. Warning: unpredictable results can arise... 00035 00036 See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481 00037 Is there anyone with a definitive word about this? 00038 */ 00039 // #include <float.h> 00040 // namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); } 00041 #endif 00042 00043 #include <boost/timer.hpp> 00044 #include <iostream> 00045 #include <iomanip> 00046 00047 using namespace QuantLib; 00048 00049 #if defined(QL_ENABLE_SESSIONS) 00050 namespace QuantLib { 00051 00052 Integer sessionId() { return 0; } 00053 00054 } 00055 #endif 00056 00057 int main(int, char* []) { 00058 00059 try { 00060 00061 boost::timer timer; 00062 std::cout << std::endl; 00063 00064 Date today(29, May, 2006); 00065 Settings::instance().evaluationDate() = today; 00066 00067 // the option to replicate 00068 Barrier::Type barrierType = Barrier::DownOut; 00069 Real barrier = 70.0; 00070 Real rebate = 0.0; 00071 Option::Type type = Option::Put; 00072 Real underlyingValue = 100.0; 00073 boost::shared_ptr<SimpleQuote> underlying( 00074 new SimpleQuote(underlyingValue)); 00075 Real strike = 100.0; 00076 boost::shared_ptr<SimpleQuote> riskFreeRate(new SimpleQuote(0.04)); 00077 boost::shared_ptr<SimpleQuote> volatility(new SimpleQuote(0.20)); 00078 Date maturity = today + 1*Years; 00079 00080 std::cout << std::endl ; 00081 00082 // write column headings 00083 Size widths[] = { 45, 15, 15 }; 00084 Size totalWidth = widths[0]+widths[1]+widths[2]; 00085 std::string rule(totalWidth, '-'), dblrule(totalWidth, '='); 00086 00087 std::cout << dblrule << std::endl; 00088 std::cout << "Initial market conditions" << std::endl; 00089 std::cout << dblrule << std::endl; 00090 std::cout << std::setw(widths[0]) << std::left << "Option" 00091 << std::setw(widths[1]) << std::left << "NPV" 00092 << std::setw(widths[2]) << std::left << "Error" 00093 << std::endl; 00094 std::cout << rule << std::endl; 00095 00096 // bootstrap the yield/vol curves 00097 DayCounter dayCounter = Actual365Fixed(); 00098 Handle<Quote> h1(riskFreeRate); 00099 Handle<Quote> h2(volatility); 00100 Handle<YieldTermStructure> flatRate( 00101 boost::shared_ptr<YieldTermStructure>( 00102 new FlatForward(0, NullCalendar(), 00103 h1, dayCounter))); 00104 Handle<BlackVolTermStructure> flatVol( 00105 boost::shared_ptr<BlackVolTermStructure>( 00106 new BlackConstantVol(0, NullCalendar(), 00107 h2, dayCounter))); 00108 00109 // instantiate the option 00110 boost::shared_ptr<Exercise> exercise( 00111 new EuropeanExercise(maturity)); 00112 boost::shared_ptr<StrikedTypePayoff> payoff( 00113 new PlainVanillaPayoff(type, strike)); 00114 00115 boost::shared_ptr<StochasticProcess> stochasticProcess( 00116 new BlackScholesProcess(Handle<Quote>(underlying), 00117 flatRate, flatVol)); 00118 00119 BarrierOption referenceOption(barrierType, barrier, rebate, 00120 stochasticProcess, payoff, exercise); 00121 00122 Real referenceValue = referenceOption.NPV(); 00123 00124 std::cout << std::setw(widths[0]) << std::left 00125 << "Original barrier option" 00126 << std::fixed 00127 << std::setw(widths[1]) << std::left << referenceValue 00128 << std::setw(widths[2]) << std::left << "N/A" 00129 << std::endl; 00130 00131 // Replicating portfolios 00132 CompositeInstrument portfolio1, portfolio2, portfolio3; 00133 00134 // Final payoff first (the same for all portfolios): 00135 // as shown in Joshi, a put struck at K... 00136 boost::shared_ptr<Instrument> put1( 00137 new EuropeanOption(stochasticProcess, payoff, exercise)); 00138 portfolio1.add(put1); 00139 portfolio2.add(put1); 00140 portfolio3.add(put1); 00141 // ...minus a digital put struck at B of notional K-B... 00142 boost::shared_ptr<StrikedTypePayoff> digitalPayoff( 00143 new CashOrNothingPayoff(Option::Put, barrier, 1.0)); 00144 boost::shared_ptr<Instrument> digitalPut( 00145 new EuropeanOption(stochasticProcess, digitalPayoff, exercise)); 00146 portfolio1.subtract(digitalPut, strike-barrier); 00147 portfolio2.subtract(digitalPut, strike-barrier); 00148 portfolio3.subtract(digitalPut, strike-barrier); 00149 // ...minus a put option struck at B. 00150 boost::shared_ptr<StrikedTypePayoff> lowerPayoff( 00151 new PlainVanillaPayoff(Option::Put, barrier)); 00152 boost::shared_ptr<Instrument> put2( 00153 new EuropeanOption(stochasticProcess, lowerPayoff, exercise)); 00154 portfolio1.subtract(put2); 00155 portfolio2.subtract(put2); 00156 portfolio3.subtract(put2); 00157 00158 // Now we use puts struck at B to kill the value of the 00159 // portfolio on a number of points (B,t). For the first 00160 // portfolio, we'll use 12 dates at one-month's distance. 00161 Integer i; 00162 for (i=12; i>=1; i--) { 00163 // First, we instantiate the option... 00164 Date innerMaturity = today + i*Months; 00165 boost::shared_ptr<Exercise> innerExercise( 00166 new EuropeanExercise(innerMaturity)); 00167 boost::shared_ptr<StrikedTypePayoff> innerPayoff( 00168 new PlainVanillaPayoff(Option::Put, barrier)); 00169 boost::shared_ptr<Instrument> putn( 00170 new EuropeanOption(stochasticProcess, 00171 innerPayoff, innerExercise)); 00172 // ...second, we evaluate the current portfolio and the 00173 // latest put at (B,t)... 00174 Date killDate = today + (i-1)*Months; 00175 Settings::instance().evaluationDate() = killDate; 00176 underlying->setValue(barrier); 00177 Real portfolioValue = portfolio1.NPV(); 00178 Real putValue = putn->NPV(); 00179 // ...finally, we estimate the notional that kills the 00180 // portfolio value at that point... 00181 Real notional = portfolioValue/putValue; 00182 // ...and we subtract from the portfolio a put with such 00183 // notional. 00184 portfolio1.subtract(putn, notional); 00185 } 00186 // The portfolio being complete, we return to today's market... 00187 Settings::instance().evaluationDate() = today; 00188 underlying->setValue(underlyingValue); 00189 // ...and output the value. 00190 Real portfolioValue = portfolio1.NPV(); 00191 Real error = portfolioValue - referenceValue; 00192 std::cout << std::setw(widths[0]) << std::left 00193 << "Replicating portfolio (12 dates)" 00194 << std::fixed 00195 << std::setw(widths[1]) << std::left << portfolioValue 00196 << std::setw(widths[2]) << std::left << error 00197 << std::endl; 00198 00199 // For the second portfolio, we'll use 26 dates at two-weeks' 00200 // distance. 00201 for (i=52; i>=2; i-=2) { 00202 // Same as above. 00203 Date innerMaturity = today + i*Weeks; 00204 boost::shared_ptr<Exercise> innerExercise( 00205 new EuropeanExercise(innerMaturity)); 00206 boost::shared_ptr<StrikedTypePayoff> innerPayoff( 00207 new PlainVanillaPayoff(Option::Put, barrier)); 00208 boost::shared_ptr<Instrument> putn( 00209 new EuropeanOption(stochasticProcess, 00210 innerPayoff, innerExercise)); 00211 Date killDate = today + (i-2)*Weeks; 00212 Settings::instance().evaluationDate() = killDate; 00213 underlying->setValue(barrier); 00214 Real portfolioValue = portfolio2.NPV(); 00215 Real putValue = putn->NPV(); 00216 Real notional = portfolioValue/putValue; 00217 portfolio2.subtract(putn, notional); 00218 } 00219 Settings::instance().evaluationDate() = today; 00220 underlying->setValue(underlyingValue); 00221 portfolioValue = portfolio2.NPV(); 00222 error = portfolioValue - referenceValue; 00223 std::cout << std::setw(widths[0]) << std::left 00224 << "Replicating portfolio (26 dates)" 00225 << std::fixed 00226 << std::setw(widths[1]) << std::left << portfolioValue 00227 << std::setw(widths[2]) << std::left << error 00228 << std::endl; 00229 00230 // For the third portfolio, we'll use 52 dates at one-week's 00231 // distance. 00232 for (i=52; i>=1; i--) { 00233 // Same as above. 00234 Date innerMaturity = today + i*Weeks; 00235 boost::shared_ptr<Exercise> innerExercise( 00236 new EuropeanExercise(innerMaturity)); 00237 boost::shared_ptr<StrikedTypePayoff> innerPayoff( 00238 new PlainVanillaPayoff(Option::Put, barrier)); 00239 boost::shared_ptr<Instrument> putn( 00240 new EuropeanOption(stochasticProcess, 00241 innerPayoff, innerExercise)); 00242 Date killDate = today + (i-1)*Weeks; 00243 Settings::instance().evaluationDate() = killDate; 00244 underlying->setValue(barrier); 00245 Real portfolioValue = portfolio3.NPV(); 00246 Real putValue = putn->NPV(); 00247 Real notional = portfolioValue/putValue; 00248 portfolio3.subtract(putn, notional); 00249 } 00250 Settings::instance().evaluationDate() = today; 00251 underlying->setValue(underlyingValue); 00252 portfolioValue = portfolio3.NPV(); 00253 error = portfolioValue - referenceValue; 00254 std::cout << std::setw(widths[0]) << std::left 00255 << "Replicating portfolio (52 dates)" 00256 << std::fixed 00257 << std::setw(widths[1]) << std::left << portfolioValue 00258 << std::setw(widths[2]) << std::left << error 00259 << std::endl; 00260 00261 // Now we modify the market condition to see whether the 00262 // replication holds. First, we change the underlying value so 00263 // that the option is out of the money. 00264 std::cout << dblrule << std::endl; 00265 std::cout << "Modified market conditions: out of the money" 00266 << std::endl; 00267 std::cout << dblrule << std::endl; 00268 std::cout << std::setw(widths[0]) << std::left << "Option" 00269 << std::setw(widths[1]) << std::left << "NPV" 00270 << std::setw(widths[2]) << std::left << "Error" 00271 << std::endl; 00272 std::cout << rule << std::endl; 00273 00274 underlying->setValue(110.0); 00275 00276 referenceValue = referenceOption.NPV(); 00277 std::cout << std::setw(widths[0]) << std::left 00278 << "Original barrier option" 00279 << std::fixed 00280 << std::setw(widths[1]) << std::left << referenceValue 00281 << std::setw(widths[2]) << std::left << "N/A" 00282 << std::endl; 00283 portfolioValue = portfolio1.NPV(); 00284 error = portfolioValue - referenceValue; 00285 std::cout << std::setw(widths[0]) << std::left 00286 << "Replicating portfolio (12 dates)" 00287 << std::fixed 00288 << std::setw(widths[1]) << std::left << portfolioValue 00289 << std::setw(widths[2]) << std::left << error 00290 << std::endl; 00291 portfolioValue = portfolio2.NPV(); 00292 error = portfolioValue - referenceValue; 00293 std::cout << std::setw(widths[0]) << std::left 00294 << "Replicating portfolio (26 dates)" 00295 << std::fixed 00296 << std::setw(widths[1]) << std::left << portfolioValue 00297 << std::setw(widths[2]) << std::left << error 00298 << std::endl; 00299 portfolioValue = portfolio3.NPV(); 00300 error = portfolioValue - referenceValue; 00301 std::cout << std::setw(widths[0]) << std::left 00302 << "Replicating portfolio (52 dates)" 00303 << std::fixed 00304 << std::setw(widths[1]) << std::left << portfolioValue 00305 << std::setw(widths[2]) << std::left << error 00306 << std::endl; 00307 00308 // Next, we change the underlying value so that the option is 00309 // in the money. 00310 std::cout << dblrule << std::endl; 00311 std::cout << "Modified market conditions: in the money" << std::endl; 00312 std::cout << dblrule << std::endl; 00313 std::cout << std::setw(widths[0]) << std::left << "Option" 00314 << std::setw(widths[1]) << std::left << "NPV" 00315 << std::setw(widths[2]) << std::left << "Error" 00316 << std::endl; 00317 std::cout << rule << std::endl; 00318 00319 underlying->setValue(90.0); 00320 00321 referenceValue = referenceOption.NPV(); 00322 std::cout << std::setw(widths[0]) << std::left 00323 << "Original barrier option" 00324 << std::fixed 00325 << std::setw(widths[1]) << std::left << referenceValue 00326 << std::setw(widths[2]) << std::left << "N/A" 00327 << std::endl; 00328 portfolioValue = portfolio1.NPV(); 00329 error = portfolioValue - referenceValue; 00330 std::cout << std::setw(widths[0]) << std::left 00331 << "Replicating portfolio (12 dates)" 00332 << std::fixed 00333 << std::setw(widths[1]) << std::left << portfolioValue 00334 << std::setw(widths[2]) << std::left << error 00335 << std::endl; 00336 portfolioValue = portfolio2.NPV(); 00337 error = portfolioValue - referenceValue; 00338 std::cout << std::setw(widths[0]) << std::left 00339 << "Replicating portfolio (26 dates)" 00340 << std::fixed 00341 << std::setw(widths[1]) << std::left << portfolioValue 00342 << std::setw(widths[2]) << std::left << error 00343 << std::endl; 00344 portfolioValue = portfolio3.NPV(); 00345 error = portfolioValue - referenceValue; 00346 std::cout << std::setw(widths[0]) << std::left 00347 << "Replicating portfolio (52 dates)" 00348 << std::fixed 00349 << std::setw(widths[1]) << std::left << portfolioValue 00350 << std::setw(widths[2]) << std::left << error 00351 << std::endl; 00352 00353 // Finally, a word of warning for those (shame on them) who 00354 // run the example but do not read the code. 00355 std::cout << dblrule << std::endl; 00356 std::cout 00357 << std::endl 00358 << "The replication seems to be less robust when volatility and \n" 00359 << "risk-free rate are changed. Feel free to experiment with \n" 00360 << "the example and contribute a patch if you spot any errors." 00361 << std::endl; 00362 00363 Real seconds = timer.elapsed(); 00364 Integer hours = int(seconds/3600); 00365 seconds -= hours * 3600; 00366 Integer minutes = int(seconds/60); 00367 seconds -= minutes * 60; 00368 std::cout << " \nRun completed in "; 00369 if (hours > 0) 00370 std::cout << hours << " h "; 00371 if (hours > 0 || minutes > 0) 00372 std::cout << minutes << " m "; 00373 std::cout << std::fixed << std::setprecision(0) 00374 << seconds << " s\n" << std::endl; 00375 00376 return 0; 00377 } catch (std::exception& e) { 00378 std::cout << e.what() << std::endl; 00379 return 1; 00380 } catch (...) { 00381 std::cout << "unknown error" << std::endl; 00382 return 1; 00383 } 00384 }