void SwingOptionTest::testExtOUJumpVanillaEngine() {

    BOOST_TEST_MESSAGE("Testing finite difference pricer for the Kluge model...");

    SavedSettings backup;

    boost::shared_ptr<ExtOUWithJumpsProcess> jumpProcess = createKlugeProcess();

    const Date today = Date::todaysDate();
    Settings::instance().evaluationDate() = today;

    const DayCounter dc = ActualActual();
    const Date maturityDate = today + Period(12, Months);
    const Time maturity = dc.yearFraction(today, maturityDate);

    const Rate irRate = 0.1;
    boost::shared_ptr<YieldTermStructure> rTS(flatRate(today, irRate, dc));
    boost::shared_ptr<StrikedTypePayoff> payoff(
                                     new PlainVanillaPayoff(Option::Call, 30));
    boost::shared_ptr<Exercise> exercise(new EuropeanExercise(maturityDate));

    boost::shared_ptr<PricingEngine> engine(
                 new FdExtOUJumpVanillaEngine(jumpProcess, rTS, 25, 200, 50));

    VanillaOption option(payoff, exercise);
    option.setPricingEngine(engine);
    const Real fdNPV = option.NPV();

    const Size steps = 100;
    const Size nrTrails = 200000;
    TimeGrid grid(maturity, steps);

    typedef PseudoRandom::rsg_type rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;
    rsg_type rsg = PseudoRandom::make_sequence_generator(
                    jumpProcess->factors()*(grid.size()-1), BigNatural(421));

    GeneralStatistics npv;
    MultiPathGenerator<rsg_type> generator(jumpProcess, grid, rsg, false);

    for (Size n=0; n < nrTrails; ++n) {
        sample_type path = generator.next();

        const Real x = path.value[0].back();
        const Real y = path.value[1].back();

        const Real cashflow = (*payoff)(std::exp(x+y));
        npv.add(cashflow*rTS->discount(maturity));
    }

    const Real mcNPV = npv.mean();
    const Real mcError = npv.errorEstimate();

    if ( std::fabs(fdNPV - mcNPV) > 3.0*mcError) {
        BOOST_ERROR("Failed to reproduce FD and MC prices"
                    << "\n    FD NPV: " << fdNPV
                    << "\n    MC NPV: " << mcNPV
                    << " +/- " << mcError);
    }
}
void LiborMarketModelTest::testSwaptionPricing() {
    BOOST_TEST_MESSAGE("Testing forward swap and swaption pricing...");

    SavedSettings backup;

    const Size size  = 10;
    const Size steps = 8*size;
    #if defined(QL_USE_INDEXED_COUPON)
    const Real tolerance = 1e-6;
    #else
    const Real tolerance = 1e-12;
    #endif

    std::vector<Date> dates;
    std::vector<Rate> rates;
    dates.push_back(Date(4,September,2005));
    dates.push_back(Date(4,September,2011));
    rates.push_back(0.04);
    rates.push_back(0.08);

    boost::shared_ptr<IborIndex> index = makeIndex(dates, rates);

    boost::shared_ptr<LiborForwardModelProcess> process(
                                   new LiborForwardModelProcess(size, index));

    boost::shared_ptr<LmCorrelationModel> corrModel(
                                new LmExponentialCorrelationModel(size, 0.5));

    boost::shared_ptr<LmVolatilityModel> volaModel(
        new LmLinearExponentialVolatilityModel(process->fixingTimes(),
                                               0.291, 1.483, 0.116, 0.00001));

   // set-up pricing engine
    process->setCovarParam(boost::shared_ptr<LfmCovarianceParameterization>(
                               new LfmCovarianceProxy(volaModel, corrModel)));

    // set-up a small Monte-Carlo simulation to price swations
    typedef PseudoRandom::rsg_type rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;

    std::vector<Time> tmp = process->fixingTimes();
    TimeGrid grid(tmp.begin(), tmp.end(), steps);

    Size i;
    std::vector<Size> location;
    for (i=0; i < tmp.size(); ++i) {
        location.push_back(
                      std::find(grid.begin(),grid.end(),tmp[i])-grid.begin());
    }

    rsg_type rsg = PseudoRandom::make_sequence_generator(
                       process->factors()*(grid.size()-1),
                       BigNatural(42));

    const Size nrTrails = 5000;
    MultiPathGenerator<rsg_type> generator(process, grid, rsg, false);

    boost::shared_ptr<LiborForwardModel>
        liborModel(new LiborForwardModel(process, volaModel, corrModel));

    Calendar calendar = index->fixingCalendar();
    DayCounter dayCounter = index->forwardingTermStructure()->dayCounter();
    BusinessDayConvention convention = index->businessDayConvention();

    Date settlement  = index->forwardingTermStructure()->referenceDate();

    boost::shared_ptr<SwaptionVolatilityMatrix> m =
                liborModel->getSwaptionVolatilityMatrix();

    for (i=1; i < size; ++i) {
        for (Size j=1; j <= size-i; ++j) {
            Date fwdStart    = settlement + Period(6*i, Months);
            Date fwdMaturity = fwdStart + Period(6*j, Months);

            Schedule schedule(fwdStart, fwdMaturity, index->tenor(), calendar,
                               convention, convention, DateGeneration::Forward, false);

            Rate swapRate  = 0.0404;
            boost::shared_ptr<VanillaSwap> forwardSwap(
                new VanillaSwap(VanillaSwap::Receiver, 1.0,
                                schedule, swapRate, dayCounter,
                                schedule, index, 0.0, index->dayCounter()));
            forwardSwap->setPricingEngine(boost::shared_ptr<PricingEngine>(
                new DiscountingSwapEngine(index->forwardingTermStructure())));

            // check forward pricing first
            const Real expected = forwardSwap->fairRate();
            const Real calculated = liborModel->S_0(i-1,i+j-1);

            if (std::fabs(expected - calculated) > tolerance)
                BOOST_ERROR("Failed to reproduce fair forward swap rate"
                            << "\n    calculated: " << calculated
                            << "\n    expected:   " << expected);

            swapRate = forwardSwap->fairRate();
            forwardSwap = boost::shared_ptr<VanillaSwap>(
                new VanillaSwap(VanillaSwap::Receiver, 1.0,
                                schedule, swapRate, dayCounter,
                                schedule, index, 0.0, index->dayCounter()));
            forwardSwap->setPricingEngine(boost::shared_ptr<PricingEngine>(
                new DiscountingSwapEngine(index->forwardingTermStructure())));

            if (i == j && i<=size/2) {
                boost::shared_ptr<PricingEngine> engine(
                     new LfmSwaptionEngine(liborModel,
                                           index->forwardingTermStructure()));
                boost::shared_ptr<Exercise> exercise(
                    new EuropeanExercise(process->fixingDates()[i]));

                boost::shared_ptr<Swaption> swaption(
                    new Swaption(forwardSwap, exercise));
                swaption->setPricingEngine(engine);

                GeneralStatistics stat;

                for (Size n=0; n<nrTrails; ++n) {
                    sample_type path = (n%2) ? generator.antithetic()
                                             : generator.next();

                    std::vector<Rate> rates(size);
                    for (Size k=0; k<process->size(); ++k) {
                        rates[k] = path.value[k][location[i]];
                    }
                    std::vector<DiscountFactor> dis =
                        process->discountBond(rates);

                    Real npv=0.0;
                    for (Size m=i; m < i+j; ++m) {
                        npv += (swapRate - rates[m])
                               * (  process->accrualEndTimes()[m]
                                  - process->accrualStartTimes()[m])*dis[m];
                    }
                    stat.add(std::max(npv, 0.0));
                }

                if (std::fabs(swaption->NPV() - stat.mean())
                    > stat.errorEstimate()*2.35)
                    BOOST_ERROR("Failed to reproduce swaption npv"
                                << "\n    calculated: " << stat.mean()
                                << "\n    expected:   " << swaption->NPV());
            }
        }
    }
}
Beispiel #3
0
void VPPTest::testVPPPricing() {
    BOOST_TEST_MESSAGE("Testing VPP pricing using perfect foresight or FDM...");

    SavedSettings backup;

    const Date today = Date(18, December, 2011);
    const DayCounter dc = ActualActual();
    Settings::instance().evaluationDate() = today;

    // vpp paramter
    const Real heatRate       = 2.5;
    const Real pMin           = 8;
    const Real pMax           = 40;
    const Size tMinUp         = 6;
    const Size tMinDown       = 2;
    const Real startUpFuel    = 20;
    const Real startUpFixCost = 100;

    const boost::shared_ptr<SwingExercise> exercise(
                                new SwingExercise(today, today+6, 3600u));

    VanillaVPPOption vppOption(heatRate, pMin, pMax, tMinUp, tMinDown,
                               startUpFuel, startUpFixCost, exercise);

    const boost::shared_ptr<KlugeExtOUProcess> klugeOUProcess
        = createKlugeExtOUProcess();
    const boost::shared_ptr<ExtOUWithJumpsProcess> lnPowerProcess
        = klugeOUProcess->getKlugeProcess();
    const boost::shared_ptr<ExtendedOrnsteinUhlenbeckProcess> ouProcess
        = lnPowerProcess->getExtendedOrnsteinUhlenbeckProcess();
    const boost::shared_ptr<ExtendedOrnsteinUhlenbeckProcess> lnGasProcess
        = klugeOUProcess->getExtOUProcess();

    const Real beta         = lnPowerProcess->beta();
    const Real eta          = lnPowerProcess->eta();
    const Real lambda       = lnPowerProcess->jumpIntensity();
    const Real alpha        = ouProcess->speed();
    const Real volatility_x = ouProcess->volatility();
    const Real kappa        = lnGasProcess->speed();
    const Real volatility_u = lnGasProcess->volatility();

    const Rate irRate       = 0.00;
    const Real fuelCostAddon  = 3.0;

    const boost::shared_ptr<YieldTermStructure> rTS
        = flatRate(today, irRate, dc);

    const Size nHours = LENGTH(powerPrices);

    typedef FdSimpleKlugeExtOUVPPEngine::Shape Shape;
    boost::shared_ptr<Shape> fuelShape(new Shape(nHours));
    boost::shared_ptr<Shape> powerShape(new Shape(nHours));

    for (Size i=0; i < nHours; ++i) {
        const Time t = (i+1)/(365*24.);

        const Real fuelPrice = fuelPrices[i];
        const Real gs = std::log(fuelPrice)-square<Real>()(volatility_u)
                               /(4*kappa)*(1-std::exp(-2*kappa*t));
        (*fuelShape)[i] = Shape::value_type(t, gs);

        const Real powerPrice = powerPrices[i];
        const Real ps = std::log(powerPrice)-square<Real>()(volatility_x)
                 /(4*alpha)*(1-std::exp(-2*alpha*t))
                -lambda/beta*std::log((eta-std::exp(-beta*t))/(eta-1.0));

        (*powerShape)[i] = Shape::value_type(t, ps);
    }

    // Test: intrinsic value
    vppOption.setPricingEngine(boost::shared_ptr<PricingEngine>(
        new DynProgVPPIntrinsicValueEngine(
            std::vector<Real>(fuelPrices, fuelPrices+nHours),
            std::vector<Real>(powerPrices, powerPrices+nHours),
            fuelCostAddon, flatRate(0.0, dc))));

    const Real intrinsic = vppOption.NPV();
    const Real expectedIntrinsic = 2056.04;
    if (std::fabs(intrinsic - expectedIntrinsic) > 0.1) {
        BOOST_ERROR("Failed to reproduce intrinsic value"
                   << "\n    calculated: " << intrinsic
                   << "\n    expected  : " << expectedIntrinsic);
    }

    // Test: finite difference price
    const boost::shared_ptr<PricingEngine> engine(
        new FdSimpleKlugeExtOUVPPEngine(klugeOUProcess, rTS,
                                        fuelShape, powerShape, fuelCostAddon,
                                        1, 25, 11, 10));

    vppOption.setPricingEngine(engine);

    const Real fdmPrice = vppOption.NPV();
    const Real expectedFdmPrice = 5217.68;
    if (std::fabs(fdmPrice - expectedFdmPrice) > 0.1) {
       BOOST_ERROR("Failed to reproduce finite difference price"
                   << "\n    calculated: " << fdmPrice
                   << "\n    expected  : " << expectedFdmPrice);
    }

    // Test: Monte-Carlo perfect foresight price
    VanillaVPPOption::arguments args;
    vppOption.setupArguments(&args);

    const FdmVPPStepConditionFactory stepConditionFactory(args);

    const boost::shared_ptr<FdmMesher> oneDimMesher(new FdmMesherComposite(
        stepConditionFactory.stateMesher()));
    const Size nStates = oneDimMesher->layout()->dim()[0];

    const FdmVPPStepConditionMesher vppMesh = { 0u, oneDimMesher };

    const TimeGrid grid(dc.yearFraction(today, exercise->lastDate()+1),
                        exercise->dates().size());
    typedef PseudoRandom::rsg_type rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;

    rsg_type rsg = PseudoRandom::make_sequence_generator(
                           klugeOUProcess->factors()*(grid.size()-1), 1234ul);
    MultiPathGenerator<rsg_type> generator(klugeOUProcess, grid, rsg, false);

    GeneralStatistics npv;
    const Size nTrails = 2500;

    for (Size i=0; i < nTrails; ++i) {
        const sample_type& path = generator.next();
        const boost::shared_ptr<FdmVPPStepCondition> stepCondition(
            stepConditionFactory.build(
                vppMesh, fuelCostAddon,
                boost::shared_ptr<FdmInnerValueCalculator>(
                    new PathFuelPrice(path.value, fuelShape)),
                boost::shared_ptr<FdmInnerValueCalculator>(
                    new PathSparkSpreadPrice(heatRate, path.value,
                                             fuelShape, powerShape))));

        Array state(nStates, 0.0);
        for (Size j=exercise->dates().size(); j > 0; --j) {
            stepCondition->applyTo(state, grid.at(j));
            state*=rTS->discount(grid.at(j))/rTS->discount(grid.at(j-1));
        }

        npv.add(state.back());
    }
    Real npvMC = npv.mean();
    Real errorMC = npv.errorEstimate();
    const Real expectedMC = 5250.0;
    if (std::fabs(npvMC-expectedMC) > 3*errorMC) {
        BOOST_ERROR("Failed to reproduce Monte-Carlo price"
                   << "\n    calculated: " << npvMC
                   << "\n    error     ; " << errorMC
                   << "\n    expected  : " << expectedMC);
    }
    npv.reset();

    // Test: Longstaff Schwartz least square Monte-Carlo
    const Size nCalibrationTrails = 1000u;
    std::vector<sample_type> calibrationPaths;
    std::vector<boost::shared_ptr<FdmVPPStepCondition> > stepConditions;
    std::vector<boost::shared_ptr<FdmInnerValueCalculator> > sparkSpreads;

    sparkSpreads.reserve(nCalibrationTrails);
    stepConditions.reserve(nCalibrationTrails);
    calibrationPaths.reserve(nCalibrationTrails);

    for (Size i=0; i < nCalibrationTrails; ++i) {
        calibrationPaths.push_back(generator.next());

        sparkSpreads.push_back(boost::shared_ptr<FdmInnerValueCalculator>(
            new PathSparkSpreadPrice(heatRate, calibrationPaths.back().value,
                                     fuelShape, powerShape)));
        stepConditions.push_back(stepConditionFactory.build(
            vppMesh, fuelCostAddon,
            boost::shared_ptr<FdmInnerValueCalculator>(
                new PathFuelPrice(calibrationPaths.back().value, fuelShape)),
            sparkSpreads.back()));
    }


    const FdmLinearOpIterator iter = oneDimMesher->layout()->begin();

    // prices of all calibration paths for all states
    std::vector<Array> prices(nCalibrationTrails, Array(nStates, 0.0));

    // regression coefficients for all states and all exercise dates
    std::vector<std::vector<Array> > coeff(
        nStates, std::vector<Array>(exercise->dates().size(), Array()));

    // regression functions
    const Size dim = 1u;
    std::vector<boost::function1<Real, Array> > v(
        LsmBasisSystem::multiPathBasisSystem(dim,5u, LsmBasisSystem::Monomial));

    for (Size i=exercise->dates().size(); i > 0u; --i) {
        const Time t = grid.at(i);

        std::vector<Array> x(nCalibrationTrails, Array(dim));

        for (Size j=0; j < nCalibrationTrails; ++j) {
            x[j][0] = sparkSpreads[j]->innerValue(iter, t);
        }

        for (Size k=0; k < nStates; ++k) {
            std::vector<Real> y(nCalibrationTrails);

            for (Size j=0; j < nCalibrationTrails; ++j) {
                y[j] = prices[j][k];
            }
            coeff[k][i-1] = GeneralLinearLeastSquares(x, y, v).coefficients();

            for (Size j=0; j < nCalibrationTrails; ++j) {
                prices[j][k] = 0.0;
                for (Size l=0; l < v.size(); ++l) {
                    prices[j][k] += coeff[k][i-1][l]*v[l](x[j]);
                }
            }
        }

        for (Size j=0; j < nCalibrationTrails; ++j) {
            stepConditions[j]->applyTo(prices[j], grid.at(i));
        }
    }

    Real tmpValue = 0.0;
    for (Size i=0; i < nTrails; ++i) {
        Array x(dim), state(nStates, 0.0), contState(nStates, 0.0);

        const sample_type& path = (i % 2) ? generator.antithetic()
                                          : generator.next();

        const boost::shared_ptr<FdmInnerValueCalculator> fuelPrices(
            new PathFuelPrice(path.value, fuelShape));

        const boost::shared_ptr<FdmInnerValueCalculator> sparkSpreads(
            new PathSparkSpreadPrice(heatRate, path.value,
                                     fuelShape, powerShape));

        for (Size j=exercise->dates().size(); j > 0u; --j) {
            const Time t = grid.at(j);
            const Real fuelPrice = fuelPrices->innerValue(iter, t);
            const Real sparkSpread = sparkSpreads->innerValue(iter, t);
            const Real startUpCost
                    = startUpFixCost + (fuelPrice + fuelCostAddon)*startUpFuel;

            x[0] = sparkSpread;
            for (Size k=0; k < nStates; ++k) {
                contState[k] = 0.0;
                for (Size l=0; l < v.size(); ++l) {
                    contState[k] += coeff[k][j-1][l]*v[l](x);
                }
            }

            const Real pMinFlow = pMin*(sparkSpread - heatRate*fuelCostAddon);
            const Real pMaxFlow = pMax*(sparkSpread - heatRate*fuelCostAddon);

            // rollback continuation states and the path states
            for (Size i=0; i < 2*tMinUp; ++i) {
                if (i < tMinUp) {
                    state[i]    += pMinFlow;
                    contState[i]+= pMinFlow;
                }
                else {
                    state[i]    += pMaxFlow;
                    contState[i]+= pMaxFlow;
                }
            }

            // dynamic programming using the continuation values
            Array retVal(nStates);
            for (Size i=0; i < tMinUp-1; ++i) {
                retVal[i] = retVal[tMinUp + i]
                          = (contState[i+1] > contState[tMinUp + i+1])?
                                          state[i+1] : state[tMinUp + i+1];
            }

            if (contState[2*tMinUp] >=
                std::max(contState[tMinUp-1], contState[2*tMinUp-1])) {
                retVal[tMinUp-1] = retVal[2*tMinUp-1] = state[2*tMinUp];
            }
            else if (contState[tMinUp-1] >= contState[2*tMinUp-1]) {
                retVal[tMinUp-1] = retVal[2*tMinUp-1] = state[tMinUp-1];
            }
            else {
                retVal[tMinUp-1] = retVal[2*tMinUp-1] = state[2*tMinUp-1];
            }

            for (Size i=0; i < tMinDown-1; ++i) {
                retVal[2*tMinUp + i] = state[2*tMinUp + i+1];
            }

            if (contState.back() >=
                std::max(contState.front(), contState[tMinUp]) - startUpCost) {
                retVal.back() = state.back();
            }
            else if (contState.front() >  contState[tMinUp]) {
                retVal.back() = state.front()-startUpCost;
            }
            else {
                retVal.back() = state[tMinUp]-startUpCost;
            }
            state = retVal;
        }
        tmpValue+=0.5*state.back();
        if ((i%2)) {
            npv.add(tmpValue, 1.0);
            tmpValue = 0.0;
        }
    }

    npvMC = npv.mean();
    errorMC = npv.errorEstimate();
    if (std::fabs(npvMC-fdmPrice) > 3*errorMC) {
        BOOST_ERROR("Failed to reproduce Least Square Monte-Carlo price"
                   << "\n    calculated   : " << npvMC
                   << "\n    error        : " << errorMC
                   << "\n    expected FDM : " << fdmPrice);
    }
}
Beispiel #4
0
void VPPTest::testKlugeExtOUSpreadOption() {

    BOOST_TEST_MESSAGE("Testing Simple Kluge ext-Ornstein-Uhlenbeck spread option...");

    SavedSettings backup;

    Date settlementDate = Date(18, December, 2011);
    Settings::instance().evaluationDate() = settlementDate;

    DayCounter dayCounter = ActualActual();
    Date maturityDate = settlementDate + Period(1, Years);
    Time maturity = dayCounter.yearFraction(settlementDate, maturityDate);

    const Real speed     = 1.0;
    const Volatility vol = std::sqrt(1.4);
    const Real betaG     = 0.0;
    const Real alphaG    = 3.0;
    const Real x0G       = 3.0;

    const Rate irRate      = 0.0;
    const Real heatRate    = 2.0;
    const Real rho         = 0.5;

    boost::shared_ptr<ExtOUWithJumpsProcess>
                                           klugeProcess = createKlugeProcess();
    boost::function<Real (Real)> f = alphaG + betaG*boost::lambda::_1;

    boost::shared_ptr<ExtendedOrnsteinUhlenbeckProcess> extOUProcess(
        new ExtendedOrnsteinUhlenbeckProcess(speed, vol, x0G, f,
                           ExtendedOrnsteinUhlenbeckProcess::Trapezodial));

    boost::shared_ptr<YieldTermStructure> rTS(
                                flatRate(settlementDate, irRate, dayCounter));

    boost::shared_ptr<KlugeExtOUProcess> klugeOUProcess(
                    new KlugeExtOUProcess(rho, klugeProcess, extOUProcess));


    boost::shared_ptr<Payoff> payoff(new PlainVanillaPayoff(Option::Call, 0.0));

    Array spreadFactors(2);
    spreadFactors[0] = 1.0; spreadFactors[1] = -heatRate;
    boost::shared_ptr<BasketPayoff> basketPayoff(
                               new AverageBasketPayoff(payoff, spreadFactors));

    boost::shared_ptr<Exercise> exercise(new EuropeanExercise(maturityDate));

    BasketOption option(basketPayoff, exercise);
    option.setPricingEngine(boost::shared_ptr<PricingEngine>(
        new FdKlugeExtOUSpreadEngine(klugeOUProcess, rTS,
                                     5, 200, 50, 20)));

    TimeGrid grid(maturity, 50);
    typedef PseudoRandom::rsg_type rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;

    rsg_type rsg = PseudoRandom::make_sequence_generator(
                           klugeOUProcess->factors()*(grid.size()-1), 1234ul);

    MultiPathGenerator<rsg_type> generator(klugeOUProcess, grid, rsg, false);


    GeneralStatistics npv;
    const Size nTrails = 20000;
    for (Size i=0; i < nTrails; ++i) {
        const sample_type path = generator.next();

        Array p(2);
        p[0] = path.value[0].back() + path.value[1].back();
        p[1] = path.value[2].back();
        npv.add((*basketPayoff)(Exp(p)));
    }

    const Real calculated = option.NPV();
    const Real expectedMC = npv.mean();
    const Real mcError = npv.errorEstimate();
    if (std::fabs(expectedMC - calculated) > 3*mcError) {
            BOOST_ERROR("Failed to reproduce referenc values"
                       << "\n    calculated:   " << calculated
                       << "\n    expected(MC): " << expectedMC
                       << "\n    mc error    : " << mcError);

    }
}
    CatBond::results MonteCarloCatBondEngine::npv(bool includeSettlementDateFlows, 
												  Date settlementDate, 
									              Date npvDate) const
    {
	    const size_t MAX_PATHS = 10000; //TODO
		CatBond::results result;
		result.reset();

        double lossProbability =  0.0;
        double exhaustionProbability = 0.0;
        double expectedLoss = 0.0;
		//GenericRiskStatistics<GeneralStatistics> statistics;
		GeneralStatistics statistics;
        if (arguments_.cashflows.empty()) {
			 return result;
		}

        if (settlementDate == Date())
            settlementDate = Settings::instance().evaluationDate();

        if (npvDate == Date())
            npvDate = settlementDate;

        Real totalNPV = 0.0;
        Date effectiveDate = std::max(arguments_.startDate, settlementDate);
        Date maturityDate = (*arguments_.cashflows.rbegin())->date();
        auto catSimulation = catRisk_->newSimulation(effectiveDate, maturityDate);
        std::vector<std::pair<Date, Real> > eventsPath;
        NotionalPath notionalPath;
        Real riskFreeNPV = pathNpv(includeSettlementDateFlows, settlementDate, notionalPath);
        size_t pathCount=0;
        while(catSimulation->nextPath(eventsPath) && pathCount<MAX_PATHS)
        {
            arguments_.notionalRisk->updatePath(eventsPath, notionalPath);
            if(notionalPath.loss()>0) { //optimization, most paths will not include any loss
				double thisNpv = pathNpv(includeSettlementDateFlows, settlementDate, notionalPath);
                totalNPV += thisNpv;
                lossProbability+=1;
                if (notionalPath.loss()==1) 
                    exhaustionProbability+=1;
                expectedLoss+=notionalPath.loss();
				statistics.add(thisNpv, 1.0);
            } else {
                totalNPV += riskFreeNPV;
				statistics.add(riskFreeNPV, 1.0);
            }
            pathCount++;
        }
        lossProbability/=pathCount;
        exhaustionProbability/=pathCount;
        expectedLoss/=pathCount;

		result.valuationDate = settlementDate;

		result.value = totalNPV/(pathCount*discountCurve_->discount(npvDate));
		result.settlementValue = result.value;
		result.lossProbability = lossProbability;
		result.exhaustionProbability = exhaustionProbability;
		result.expectedLoss = expectedLoss;

		if(Null<Real>()!=varLevel_) {
			result.var = result.value - statistics.percentile(1.0-varLevel_);
			result.stdDev = statistics.standardDeviation();
			result.skew = statistics.skewness();
			result.kurtosis = statistics.kurtosis();
		}
        return result;
    }
