PathGeneratorFactory::PathGeneratorFactory(const boost::shared_ptr<StochasticProcessArray>& processes,
										   const TimeGrid& timeGrid)
: processes_(processes), grid_(timeGrid)
{
	unsigned long myseed = static_cast<unsigned long>(1);
	rand_ = std::tr1::mt19937(myseed);

	this->numAssets_ = processes->size();
	this->pathSize_ = timeGrid.size();

	PseudoRandom::rsg_type gen =
		PseudoRandom::make_sequence_generator(numAssets_*(timeGrid.size()-1),1);

	gen_ = boost::shared_ptr<MultiVariate<PseudoRandom>::path_generator_type>(new MultiVariate<PseudoRandom>::path_generator_type(processes,
                                timeGrid, gen, false));
				
	this->next_ = MultiPath(numAssets_,this->grid_);

	S0_ = std::valarray<double>(numAssets_);
	randArrs_ = std::valarray<double>(numAssets_);

	//this->testRandom_ = Array(numAssets_ * pathSize_);
	previousRand_ = Matrix(numAssets_, pathSize_);

	Matrix corr = processes->correlation();
	chol_=CholeskyDecomposition(corr);

	random_ = MultiPath(numAssets_,timeGrid);

	// num - 1
	this->muGrid_ = Matrix(numAssets_, timeGrid.size() - 1);
	this->volGrid_ = Matrix(numAssets_,timeGrid.size() - 1);

	antitheticFlag_ = false;

	for (Size asset = 0 ; asset<numAssets_ ;++asset)
	{
		//초기화 수익률 or 절대값
		S0_[asset] = processes->process(asset)->x0() / processes->process(asset)->basePrice();

		for (Size t = 0 ; t < pathSize_ - 1 ;++t)
		{
			double mu_t = processes->process(asset)->drift(timeGrid[t],1.0);
			double sigma_t = processes->process(asset)->diffusion(timeGrid[t],1.0);
			double dt_t = timeGrid.dt(t);

			// exp( ( mu[t] - 0.5 * vol[t] * vol[t] ) * dt[t] )
			muGrid_[asset][t] = std::exp( ( mu_t - 0.5 * sigma_t * sigma_t ) * dt_t );

			//  vol[t] * sqrt(dt[t]) 
			volGrid_[asset][t] = sigma_t * std::sqrt(dt_t);
		}

	}
}
示例#2
0
    boost::shared_ptr<Lattice> HullWhite::tree(const TimeGrid& grid) const {

        TermStructureFittingParameter phi(termStructure());
        boost::shared_ptr<ShortRateDynamics> numericDynamics(
                                             new Dynamics(phi, a(), sigma()));
        boost::shared_ptr<TrinomialTree> trinomial(
                         new TrinomialTree(numericDynamics->process(), grid));
        boost::shared_ptr<ShortRateTree> numericTree(
                         new ShortRateTree(trinomial, numericDynamics, grid));

        typedef TermStructureFittingParameter::NumericalImpl NumericalImpl;
        boost::shared_ptr<NumericalImpl> impl =
            boost::dynamic_pointer_cast<NumericalImpl>(phi.implementation());
        impl->reset();
        for (Size i=0; i<(grid.size() - 1); i++) {
            Real discountBond = termStructure()->discount(grid[i+1]);
            const Array& statePrices = numericTree->statePrices(i);
            Size size = numericTree->size(i);
            Time dt = numericTree->timeGrid().dt(i);
            Real dx = trinomial->dx(i);
            Real x = trinomial->underlying(i,0);
            Real value = 0.0;
            for (Size j=0; j<size; j++) {
                value += statePrices[j]*std::exp(-x*dt);
                x += dx;
            }
            value = std::log(value/discountBond)/dt;
            impl->set(grid[i], value);
        }
        return numericTree;
    }
        boost::shared_ptr<path_generator_type> pathGenerator() const {

            Size numAssets = processes_->size();

            TimeGrid grid = timeGrid();

			Calendar calendar = this->arguments_.sp_payoff->calendar();
			
            typename RNG::rsg_type gen =
                RNG::make_sequence_generator(numAssets*(grid.size()-1),seed_);

			boost::shared_ptr<path_generator_type> pgPtr 
				= boost::shared_ptr<path_generator_type>(
					       new path_generator_type(processes_,
                                                 grid, 
												 gen));

			PathInformation::instance().initialize(pgPtr->multiPath(),calendar);

			//history setting here..

			//index의 Calendar와 product의 Calendar(calculation Date Calendar)는 따로 가야함
			//과거 index fixing은 각각 클래스에서 fixing하는데 그 방법은 pathmanager가 indexHistory를 가지고 있어서 거기에 문의해서, 
			//따로 클래스 내에 가져와서 fixing 시켜놈. 결과만 저장해서 나중에 함침.
			//그러므로 path에 따로 무언가를 붙일 필요는 없음.
			//MultiPath Grid는 product calendar로 grid숫자 계산함.

            return pgPtr;
        }
