static void Main(string[] args) { DateTime timer = DateTime.Now; /********************* *** MARKET DATA *** *********************/ Calendar calendar = new TARGET(); Date settlementDate = new Date(18, Month.September, 2008); // must be a business day settlementDate = calendar.adjust(settlementDate); int fixingDays = 3; int settlementDays = 3; Date todaysDate = calendar.advance(settlementDate, -fixingDays, TimeUnit.Days); // nothing to do with Date::todaysDate Settings.setEvaluationDate(todaysDate); Console.WriteLine("Today: {0}, {1}", todaysDate.DayOfWeek, todaysDate); Console.WriteLine("Settlement date: {0}, {1}", settlementDate.DayOfWeek, settlementDate); // Building of the bonds discounting yield curve /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // Common data // ZC rates for the short end double zc3mQuote=0.0096; double zc6mQuote=0.0145; double zc1yQuote=0.0194; Quote zc3mRate = new SimpleQuote(zc3mQuote); Quote zc6mRate = new SimpleQuote(zc6mQuote); Quote zc1yRate = new SimpleQuote(zc1yQuote); DayCounter zcBondsDayCounter = new Actual365Fixed(); RateHelper zc3m = new DepositRateHelper(new Handle<Quote>(zc3mRate), new Period(3, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, zcBondsDayCounter); RateHelper zc6m = new DepositRateHelper(new Handle<Quote>(zc6mRate), new Period(6, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, zcBondsDayCounter); RateHelper zc1y = new DepositRateHelper(new Handle<Quote>(zc1yRate), new Period(1, TimeUnit.Years), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, zcBondsDayCounter); // setup bonds double redemption = 100.0; const int numberOfBonds = 5; Date[] issueDates = { new Date (15, Month.March, 2005), new Date (15, Month.June, 2005), new Date (30, Month.June, 2006), new Date (15, Month.November, 2002), new Date (15, Month.May, 1987) }; Date[] maturities = { new Date (31, Month.August, 2010), new Date (31, Month.August, 2011), new Date (31, Month.August, 2013), new Date (15, Month.August, 2018), new Date (15, Month.May, 2038) }; double[] couponRates = { 0.02375, 0.04625, 0.03125, 0.04000, 0.04500 }; double[] marketQuotes = { 100.390625, 106.21875, 100.59375, 101.6875, 102.140625 }; List<SimpleQuote> quote = new List<SimpleQuote>(); for (int i=0; i<numberOfBonds; i++) { SimpleQuote cp = new SimpleQuote(marketQuotes[i]); quote.Add(cp); } List<RelinkableHandle<Quote>> quoteHandle = new InitializedList<RelinkableHandle<Quote>>(numberOfBonds); for (int i=0; i<numberOfBonds; i++) { quoteHandle[i].linkTo(quote[i]); } // Definition of the rate helpers List<FixedRateBondHelper> bondsHelpers = new List<FixedRateBondHelper>(); for (int i=0; i<numberOfBonds; i++) { Schedule schedule = new Schedule(issueDates[i], maturities[i], new Period(Frequency.Semiannual), new UnitedStates(UnitedStates.Market.GovernmentBond), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBondHelper bondHelper = new FixedRateBondHelper(quoteHandle[i], settlementDays, 100.0, schedule, new List<double>() { couponRates[i] }, new ActualActual(ActualActual.Convention.Bond), BusinessDayConvention.Unadjusted, redemption, issueDates[i]); bondsHelpers.Add(bondHelper); } /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = new ActualActual(ActualActual.Convention.ISDA); double tolerance = 1.0e-15; // A depo-bond curve List<RateHelper> bondInstruments = new List<RateHelper>(); // Adding the ZC bonds to the curve for the short end bondInstruments.Add(zc3m); bondInstruments.Add(zc6m); bondInstruments.Add(zc1y); // Adding the Fixed rate bonds to the curve for the long end for (int i=0; i<numberOfBonds; i++) { bondInstruments.Add(bondsHelpers[i]); } YieldTermStructure bondDiscountingTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, bondInstruments, termStructureDayCounter, new List<Handle<Quote>>(), new List<Date>(), tolerance); // Building of the Libor forecasting curve // deposits double d1wQuote=0.043375; double d1mQuote=0.031875; double d3mQuote=0.0320375; double d6mQuote=0.03385; double d9mQuote=0.0338125; double d1yQuote=0.0335125; // swaps double s2yQuote=0.0295; double s3yQuote=0.0323; double s5yQuote=0.0359; double s10yQuote=0.0412; double s15yQuote=0.0433; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits Quote d1wRate = new SimpleQuote(d1wQuote); Quote d1mRate = new SimpleQuote(d1mQuote); Quote d3mRate = new SimpleQuote(d3mQuote); Quote d6mRate = new SimpleQuote(d6mQuote); Quote d9mRate = new SimpleQuote(d9mQuote); Quote d1yRate = new SimpleQuote(d1yQuote); // swaps Quote s2yRate = new SimpleQuote(s2yQuote); Quote s3yRate = new SimpleQuote(s3yQuote); Quote s5yRate = new SimpleQuote(s5yQuote); Quote s10yRate = new SimpleQuote(s10yQuote); Quote s15yRate = new SimpleQuote(s15yQuote); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = new Actual360(); RateHelper d1w = new DepositRateHelper( new Handle<Quote>(d1wRate), new Period(1, TimeUnit.Weeks), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d1m = new DepositRateHelper( new Handle<Quote>(d1mRate), new Period(1, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d3m = new DepositRateHelper( new Handle<Quote>(d3mRate), new Period(3, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d6m = new DepositRateHelper( new Handle<Quote>(d6mRate), new Period(6, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d9m = new DepositRateHelper( new Handle<Quote>(d9mRate), new Period(9, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d1y = new DepositRateHelper( new Handle<Quote>(d1yRate), new Period(1, TimeUnit.Years), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); // setup swaps Frequency swFixedLegFrequency =Frequency.Annual; BusinessDayConvention swFixedLegConvention = BusinessDayConvention.Unadjusted; DayCounter swFixedLegDayCounter = new Thirty360(Thirty360.Thirty360Convention.European); IborIndex swFloatingLegIndex = new Euribor6M(); Period forwardStart = new Period(1, TimeUnit.Days); RateHelper s2y = new SwapRateHelper( new Handle<Quote>(s2yRate), new Period(2, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, new Handle<Quote>(),forwardStart); RateHelper s3y = new SwapRateHelper( new Handle<Quote>(s3yRate), new Period(3, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, new Handle<Quote>(),forwardStart); RateHelper s5y = new SwapRateHelper( new Handle<Quote>(s5yRate), new Period(5, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, new Handle<Quote>(),forwardStart); RateHelper s10y = new SwapRateHelper( new Handle<Quote>(s10yRate), new Period(10, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, new Handle<Quote>(),forwardStart); RateHelper s15y = new SwapRateHelper( new Handle<Quote>(s15yRate), new Period(15, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, new Handle<Quote>(),forwardStart); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 // A depo-swap curve List<RateHelper> depoSwapInstruments = new List<RateHelper>(); depoSwapInstruments.Add(d1w); depoSwapInstruments.Add(d1m); depoSwapInstruments.Add(d3m); depoSwapInstruments.Add(d6m); depoSwapInstruments.Add(d9m); depoSwapInstruments.Add(d1y); depoSwapInstruments.Add(s2y); depoSwapInstruments.Add(s3y); depoSwapInstruments.Add(s5y); depoSwapInstruments.Add(s10y); depoSwapInstruments.Add(s15y); YieldTermStructure depoSwapTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, new List<Handle<Quote> >(), new List<Date>(), tolerance); // Term structures that will be used for pricing: // the one used for discounting cash flows RelinkableHandle<YieldTermStructure> discountingTermStructure = new RelinkableHandle<YieldTermStructure>(); // the one used for forward rate forecasting RelinkableHandle<YieldTermStructure> forecastingTermStructure = new RelinkableHandle<YieldTermStructure>(); /********************* * BONDS TO BE PRICED * **********************/ // Common data double faceAmount = 100; // Pricing engine IPricingEngine bondEngine = new DiscountingBondEngine(discountingTermStructure); // Zero coupon bond ZeroCouponBond zeroCouponBond = new ZeroCouponBond( settlementDays, new UnitedStates(UnitedStates.Market.GovernmentBond), faceAmount, new Date(15, Month.August,2013), BusinessDayConvention.Following, 116.92, new Date(15, Month.August,2003)); zeroCouponBond.setPricingEngine(bondEngine); // Fixed 4.5% US Treasury Note Schedule fixedBondSchedule = new Schedule(new Date(15, Month.May, 2007), new Date(15,Month.May,2017), new Period(Frequency.Semiannual), new UnitedStates(UnitedStates.Market.GovernmentBond), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBond fixedRateBond = new FixedRateBond( settlementDays, faceAmount, fixedBondSchedule, new List<double>() { 0.045 }, new ActualActual(ActualActual.Convention.Bond), BusinessDayConvention.ModifiedFollowing, 100.0, new Date(15, Month.May, 2007)); fixedRateBond.setPricingEngine(bondEngine); // Floating rate bond (3M USD Libor + 0.1%) // Should and will be priced on another curve later... RelinkableHandle<YieldTermStructure> liborTermStructure = new RelinkableHandle<YieldTermStructure>(); IborIndex libor3m = new USDLibor(new Period(3, TimeUnit.Months), liborTermStructure); libor3m.addFixing(new Date(17, Month.July, 2008),0.0278625); Schedule floatingBondSchedule = new Schedule(new Date(21, Month.October, 2005), new Date(21, Month.October, 2010), new Period(Frequency.Quarterly), new UnitedStates(UnitedStates.Market.NYSE), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, true); FloatingRateBond floatingRateBond = new FloatingRateBond( settlementDays, faceAmount, floatingBondSchedule, libor3m, new Actual360(), BusinessDayConvention.ModifiedFollowing, 2, // Gearings new List<double>() { 1.0 }, // Spreads new List<double>() { 0.001 }, // Caps new List<double>(), // Floors new List<double>(), // Fixing in arrears true, 100.0, new Date(21, Month.October, 2005)); floatingRateBond.setPricingEngine(bondEngine); // Coupon pricers IborCouponPricer pricer = new BlackIborCouponPricer(); // optionLet volatilities double volatility = 0.0; Handle<OptionletVolatilityStructure> vol; vol = new Handle<OptionletVolatilityStructure>( new ConstantOptionletVolatility( settlementDays, calendar, BusinessDayConvention.ModifiedFollowing, volatility, new Actual365Fixed())); pricer.setCapletVolatility(vol); Utils.setCouponPricer(floatingRateBond.cashflows(),pricer); // Yield curve bootstrapping forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(bondDiscountingTermStructure); // We are using the depo & swap curve to estimate the future Libor rates liborTermStructure.linkTo(depoSwapTermStructure); /*************** * BOND PRICING * ****************/ // write column headings int[] widths = { 18, 10, 10, 10 }; Console.WriteLine("{0,18}{1,10}{2,10}{3,10}", "", "ZC", "Fixed", "Floating"); string separator = " | "; int width = widths[0] + widths[1] + widths[2] + widths[3]; string rule = "".PadLeft(width, '-'), dblrule = "".PadLeft(width, '='); string tab = "".PadLeft(8, ' '); Console.WriteLine(rule); Console.WriteLine("Net present value".PadLeft(widths[0]) + "{0,10:n2}{1,10:n2}{2,10:n2}", zeroCouponBond.NPV(), fixedRateBond.NPV(), floatingRateBond.NPV()); Console.WriteLine("Clean price".PadLeft(widths[0]) + "{0,10:n2}{1,10:n2}{2,10:n2}", zeroCouponBond.cleanPrice(), fixedRateBond.cleanPrice(), floatingRateBond.cleanPrice()); Console.WriteLine("Dirty price".PadLeft(widths[0]) + "{0,10:n2}{1,10:n2}{2,10:n2}", zeroCouponBond.dirtyPrice(), fixedRateBond.dirtyPrice(), floatingRateBond.dirtyPrice()); Console.WriteLine("Accrued coupon".PadLeft(widths[0]) + "{0,10:n2}{1,10:n2}{2,10:n2}", zeroCouponBond.accruedAmount(), fixedRateBond.accruedAmount(), floatingRateBond.accruedAmount()); Console.WriteLine("Previous coupon".PadLeft(widths[0]) + "{0,10:0.00%}{1,10:0.00%}{2,10:0.00%}", "N/A", fixedRateBond.previousCoupon(), floatingRateBond.previousCoupon()); Console.WriteLine("Next coupon".PadLeft(widths[0]) + "{0,10:0.00%}{1,10:0.00%}{2,10:0.00%}", "N/A", fixedRateBond.nextCoupon(), floatingRateBond.nextCoupon()); Console.WriteLine("Yield".PadLeft(widths[0]) + "{0,10:0.00%}{1,10:0.00%}{2,10:0.00%}", zeroCouponBond.yield(new Actual360(), Compounding.Compounded, Frequency.Annual), fixedRateBond.yield(new Actual360(), Compounding.Compounded, Frequency.Annual), floatingRateBond.yield(new Actual360(), Compounding.Compounded, Frequency.Annual)); Console.WriteLine(); // Other computations Console.WriteLine("Sample indirect computations (for the floating rate bond): "); Console.WriteLine(rule); Console.WriteLine("Yield to Clean Price: {0:n2}", floatingRateBond.cleanPrice(floatingRateBond.yield(new Actual360(), Compounding.Compounded, Frequency.Annual), new Actual360(), Compounding.Compounded, Frequency.Annual, settlementDate)); Console.WriteLine("Clean Price to Yield: {0:0.00%}", floatingRateBond.yield(floatingRateBond.cleanPrice(),new Actual360(), Compounding.Compounded, Frequency.Annual, settlementDate)); /* "Yield to Price" "Price to Yield" */ Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.WriteLine(); Console.Write("Press any key to continue ..."); Console.ReadKey(); }
public void testBondFromScheduleWithDateVector() { // Testing South African R2048 bond price using Schedule constructor with Date vector SavedSettings backup = new SavedSettings(); //When pricing bond from Yield To Maturity, use NullCalendar() Calendar calendar = new NullCalendar(); int settlementDays = 3; Date issueDate = new Date(29, Month.June, 2012); Date today = new Date(7, Month.September, 2015); Date evaluationDate = calendar.adjust(today); Date settlementDate = calendar.advance(evaluationDate, new Period(settlementDays, TimeUnit.Days)); Settings.setEvaluationDate(evaluationDate); // For the schedule to generate correctly for Feb-28's, make maturity date on Feb 29 Date maturityDate = new Date(29, Month.February, 2048); double coupon = 0.0875; Compounding comp = Compounding.Compounded; Frequency freq = Frequency.Semiannual; DayCounter dc = new ActualActual(ActualActual.Convention.Bond); // Yield as quoted in market InterestRate yield = new InterestRate(0.09185, dc, comp, freq); Period tenor = new Period(6, TimeUnit.Months); Period exCouponPeriod = new Period(10, TimeUnit.Days); // Generate coupon dates for 31 Aug and end of Feb each year // For leap years, this will generate 29 Feb, but the bond // actually pays coupons on 28 Feb, regardsless of whether // it is a leap year or not. Schedule schedule = new Schedule(issueDate, maturityDate, tenor, new NullCalendar(), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, true); // Adjust the 29 Feb's to 28 Feb List<Date> dates = new List<Date>(); for (int i = 0; i < schedule.Count; ++i) { Date d = schedule.date(i); if (d.Month == 2 && d.Day == 29) dates.Add(new Date(28, Month.February, d.Year)); else dates.Add(d); } schedule = new Schedule(dates, schedule.calendar(), schedule.businessDayConvention(), schedule.terminationDateBusinessDayConvention(), schedule.tenor(), schedule.rule(), schedule.endOfMonth(), schedule.isRegular()); FixedRateBond bond = new FixedRateBond( 0, 100.0, schedule, new List<double>() { coupon }, dc, BusinessDayConvention.Following, 100.0, issueDate, calendar, exCouponPeriod, calendar, BusinessDayConvention.Unadjusted, false); double calculatedPrice = BondFunctions.dirtyPrice(bond, yield, settlementDate); double expectedPrice = 95.75706; double tolerance = 1e-5; if (Math.Abs(calculatedPrice - expectedPrice) > tolerance) { Assert.Fail(string.Format("failed to reproduce R2048 dirty price\nexpected: {0}\ncalculated: {1}", expectedPrice, calculatedPrice)); } }
public void testCached() { // ("Testing bond price/yield calculation against cached values..."); CommonVars vars = new CommonVars(); // with implicit settlement calculation: Date today = new Date(22, Month.November, 2004); Settings.setEvaluationDate(today); Calendar bondCalendar = new NullCalendar(); DayCounter bondDayCount = new ActualActual(ActualActual.Convention.ISMA); int settlementDays = 1; var discountCurve = new Handle<YieldTermStructure>(Utilities.flatRate(today, new SimpleQuote(0.03), new Actual360())); // actual market values from the evaluation date Frequency freq = Frequency.Semiannual; Schedule sch1 = new Schedule(new Date(31, Month.October, 2004), new Date(31, Month.October, 2006), new Period(freq), bondCalendar, BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBond bond1 = new FixedRateBond(settlementDays, vars.faceAmount, sch1, new List<double>() { 0.025 }, bondDayCount, BusinessDayConvention.ModifiedFollowing, 100.0, new Date(1, Month.November, 2004)); IPricingEngine bondEngine = new DiscountingBondEngine(discountCurve); bond1.setPricingEngine(bondEngine); double marketPrice1 = 99.203125; double marketYield1 = 0.02925; Schedule sch2 = new Schedule(new Date(15, Month.November, 2004), new Date(15, Month.November, 2009), new Period(freq), bondCalendar, BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBond bond2 = new FixedRateBond(settlementDays, vars.faceAmount, sch2, new List<double>() { 0.035 }, bondDayCount, BusinessDayConvention.ModifiedFollowing, 100.0, new Date(15, Month.November, 2004)); bond2.setPricingEngine(bondEngine); double marketPrice2 = 99.6875; double marketYield2 = 0.03569; // calculated values double cachedPrice1a = 99.204505, cachedPrice2a = 99.687192; double cachedPrice1b = 98.943393, cachedPrice2b = 101.986794; double cachedYield1a = 0.029257, cachedYield2a = 0.035689; double cachedYield1b = 0.029045, cachedYield2b = 0.035375; double cachedYield1c = 0.030423, cachedYield2c = 0.030432; // check double tolerance = 1.0e-6; double price, yield; price = bond1.cleanPrice(marketYield1, bondDayCount, Compounding.Compounded, freq); if (Math.Abs(price - cachedPrice1a) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "\n expected: " + cachedPrice1a + "\n tolerance: " + tolerance + "\n error: " + (price - cachedPrice1a)); } price = bond1.cleanPrice(); if (Math.Abs(price - cachedPrice1b) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "\n expected: " + cachedPrice1b + "\n tolerance: " + tolerance + "\n error: " + (price - cachedPrice1b)); } yield = bond1.yield(marketPrice1, bondDayCount, Compounding.Compounded, freq); if (Math.Abs(yield - cachedYield1a) > tolerance) { Assert.Fail("failed to reproduce cached compounded yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield1a + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield1a)); } yield = bond1.yield(marketPrice1, bondDayCount, Compounding.Continuous, freq); if (Math.Abs(yield - cachedYield1b) > tolerance) { Assert.Fail("failed to reproduce cached continuous yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield1b + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield1b)); } yield = bond1.yield(bondDayCount, Compounding.Continuous, freq); if (Math.Abs(yield - cachedYield1c) > tolerance) { Assert.Fail("failed to reproduce cached continuous yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield1c + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield1c)); } price = bond2.cleanPrice(marketYield2, bondDayCount, Compounding.Compounded, freq); if (Math.Abs(price - cachedPrice2a) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "\n expected: " + cachedPrice2a + "\n tolerance: " + tolerance + "\n error: " + (price - cachedPrice2a)); } price = bond2.cleanPrice(); if (Math.Abs(price - cachedPrice2b) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "\n expected: " + cachedPrice2b + "\n tolerance: " + tolerance + "\n error: " + (price - cachedPrice2b)); } yield = bond2.yield(marketPrice2, bondDayCount, Compounding.Compounded, freq); if (Math.Abs(yield - cachedYield2a) > tolerance) { Assert.Fail("failed to reproduce cached compounded yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield2a + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield2a)); } yield = bond2.yield(marketPrice2, bondDayCount, Compounding.Continuous, freq); if (Math.Abs(yield - cachedYield2b) > tolerance) { Assert.Fail("failed to reproduce cached continuous yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield2b + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield2b)); } yield = bond2.yield(bondDayCount, Compounding.Continuous, freq); if (Math.Abs(yield - cachedYield2c) > tolerance) { Assert.Fail("failed to reproduce cached continuous yield:" + "\n calculated: " + yield + "\n expected: " + cachedYield2c + "\n tolerance: " + tolerance + "\n error: " + (yield - cachedYield2c)); } // with explicit settlement date: Schedule sch3 = new Schedule(new Date(30, Month.November, 2004), new Date(30, Month.November, 2006), new Period(freq), new UnitedStates(UnitedStates.Market.GovernmentBond), BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); FixedRateBond bond3 = new FixedRateBond(settlementDays, vars.faceAmount, sch3, new List<double>() { 0.02875 }, new ActualActual(ActualActual.Convention.ISMA), BusinessDayConvention.ModifiedFollowing, 100.0, new Date(30, Month.November, 2004)); bond3.setPricingEngine(bondEngine); double marketYield3 = 0.02997; Date settlementDate = new Date(30, Month.November, 2004); double cachedPrice3 = 99.764759; price = bond3.cleanPrice(marketYield3, bondDayCount, Compounding.Compounded, freq, settlementDate); if (Math.Abs(price - cachedPrice3) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "" + "\n expected: " + cachedPrice3 + "" + "\n error: " + (price - cachedPrice3)); } // this should give the same result since the issue date is the // earliest possible settlement date Settings.setEvaluationDate(new Date(22, Month.November, 2004)); price = bond3.cleanPrice(marketYield3, bondDayCount, Compounding.Compounded, freq); if (Math.Abs(price - cachedPrice3) > tolerance) { Assert.Fail("failed to reproduce cached price:" + "\n calculated: " + price + "" + "\n expected: " + cachedPrice3 + "" + "\n error: " + (price - cachedPrice3)); } }
public void testActualActual() { SingleCase[] testCases = { // first example new SingleCase(ActualActual.Convention.ISDA, new Date(1,Month.November,2003), new Date(1,Month.May,2004), 0.497724380567), new SingleCase(ActualActual.Convention.ISMA, new Date(1,Month.November,2003), new Date(1,Month.May,2004), new Date(1,Month.November,2003), new Date(1,Month.May,2004), 0.500000000000), new SingleCase(ActualActual.Convention.AFB, new Date(1,Month.November,2003), new Date(1,Month.May,2004), 0.497267759563), // short first calculation period (first period) new SingleCase(ActualActual.Convention.ISDA, new Date(1,Month.February,1999), new Date(1,Month.July,1999), 0.410958904110), new SingleCase(ActualActual.Convention.ISMA, new Date(1,Month.February,1999), new Date(1,Month.July,1999), new Date(1,Month.July,1998), new Date(1,Month.July,1999), 0.410958904110), new SingleCase(ActualActual.Convention.AFB, new Date(1,Month.February,1999), new Date(1,Month.July,1999), 0.410958904110), // short first calculation period (second period) new SingleCase(ActualActual.Convention.ISDA, new Date(1,Month.July,1999), new Date(1,Month.July,2000), 1.001377348600), new SingleCase(ActualActual.Convention.ISMA, new Date(1,Month.July,1999), new Date(1,Month.July,2000), new Date(1,Month.July,1999), new Date(1,Month.July,2000), 1.000000000000), new SingleCase(ActualActual.Convention.AFB, new Date(1,Month.July,1999), new Date(1,Month.July,2000), 1.000000000000), // long first calculation period (first period) new SingleCase(ActualActual.Convention.ISDA, new Date(15,Month.August,2002), new Date(15,Month.July,2003), 0.915068493151), new SingleCase(ActualActual.Convention.ISMA, new Date(15,Month.August,2002), new Date(15,Month.July,2003), new Date(15,Month.January,2003), new Date(15,Month.July,2003), 0.915760869565), new SingleCase(ActualActual.Convention.AFB, new Date(15,Month.August,2002), new Date(15,Month.July,2003), 0.915068493151), // long first calculation period (second period) /* Warning: the ISDA case is in disagreement with mktc1198.pdf */ new SingleCase(ActualActual.Convention.ISDA, new Date(15,Month.July,2003), new Date(15,Month.January,2004), 0.504004790778), new SingleCase(ActualActual.Convention.ISMA, new Date(15,Month.July,2003), new Date(15,Month.January,2004), new Date(15,Month.July,2003), new Date(15,Month.January,2004), 0.500000000000), new SingleCase(ActualActual.Convention.AFB, new Date(15,Month.July,2003), new Date(15,Month.January,2004), 0.504109589041), // short final calculation period (penultimate period) new SingleCase(ActualActual.Convention.ISDA, new Date(30,Month.July,1999), new Date(30,Month.January,2000), 0.503892506924), new SingleCase(ActualActual.Convention.ISMA, new Date(30,Month.July,1999), new Date(30,Month.January,2000), new Date(30,Month.July,1999), new Date(30,Month.January,2000), 0.500000000000), new SingleCase(ActualActual.Convention.AFB, new Date(30,Month.July,1999), new Date(30,Month.January,2000), 0.504109589041), // short final calculation period (final period) new SingleCase(ActualActual.Convention.ISDA, new Date(30,Month.January,2000), new Date(30,Month.June,2000), 0.415300546448), new SingleCase(ActualActual.Convention.ISMA, new Date(30,Month.January,2000), new Date(30,Month.June,2000), new Date(30,Month.January,2000), new Date(30,Month.July,2000), 0.417582417582), new SingleCase(ActualActual.Convention.AFB, new Date(30,Month.January,2000), new Date(30,Month.June,2000), 0.41530054644) }; int n = testCases.Length; /// sizeof(SingleCase); for (int i = 0; i < n; i++) { ActualActual dayCounter = new ActualActual(testCases[i]._convention); Date d1 = testCases[i]._start; Date d2 = testCases[i]._end; Date rd1 = testCases[i]._refStart; Date rd2 = testCases[i]._refEnd; double calculated = dayCounter.yearFraction(d1, d2, rd1, rd2); if (Math.Abs(calculated - testCases[i]._result) > 1.0e-10) { Assert.Fail(dayCounter.name() + "period: " + d1 + " to " + d2 + " calculated: " + calculated + " expected: " + testCases[i]._result); } } }
public SingleCase(ActualActual.Convention convention, Date start, Date end, double result) { _convention = convention; _start = start; _end = end; _refStart = new Date(); _refEnd = new Date(); _result = result; }
static void Main(string[] args) { // boost::timer timer; Date today = new Date(16,Month.October,2007); Settings.setEvaluationDate(today); Console.WriteLine(); Console.WriteLine("Pricing a callable fixed rate bond using"); Console.WriteLine("Hull White model w/ reversion parameter = 0.03"); Console.WriteLine("BAC4.65 09/15/12 ISIN: US06060WBJ36"); Console.WriteLine("roughly five year tenor, "); Console.WriteLine("quarterly coupon and call dates"); Console.WriteLine("reference date is : " + today ); /* Bloomberg OAS1: "N" model (Hull White) varying volatility parameter The curve entered into Bloomberg OAS1 is a flat curve, at constant yield = 5.5%, semiannual compounding. Assume here OAS1 curve uses an ACT/ACT day counter, as documented in PFC1 as a "default" in the latter case. */ // set up a flat curve corresponding to Bloomberg flat curve double bbCurveRate = 0.055; DayCounter bbDayCounter = new ActualActual(ActualActual.Convention.Bond); InterestRate bbIR = new InterestRate(bbCurveRate,bbDayCounter,Compounding.Compounded ,Frequency.Semiannual); Handle<YieldTermStructure> termStructure = new Handle<YieldTermStructure>(flatRate( today, bbIR.rate(), bbIR.dayCounter(), bbIR.compounding(), bbIR.frequency())); // set up the call schedule CallabilitySchedule callSchedule = new CallabilitySchedule(); double callPrice = 100.0; int numberOfCallDates = 24; Date callDate = new Date(15,Month.September,2006); for (int i=0; i< numberOfCallDates; i++) { Calendar nullCalendar = new NullCalendar(); Callability.Price myPrice = new Callability.Price(callPrice, Callability.Price.Type.Clean); callSchedule.Add( new Callability(myPrice,Callability.Type.Call, callDate )); callDate = nullCalendar.advance(callDate, 3, TimeUnit.Months); } // set up the callable bond Date dated = new Date(16,Month.September,2004); Date issue = dated; Date maturity = new Date(15,Month.September,2012); int settlementDays = 3; // Bloomberg OAS1 settle is Oct 19, 2007 Calendar bondCalendar = new UnitedStates(UnitedStates.Market.GovernmentBond); double coupon = .0465; Frequency frequency = Frequency.Quarterly; double redemption = 100.0; double faceAmount = 100.0; /* The 30/360 day counter Bloomberg uses for this bond cannot reproduce the US Bond/ISMA (constant) cashflows used in PFC1. Therefore use ActAct(Bond) */ DayCounter bondDayCounter = new ActualActual(ActualActual.Convention.Bond); // PFC1 shows no indication dates are being adjusted // for weekends/holidays for vanilla bonds BusinessDayConvention accrualConvention = BusinessDayConvention.Unadjusted; BusinessDayConvention paymentConvention = BusinessDayConvention.Unadjusted; Schedule sch = new Schedule( dated, maturity, new Period(frequency), bondCalendar, accrualConvention, accrualConvention, DateGeneration.Rule.Backward, false); int maxIterations = 1000; double accuracy = 1e-8; int gridIntervals = 40; double reversionParameter = .03; // output price/yield results for varying volatility parameter double sigma = Const.QL_Epsilon; // core dumps if zero on Cygwin ShortRateModel hw0 = new HullWhite(termStructure,reversionParameter,sigma); IPricingEngine engine0 = new TreeCallableFixedRateBondEngine(hw0, gridIntervals, termStructure); CallableFixedRateBond callableBond = new CallableFixedRateBond( settlementDays, faceAmount, sch, new InitializedList<double>(1, coupon), bondDayCounter, paymentConvention, redemption, issue, callSchedule); callableBond.setPricingEngine(engine0); Console.WriteLine("sigma/vol (%) = {0:0.00}", (100.0 * sigma)); Console.WriteLine("QuantLib price/yld (%) "); Console.WriteLine( "{0:0.00} / {1:0.00} ", callableBond.cleanPrice() , 100.0 * callableBond.yield(bondDayCounter, Compounding.Compounded, frequency, accuracy, maxIterations)); Console.WriteLine("Bloomberg price/yld (%) "); Console.WriteLine("96.50 / 5.47"); // sigma = .01; Console.WriteLine("sigma/vol (%) = {0:0.00}", (100.0 * sigma)); ShortRateModel hw1 = new HullWhite(termStructure,reversionParameter,sigma); IPricingEngine engine1 = new TreeCallableFixedRateBondEngine(hw1,gridIntervals,termStructure); callableBond.setPricingEngine(engine1); Console.WriteLine("QuantLib price/yld (%) "); Console.WriteLine( "{0:0.00} / {1:0.00} ", callableBond.cleanPrice() , 100.0 * callableBond.yield(bondDayCounter, Compounding.Compounded, frequency, accuracy, maxIterations)); Console.WriteLine("Bloomberg price/yld (%) "); Console.WriteLine("95.68 / 5.66"); // sigma = .03; Console.WriteLine("sigma/vol (%) = {0:0.00}", (100.0 * sigma)); ShortRateModel hw2 = new HullWhite(termStructure, reversionParameter, sigma); IPricingEngine engine2 = new TreeCallableFixedRateBondEngine(hw2, gridIntervals, termStructure); callableBond.setPricingEngine(engine2); Console.WriteLine("QuantLib price/yld (%) "); Console.WriteLine("{0:0.00} / {1:0.00} ", callableBond.cleanPrice(), 100.0 * callableBond.yield(bondDayCounter, Compounding.Compounded, frequency, accuracy, maxIterations)); Console.WriteLine("Bloomberg price/yld (%) "); Console.WriteLine("92.34 / 6.49"); // sigma = .06; Console.WriteLine("sigma/vol (%) = {0:0.00}", (100.0 * sigma)); ShortRateModel hw3 = new HullWhite(termStructure, reversionParameter, sigma); IPricingEngine engine3 = new TreeCallableFixedRateBondEngine(hw3, gridIntervals, termStructure); callableBond.setPricingEngine(engine3); Console.WriteLine("QuantLib price/yld (%) "); Console.WriteLine("{0:0.00} / {1:0.00} ", callableBond.cleanPrice(), 100.0 * callableBond.yield(bondDayCounter, Compounding.Compounded, frequency, accuracy, maxIterations)); Console.WriteLine("Bloomberg price/yld (%) "); Console.WriteLine("87.16 / 7.83"); // sigma = .12; Console.WriteLine("sigma/vol (%) = {0:0.00}", (100.0 * sigma)); ShortRateModel hw4 = new HullWhite(termStructure, reversionParameter, sigma); IPricingEngine engine4 = new TreeCallableFixedRateBondEngine(hw4, gridIntervals, termStructure); callableBond.setPricingEngine(engine4); Console.WriteLine("QuantLib price/yld (%) "); Console.WriteLine("{0:0.00} / {1:0.00} ", callableBond.cleanPrice(), 100.0 * callableBond.yield(bondDayCounter, Compounding.Compounded, frequency, accuracy, maxIterations)); Console.WriteLine("Bloomberg price/yld (%) "); Console.WriteLine("77.31 / 10.65"); }
static void Main() { DateTime timer = DateTime.Now; /********************* *** MARKET DATA *** *********************/ RelinkableHandle<YieldTermStructure> euriborTermStructure = new RelinkableHandle<YieldTermStructure>(); IborIndex euribor3m = new Euribor3M(euriborTermStructure); Date todaysDate = new Date(23, Month.May, 2006); Settings.setEvaluationDate(todaysDate); Calendar calendar = euribor3m.fixingCalendar(); int fixingDays = euribor3m.fixingDays(); Date settlementDate = calendar.advance(todaysDate, fixingDays, TimeUnit.Days); Console.WriteLine("Today: " + todaysDate.DayOfWeek + ", " + todaysDate); Console.WriteLine("Settlement date: " + settlementDate.DayOfWeek + ", " + settlementDate); // 3 month term FRA quotes (index refers to monthsToStart) double[] threeMonthFraQuote = new double[10]; threeMonthFraQuote[1]=0.030; threeMonthFraQuote[2]=0.031; threeMonthFraQuote[3]=0.032; threeMonthFraQuote[6]=0.033; threeMonthFraQuote[9]=0.034; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // FRAs SimpleQuote fra1x4Rate = new SimpleQuote(threeMonthFraQuote[1]); SimpleQuote fra2x5Rate = new SimpleQuote(threeMonthFraQuote[2]); SimpleQuote fra3x6Rate = new SimpleQuote(threeMonthFraQuote[3]); SimpleQuote fra6x9Rate = new SimpleQuote(threeMonthFraQuote[6]); SimpleQuote fra9x12Rate = new SimpleQuote(threeMonthFraQuote[9]); RelinkableHandle<Quote> h1x4 = new RelinkableHandle<Quote>(); h1x4.linkTo(fra1x4Rate); RelinkableHandle<Quote> h2x5 = new RelinkableHandle<Quote>(); h2x5.linkTo(fra2x5Rate); RelinkableHandle<Quote> h3x6 = new RelinkableHandle<Quote>(); h3x6.linkTo(fra3x6Rate); RelinkableHandle<Quote> h6x9 = new RelinkableHandle<Quote>(); h6x9.linkTo(fra6x9Rate); RelinkableHandle<Quote> h9x12 = new RelinkableHandle<Quote>(); h9x12.linkTo(fra9x12Rate); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. DayCounter fraDayCounter = euribor3m.dayCounter(); BusinessDayConvention convention = euribor3m.businessDayConvention(); bool endOfMonth = euribor3m.endOfMonth(); RateHelper fra1x4 = new FraRateHelper(h1x4, 1, 4, fixingDays, calendar, convention, endOfMonth, fraDayCounter); RateHelper fra2x5 = new FraRateHelper(h2x5, 2, 5, fixingDays, calendar, convention, endOfMonth, fraDayCounter); RateHelper fra3x6 = new FraRateHelper(h3x6, 3, 6, fixingDays, calendar, convention, endOfMonth, fraDayCounter); RateHelper fra6x9 = new FraRateHelper(h6x9, 6, 9, fixingDays, calendar, convention, endOfMonth, fraDayCounter); RateHelper fra9x12 = new FraRateHelper(h9x12, 9, 12, fixingDays, calendar, convention, endOfMonth, fraDayCounter); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = new ActualActual(ActualActual.Convention.ISDA); double tolerance = 1.0e-15; // A FRA curve List<RateHelper> fraInstruments = new List<RateHelper>(); fraInstruments.Add(fra1x4); fraInstruments.Add(fra2x5); fraInstruments.Add(fra3x6); fraInstruments.Add(fra6x9); fraInstruments.Add(fra9x12); YieldTermStructure fraTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, fraInstruments, termStructureDayCounter, new List<Handle<Quote>>(), new List<Date>(), tolerance); // Term structures used for pricing/discounting RelinkableHandle<YieldTermStructure> discountingTermStructure = new RelinkableHandle<YieldTermStructure>(); discountingTermStructure.linkTo(fraTermStructure); /*********************** *** construct FRA's *** ***********************/ Calendar fraCalendar = euribor3m.fixingCalendar(); BusinessDayConvention fraBusinessDayConvention = euribor3m.businessDayConvention(); Position.Type fraFwdType = Position.Type.Long; double fraNotional = 100.0; const int FraTermMonths = 3; int[] monthsToStart = new [] { 1, 2, 3, 6, 9 }; euriborTermStructure.linkTo(fraTermStructure); Console.WriteLine("\nTest FRA construction, NPV calculation, and FRA purchase\n"); int i; for (i=0; i<monthsToStart.Length; i++) { Date fraValueDate = fraCalendar.advance( settlementDate,monthsToStart[i], TimeUnit.Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate, FraTermMonths, TimeUnit.Months, fraBusinessDayConvention); double fraStrikeRate = threeMonthFraQuote[monthsToStart[i]]; ForwardRateAgreement myFRA = new ForwardRateAgreement(fraValueDate, fraMaturityDate, fraFwdType,fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); Console.WriteLine("3m Term FRA, Months to Start: " + monthsToStart[i]); Console.WriteLine("strike FRA rate: {0:0.00%}", fraStrikeRate); Console.WriteLine("FRA 3m forward rate: {0:0.00%}", myFRA.forwardRate()); Console.WriteLine("FRA market quote: {0:0.00%}", threeMonthFraQuote[monthsToStart[i]]); Console.WriteLine("FRA spot value: " + myFRA.spotValue()); Console.WriteLine("FRA forward value: " + myFRA.forwardValue()); Console.WriteLine("FRA implied Yield: {0:0.00%}", myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Compounding.Simple, fraDayCounter)); Console.WriteLine("market Zero Rate: {0:0.00%}", discountingTermStructure.link.zeroRate(fraMaturityDate, fraDayCounter, Compounding.Simple)); Console.WriteLine("FRA NPV [should be zero]: {0}\n", myFRA.NPV()); } Console.WriteLine("\n"); Console.WriteLine("Now take a 100 basis-point upward shift in FRA quotes and examine NPV\n"); const double BpsShift = 0.01; threeMonthFraQuote[1]=0.030+BpsShift; threeMonthFraQuote[2]=0.031+BpsShift; threeMonthFraQuote[3]=0.032+BpsShift; threeMonthFraQuote[6]=0.033+BpsShift; threeMonthFraQuote[9]=0.034+BpsShift; fra1x4Rate.setValue(threeMonthFraQuote[1]); fra2x5Rate.setValue(threeMonthFraQuote[2]); fra3x6Rate.setValue(threeMonthFraQuote[3]); fra6x9Rate.setValue(threeMonthFraQuote[6]); fra9x12Rate.setValue(threeMonthFraQuote[9]); for (i=0; i<monthsToStart.Length; i++) { Date fraValueDate = fraCalendar.advance( settlementDate, monthsToStart[i], TimeUnit.Months, fraBusinessDayConvention); Date fraMaturityDate = fraCalendar.advance( fraValueDate, FraTermMonths, TimeUnit.Months, fraBusinessDayConvention); double fraStrikeRate = threeMonthFraQuote[monthsToStart[i]] - BpsShift; ForwardRateAgreement myFRA = new ForwardRateAgreement(fraValueDate, fraMaturityDate, fraFwdType, fraStrikeRate, fraNotional, euribor3m, discountingTermStructure); Console.WriteLine("3m Term FRA, 100 notional, Months to Start: " + monthsToStart[i]); Console.WriteLine("strike FRA rate: {0:0.00%}", fraStrikeRate); Console.WriteLine("FRA 3m forward rate: {0:0.00%}", myFRA.forwardRate()); Console.WriteLine("FRA market quote: {0:0.00%}", threeMonthFraQuote[monthsToStart[i]]); Console.WriteLine("FRA spot value: " + myFRA.spotValue()); Console.WriteLine("FRA forward value: " + myFRA.forwardValue()); Console.WriteLine("FRA implied Yield: {0:0.00%}", myFRA.impliedYield(myFRA.spotValue(), myFRA.forwardValue(), settlementDate, Compounding.Simple, fraDayCounter)); Console.WriteLine("market Zero Rate: {0:0.00%}", discountingTermStructure.link.zeroRate(fraMaturityDate, fraDayCounter, Compounding.Simple)); Console.WriteLine("FRA NPV [should be positive]: {0}\n", myFRA.NPV()); } Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.WriteLine(); Console.Write("Press any key to continue ..."); Console.ReadKey(); }
static void Main(string[] args) { DateTime timer = DateTime.Now; /********************* *** MARKET DATA *** *********************/ Calendar calendar = new TARGET(); Date settlementDate = new Date(22, Month.September, 2004); // must be a business day settlementDate = calendar.adjust(settlementDate); int fixingDays = 2; Date todaysDate = calendar.advance(settlementDate, -fixingDays, TimeUnit.Days); // nothing to do with Date::todaysDate Settings.setEvaluationDate(todaysDate); todaysDate = Settings.evaluationDate(); Console.WriteLine("Today: {0}, {1}", todaysDate.DayOfWeek, todaysDate); Console.WriteLine("Settlement date: {0}, {1}", settlementDate.DayOfWeek, settlementDate); // deposits double d1wQuote = 0.0382; double d1mQuote = 0.0372; double d3mQuote = 0.0363; double d6mQuote = 0.0353; double d9mQuote = 0.0348; double d1yQuote = 0.0345; // FRAs double fra3x6Quote = 0.037125; double fra6x9Quote = 0.037125; double fra6x12Quote = 0.037125; // futures double fut1Quote = 96.2875; double fut2Quote = 96.7875; double fut3Quote = 96.9875; double fut4Quote = 96.6875; double fut5Quote = 96.4875; double fut6Quote = 96.3875; double fut7Quote = 96.2875; double fut8Quote = 96.0875; // swaps double s2yQuote = 0.037125; double s3yQuote = 0.0398; double s5yQuote = 0.0443; double s10yQuote = 0.05165; double s15yQuote = 0.055175; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits Quote d1wRate = new SimpleQuote(d1wQuote); Quote d1mRate = new SimpleQuote(d1mQuote); Quote d3mRate = new SimpleQuote(d3mQuote); Quote d6mRate = new SimpleQuote(d6mQuote); Quote d9mRate = new SimpleQuote(d9mQuote); Quote d1yRate = new SimpleQuote(d1yQuote); // FRAs Quote fra3x6Rate = new SimpleQuote(fra3x6Quote); Quote fra6x9Rate = new SimpleQuote(fra6x9Quote); Quote fra6x12Rate = new SimpleQuote(fra6x12Quote); // futures Quote fut1Price = new SimpleQuote(fut1Quote); Quote fut2Price = new SimpleQuote(fut2Quote); Quote fut3Price = new SimpleQuote(fut3Quote); Quote fut4Price = new SimpleQuote(fut4Quote); Quote fut5Price = new SimpleQuote(fut5Quote); Quote fut6Price = new SimpleQuote(fut6Quote); Quote fut7Price = new SimpleQuote(fut7Quote); Quote fut8Price = new SimpleQuote(fut8Quote); // swaps Quote s2yRate = new SimpleQuote(s2yQuote); Quote s3yRate = new SimpleQuote(s3yQuote); Quote s5yRate = new SimpleQuote(s5yQuote); Quote s10yRate = new SimpleQuote(s10yQuote); Quote s15yRate = new SimpleQuote(s15yQuote); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = new Actual360(); RateHelper d1w = new DepositRateHelper(new Handle<Quote>(d1wRate), new Period(1, TimeUnit.Weeks), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d1m = new DepositRateHelper(new Handle<Quote>(d1mRate), new Period(1, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d3m = new DepositRateHelper(new Handle<Quote>(d3mRate), new Period(3, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d6m = new DepositRateHelper(new Handle<Quote>(d6mRate), new Period(6, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d9m = new DepositRateHelper(new Handle<Quote>(d9mRate), new Period(9, TimeUnit.Months), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper d1y = new DepositRateHelper(new Handle<Quote>(d1yRate), new Period(1, TimeUnit.Years), fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); // setup FRAs RateHelper fra3x6 = new FraRateHelper(new Handle<Quote>(fra3x6Rate), 3, 6, fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper fra6x9 = new FraRateHelper(new Handle<Quote>(fra6x9Rate), 6, 9, fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); RateHelper fra6x12 = new FraRateHelper(new Handle<Quote>(fra6x12Rate), 6, 12, fixingDays, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); // setup futures // Handle<Quote> convexityAdjustment = new Handle<Quote>(new SimpleQuote(0.0)); int futMonths = 3; Date imm = IMM.nextDate(settlementDate); RateHelper fut1 = new FuturesRateHelper(new Handle<Quote>(fut1Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut2 = new FuturesRateHelper(new Handle<Quote>(fut2Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut3 = new FuturesRateHelper(new Handle<Quote>(fut3Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut4 = new FuturesRateHelper(new Handle<Quote>(fut4Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut5 = new FuturesRateHelper(new Handle<Quote>(fut5Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut6 = new FuturesRateHelper(new Handle<Quote>(fut6Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut7 = new FuturesRateHelper(new Handle<Quote>(fut7Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); imm = IMM.nextDate(imm + 1); RateHelper fut8 = new FuturesRateHelper(new Handle<Quote>(fut8Price), imm, futMonths, calendar, BusinessDayConvention.ModifiedFollowing, true, depositDayCounter); // setup swaps Frequency swFixedLegFrequency = Frequency.Annual; BusinessDayConvention swFixedLegConvention = BusinessDayConvention.Unadjusted; DayCounter swFixedLegDayCounter = new Thirty360(Thirty360.Thirty360Convention.European); IborIndex swFloatingLegIndex = new Euribor6M(); RateHelper s2y = new SwapRateHelper(new Handle<Quote>(s2yRate), new Period(2, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex); RateHelper s3y = new SwapRateHelper(new Handle<Quote>(s3yRate), new Period(3, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex); RateHelper s5y = new SwapRateHelper(new Handle<Quote>(s5yRate), new Period(5, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex); RateHelper s10y = new SwapRateHelper(new Handle<Quote>(s10yRate), new Period(10, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex); RateHelper s15y = new SwapRateHelper(new Handle<Quote>(s15yRate), new Period(15, TimeUnit.Years), calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = new ActualActual(ActualActual.Convention.ISDA); double tolerance = 1.0e-15; // A depo-swap curve List<RateHelper> depoSwapInstruments = new List<RateHelper>(); depoSwapInstruments.Add(d1w); depoSwapInstruments.Add(d1m); depoSwapInstruments.Add(d3m); depoSwapInstruments.Add(d6m); depoSwapInstruments.Add(d9m); depoSwapInstruments.Add(d1y); depoSwapInstruments.Add(s2y); depoSwapInstruments.Add(s3y); depoSwapInstruments.Add(s5y); depoSwapInstruments.Add(s10y); depoSwapInstruments.Add(s15y); YieldTermStructure depoSwapTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, new List<Handle<Quote>>(), new List<Date>(), tolerance); // A depo-futures-swap curve List<RateHelper> depoFutSwapInstruments = new List<RateHelper>(); depoFutSwapInstruments.Add(d1w); depoFutSwapInstruments.Add(d1m); depoFutSwapInstruments.Add(fut1); depoFutSwapInstruments.Add(fut2); depoFutSwapInstruments.Add(fut3); depoFutSwapInstruments.Add(fut4); depoFutSwapInstruments.Add(fut5); depoFutSwapInstruments.Add(fut6); depoFutSwapInstruments.Add(fut7); depoFutSwapInstruments.Add(fut8); depoFutSwapInstruments.Add(s3y); depoFutSwapInstruments.Add(s5y); depoFutSwapInstruments.Add(s10y); depoFutSwapInstruments.Add(s15y); YieldTermStructure depoFutSwapTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFutSwapInstruments, termStructureDayCounter, new List<Handle<Quote>>(), new List<Date>(), tolerance); // A depo-FRA-swap curve List<RateHelper> depoFRASwapInstruments = new List<RateHelper>(); depoFRASwapInstruments.Add(d1w); depoFRASwapInstruments.Add(d1m); depoFRASwapInstruments.Add(d3m); depoFRASwapInstruments.Add(fra3x6); depoFRASwapInstruments.Add(fra6x9); depoFRASwapInstruments.Add(fra6x12); depoFRASwapInstruments.Add(s2y); depoFRASwapInstruments.Add(s3y); depoFRASwapInstruments.Add(s5y); depoFRASwapInstruments.Add(s10y); depoFRASwapInstruments.Add(s15y); YieldTermStructure depoFRASwapTermStructure = new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoFRASwapInstruments, termStructureDayCounter, new List<Handle<Quote>>(), new List<Date>(), tolerance); // Term structures that will be used for pricing: // the one used for discounting cash flows RelinkableHandle<YieldTermStructure> discountingTermStructure = new RelinkableHandle<YieldTermStructure>(); // the one used for forward rate forecasting RelinkableHandle<YieldTermStructure> forecastingTermStructure = new RelinkableHandle<YieldTermStructure>(); /********************* * SWAPS TO BE PRICED * **********************/ // constant nominal 1,000,000 Euro double nominal = 1000000.0; // fixed leg Frequency fixedLegFrequency = Frequency.Annual; BusinessDayConvention fixedLegConvention = BusinessDayConvention.Unadjusted; BusinessDayConvention floatingLegConvention = BusinessDayConvention.ModifiedFollowing; DayCounter fixedLegDayCounter = new Thirty360(Thirty360.Thirty360Convention.European); double fixedRate = 0.04; DayCounter floatingLegDayCounter = new Actual360(); // floating leg Frequency floatingLegFrequency = Frequency.Semiannual; IborIndex euriborIndex = new Euribor6M(forecastingTermStructure); double spread = 0.0; int lenghtInYears = 5; VanillaSwap.Type swapType = VanillaSwap.Type.Payer; Date maturity = settlementDate + new Period(lenghtInYears, TimeUnit.Years); Schedule fixedSchedule = new Schedule(settlementDate, maturity, new Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration.Rule.Forward, false); Schedule floatSchedule = new Schedule(settlementDate, maturity, new Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration.Rule.Forward, false); VanillaSwap spot5YearSwap = new VanillaSwap(swapType, nominal, fixedSchedule, fixedRate, fixedLegDayCounter, floatSchedule, euriborIndex, spread, floatingLegDayCounter); Date fwdStart = calendar.advance(settlementDate, 1, TimeUnit.Years); Date fwdMaturity = fwdStart + new Period(lenghtInYears, TimeUnit.Years); Schedule fwdFixedSchedule = new Schedule(fwdStart, fwdMaturity, new Period(fixedLegFrequency), calendar, fixedLegConvention, fixedLegConvention, DateGeneration.Rule.Forward, false); Schedule fwdFloatSchedule = new Schedule(fwdStart, fwdMaturity, new Period(floatingLegFrequency), calendar, floatingLegConvention, floatingLegConvention, DateGeneration.Rule.Forward, false); VanillaSwap oneYearForward5YearSwap = new VanillaSwap(swapType, nominal, fwdFixedSchedule, fixedRate, fixedLegDayCounter, fwdFloatSchedule, euriborIndex, spread, floatingLegDayCounter); /*************** * SWAP PRICING * ****************/ // utilities for reporting List<string> headers = new List<string>(); headers.Add("term structure"); headers.Add("net present value"); headers.Add("fair spread"); headers.Add("fair fixed rate"); string separator = " | "; int width = headers[0].Length + separator.Length + headers[1].Length + separator.Length + headers[2].Length + separator.Length + headers[3].Length + separator.Length - 1; string rule = string.Format("").PadLeft(width, '-'), dblrule = string.Format("").PadLeft(width, '='); string tab = string.Format("").PadLeft(8, ' '); // calculations Console.WriteLine(dblrule); Console.WriteLine("5-year market swap-rate = {0:0.00%}", s5yRate.value()); Console.WriteLine(dblrule); Console.WriteLine(tab + "5-years swap paying {0:0.00%}", fixedRate); Console.WriteLine(headers[0] + separator + headers[1] + separator + headers[2] + separator + headers[3] + separator); Console.WriteLine(rule); double NPV; double fairRate; double fairSpread; IPricingEngine swapEngine = new DiscountingSwapEngine(discountingTermStructure); spot5YearSwap.setPricingEngine(swapEngine); oneYearForward5YearSwap.setPricingEngine(swapEngine); // Of course, you're not forced to really use different curves forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); // let's check that the 5 years swap has been correctly re-priced if (!(Math.Abs(fairRate-s5yQuote)<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yQuote)); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-fut-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); if (!(Math.Abs(fairRate-s5yQuote)<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yQuote)); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-FRA-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); if (!(Math.Abs(fairRate-s5yQuote)<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yQuote)); Console.WriteLine(rule); // now let's price the 1Y forward 5Y swap Console.WriteLine(tab + "5-years, 1-year forward swap paying {0:0.00%}", fixedRate); Console.WriteLine(headers[0] + separator + headers[1] + separator + headers[2] + separator + headers[3] + separator); Console.WriteLine(rule); forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-fut-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-FRA-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); // now let's say that the 5-years swap rate goes up to 4.60%. // A smarter market element--say, connected to a data source-- would // notice the change itself. Since we're using SimpleQuotes, // we'll have to change the value manually--which forces us to // downcast the handle and use the SimpleQuote // interface. In any case, the point here is that a change in the // value contained in the Quote triggers a new bootstrapping // of the curve and a repricing of the swap. SimpleQuote fiveYearsRate = s5yRate as SimpleQuote; fiveYearsRate.setValue(0.0460); Console.WriteLine(dblrule); Console.WriteLine("5-year market swap-rate = {0:0.00%}", s5yRate.value()); Console.WriteLine(dblrule); Console.WriteLine(tab + "5-years swap paying {0:0.00%}", fixedRate); Console.WriteLine(headers[0] + separator + headers[1] + separator + headers[2] + separator + headers[3] + separator); Console.WriteLine(rule); // now get the updated results forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); if (!(Math.Abs(fairRate-s5yRate.value())<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yRate.value())); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-fut-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); if (!(Math.Abs(fairRate-s5yRate.value())<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yRate.value())); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = spot5YearSwap.NPV(); fairSpread = spot5YearSwap.fairSpread(); fairRate = spot5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-FRA-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); if (!(Math.Abs(fairRate-s5yRate.value())<1e-8)) throw new ApplicationException("5-years swap mispriced by " + Math.Abs(fairRate-s5yRate.value())); Console.WriteLine(rule); // the 1Y forward 5Y swap changes as well Console.WriteLine(tab + "5-years, 1-year forward swap paying {0:0.00%}", fixedRate); Console.WriteLine(headers[0] + separator + headers[1] + separator + headers[2] + separator + headers[3] + separator); Console.WriteLine(rule); forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(depoSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); forecastingTermStructure.linkTo(depoFutSwapTermStructure); discountingTermStructure.linkTo(depoFutSwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-fut-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); forecastingTermStructure.linkTo(depoFRASwapTermStructure); discountingTermStructure.linkTo(depoFRASwapTermStructure); NPV = oneYearForward5YearSwap.NPV(); fairSpread = oneYearForward5YearSwap.fairSpread(); fairRate = oneYearForward5YearSwap.fairRate(); Console.Write("{0," + headers[0].Length + ":0.00}" + separator, "depo-FRA-swap"); Console.Write("{0," + headers[1].Length + ":0.00}" + separator, NPV); Console.Write("{0," + headers[2].Length + ":0.00%}" + separator, fairSpread); Console.WriteLine("{0," + headers[3].Length + ":0.00%}" + separator, fairRate); Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.Write("Press any key to continue ..."); Console.ReadKey(); }