FdmDividendHandler::FdmDividendHandler( const DividendSchedule& schedule, const boost::shared_ptr<FdmMesher>& mesher, const Date& referenceDate, const DayCounter& dayCounter, Size equityDirection) : x_(mesher->layout()->dim()[equityDirection]), mesher_(mesher), equityDirection_(equityDirection) { dividends_.reserve(schedule.size()); dividendDates_.reserve(schedule.size()); dividendTimes_.reserve(schedule.size()); for (DividendSchedule::const_iterator iter=schedule.begin(); iter!=schedule.end(); ++iter) { dividends_.push_back((*iter)->amount()); dividendDates_.push_back((*iter)->date()); dividendTimes_.push_back( dayCounter.yearFraction(referenceDate,(*iter)->date())); } Array tmp = mesher_->locations(equityDirection); Size spacing = mesher_->layout()->spacing()[equityDirection]; for (Size i = 0; i < x_.size(); ++i) { x_[i] = std::exp(tmp[i*spacing]); } }
boost::shared_ptr<FdmStepConditionComposite> FdmStepConditionComposite::vanillaComposite( const DividendSchedule& cashFlow, const boost::shared_ptr<Exercise>& exercise, const boost::shared_ptr<FdmMesher>& mesher, const boost::shared_ptr<FdmInnerValueCalculator>& calculator, const Date& refDate, const DayCounter& dayCounter) { std::list<std::vector<Time> > stoppingTimes; std::list<boost::shared_ptr<StepCondition<Array> > > stepConditions; if(!cashFlow.empty()) { boost::shared_ptr<FdmDividendHandler> dividendCondition( new FdmDividendHandler(cashFlow, mesher, refDate, dayCounter, 0)); stepConditions.push_back(dividendCondition); stoppingTimes.push_back(dividendCondition->dividendTimes()); } QL_REQUIRE( exercise->type() == Exercise::American || exercise->type() == Exercise::European || exercise->type() == Exercise::Bermudan, "exercise type is not supported"); if (exercise->type() == Exercise::American) { stepConditions.push_back(boost::shared_ptr<StepCondition<Array> >( new FdmAmericanStepCondition(mesher,calculator))); } else if (exercise->type() == Exercise::Bermudan) { boost::shared_ptr<FdmBermudanStepCondition> bermudanCondition( new FdmBermudanStepCondition(exercise->dates(), refDate, dayCounter, mesher, calculator)); stepConditions.push_back(bermudanCondition); stoppingTimes.push_back(bermudanCondition->exerciseTimes()); } return boost::shared_ptr<FdmStepConditionComposite>( new FdmStepConditionComposite(stoppingTimes, stepConditions)); }
int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; Option::Type type(Option::Put); Real underlying = 36.0; Real spreadRate = 0.005; Spread dividendYield = 0.02; Rate riskFreeRate = 0.06; Volatility volatility = 0.20; Integer settlementDays = 3; Integer length = 5; Real redemption = 100.0; Real conversionRatio = redemption/underlying; // at the money // set up dates/schedules Calendar calendar = TARGET(); Date today = calendar.adjust(Date::todaysDate()); Settings::instance().evaluationDate() = today; Date settlementDate = calendar.advance(today, settlementDays, Days); Date exerciseDate = calendar.advance(settlementDate, length, Years); Date issueDate = calendar.advance(exerciseDate, -length, Years); BusinessDayConvention convention = ModifiedFollowing; Frequency frequency = Annual; Schedule schedule(issueDate, exerciseDate, Period(frequency), calendar, convention, convention, DateGeneration::Backward, false); DividendSchedule dividends; CallabilitySchedule callability; std::vector<Real> coupons(1, 0.05); DayCounter bondDayCount = Thirty360(); Integer callLength[] = { 2, 4 }; // Call dates, years 2, 4. Integer putLength[] = { 3 }; // Put dates year 3 Real callPrices[] = { 101.5, 100.85 }; Real putPrices[]= { 105.0 }; // Load call schedules for (Size i=0; i<LENGTH(callLength); i++) { callability.push_back( boost::shared_ptr<Callability>( new SoftCallability(Callability::Price( callPrices[i], Callability::Price::Clean), schedule.date(callLength[i]), 1.20))); } for (Size j=0; j<LENGTH(putLength); j++) { callability.push_back( boost::shared_ptr<Callability>( new Callability(Callability::Price( putPrices[j], Callability::Price::Clean), Callability::Put, schedule.date(putLength[j])))); } // Assume dividends are paid every 6 months. for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) { dividends.push_back( boost::shared_ptr<Dividend>(new FixedDividend(1.0, d))); } DayCounter dayCounter = Actual365Fixed(); Time maturity = dayCounter.yearFraction(settlementDate, exerciseDate); std::cout << "option type = " << type << std::endl; std::cout << "Time to maturity = " << maturity << std::endl; std::cout << "Underlying price = " << underlying << 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 }; Size totalWidth = widths[0] + widths[1] + widths[2]; std::string rule(totalWidth, '-'), dblrule(totalWidth, '='); std::cout << dblrule << std::endl; std::cout << "Tsiveriotis-Fernandes method" << std::endl; std::cout << dblrule << std::endl; std::cout << std::setw(widths[0]) << std::left << "Tree type" << std::setw(widths[1]) << std::left << "European" << std::setw(widths[1]) << std::left << "American" << std::endl; std::cout << rule << std::endl; boost::shared_ptr<Exercise> exercise( new EuropeanExercise(exerciseDate)); boost::shared_ptr<Exercise> amExercise( new AmericanExercise(settlementDate, exerciseDate)); Handle<Quote> underlyingH( boost::shared_ptr<Quote>(new SimpleQuote(underlying))); 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<BlackScholesMertonProcess> stochasticProcess( new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS)); Size timeSteps = 801; Handle<Quote> creditSpread( boost::shared_ptr<Quote>(new SimpleQuote(spreadRate))); boost::shared_ptr<Quote> rate(new SimpleQuote(riskFreeRate)); Handle<YieldTermStructure> discountCurve( boost::shared_ptr<YieldTermStructure>( new FlatForward(today, Handle<Quote>(rate), dayCounter))); boost::shared_ptr<PricingEngine> engine( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps)); ConvertibleFixedCouponBond europeanBond( exercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); europeanBond.setPricingEngine(engine); ConvertibleFixedCouponBond americanBond( amExercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); americanBond.setPricingEngine(engine); method = "Jarrow-Rudd"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<JarrowRudd>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Cox-Ross-Rubinstein"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<CoxRossRubinstein>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Additive equiprobabilities"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<AdditiveEQPBinomialTree>( stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<AdditiveEQPBinomialTree>( stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Trigeorgis"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Trigeorgis>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Tian"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Tian>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Tian>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Leisen-Reimer"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<LeisenReimer>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; method = "Joshi"; europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Joshi4>(stochasticProcess, timeSteps))); americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>( new BinomialConvertibleEngine<Joshi4>(stochasticProcess, timeSteps))); std::cout << std::setw(widths[0]) << std::left << method << std::fixed << std::setw(widths[1]) << std::left << europeanBond.NPV() << std::setw(widths[2]) << std::left << americanBond.NPV() << std::endl; std::cout << dblrule << std::endl; Real 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; } }
FdmBlackScholesMesher::FdmBlackScholesMesher( Size size, const ext::shared_ptr<GeneralizedBlackScholesProcess>& process, Time maturity, Real strike, Real xMinConstraint, Real xMaxConstraint, Real eps, Real scaleFactor, const std::pair<Real, Real>& cPoint, const DividendSchedule& dividendSchedule) : Fdm1dMesher(size) { const Real S = process->x0(); QL_REQUIRE(S > 0.0, "negative or null underlying given"); std::vector<std::pair<Time, Real> > intermediateSteps; for (Size i=0; i < dividendSchedule.size() && process->time(dividendSchedule[i]->date()) <= maturity; ++i) intermediateSteps.push_back( std::make_pair( process->time(dividendSchedule[i]->date()), dividendSchedule[i]->amount() ) ); const Size intermediateTimeSteps = std::max<Size>(2, Size(24.0*maturity)); for (Size i=0; i < intermediateTimeSteps; ++i) intermediateSteps.push_back( std::make_pair((i+1)*(maturity/intermediateTimeSteps), 0.0)); std::sort(intermediateSteps.begin(), intermediateSteps.end()); const Handle<YieldTermStructure> rTS = process->riskFreeRate(); const Handle<YieldTermStructure> qTS = process->dividendYield(); Time lastDivTime = 0.0; Real fwd = S, mi = S, ma = S; for (Size i=0; i < intermediateSteps.size(); ++i) { const Time divTime = intermediateSteps[i].first; const Real divAmount = intermediateSteps[i].second; fwd = fwd / rTS->discount(divTime) * rTS->discount(lastDivTime) * qTS->discount(divTime) / qTS->discount(lastDivTime); mi = std::min(mi, fwd); ma = std::max(ma, fwd); fwd-= divAmount; mi = std::min(mi, fwd); ma = std::max(ma, fwd); lastDivTime = divTime; } // Set the grid boundaries const Real normInvEps = InverseCumulativeNormal()(1-eps); const Real sigmaSqrtT = process->blackVolatility()->blackVol(maturity, strike) *std::sqrt(maturity); Real xMin = std::log(mi) - sigmaSqrtT*normInvEps*scaleFactor; Real xMax = std::log(ma) + sigmaSqrtT*normInvEps*scaleFactor; if (xMinConstraint != Null<Real>()) { xMin = xMinConstraint; } if (xMaxConstraint != Null<Real>()) { xMax = xMaxConstraint; } ext::shared_ptr<Fdm1dMesher> helper; if ( cPoint.first != Null<Real>() && std::log(cPoint.first) >=xMin && std::log(cPoint.first) <=xMax) { helper = ext::shared_ptr<Fdm1dMesher>( new Concentrating1dMesher(xMin, xMax, size, std::pair<Real,Real>(std::log(cPoint.first), cPoint.second))); } else { helper = ext::shared_ptr<Fdm1dMesher>( new Uniform1dMesher(xMin, xMax, size)); } locations_ = helper->locations(); for (Size i=0; i < locations_.size(); ++i) { dplus_[i] = helper->dplus(i); dminus_[i] = helper->dminus(i); } }