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 }