示例#4
0
		//! Return shared pointer to path generator who generates sample pathes in simulation
		virtual boost::shared_ptr<path_generator_type> pathGenerator() const {
			Size factors = this->process()->factors();
			TimeGrid grid = timeGrid();
			Size steps = grid.size() - 1;
			typename RNG::rsg_type gen = RNG::make_sequence_generator(factors * steps, seed_);
			return boost::make_shared<path_generator_type>(this->process(), grid, gen, false);
		}
示例#5
0
    inline
    ext::shared_ptr<typename MCDigitalEngine<RNG,S>::path_pricer_type>
    MCDigitalEngine<RNG,S>::pathPricer() const {

        ext::shared_ptr<CashOrNothingPayoff> payoff =
            ext::dynamic_pointer_cast<CashOrNothingPayoff>(
                this->arguments_.payoff);
        QL_REQUIRE(payoff, "wrong payoff given");

        ext::shared_ptr<AmericanExercise> exercise =
            ext::dynamic_pointer_cast<AmericanExercise>(
                this->arguments_.exercise);
        QL_REQUIRE(exercise, "wrong exercise given");

        ext::shared_ptr<GeneralizedBlackScholesProcess> process =
            ext::dynamic_pointer_cast<GeneralizedBlackScholesProcess>(
                                                              this->process_);
        QL_REQUIRE(process, "Black-Scholes process required");

        TimeGrid grid = this->timeGrid();
        PseudoRandom::ursg_type sequenceGen(grid.size()-1,
                                            PseudoRandom::urng_type(76));

        return ext::shared_ptr<
                        typename MCDigitalEngine<RNG,S>::path_pricer_type>(
          new DigitalPathPricer(payoff,
                                exercise,
                                process->riskFreeRate(),
                                process,
                                sequenceGen));
    }
