public void testRegression() { // Testing fixed-coupon convertible bond in known regression case Date today = new Date(23, Month.December, 2008); Date tomorrow = today + 1; Settings.Instance.setEvaluationDate(tomorrow); Handle <Quote> u = new Handle <Quote>(new SimpleQuote(2.9084382818797443)); List <Date> dates = new InitializedList <Date>(25); List <double> forwards = new InitializedList <double>(25); dates[0] = new Date(29, Month.December, 2008); forwards[0] = 0.0025999342800; dates[1] = new Date(5, Month.January, 2009); forwards[1] = 0.0025999342800; dates[2] = new Date(29, Month.January, 2009); forwards[2] = 0.0053123275500; dates[3] = new Date(27, Month.February, 2009); forwards[3] = 0.0197049598721; dates[4] = new Date(30, Month.March, 2009); forwards[4] = 0.0220524845296; dates[5] = new Date(29, Month.June, 2009); forwards[5] = 0.0217076395643; dates[6] = new Date(29, Month.December, 2009); forwards[6] = 0.0230349627478; dates[7] = new Date(29, Month.December, 2010); forwards[7] = 0.0087631647476; dates[8] = new Date(29, Month.December, 2011); forwards[8] = 0.0219084299499; dates[9] = new Date(31, Month.December, 2012); forwards[9] = 0.0244798766219; dates[10] = new Date(30, Month.December, 2013); forwards[10] = 0.0267885498456; dates[11] = new Date(29, Month.December, 2014); forwards[11] = 0.0266922867562; dates[12] = new Date(29, Month.December, 2015); forwards[12] = 0.0271052126386; dates[13] = new Date(29, Month.December, 2016); forwards[13] = 0.0268829891648; dates[14] = new Date(29, Month.December, 2017); forwards[14] = 0.0264594744498; dates[15] = new Date(31, Month.December, 2018); forwards[15] = 0.0273450367424; dates[16] = new Date(30, Month.December, 2019); forwards[16] = 0.0294852614749; dates[17] = new Date(29, Month.December, 2020); forwards[17] = 0.0285556119719; dates[18] = new Date(29, Month.December, 2021); forwards[18] = 0.0305557764659; dates[19] = new Date(29, Month.December, 2022); forwards[19] = 0.0292244738422; dates[20] = new Date(29, Month.December, 2023); forwards[20] = 0.0263917004194; dates[21] = new Date(29, Month.December, 2028); forwards[21] = 0.0239626970243; dates[22] = new Date(29, Month.December, 2033); forwards[22] = 0.0216417108090; dates[23] = new Date(29, Month.December, 2038); forwards[23] = 0.0228343838422; dates[24] = new Date(31, Month.December, 2199); forwards[24] = 0.0228343838422; Handle <YieldTermStructure> r = new Handle <YieldTermStructure>(new InterpolatedForwardCurve <BackwardFlat>(dates, forwards, new Actual360())); Handle <BlackVolTermStructure> sigma = new Handle <BlackVolTermStructure>(new BlackConstantVol(tomorrow, new NullCalendar(), 21.685235548092248, new Thirty360(Thirty360.Thirty360Convention.BondBasis))); BlackProcess process = new BlackProcess(u, r, sigma); Handle <Quote> spread = new Handle <Quote>(new SimpleQuote(0.11498700678012874)); Date issueDate = new Date(23, Month.July, 2008); Date maturityDate = new Date(1, Month.August, 2013); Calendar calendar = new UnitedStates(); Schedule schedule = new MakeSchedule().from(issueDate) .to(maturityDate) .withTenor(new Period(6, TimeUnit.Months)) .withCalendar(calendar) .withConvention(BusinessDayConvention.Unadjusted).value(); int settlementDays = 3; Exercise exercise = new EuropeanExercise(maturityDate); double conversionRatio = 100.0 / 20.3175; List <double> coupons = new InitializedList <double>(schedule.size() - 1, 0.05); DayCounter dayCounter = new Thirty360(Thirty360.Thirty360Convention.BondBasis); CallabilitySchedule no_callability = new CallabilitySchedule(); DividendSchedule no_dividends = new DividendSchedule(); double redemption = 100.0; ConvertibleFixedCouponBond bond = new ConvertibleFixedCouponBond(exercise, conversionRatio, no_dividends, no_callability, spread, issueDate, settlementDays, coupons, dayCounter, schedule, redemption); bond.setPricingEngine(new BinomialConvertibleEngine <CoxRossRubinstein> (process, 600)); try { double x = bond.NPV(); // should throw; if not, an INF was not detected. QAssert.Fail("INF result was not detected: " + x + " returned"); } catch (Exception) { // as expected. Do nothing. // Note: we're expecting an Error we threw, not just any // exception. If something else is thrown, then there's // another problem and the test must fail. } }
public void testBond() { /* when deeply out-of-the-money, the value of the convertible bond * should equal that of the underlying plain-vanilla bond. */ // Testing out-of-the-money convertible bonds against vanilla bonds CommonVars vars = new CommonVars(); vars.conversionRatio = 1.0e-16; Exercise euExercise = new EuropeanExercise(vars.maturityDate); Exercise amExercise = new AmericanExercise(vars.issueDate, vars.maturityDate); int timeSteps = 1001; IPricingEngine engine = new BinomialConvertibleEngine <CoxRossRubinstein>(vars.process, timeSteps); Handle <YieldTermStructure> discountCurve = new Handle <YieldTermStructure>(new ForwardSpreadedTermStructure(vars.riskFreeRate, vars.creditSpread)); // zero-coupon Schedule schedule = new MakeSchedule().from(vars.issueDate) .to(vars.maturityDate) .withFrequency(Frequency.Once) .withCalendar(vars.calendar) .backwards().value(); ConvertibleZeroCouponBond euZero = new ConvertibleZeroCouponBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, vars.dayCounter, schedule, vars.redemption); euZero.setPricingEngine(engine); ConvertibleZeroCouponBond amZero = new ConvertibleZeroCouponBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, vars.dayCounter, schedule, vars.redemption); amZero.setPricingEngine(engine); ZeroCouponBond zero = new ZeroCouponBond(vars.settlementDays, vars.calendar, 100.0, vars.maturityDate, BusinessDayConvention.Following, vars.redemption, vars.issueDate); IPricingEngine bondEngine = new DiscountingBondEngine(discountCurve); zero.setPricingEngine(bondEngine); double tolerance = 1.0e-2 * (vars.faceAmount / 100.0); double error = Math.Abs(euZero.NPV() - zero.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce zero-coupon bond price:" + "\n calculated: " + euZero.NPV() + "\n expected: " + zero.settlementValue() + "\n error: " + error); } error = Math.Abs(amZero.NPV() - zero.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce zero-coupon bond price:" + "\n calculated: " + amZero.NPV() + "\n expected: " + zero.settlementValue() + "\n error: " + error); } // coupon List <double> coupons = new InitializedList <double>(1, 0.05); schedule = new MakeSchedule().from(vars.issueDate) .to(vars.maturityDate) .withFrequency(vars.frequency) .withCalendar(vars.calendar) .backwards().value(); ConvertibleFixedCouponBond euFixed = new ConvertibleFixedCouponBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, coupons, vars.dayCounter, schedule, vars.redemption); euFixed.setPricingEngine(engine); ConvertibleFixedCouponBond amFixed = new ConvertibleFixedCouponBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, coupons, vars.dayCounter, schedule, vars.redemption); amFixed.setPricingEngine(engine); FixedRateBond fixedBond = new FixedRateBond(vars.settlementDays, vars.faceAmount, schedule, coupons, vars.dayCounter, BusinessDayConvention.Following, vars.redemption, vars.issueDate); fixedBond.setPricingEngine(bondEngine); tolerance = 2.0e-2 * (vars.faceAmount / 100.0); error = Math.Abs(euFixed.NPV() - fixedBond.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce fixed-coupon bond price:" + "\n calculated: " + euFixed.NPV() + "\n expected: " + fixedBond.settlementValue() + "\n error: " + error); } error = Math.Abs(amFixed.NPV() - fixedBond.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce fixed-coupon bond price:" + "\n calculated: " + amFixed.NPV() + "\n expected: " + fixedBond.settlementValue() + "\n error: " + error); } // floating-rate IborIndex index = new Euribor1Y(discountCurve); int fixingDays = 2; List <double> gearings = new InitializedList <double>(1, 1.0); List <double> spreads = new List <double>(); ConvertibleFloatingRateBond euFloating = new ConvertibleFloatingRateBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, index, fixingDays, spreads, vars.dayCounter, schedule, vars.redemption); euFloating.setPricingEngine(engine); ConvertibleFloatingRateBond amFloating = new ConvertibleFloatingRateBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, index, fixingDays, spreads, vars.dayCounter, schedule, vars.redemption); amFloating.setPricingEngine(engine); IborCouponPricer pricer = new BlackIborCouponPricer(new Handle <OptionletVolatilityStructure>()); Schedule floatSchedule = new Schedule(vars.issueDate, vars.maturityDate, new Period(vars.frequency), vars.calendar, BusinessDayConvention.Following, BusinessDayConvention.Following, DateGeneration.Rule.Backward, false); FloatingRateBond floating = new FloatingRateBond(vars.settlementDays, vars.faceAmount, floatSchedule, index, vars.dayCounter, BusinessDayConvention.Following, fixingDays, gearings, spreads, new List <double?>(), new List <double?>(), false, vars.redemption, vars.issueDate); floating.setPricingEngine(bondEngine); Utils.setCouponPricer(floating.cashflows(), pricer); tolerance = 2.0e-2 * (vars.faceAmount / 100.0); error = Math.Abs(euFloating.NPV() - floating.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce floating-rate bond price:" + "\n calculated: " + euFloating.NPV() + "\n expected: " + floating.settlementValue() + "\n error: " + error); } error = Math.Abs(amFloating.NPV() - floating.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce floating-rate bond price:" + "\n calculated: " + amFloating.NPV() + "\n expected: " + floating.settlementValue() + "\n error: " + error); } }
static void Main(string[] args) { try { Option.Type type = Option.Type.Put; double underlying = 36.0; double spreadRate = 0.005; double dividendYield = 0.02; double riskFreeRate = 0.06; double volatility = 0.2; int settlementDays = 3; int length = 5; double redemption = 100.0; double conversionRatio = redemption / underlying; // at the money // set up dates/schedules Calendar calendar = new TARGET(); Date today = calendar.adjust(Date.Today); Settings.setEvaluationDate(today); Date settlementDate = calendar.advance(today, settlementDays, TimeUnit.Days); Date exerciseDate = calendar.advance(settlementDate, length, TimeUnit.Years); Date issueDate = calendar.advance(exerciseDate, -length, TimeUnit.Years); BusinessDayConvention convention = BusinessDayConvention.ModifiedFollowing; Frequency frequency = Frequency.Annual; Schedule schedule = new Schedule(issueDate, exerciseDate, new Period(frequency), calendar, convention, convention, DateGeneration.Rule.Backward, false); DividendSchedule dividends = new DividendSchedule(); CallabilitySchedule callability = new CallabilitySchedule(); List <double> coupons = new InitializedList <double>(1, 0.05); DayCounter bondDayCount = new Thirty360(); int[] callLength = { 2, 4 }; // Call dates, years 2,4. int[] putLength = { 3 }; // Put dates year 3. double[] callPrices = { 101.5, 100.85 }; double[] putPrices = { 105.0 }; // Load call schedules for (int i = 0; i < callLength.Length; i++) { SoftCallability s = new SoftCallability( new Callability.Price(callPrices[i], Callability.Price.Type.Clean), schedule.date(callLength[i]), 1.20); callability.Add(s); } for (int j = 0; j < putLength.Length; j++) { Callability s = new Callability(new Callability.Price(putPrices[j], Callability.Price.Type.Clean), Callability.Type.Put, schedule.date(putLength[j])); callability.Add(s); } // Assume dividends are paid every 6 months . for (Date d = today + new Period(6, TimeUnit.Months); d < exerciseDate; d += new Period(6, TimeUnit.Months)) { Dividend div = new FixedDividend(1.0, d); dividends.Add(div); } DayCounter dayCounter = new Actual365Fixed(); double maturity = dayCounter.yearFraction(settlementDate, exerciseDate); Console.WriteLine("option type = " + type); Console.WriteLine("Time to maturity = " + maturity); Console.WriteLine("Underlying price = " + underlying); Console.WriteLine("Risk-free interest rate = {0:0.0%}", riskFreeRate); Console.WriteLine("Dividend yield = {0:0.0%}%", dividendYield); Console.WriteLine("Volatility = {0:0.0%}%", volatility); Console.WriteLine(""); // write column headings int[] widths = { 35, 14, 14 }; int totalWidth = widths[0] + widths[1] + widths[2]; string rule = new string('-', totalWidth); string dblrule = new string('=', totalWidth); Console.WriteLine(dblrule); Console.WriteLine("Tsiveriotis-Fernandes method"); Console.WriteLine(dblrule); Console.WriteLine("Tree Type European American "); Console.WriteLine(rule); Exercise exercise = new EuropeanExercise(exerciseDate); Exercise amexercise = new AmericanExercise(settlementDate, exerciseDate); Handle <Quote> underlyingH = new Handle <Quote>(new SimpleQuote(underlying)); Handle <YieldTermStructure> flatTermStructure = new Handle <YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)); Handle <YieldTermStructure> flatDividendTS = new Handle <YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)); Handle <BlackVolTermStructure> flatVolTS = new Handle <BlackVolTermStructure>(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); BlackScholesMertonProcess stochasticProcess = new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS); int timeSteps = 801; Handle <Quote> creditSpread = new Handle <Quote>(new SimpleQuote(spreadRate)); Quote rate = new SimpleQuote(riskFreeRate); Handle <YieldTermStructure> discountCurve = new Handle <YieldTermStructure>(new FlatForward(today, new Handle <Quote>(rate), dayCounter)); IPricingEngine engine = new BinomialConvertibleEngine <JarrowRudd>(stochasticProcess, timeSteps); ConvertibleFixedCouponBond europeanBond = new ConvertibleFixedCouponBond(exercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); europeanBond.setPricingEngine(engine); ConvertibleFixedCouponBond americanBond = new ConvertibleFixedCouponBond(amexercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); americanBond.setPricingEngine(engine); Console.WriteLine("Jarrow-Rudd {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <CoxRossRubinstein>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <CoxRossRubinstein>(stochasticProcess, timeSteps)); Console.WriteLine("CoxRossRubinstein {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <AdditiveEQPBinomialTree>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <AdditiveEQPBinomialTree>(stochasticProcess, timeSteps)); Console.WriteLine("AdditiveEQPBinomialTree {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Trigeorgis>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Trigeorgis>(stochasticProcess, timeSteps)); Console.WriteLine("Trigeorgis {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Tian>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Tian>(stochasticProcess, timeSteps)); Console.WriteLine("Tian {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <LeisenReimer>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <LeisenReimer>(stochasticProcess, timeSteps)); Console.WriteLine("LeisenReimer {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Joshi4>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Joshi4>(stochasticProcess, timeSteps)); Console.WriteLine("Joshi4 {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); Console.WriteLine("==========================================================================="); } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.ReadKey(); }