Good afternoon all,
I took a look at the results from the convertible-bond pricer in Examples/ConvertibleBonds/ and it seems that when the bond is converted, the coupon is not being paid, which as I understand it is NOT correct. If you convert at the end of a coupon period, the coupon should be paid first, then the conversion happens. For example suppose we have stock price = 100 conversion_ratio = 2 redemption = 100 coupon = 50% tenor 1 year volatility = 1% interest_rates = dividends = credit_spread = 0 no callability or putability the value of that bond should be 250 i.e. in this case we will (almost) always convert the bond into 2 shares, so it is basically a forward on 2 * the stock, with no divs or interest rates the coupon of 50% also needs to be paid It may be that I'm just not using the convert pricer correctly, in which case, I'd very much like to find out how to use it correctly. Below I've included the code used to generate the result. Regards Philip P.S. the results I got were: :ConvertibleBonds-vc90-mt.exe option type = Put Time to maturity = 1 Underlying price = 100 Risk-free interest rate = 0.000000 % Dividend yield = 0.000000 % Volatility = 1.000000 % =============================================================== Tsiveriotis-Fernandes method =============================================================== Tree type European American --------------------------------------------------------------- Jarrow-Rudd 200.000000 200.000000 Cox-Ross-Rubinstein 200.000000 200.000000 Additive equiprobabilities 200.000002 200.000002 Trigeorgis 200.000000 200.000000 Tian 200.000000 200.000000 Leisen-Reimer 200.000000 200.000000 Joshi 200.010822 200.010822 =============================================================== Run completed in 1 s And here is the code, it is taken from Examples/ConvertibleBonds/ConvertibleBonds.cpp with just a few minor changes. /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /*! Copyright (C) 2005, 2006 Theo Boafo Copyright (C) 2006, 2007 StatPro Italia srl This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ QuantLib is free software: you can redistribute it and/or modify it under the terms of the QuantLib license. You should have received a copy of the license along with this program; if not, please email <[hidden email]>. The license is also available online at <http://quantlib.org/license.shtml>. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details. */ // the only header you need to use QuantLib #include <ql/quantlib.hpp> #ifdef BOOST_MSVC /* Uncomment the following lines to unmask floating-point exceptions. Warning: unpredictable results can arise... See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481 Is there anyone with a definitive word about this? */ // #include <float.h> // namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); } #endif #include <boost/timer.hpp> #include <iostream> #include <iomanip> #define LENGTH(a) (sizeof(a)/sizeof(a[0])) using namespace QuantLib; #if defined(QL_ENABLE_SESSIONS) namespace QuantLib { Integer sessionId() { return 0; } } #endif int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; Option::Type type(Option::Put); Real underlying = 100.0; Real spreadRate = 0.000; Spread dividendYield = 0.00; Rate riskFreeRate = 0.00; Volatility volatility = 0.01; Integer settlementDays = 0; Integer length = 1; Real redemption = 100.0; Real conversionRatio = 2.0 * redemption/underlying; // 2 * 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.5); // a massive 50% coupon DayCounter bondDayCount = Thirty360(); /* no callability Integer callLength[] = { 1 }; // just one call date at maturity Integer putLength[] = { }; // no put date 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, but here they are zero. for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) { dividends.push_back( boost::shared_ptr<Dividend>(new FixedDividend(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; } } ------------------------------------------------------------------------------ ThinkGeek and WIRED's GeekDad team up for the Ultimate GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the lucky parental unit. See the prize list and enter to win: http://p.sf.net/sfu/thinkgeek-promo _______________________________________________ QuantLib-users mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-users |
Thanks David Klein for the clarification:
____________________________________________________________________________________________ David wrote: I am not really familiar with quantlib, but I am quite knowledgable
in convertible bonds. Though conventions often differ with respecet to
the so called "screw clause" which determines if one receives a coupon
when converting due to a hardcall, the situation with maturity is much
clearer. I don't remember seeing a single prospectus where you can
convert and still receive the coupon at maturity. So quantlib is
handling this aspect correctly.
Yours,
David
On Wed, Jun 9, 2010 at 6:59 PM, P Nelnik <[hidden email]> wrote: Good afternoon all, ------------------------------------------------------------------------------ ThinkGeek and WIRED's GeekDad team up for the Ultimate GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the lucky parental unit. See the prize list and enter to win: http://p.sf.net/sfu/thinkgeek-promo _______________________________________________ QuantLib-users mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-users |
Free forum by Nabble | Edit this page |