示例#6
0
    boost::shared_ptr<Lattice>
    BlackKarasinski::tree(const TimeGrid& grid) const {

        TermStructureFittingParameter phi(termStructure());

        boost::shared_ptr<ShortRateDynamics> numericDynamics(
                                             new Dynamics(phi, a(), sigma()));

        boost::shared_ptr<TrinomialTree> trinomial(
                         new TrinomialTree(numericDynamics->process(), grid));
        boost::shared_ptr<ShortRateTree> numericTree(
                         new ShortRateTree(trinomial, numericDynamics, grid));

        typedef TermStructureFittingParameter::NumericalImpl NumericalImpl;
        boost::shared_ptr<NumericalImpl> impl =
            boost::dynamic_pointer_cast<NumericalImpl>(phi.implementation());
        impl->reset();
        Real value = 1.0;
        Real vMin = -50.0;
        Real vMax = 50.0;
        for (Size i=0; i<(grid.size() - 1); i++) {
            Real discountBond = termStructure()->discount(grid[i+1]);
            Real xMin = trinomial->underlying(i, 0);
            Real dx = trinomial->dx(i);
            Helper finder(i, xMin, dx, discountBond, numericTree);
            Brent s1d;
            s1d.setMaxEvaluations(1000);
            value = s1d.solve(finder, 1e-7, value, vMin, vMax);
            impl->set(grid[i], value);
            // vMin = value - 10.0;
            // vMax = value + 10.0;
        }
        return numericTree;
    }
    template <class PathType> inline
    LongstaffSchwartzPathPricer<PathType>::LongstaffSchwartzPathPricer(
        const TimeGrid& times,
        const boost::shared_ptr<EarlyExercisePathPricer<PathType> >&
            pathPricer,
        const boost::shared_ptr<YieldTermStructure>& termStructure)
    : calibrationPhase_(true),
      pathPricer_(pathPricer),
      coeff_     (new Array[times.size()-1]),
      dF_        (new DiscountFactor[times.size()-1]),
      v_         (pathPricer_->basisSystem()) {

        for (Size i=0; i<times.size()-1; ++i) {
            dF_[i] =   termStructure->discount(times[i+1])
                     / termStructure->discount(times[i]);
        }
    }
        boost::shared_ptr<path_generator_type> pathGenerator() const {

            TimeGrid grid = this->timeGrid();
            typename RNG::rsg_type gen =
                RNG::make_sequence_generator(grid.size()-1,seed_);
            return boost::shared_ptr<path_generator_type>(
                         new path_generator_type(process_, grid,
                                                 gen, brownianBridge_));
        }
示例#9
0
        boost::shared_ptr<path_generator_type> pathGenerator() const {

            Size dimensions = process_->factors();
            TimeGrid grid = this->timeGrid();
            typename RNG::rsg_type generator =
                RNG::make_sequence_generator(dimensions*(grid.size()-1),seed_);
            return boost::shared_ptr<path_generator_type>(
                   new path_generator_type(process_, grid,
                                           generator, brownianBridge_));
        }
void HifivePricer::initializeImpl(const TimeGrid& timeGrid,
									   const boost::shared_ptr<YieldTermStructure>& discountCurve,
									   const boost::shared_ptr<PathGeneratorFactory>& pathGenFactory)
{
	//this->discount_ = Array(eventTriggerList_.size(),1.0);

	//for ( Size i = 0 ; i < eventTriggerList_.size() ; i++ )
	//{
	//	eventTriggerList_[i]->initialize(timeGrid,discountCurve);

	//	if ( eventTriggerList_[i]->isExpired() )
	//	{
	//		remainEventPosition_ = i + 1;
	//		this->discount_[i] = 1.0;
	//	}else
	//	{
	//		this->discount_[i] = discountCurve_->discount(eventTriggerList_[i]->payoffDate());
	//	}
	//}

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

	remainEventPosition_ = 0;
	autoCallEventPosition_ = timeGrid.size() - 1;

	this->cf_results_ = std::vector<boost::shared_ptr<CashFlowResult>>( this->eventTriggerList_.size() );
	this->calcValue_results_ = std::vector<boost::shared_ptr<CalcValueResult>>(1);

	this->calcValue_results_[0] = boost::shared_ptr<CalcValueResult>(
									new CalcValueResult("NET","KRW"));

	for ( Size i = 0 ; i < eventTriggerList_.size() ; i++ )
	{
		this->eventTriggerList_[i]->initialize(timeGrid,discountCurve,pathGenFactory);
		this->cf_results_[i] =  boost::shared_ptr<CashFlowResult>( 
							new CashFlowResult(i+1,
											   this->eventTriggerList_[i]->getEventDate(),
											   this->eventTriggerList_[i]->getPayoffDate(),
											   "NET",
											   this->currency_,
											   this->payoffDateInfo_->discount()) );

		bool kk = eventTriggerList_[i]->isExpired();

		if ( eventTriggerList_[i]->isExpired() )
		{
			remainEventPosition_ = i + 1;
		}
	}

	this->kiBarrierEventTrigger_->initialize(timeGrid,discountCurve,pathGenFactory);
	//this->nonOccBarrierEventTrigger_->initialize(timeGrid,discountCurve,pathGenFactory);
	//pastkiBarrierEventOcc_ = kiBarrierEventTrigger_->pastEventOcc();

}
    MultiPathGeneratorFixedPath<GSG>::MultiPathGeneratorFixedPath(
                   const boost::shared_ptr<StochasticProcess>& process,
                   const TimeGrid& times,
                   GSG generator,
                   bool brownianBridge)
    : brownianBridge_(brownianBridge), process_(process),
      generator_(generator), next_(boost::shared_ptr<MultiPath>(new MultiPath(process->size(), times)))
	{
		nextPtr_ = next_.get();

        QL_REQUIRE(generator_.dimension() ==
                   process->factors()*(times.size()-1),
                   "dimension (" << generator_.dimension()
                   << ") is not equal to ("
                   << process->factors() << " * " << times.size()-1
                   << ") the number of factors "
                   << "times the number of time steps");
        QL_REQUIRE(times.size() > 1,
                   "no times given");
    }
