Posted by
P Nelnik on
URL: http://quantlib.414.s1.nabble.com/possible-bug-in-convertible-bond-pricers-coupon-not-paid-on-conversion-tp8606.html
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