| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /*! |
| 4 | Copyright (C) 2005, 2006 Theo Boafo |
| 5 | Copyright (C) 2006, 2007 StatPro Italia srl |
| 6 | |
| 7 | This file is part of QuantLib, a free-software/open-source library |
| 8 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 9 | |
| 10 | QuantLib is free software: you can redistribute it and/or modify it |
| 11 | under the terms of the QuantLib license. You should have received a |
| 12 | copy of the license along with this program; if not, please email |
| 13 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 14 | <http://quantlib.org/license.shtml>. |
| 15 | |
| 16 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 17 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 18 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 19 | */ |
| 20 | |
| 21 | #include <ql/qldefines.hpp> |
| 22 | #if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC) |
| 23 | # include <ql/auto_link.hpp> |
| 24 | #endif |
| 25 | #include <ql/instruments/bonds/convertiblebonds.hpp> |
| 26 | #include <ql/pricingengines/bond/binomialconvertibleengine.hpp> |
| 27 | #include <ql/time/calendars/target.hpp> |
| 28 | #include <ql/time/daycounters/thirty360.hpp> |
| 29 | #include <ql/utilities/dataformatters.hpp> |
| 30 | |
| 31 | #include <iostream> |
| 32 | #include <iomanip> |
| 33 | |
| 34 | #define LENGTH(a) (sizeof(a)/sizeof(a[0])) |
| 35 | |
| 36 | using namespace QuantLib; |
| 37 | |
| 38 | int main(int, char* []) { |
| 39 | |
| 40 | try { |
| 41 | |
| 42 | std::cout << std::endl; |
| 43 | |
| 44 | Option::Type type(Option::Put); |
| 45 | Real underlying = 36.0; |
| 46 | Real spreadRate = 0.005; |
| 47 | |
| 48 | Spread dividendYield = 0.02; |
| 49 | Rate riskFreeRate = 0.06; |
| 50 | Volatility volatility = 0.20; |
| 51 | |
| 52 | Integer settlementDays = 3; |
| 53 | Integer length = 5; |
| 54 | Real redemption = 100.0; |
| 55 | Real conversionRatio = redemption/underlying; // at the money |
| 56 | |
| 57 | // set up dates/schedules |
| 58 | Calendar calendar = TARGET(); |
| 59 | Date today = calendar.adjust(Date::todaysDate()); |
| 60 | |
| 61 | Settings::instance().evaluationDate() = today; |
| 62 | Date settlementDate = calendar.advance(today, n: settlementDays, unit: Days); |
| 63 | Date exerciseDate = calendar.advance(settlementDate, n: length, unit: Years); |
| 64 | Date issueDate = calendar.advance(exerciseDate, n: -length, unit: Years); |
| 65 | |
| 66 | BusinessDayConvention convention = ModifiedFollowing; |
| 67 | |
| 68 | Frequency frequency = Annual; |
| 69 | |
| 70 | Schedule schedule(issueDate, exerciseDate, |
| 71 | Period(frequency), calendar, |
| 72 | convention, convention, |
| 73 | DateGeneration::Backward, false); |
| 74 | |
| 75 | DividendSchedule dividends; |
| 76 | CallabilitySchedule callability; |
| 77 | |
| 78 | std::vector<Real> coupons(1, 0.05); |
| 79 | |
| 80 | DayCounter bondDayCount = Thirty360(Thirty360::BondBasis); |
| 81 | |
| 82 | Integer callLength[] = { 2, 4 }; // Call dates, years 2, 4. |
| 83 | Integer putLength[] = { 3 }; // Put dates year 3 |
| 84 | |
| 85 | Real callPrices[] = { 101.5, 100.85 }; |
| 86 | Real putPrices[]= { 105.0 }; |
| 87 | |
| 88 | // Load call schedules |
| 89 | for (Size i=0; i<LENGTH(callLength); i++) { |
| 90 | callability.push_back( |
| 91 | x: ext::make_shared<SoftCallability>(args: Bond::Price(callPrices[i], |
| 92 | Bond::Price::Clean), |
| 93 | args: schedule.date(i: callLength[i]), |
| 94 | args: 1.20)); |
| 95 | } |
| 96 | |
| 97 | for (Size j=0; j<LENGTH(putLength); j++) { |
| 98 | callability.push_back( |
| 99 | x: ext::make_shared<Callability>(args: Bond::Price(putPrices[j], |
| 100 | Bond::Price::Clean), |
| 101 | args: Callability::Put, |
| 102 | args: schedule.date(i: putLength[j]))); |
| 103 | } |
| 104 | |
| 105 | // Assume dividends are paid every 6 months. |
| 106 | for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) { |
| 107 | dividends.push_back(x: ext::make_shared<FixedDividend>(args: 1.0, args&: d)); |
| 108 | } |
| 109 | |
| 110 | DayCounter dayCounter = Actual365Fixed(); |
| 111 | Time maturity = dayCounter.yearFraction(d1: settlementDate, |
| 112 | d2: exerciseDate); |
| 113 | |
| 114 | std::cout << "option type = " << type << std::endl; |
| 115 | std::cout << "Time to maturity = " << maturity |
| 116 | << std::endl; |
| 117 | std::cout << "Underlying price = " << underlying |
| 118 | << std::endl; |
| 119 | std::cout << "Risk-free interest rate = " << io::rate(r: riskFreeRate) |
| 120 | << std::endl; |
| 121 | std::cout << "Dividend yield = " << io::rate(r: dividendYield) |
| 122 | << std::endl; |
| 123 | std::cout << "Volatility = " << io::volatility(v: volatility) |
| 124 | << std::endl; |
| 125 | std::cout << std::endl; |
| 126 | |
| 127 | std::string method; |
| 128 | std::cout << std::endl ; |
| 129 | |
| 130 | // write column headings |
| 131 | Size widths[] = { 35, 14, 14 }; |
| 132 | Size totalWidth = widths[0] + widths[1] + widths[2]; |
| 133 | std::string rule(totalWidth, '-'), dblrule(totalWidth, '='); |
| 134 | |
| 135 | std::cout << dblrule << std::endl; |
| 136 | std::cout << "Tsiveriotis-Fernandes method" << std::endl; |
| 137 | std::cout << dblrule << std::endl; |
| 138 | std::cout << std::setw(widths[0]) << std::left << "Tree type" |
| 139 | << std::setw(widths[1]) << std::left << "European" |
| 140 | << std::setw(widths[1]) << std::left << "American" |
| 141 | << std::endl; |
| 142 | std::cout << rule << std::endl; |
| 143 | |
| 144 | auto exercise = ext::make_shared<EuropeanExercise>(args&: exerciseDate); |
| 145 | auto amExercise = ext::make_shared<AmericanExercise>(args&: settlementDate, args&: exerciseDate); |
| 146 | |
| 147 | Handle<Quote> underlyingH(ext::make_shared<SimpleQuote>(args&: underlying)); |
| 148 | |
| 149 | Handle<YieldTermStructure> flatTermStructure( |
| 150 | ext::make_shared<FlatForward>(args&: settlementDate, args&: riskFreeRate, args&: dayCounter)); |
| 151 | |
| 152 | Handle<YieldTermStructure> flatDividendTS( |
| 153 | ext::make_shared<FlatForward>(args&: settlementDate, args&: dividendYield, args&: dayCounter)); |
| 154 | |
| 155 | Handle<BlackVolTermStructure> flatVolTS( |
| 156 | ext::make_shared<BlackConstantVol>(args&: settlementDate, args&: calendar, args&: volatility, args&: dayCounter)); |
| 157 | |
| 158 | auto stochasticProcess = ext::make_shared<BlackScholesMertonProcess>( |
| 159 | args&: underlyingH, args&: flatDividendTS, args&: flatTermStructure, args&: flatVolTS); |
| 160 | |
| 161 | Size timeSteps = 801; |
| 162 | |
| 163 | Handle<Quote> creditSpread(ext::make_shared<SimpleQuote>(args&: spreadRate)); |
| 164 | |
| 165 | auto rate = ext::make_shared<SimpleQuote>(args&: riskFreeRate); |
| 166 | |
| 167 | Handle<YieldTermStructure> discountCurve( |
| 168 | ext::make_shared<FlatForward>(args&: today, args: Handle<Quote>(rate), args&: dayCounter)); |
| 169 | |
| 170 | ConvertibleFixedCouponBond europeanBond( |
| 171 | exercise, conversionRatio, callability, |
| 172 | issueDate, settlementDays, |
| 173 | coupons, bondDayCount, schedule, redemption); |
| 174 | |
| 175 | ConvertibleFixedCouponBond americanBond( |
| 176 | amExercise, conversionRatio, callability, |
| 177 | issueDate, settlementDays, |
| 178 | coupons, bondDayCount, schedule, redemption); |
| 179 | |
| 180 | method = "Jarrow-Rudd" ; |
| 181 | auto jrEngine = ext::make_shared<BinomialConvertibleEngine<JarrowRudd>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 182 | europeanBond.setPricingEngine(jrEngine); |
| 183 | americanBond.setPricingEngine(jrEngine); |
| 184 | std::cout << std::setw(widths[0]) << std::left << method |
| 185 | << std::fixed |
| 186 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 187 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 188 | << std::endl; |
| 189 | |
| 190 | method = "Cox-Ross-Rubinstein" ; |
| 191 | auto crrEngine = ext::make_shared<BinomialConvertibleEngine<CoxRossRubinstein>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 192 | europeanBond.setPricingEngine(crrEngine); |
| 193 | americanBond.setPricingEngine(crrEngine); |
| 194 | std::cout << std::setw(widths[0]) << std::left << method |
| 195 | << std::fixed |
| 196 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 197 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 198 | << std::endl; |
| 199 | |
| 200 | method = "Additive equiprobabilities" ; |
| 201 | auto aeqpEngine = ext::make_shared<BinomialConvertibleEngine<AdditiveEQPBinomialTree>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 202 | europeanBond.setPricingEngine(aeqpEngine); |
| 203 | americanBond.setPricingEngine(aeqpEngine); |
| 204 | std::cout << std::setw(widths[0]) << std::left << method |
| 205 | << std::fixed |
| 206 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 207 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 208 | << std::endl; |
| 209 | |
| 210 | method = "Trigeorgis" ; |
| 211 | auto trEngine = ext::make_shared<BinomialConvertibleEngine<Trigeorgis>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 212 | europeanBond.setPricingEngine(trEngine); |
| 213 | americanBond.setPricingEngine(trEngine); |
| 214 | std::cout << std::setw(widths[0]) << std::left << method |
| 215 | << std::fixed |
| 216 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 217 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 218 | << std::endl; |
| 219 | |
| 220 | method = "Tian" ; |
| 221 | auto tianEngine = ext::make_shared<BinomialConvertibleEngine<Tian>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 222 | europeanBond.setPricingEngine(tianEngine); |
| 223 | americanBond.setPricingEngine(tianEngine); |
| 224 | std::cout << std::setw(widths[0]) << std::left << method |
| 225 | << std::fixed |
| 226 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 227 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 228 | << std::endl; |
| 229 | |
| 230 | method = "Leisen-Reimer" ; |
| 231 | auto lrEngine = ext::make_shared<BinomialConvertibleEngine<LeisenReimer>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 232 | europeanBond.setPricingEngine(lrEngine); |
| 233 | americanBond.setPricingEngine(lrEngine); |
| 234 | std::cout << std::setw(widths[0]) << std::left << method |
| 235 | << std::fixed |
| 236 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 237 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 238 | << std::endl; |
| 239 | |
| 240 | method = "Joshi" ; |
| 241 | auto joshiEngine = ext::make_shared<BinomialConvertibleEngine<Joshi4>>(args&: stochasticProcess, args&: timeSteps, args&: creditSpread, args&: dividends); |
| 242 | europeanBond.setPricingEngine(joshiEngine); |
| 243 | americanBond.setPricingEngine(joshiEngine); |
| 244 | std::cout << std::setw(widths[0]) << std::left << method |
| 245 | << std::fixed |
| 246 | << std::setw(widths[1]) << std::left << europeanBond.NPV() |
| 247 | << std::setw(widths[2]) << std::left << americanBond.NPV() |
| 248 | << std::endl; |
| 249 | |
| 250 | std::cout << dblrule << std::endl; |
| 251 | |
| 252 | return 0; |
| 253 | } catch (std::exception& e) { |
| 254 | std::cerr << e.what() << std::endl; |
| 255 | return 1; |
| 256 | } catch (...) { |
| 257 | std::cerr << "unknown error" << std::endl; |
| 258 | return 1; |
| 259 | } |
| 260 | |
| 261 | } |
| 262 | |
| 263 | |