示例#12
0
        ext::shared_ptr<path_generator_type> pathGenerator() const {

            Size numAssets = processes_->size();

            TimeGrid grid = timeGrid();
            typename RNG::rsg_type gen =
                RNG::make_sequence_generator(numAssets*(grid.size()-1),seed_);

            return ext::shared_ptr<path_generator_type>(
                         new path_generator_type(processes_,
                                                 grid, gen, brownianBridge_));
        }
    inline
    boost::shared_ptr<typename
    MCLongstaffSchwartzEngine<GenericEngine,MC,RNG,S>::path_generator_type>
    MCLongstaffSchwartzEngine<GenericEngine,MC,RNG,S>::pathGenerator() const {

        Size dimensions = process_->factors();
        TimeGrid grid = this->timeGrid();
        typename RNG::rsg_type generator =
            RNG::make_sequence_generator(dimensions*(grid.size()-1),seed_);
        return boost::shared_ptr<path_generator_type>(
                   new path_generator_type(process_,
                                           grid, generator, brownianBridge_));
    }
示例#14
0
    MultiPathGenerator<GSG>::MultiPathGenerator(
                   const boost::shared_ptr<StochasticProcess>& process,
                   const TimeGrid& times,
                   GSG generator,
                   bool brownianBridge)
    : brownianBridge_(brownianBridge), process_(process),
      generator_(generator), next_(MultiPath(process->size(), times), 1.0) {

		//std::cout << "dimension (" << generator_.dimension()
		//	<< ") is not equal to ("
		//	<< process->factors() << " * " << times.size() - 1
		//	<< ") the number of factors "
		//	<< "times the number of time steps";

        QL_REQUIRE(generator_.dimension() ==
                   process->factors()*(times.size()-1),
                   "dimension (" << generator_.dimension()
                   << ") is not equal to ("
                   << process->factors() << " * " << times.size()-1
                   << ") the number of factors "
                   << "times the number of time steps");
        QL_REQUIRE(times.size() > 1,
                   "no times given");
    }
