This is an example of using the QuantLib Monte Carlo framework.
00001
00002
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 #define BOOST_LIB_DIAGNOSTIC
00050 # include <ql/quantlib.hpp>
00051 #undef BOOST_LIB_DIAGNOSTIC
00052
00053 #ifdef BOOST_MSVC
00054
00055
00056
00057
00058
00059
00060
00061
00062 #endif
00063
00064 #include <boost/timer.hpp>
00065 #include <iostream>
00066 #include <iomanip>
00067
00068 using namespace QuantLib;
00069
00070 #if defined(QL_ENABLE_SESSIONS)
00071 namespace QuantLib {
00072
00073 Integer sessionId() { return 0; }
00074
00075 }
00076 #endif
00077
00078
00079
00080
00081
00082
00083 class ReplicationError
00084 {
00085 public:
00086 ReplicationError(Option::Type type,
00087 Time maturity,
00088 Real strike,
00089 Real s0,
00090 Volatility sigma,
00091 Rate r)
00092 : maturity_(maturity), payoff_(type, strike), s0_(s0),
00093 sigma_(sigma), r_(r) {
00094
00095
00096 DiscountFactor rDiscount = std::exp(-r_*maturity_);
00097 DiscountFactor qDiscount = 1.0;
00098 Real forward = s0_*qDiscount/rDiscount;
00099 Real variance = sigma_*sigma_*maturity_;
00100 boost::shared_ptr<StrikedTypePayoff> payoff(
00101 new PlainVanillaPayoff(payoff_));
00102 BlackFormula black(forward,rDiscount,variance,payoff);
00103 std::cout << "Option value: " << black.value() << std::endl;
00104
00105
00106 vega_ = black.vega(maturity_);
00107
00108 std::cout << std::endl;
00109 std::cout <<
00110 " | | P&L \t| P&L | Derman&Kamal | P&L"
00111 " \t| P&L" << std::endl;
00112
00113 std::cout <<
00114 "samples | trades | Mean \t| Std Dev | Formula |"
00115 " skewness \t| kurt." << std::endl;
00116
00117 std::cout << "---------------------------------"
00118 "----------------------------------------------" << std::endl;
00119 }
00120
00121
00122 void compute(Size nTimeSteps, Size nSamples);
00123 private:
00124 Time maturity_;
00125 PlainVanillaPayoff payoff_;
00126 Real s0_;
00127 Volatility sigma_;
00128 Rate r_;
00129 Real vega_;
00130 };
00131
00132
00133
00134
00135 class ReplicationPathPricer : public PathPricer<Path> {
00136 public:
00137
00138 ReplicationPathPricer(Option::Type type,
00139 Real strike,
00140 Rate r,
00141 Time maturity,
00142 Volatility sigma)
00143 : type_(type), strike_(strike),
00144 r_(r), maturity_(maturity), sigma_(sigma) {
00145 QL_REQUIRE(strike_ > 0.0, "strike must be positive");
00146 QL_REQUIRE(r_ >= 0.0,
00147 "risk free rate (r) must be positive or zero");
00148 QL_REQUIRE(maturity_ > 0.0, "maturity must be positive");
00149 QL_REQUIRE(sigma_ >= 0.0,
00150 "volatility (sigma) must be positive or zero");
00151
00152 }
00153
00154 Real operator()(const Path& path) const;
00155
00156 private:
00157 Option::Type type_;
00158 Real strike_;
00159 Rate r_;
00160 Time maturity_;
00161 Volatility sigma_;
00162 };
00163
00164
00165
00166 int main(int, char* [])
00167 {
00168 try {
00169 QL_IO_INIT
00170
00171 boost::timer timer;
00172 std::cout << std::endl;
00173
00174 Time maturity = 1.0/12.0;
00175 Real strike = 100;
00176 Real underlying = 100;
00177 Volatility volatility = 0.20;
00178 Rate riskFreeRate = 0.05;
00179 ReplicationError rp(Option::Call, maturity, strike, underlying,
00180 volatility, riskFreeRate);
00181
00182 Size scenarios = 50000;
00183 Size hedgesNum;
00184
00185 hedgesNum = 21;
00186 rp.compute(hedgesNum, scenarios);
00187
00188 hedgesNum = 84;
00189 rp.compute(hedgesNum, scenarios);
00190
00191 Real seconds = timer.elapsed();
00192 Integer hours = int(seconds/3600);
00193 seconds -= hours * 3600;
00194 Integer minutes = int(seconds/60);
00195 seconds -= minutes * 60;
00196 std::cout << " \nRun completed in ";
00197 if (hours > 0)
00198 std::cout << hours << " h ";
00199 if (hours > 0 || minutes > 0)
00200 std::cout << minutes << " m ";
00201 std::cout << std::fixed << std::setprecision(0)
00202 << seconds << " s\n" << std::endl;
00203
00204 return 0;
00205 } catch (std::exception& e) {
00206 std::cout << e.what() << std::endl;
00207 return 1;
00208 } catch (...) {
00209 std::cout << "unknown error" << std::endl;
00210 return 1;
00211 }
00212 }
00213
00214
00215
00216
00217
00218
00219
00220
00221 Real ReplicationPathPricer::operator()(const Path& path) const {
00222
00223 Size n = path.length()-1;
00224 QL_REQUIRE(n>0, "the path cannot be empty");
00225
00226
00227 Time dt = maturity_/n;
00228
00229
00230 Rate stockDividendYield = 0.0;
00231
00232
00233 Time t = 0;
00234
00235
00236 Real stock = path.front();
00237
00238
00239 Real money_account = 0.0;
00240
00241
00242
00243
00244
00245 DiscountFactor rDiscount = std::exp(-r_*maturity_);
00246 DiscountFactor qDiscount = std::exp(-stockDividendYield*maturity_);
00247 Real forward = stock*qDiscount/rDiscount;
00248 Real variance = sigma_*sigma_*maturity_;
00249 boost::shared_ptr<StrikedTypePayoff> payoff(
00250 new PlainVanillaPayoff(type_,strike_));
00251 BlackFormula black(forward,rDiscount,variance,payoff);
00252
00253 money_account += black.value();
00254
00255 Real delta = black.delta(stock);
00256
00257 Real stockAmount = delta;
00258 money_account -= stockAmount*stock;
00259
00260
00261
00262
00263 for (Size step = 0; step < n-1; step++){
00264
00265
00266 t += dt;
00267
00268
00269 money_account *= std::exp( r_*dt );
00270
00271
00272 stock = path[step+1];
00273
00274
00275
00276 rDiscount = std::exp(-r_*(maturity_-t));
00277 qDiscount = std::exp(-stockDividendYield*(maturity_-t));
00278 forward = stock*qDiscount/rDiscount;
00279 variance = sigma_*sigma_*(maturity_-t);
00280 BlackFormula black(forward,rDiscount,variance,payoff);
00281
00282
00283 delta = black.delta(stock);
00284
00285
00286 money_account -= (delta - stockAmount)*stock;
00287 stockAmount = delta;
00288 }
00289
00290
00291
00292
00293
00294 money_account *= std::exp( r_*dt );
00295
00296 stock = path[n];
00297
00298
00299 Real optionPayoff = PlainVanillaPayoff(type_, strike_)(stock);
00300 money_account -= optionPayoff;
00301
00302
00303 money_account += stockAmount*stock;
00304
00305
00306 return money_account;
00307 }
00308
00309
00310
00311 void ReplicationError::compute(Size nTimeSteps, Size nSamples)
00312 {
00313 QL_REQUIRE(nTimeSteps>0, "the number of steps must be > 0");
00314
00315
00316
00317
00318
00319
00320
00321
00322 Date today = Date::todaysDate();
00323 DayCounter dayCount = Actual365Fixed();
00324 Handle<Quote> stateVariable(
00325 boost::shared_ptr<Quote>(new SimpleQuote(s0_)));
00326 Handle<YieldTermStructure> riskFreeRate(
00327 boost::shared_ptr<YieldTermStructure>(
00328 new FlatForward(today, r_, dayCount)));
00329 Handle<YieldTermStructure> dividendYield(
00330 boost::shared_ptr<YieldTermStructure>(
00331 new FlatForward(today, 0.0, dayCount)));
00332 Handle<BlackVolTermStructure> volatility(
00333 boost::shared_ptr<BlackVolTermStructure>(
00334 new BlackConstantVol(today, sigma_, dayCount)));
00335 boost::shared_ptr<StochasticProcess1D> diffusion(
00336 new BlackScholesMertonProcess(stateVariable, dividendYield,
00337 riskFreeRate, volatility));
00338
00339
00340
00341
00342 PseudoRandom::rsg_type rsg =
00343 PseudoRandom::make_sequence_generator(nTimeSteps, 0);
00344
00345 bool brownianBridge = false;
00346
00347 typedef SingleVariate<PseudoRandom>::path_generator_type generator_type;
00348 boost::shared_ptr<generator_type> myPathGenerator(new
00349 generator_type(diffusion, maturity_, nTimeSteps,
00350 rsg, brownianBridge));
00351
00352
00353
00354
00355 boost::shared_ptr<PathPricer<Path> > myPathPricer(new
00356 ReplicationPathPricer(payoff_.optionType(), payoff_.strike(),
00357 r_, maturity_, sigma_));
00358
00359
00360 Statistics statisticsAccumulator;
00361
00362
00363
00364
00365 OneFactorMonteCarloOption MCSimulation(myPathGenerator,
00366 myPathPricer,
00367 statisticsAccumulator,
00368 false);
00369
00370
00371 MCSimulation.addSamples(nSamples);
00372
00373
00374
00375 Real PLMean = MCSimulation.sampleAccumulator().mean();
00376 Real PLStDev = MCSimulation.sampleAccumulator().standardDeviation();
00377 Real PLSkew = MCSimulation.sampleAccumulator().skewness();
00378 Real PLKurt = MCSimulation.sampleAccumulator().kurtosis();
00379
00380
00381 Real theorStD = std::sqrt(M_PI/4/nTimeSteps)*vega_*sigma_;
00382
00383
00384 std::cout << std::fixed
00385 << nSamples << "\t| "
00386 << nTimeSteps << "\t | "
00387 << std::setprecision(3) << PLMean << " \t| "
00388 << std::setprecision(2) << PLStDev << " \t | "
00389 << std::setprecision(2) << theorStD << " \t | "
00390 << std::setprecision(2) << PLSkew << " \t| "
00391 << std::setprecision(2) << PLKurt << std::endl;
00392 }