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;
            }
        }
    }
示例#2
0
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;
	}
}