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