Could anyone give me some ideas of how to price callable bonds in quantlib?
Bond features are: - initial deferred period where the bond is not callable - seperate strike price for exercise in each remaining year of the bond's life. I've seen some discussions that the convertible bond class should be used, but as a newbee I'm a little intimidated by the quantlib terminology. What I'd really need to know is what changes to make to the standard convertible sample code: http://quantlib.org/reference/_convertible_bonds_8cpp-example.html Many thanks. |
Here's where I've got to.
I started with the convertible bond example and: - set conversion ratio to 0.0000001 - removed dividends - removed the puttable bit - set my callable terms But, it seems the market value of the callable bond is unaffected by the choice of strike prices... (I tried high and low values). My program is below, I also have some specific questions on syntax: - what is the 1.20 for in: SoftCallability(Callability::Price( callPrices[i], Callability::Price::Clean), schedule.date(callLength[i]), 1.20))); - why do I get time to maturity as 4.00822 in the output, when i set it to 4? If I set it to 3, then the output says 3 exactly... - what is the 1 for in the coupon definition: std::vector<Real> coupons(1, 0.065)? Is this frequency of coupons per annum? I'll be grateful for any feedback. //// CODE SNIPPET///// boost::timer timer; std::cout << std::endl; Option::Type type(Option::Call); Real underlying = 36.0; Real spreadRate = 0.;//0.005; Spread dividendYield = 0.; //0.02; Rate riskFreeRate = 0.05; Volatility volatility = 0.10; Integer settlementDays = 0; Integer length = 4; Real redemption = 100.0; Real conversionRatio = 0.0000001; //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.065); DayCounter bondDayCount =Actual365Fixed(); // Thirty360(); Integer callLength[] = { 1, 2, 3 }; // Call dates, years 2, 4. // Integer putLength[] = { 3 }; // Put dates year 3 // these need to be less than npv of redemption and future coupons at 5% to bite! Real callPrices[] = { 140.0, 190.0, 102.0 }; // 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])))); } */ 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;
|
Tibbar: the QL convertible bond doesn't consider stochastic interest
rates, just stochastic stock prices. I think the volatility you entered below is for the latter, not the former. So I don't think it will reduce to a callable bond (where the optionality is a function of interest rate volatility). Nice project would be to integrate this feature into the convertible bond class- best way, I suppose, is to go back to the 2-D Black Scholes. Bloomberg also has a 2-D model. I don't think the TF or Ayache models naturally extend to stochastic interest rates. Was working on the generic 2-D PDE solver, but I got sidetracked with work. Callable bonds should come out in the next release- I can send you the pre-release code if you want. Luigi: Can I get this email address added to [hidden email] ? GZH On 3/8/08, tibbar <[hidden email]> wrote: > > Here's where I've got to. > > I started with the convertible bond example and: > > - set conversion ratio to 0.0000001 > - removed dividends > - removed the puttable bit > - set my callable terms > > But, it seems the market value of the callable bond is unaffected by the > choice of strike prices... (I tried high and low values). > > My program is below, I also have some specific questions on syntax: > > - what is the 1.20 for in: > SoftCallability(Callability::Price( > callPrices[i], > > Callability::Price::Clean), > schedule.date(callLength[i]), > 1.20))); > > - why do I get time to maturity as 4.00822 in the output, when i set it to > 4? If I set it to 3, then the output says 3 exactly... > > - what is the 1 for in the coupon definition: std::vector<Real> coupons(1, > 0.065)? Is this frequency of coupons per annum? > > I'll be grateful for any feedback. > > //// CODE SNIPPET///// > boost::timer timer; > std::cout << std::endl; > > Option::Type type(Option::Call); > Real underlying = 36.0; > Real spreadRate = 0.;//0.005; > > Spread dividendYield = 0.; //0.02; > Rate riskFreeRate = 0.05; > Volatility volatility = 0.10; > > Integer settlementDays = 0; > Integer length = 4; > Real redemption = 100.0; > Real conversionRatio = 0.0000001; //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.065); > > DayCounter bondDayCount =Actual365Fixed(); // Thirty360(); > > Integer callLength[] = { 1, 2, 3 }; // Call dates, years 2, 4. > // Integer putLength[] = { 3 }; // Put dates year 3 > > // these need to be less than npv of redemption and future coupons at 5% > to bite! > Real callPrices[] = { 140.0, 190.0, 102.0 }; > // 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])))); > } > */ > > 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 underlyingH( > boost::shared_ptr(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 creditSpread( > boost::shared_ptr(new SimpleQuote(spreadRate))); > > boost::shared_ptr rate(new SimpleQuote(riskFreeRate)); > > Handle<YieldTermStructure> discountCurve( > boost::shared_ptr<YieldTermStructure>( > new FlatForward(today, Handle(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; > > > > > tibbar wrote: > > > > Could anyone give me some ideas of how to price callable bonds in > > quantlib? > > > > Bond features are: > > > > - initial deferred period where the bond is not callable > > - seperate strike price for exercise in each remaining year of the bond's > > life. > > > > I've seen some discussions that the convertible bond class should be used, > > but as a newbee I'm a little intimidated by the quantlib terminology. > > > > What I'd really need to know is what changes to make to the standard > > convertible sample code: > > > > http://quantlib.org/reference/_convertible_bonds_8cpp-example.html > > > > Many thanks. > > > > -- > View this message in context: http://www.nabble.com/callable-bonds-tp15903645p15910210.html > Sent from the quantlib-dev mailing list archive at Nabble.com. > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Microsoft > Defy all challenges. Microsoft(R) Visual Studio 2008. > http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ > _______________________________________________ > QuantLib-dev mailing list > [hidden email] > https://lists.sourceforge.net/lists/listinfo/quantlib-dev > ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ QuantLib-dev mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-dev |
If you could send the code that would be fantastic - I can do some testing to verify the results.
I think ideally as you say, the convertible class should also provide this functionality, as it is only showing part of the option value. Thanks
|
In reply to this post by Allen Kuo
If you could send the code that would be fantastic - I can do some testing to verify the results.
I think ideally as you say, the convertible class should also provide this functionality, as it is only showing part of the option value. Thanks
|
In reply to this post by tibbar
On Fri, 2008-03-07 at 11:56 -0800, tibbar wrote:
> Could anyone give me some ideas of how to price callable bonds in quantlib? > > Bond features are: > > - initial deferred period where the bond is not callable > - seperate strike price for exercise in each remaining year of the bond's > life. > > I've seen some discussions that the convertible bond class should be used, > but as a newbee I'm a little intimidated by the quantlib terminology. As already pointed out, the ConvertibleBond class is not the right one to use. Instead, I would start from the Swaption class (together with its TreeSwaptionEngine) and see how it works. You can copy the engine and modify it so that it takes into account a single sequence of cash flows, instead of two legs. Feel free to write to the list if you need any help in understanding the Swaption or TreeSwaptionEngine class. Later, Luigi -- There is no opinion so absurd that some philosopher will not express it. -- Marcus Tullius Cicero, "Ad familiares" ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ QuantLib-dev mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-dev |
Free forum by Nabble | Edit this page |