void ContinuousArithmeticAsianVecerEngine::calculate() const { Real expectedAverage; QL_REQUIRE(arguments_.averageType == Average::Arithmetic, "not an Arithmetic average option"); QL_REQUIRE(arguments_.exercise->type() == Exercise::European, "not an European Option"); DayCounter rfdc = process_->riskFreeRate()->dayCounter(); DayCounter divdc = process_->dividendYield()->dayCounter(); DayCounter voldc = process_->blackVolatility()->dayCounter(); Real S_0 = process_->stateVariable()->value(); // payoff ext::shared_ptr<StrikedTypePayoff> payoff = ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff); QL_REQUIRE(payoff, "non-plain payoff given"); // original time to maturity Date maturity = arguments_.exercise->lastDate(); Real X = payoff->strike(); QL_REQUIRE(z_min_<=0 && z_max_>=0, "strike (0 for vecer fixed strike asian) not on Grid"); Volatility sigma = process_->blackVolatility()->blackVol(maturity, X); Rate r = process_->riskFreeRate()-> zeroRate(maturity, rfdc, Continuous, NoFrequency); Rate q = process_->dividendYield()-> zeroRate(maturity, divdc, Continuous, NoFrequency); Date today(Settings::instance().evaluationDate()); QL_REQUIRE(startDate_>=today, "Seasoned Asian not yet implemented"); // Expiry in Years Time T = rfdc.yearFraction(today, arguments_.exercise->lastDate()); Time T1 = rfdc.yearFraction(today, startDate_ ); // Average Begin Time T2 = T; // Average End (In this version only Maturity...) if ((T2 - T1) < 0.001) { // its a vanilla option. Use vanilla engine VanillaOption europeanOption(payoff, arguments_.exercise); europeanOption.setPricingEngine( ext::make_shared<AnalyticEuropeanEngine>(process_)); results_.value = europeanOption.NPV(); } else { Real Theta = 0.5; // Mixed Scheme: 0.5 = Crank Nicolson Real Z_0 = cont_strategy(0,T1,T2,q,r) - std::exp(-r*T) * X /S_0; QL_REQUIRE(Z_0>=z_min_ && Z_0<=z_max_, "spot not on grid"); Real h = (z_max_ - z_min_) / assetSteps_; // Space step size Real k = T / timeSteps_; // Time Step size Real sigma2 = sigma * sigma, vecerTerm; Array SVec(assetSteps_+1),u_initial(assetSteps_+1), u(assetSteps_+1),rhs(assetSteps_+1); for (Natural i= 0; i<= SVec.size()-1;i++) { SVec[i] = z_min_ + i * h; // Value of Underlying on the grid } // Begin gamma construction TridiagonalOperator gammaOp = DPlusDMinus(assetSteps_+1,h); Array upperD = gammaOp.upperDiagonal(); Array lowerD = gammaOp.lowerDiagonal(); Array Dia = gammaOp.diagonal(); // Construct Vecer operator TridiagonalOperator explicit_part(gammaOp.size()); TridiagonalOperator implicit_part(gammaOp.size()); for (Natural i= 0; i<= SVec.size()-1;i++) { u_initial[i] = std::max<Real>(SVec[i] , 0.0); // Call Payoff } u = u_initial; // Start Time Loop for (Natural j = 1; j<=timeSteps_;j++) { if (Theta != 1.0) { // Explicit Part for (Natural i = 1; i<= SVec.size()-2;i++) { vecerTerm = SVec[i] - std::exp(-q * (T-(j-1)*k)) * cont_strategy(T-(j-1)*k,T1,T2,q,r); gammaOp.setMidRow(i, 0.5 * sigma2 * vecerTerm * vecerTerm * lowerD[i-1], 0.5 * sigma2 * vecerTerm * vecerTerm * Dia[i], 0.5 * sigma2 * vecerTerm * vecerTerm * upperD[i]); } explicit_part = gammaOp.identity(gammaOp.size()) + (1 - Theta) * k * gammaOp; explicit_part.setFirstRow(1.0,0.0); // Apply before applying explicit_part.setLastRow(-1.0,1.0); // Neumann BC u = explicit_part.applyTo(u); // Apply after applying (Neumann BC) u[assetSteps_] = u[assetSteps_-1] + h; u[0] = 0; } // End Explicit Part if (Theta != 0.0) { // Implicit Part for (Natural i = 1; i<= SVec.size()-2;i++) { vecerTerm = SVec[i] - std::exp(-q * (T-j*k)) * cont_strategy(T-j*k,T1,T2,q,r); gammaOp.setMidRow(i, 0.5 * sigma2 * vecerTerm * vecerTerm * lowerD[i-1], 0.5 * sigma2 * vecerTerm * vecerTerm * Dia[i], 0.5 * sigma2 * vecerTerm * vecerTerm * upperD[i]); } implicit_part = gammaOp.identity(gammaOp.size()) - Theta * k * gammaOp; // Apply before solving implicit_part.setFirstRow(1.0,0.0); implicit_part.setLastRow(-1.0,1.0); rhs = u; rhs[0] = 0; // Lower BC rhs[assetSteps_] = h; // Upper BC (Neumann) Delta=1 u = implicit_part.solveFor(rhs); } // End implicit Part } // End Time Loop DownRounding Rounding(0); Integer lowerI = Integer(Rounding( (Z_0-z_min_)/h)); // Interpolate solution Real pv; pv = u[lowerI] + (u[lowerI+1] - u[lowerI]) * (Z_0 - SVec[lowerI])/h; results_.value = S_0 * pv; if (payoff->optionType()==Option::Put) { // Apply Call Put Parity for Asians if (r == q) { expectedAverage = S_0; } else { expectedAverage = S_0 * (std::exp( (r-q) * T2) - std::exp( (r-q) * T1)) / ((r-q) * (T2-T1)); } Real asianForward = std::exp(-r * T2) * (expectedAverage - X); results_.value = results_.value - asianForward; } } }
int TestFromQuantLib() { try { boost::timer timer; std::cout << std::endl; // set up dates Calendar calendar = TARGET(); Date todaysDate(15, May, 1998); Date settlementDate(17, May, 1998); Settings::instance().evaluationDate() = todaysDate; // our options Option::Type type(Option::Put); Real underlying = 36; Real strike = 40; Spread dividendYield = 0.00; Rate riskFreeRate = 0.06; Volatility volatility = 0.20; Date maturity(17, May, 1999); DayCounter dayCounter = Actual365Fixed(); std::cout << "Option type = " << type << std::endl; std::cout << "Maturity = " << maturity << std::endl; std::cout << "Underlying price = " << underlying << std::endl; std::cout << "Strike = " << strike << std::endl; std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl; std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl; std::cout << "Volatility = " << io::volatility(volatility) << std::endl; std::cout << std::endl; std::string method; std::cout << std::endl; // write column headings Size widths[] = { 35, 14, 14, 14 }; std::cout << std::setw(widths[0]) << std::left << "Method" << std::setw(widths[1]) << std::left << "European" << std::setw(widths[2]) << std::left << "Bermudan" << std::setw(widths[3]) << std::left << "American" << std::endl; std::vector<Date> exerciseDates; for (Integer i = 1; i <= 4; i++) exerciseDates.push_back(settlementDate + 3 * i*Months); boost::shared_ptr<Exercise> europeanExercise( new EuropeanExercise(maturity)); boost::shared_ptr<Exercise> bermudanExercise( new BermudanExercise(exerciseDates)); boost::shared_ptr<Exercise> americanExercise( new AmericanExercise(settlementDate, maturity)); Handle<Quote> underlyingH( boost::shared_ptr<Quote>(new SimpleQuote(underlying))); // bootstrap the yield/dividend/vol curves Handle<YieldTermStructure> flatTermStructure( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, riskFreeRate, dayCounter))); Handle<YieldTermStructure> flatDividendTS( boost::shared_ptr<YieldTermStructure>( new FlatForward(settlementDate, dividendYield, dayCounter))); Handle<BlackVolTermStructure> flatVolTS( boost::shared_ptr<BlackVolTermStructure>( new BlackConstantVol(settlementDate, calendar, volatility, dayCounter))); boost::shared_ptr<StrikedTypePayoff> payoff( new PlainVanillaPayoff(type, strike)); boost::shared_ptr<BlackScholesMertonProcess> bsmProcess( new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS)); // options VanillaOption europeanOption(payoff, europeanExercise); VanillaOption bermudanOption(payoff, bermudanExercise); VanillaOption americanOption(payoff, americanExercise); // Analytic formulas: // Black-Scholes for European method = "Black-Scholes"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new AnalyticEuropeanEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // semi-analytic Heston for European method = "Heston semi-analytic"; boost::shared_ptr<HestonProcess> hestonProcess( new HestonProcess(flatTermStructure, flatDividendTS, underlyingH, volatility*volatility, 1.0, volatility*volatility, 0.001, 0.0)); boost::shared_ptr<HestonModel> hestonModel( new HestonModel(hestonProcess)); europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new AnalyticHestonEngine(hestonModel))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // semi-analytic Bates for European method = "Bates semi-analytic"; boost::shared_ptr<BatesProcess> batesProcess( new BatesProcess(flatTermStructure, flatDividendTS, underlyingH, volatility*volatility, 1.0, volatility*volatility, 0.001, 0.0, 1e-14, 1e-14, 1e-14)); boost::shared_ptr<BatesModel> batesModel(new BatesModel(batesProcess)); europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BatesEngine(batesModel))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Barone-Adesi and Whaley approximation for American method = "Barone-Adesi/Whaley"; americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BaroneAdesiWhaleyApproximationEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Bjerksund and Stensland approximation for American method = "Bjerksund/Stensland"; americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BjerksundStenslandApproximationEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Integral method = "Integral"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new IntegralEngine(bsmProcess))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Finite differences Size timeSteps = 801; method = "Finite differences"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDEuropeanEngine<CrankNicolson>(bsmProcess, timeSteps, timeSteps - 1))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDBermudanEngine<CrankNicolson>(bsmProcess, timeSteps, timeSteps - 1))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new FDAmericanEngine<CrankNicolson>(bsmProcess, timeSteps, timeSteps - 1))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Jarrow-Rudd method = "Binomial Jarrow-Rudd"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<JarrowRudd>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; method = "Binomial Cox-Ross-Rubinstein"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<CoxRossRubinstein>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Additive equiprobabilities method = "Additive equiprobabilities"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<AdditiveEQPBinomialTree>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Trigeorgis method = "Binomial Trigeorgis"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Trigeorgis>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Tian method = "Binomial Tian"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Tian>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Leisen-Reimer method = "Binomial Leisen-Reimer"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<LeisenReimer>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Binomial method: Binomial Joshi method = "Binomial Joshi"; europeanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps))); bermudanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps))); americanOption.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialVanillaEngine<Joshi4>(bsmProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << bermudanOption.NPV() << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // Monte Carlo Method: MC (crude) timeSteps = 1; method = "MC (crude)"; Size mcSeed = 42; boost::shared_ptr<PricingEngine> mcengine1; mcengine1 = MakeMCEuropeanEngine<PseudoRandom>(bsmProcess) .withSteps(timeSteps) .withAbsoluteTolerance(0.02) .withSeed(mcSeed); europeanOption.setPricingEngine(mcengine1); // Real errorEstimate = europeanOption.errorEstimate(); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Monte Carlo Method: QMC (Sobol) method = "QMC (Sobol)"; Size nSamples = 32768; // 2^15 boost::shared_ptr<PricingEngine> mcengine2; mcengine2 = MakeMCEuropeanEngine<LowDiscrepancy>(bsmProcess) .withSteps(timeSteps) .withSamples(nSamples); europeanOption.setPricingEngine(mcengine2); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanOption.NPV() << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << "N/A" << std::endl; // Monte Carlo Method: MC (Longstaff Schwartz) method = "MC (Longstaff Schwartz)"; boost::shared_ptr<PricingEngine> mcengine3; mcengine3 = MakeMCAmericanEngine<PseudoRandom>(bsmProcess) .withSteps(100) .withAntitheticVariate() .withCalibrationSamples(4096) .withAbsoluteTolerance(0.02) .withSeed(mcSeed); americanOption.setPricingEngine(mcengine3); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << "N/A" << std::setw(widths[2]) << std::left << "N/A" << std::setw(widths[3]) << std::left << americanOption.NPV() << std::endl; // End test double seconds = timer.elapsed(); Integer hours = int(seconds / 3600); seconds -= hours * 3600; Integer minutes = int(seconds / 60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl; return 0; } catch (std::exception& e) { std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "unknown error" << std::endl; return 1; } }