示例#15
0
    void simulateMP(const boost::shared_ptr<StochasticProcess>& process,
                    int nbPaths, TimeGrid& grid, BigNatural seed,
                    bool antithetic_variates, double *res) {

        typedef PseudoRandom::rsg_type rsg_type;
        typedef MultiPathGenerator<rsg_type>::sample_type sample_type;
        Size assets = process->factors();
        rsg_type rsg = PseudoRandom::make_sequence_generator(
            (grid.size() - 1) * assets, seed);

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

        // fill simulated paths
        for (int i=0; i<nbPaths; ++i) {
            const bool antithetic = (i%2)==0 ? false : true;

            sample_type sample = antithetic_variates ?
                (antithetic ? generator.antithetic(): generator.next()) :
                generator.next();

            Path p1 = sample.value[0];
            std::copy(p1.begin(), p1.end(), res + i * grid.size());
        }
    }
        boost::shared_ptr<path_generator_type> pathGenerator() const {

            boost::shared_ptr<BasketPayoff> payoff =
                boost::dynamic_pointer_cast<BasketPayoff>(
                                                          arguments_.payoff);
            QL_REQUIRE(payoff, "non-basket payoff given");

            Size numAssets = processes_->size();

            TimeGrid grid = timeGrid();
            typename RNG::rsg_type gen =
                RNG::make_sequence_generator(numAssets*(grid.size()-1),seed_);

            return boost::shared_ptr<path_generator_type>(
                         new path_generator_type(processes_,
                                                 grid, gen, brownianBridge_));
        }
    inline
    boost::shared_ptr<typename MCPathBasketEngine<RNG,S>::path_generator_type>
    MCPathBasketEngine<RNG,S>::pathGenerator() const {

        boost::shared_ptr<PathPayoff> payoff = arguments_.payoff;
        QL_REQUIRE(payoff, "non-basket payoff given");

        Size numAssets = process_->size();

        TimeGrid grid = timeGrid();

        typename RNG::rsg_type gen =
            RNG::make_sequence_generator(numAssets * (grid.size() - 1), seed_);

        return boost::shared_ptr<path_generator_type>(
                         new path_generator_type(process_,
                                                 grid, gen, brownianBridge_));
    }
示例#18
0
        boost::shared_ptr<path_generator_type> pathGenerator() const {

            Handle<YieldTermStructure> curve = model_->termStructure();
            Date referenceDate = curve->referenceDate();
            DayCounter dayCounter = curve->dayCounter();

            Time forwardMeasureTime =
                dayCounter.yearFraction(referenceDate,
                                        arguments_.endDates.back());
            Array parameters = model_->params();
            Real a = parameters[0], sigma = parameters[1];
            boost::shared_ptr<HullWhiteForwardProcess> process(
                                new HullWhiteForwardProcess(curve, a, sigma));
            process->setForwardMeasureTime(forwardMeasureTime);

            TimeGrid grid = this->timeGrid();
            typename RNG::rsg_type generator =
                RNG::make_sequence_generator(grid.size()-1,seed_);
            return boost::shared_ptr<path_generator_type>(
                             new path_generator_type(process, grid, generator,
                                                     brownianBridge_));
        }
    boost::shared_ptr<path_generator_type> pathGenerator() const {
        if(ifConst) {
            Date exercisedate = GenericEngine<OneAssetOption::arguments,OneAssetOption::results>::arguments_.exercise->lastDate();
            boost::shared_ptr<BlackScholesConstProcess> constProcess_(
                new BlackScholesConstProcess(
                    exercisedate,
                    realProcess->stateVariable(),
                    realProcess->dividendYield(),
                    realProcess->riskFreeRate(),
                    realProcess->blackVolatility()
                ));

            Size dimensions = constProcess_->factors();
            TimeGrid grid = this->timeGrid();
            typename RNG::rsg_type generator =
                RNG::make_sequence_generator(dimensions*(grid.size()-1),seed_);
            return boost::shared_ptr<path_generator_type>(
                       new path_generator_type(constProcess_, grid,
                                               generator, brownianBridge_));

        } else {
            return MCEuropeanEngine<RNG,S>::pathGenerator();
        }
    };
示例#20
0
文件: vpp.cpp 项目: vdt/quantlib-1
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);
    }
}
LongstaffSchwartzProxyPathPricer::LongstaffSchwartzProxyPathPricer(
    const TimeGrid &times,
    const boost::shared_ptr<EarlyExercisePathPricer<PathType> > &pricer,
    const boost::shared_ptr<YieldTermStructure> &termStructure)
    : LongstaffSchwartzPathPricer<PathType>(times, pricer, termStructure),
      coeffItm_(times.size() - 1), coeffOtm_(times.size() - 1) {}