void SwingOptionTest::testExtOUJumpSwingOption() {

    BOOST_TEST_MESSAGE("Testing simple swing option pricing for Kluge model...");

    SavedSettings backup;

    Date settlementDate = Date::todaysDate();
    Settings::instance().evaluationDate() = settlementDate;
    DayCounter dayCounter = ActualActual();
    Date maturityDate = settlementDate + Period(12, Months);

    boost::shared_ptr<StrikedTypePayoff> payoff(
                                     new PlainVanillaPayoff(Option::Put, 30));

    std::vector<Date> exerciseDates(1, settlementDate+Period(1, Months));
    while (exerciseDates.back() < maturityDate) {
        exerciseDates.push_back(exerciseDates.back()+Period(1, Months));
    }
    boost::shared_ptr<SwingExercise> swingExercise(
                                            new SwingExercise(exerciseDates));

    std::vector<Time> exerciseTimes(exerciseDates.size());
    for (Size i=0; i < exerciseTimes.size(); ++i) {
        exerciseTimes[i]
                 = dayCounter.yearFraction(settlementDate, exerciseDates[i]);
    }

    TimeGrid grid(exerciseTimes.begin(), exerciseTimes.end(), 60);
    std::vector<Size> exerciseIndex(exerciseDates.size());
    for (Size i=0; i < exerciseIndex.size(); ++i) {
        exerciseIndex[i] = grid.closestIndex(exerciseTimes[i]);
    }

    boost::shared_ptr<ExtOUWithJumpsProcess> jumpProcess = createKlugeProcess();

    const Rate irRate = 0.1;
    boost::shared_ptr<YieldTermStructure> rTS(
                                flatRate(settlementDate, irRate, dayCounter));

    boost::shared_ptr<PricingEngine> swingEngine(
                new FdSimpleExtOUJumpSwingEngine(jumpProcess, rTS, 25, 50, 25));

    boost::shared_ptr<PricingEngine> vanillaEngine(
                new FdExtOUJumpVanillaEngine(jumpProcess, rTS, 25, 50, 25));

    VanillaOption bermudanOption(payoff, swingExercise);
    bermudanOption.setPricingEngine(vanillaEngine);
    const Real bermudanOptionPrices = bermudanOption.NPV();

    const Size nrTrails = 16000;
    typedef PseudoRandom::rsg_type rsg_type;
    typedef MultiPathGenerator<rsg_type>::sample_type sample_type;
    rsg_type rsg = PseudoRandom::make_sequence_generator(
                    jumpProcess->factors()*(grid.size()-1), BigNatural(421));

    MultiPathGenerator<rsg_type> generator(jumpProcess, grid, rsg, false);

    for (Size i=0; i < exerciseDates.size(); ++i) {
        const Size exerciseRights = i+1;

        VanillaSwingOption swingOption(payoff, swingExercise,
                                       exerciseRights, exerciseRights);
        swingOption.setPricingEngine(swingEngine);
        const Real swingOptionPrice = swingOption.NPV();

        const Real upperBound = exerciseRights*bermudanOptionPrices;

        if (swingOptionPrice - upperBound > 2e-2) {
            BOOST_ERROR("Failed to reproduce upper bounds"
                        << "\n    upper Bound: " << upperBound
                        << "\n    Price:       " << swingOptionPrice);
        }

        Real lowerBound = 0.0;
        for (Size j=exerciseDates.size()-i-1; j < exerciseDates.size(); ++j) {
            VanillaOption europeanOption(payoff, boost::shared_ptr<Exercise>(
                                     new EuropeanExercise(exerciseDates[j])));
            europeanOption.setPricingEngine(
                boost::shared_ptr<PricingEngine>(vanillaEngine));
            lowerBound += europeanOption.NPV();
        }

        if (lowerBound - swingOptionPrice > 2e-2) {
            BOOST_ERROR("Failed to reproduce lower bounds"
                       << "\n    lower Bound: " << lowerBound
                       << "\n    Price:       " << swingOptionPrice);
        }

        // use MC plus perfect forecast to find an upper bound
        GeneralStatistics npv;
        for (Size n=0; n < nrTrails; ++n) {
            sample_type path = generator.next();

            std::vector<Real> exerciseValues(exerciseTimes.size());
            for (Size k=0; k < exerciseTimes.size(); ++k) {
                const Real x = path.value[0][exerciseIndex[k]];
                const Real y = path.value[1][exerciseIndex[k]];
                const Real s = std::exp(x+y);

                exerciseValues[k] =(*payoff)(s)*rTS->discount(exerciseDates[k]);
            }
            std::sort(exerciseValues.begin(), exerciseValues.end(),
                      std::greater<Real>());

            Real npCashFlows
                = std::accumulate(exerciseValues.begin(),
                                  exerciseValues.begin()+exerciseRights, 0.0);
            npv.add(npCashFlows);
        }

        const Real mcUpperBound = npv.mean();
        const Real mcErrorUpperBound = npv.errorEstimate();
        if (swingOptionPrice - mcUpperBound > 2.36*mcErrorUpperBound) {
            BOOST_ERROR("Failed to reproduce mc upper bounds"
                       << "\n    mc upper Bound: " << mcUpperBound
                       << "\n    Price:          " << swingOptionPrice);
        }
    }
}