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); } } }
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; }
//! 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); }
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)); }
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_)); }
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"); }
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_)); }
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"); }
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_)); }
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(); } };
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 ×, 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) {}