Example #1
0
        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, quarterly coupon and call dates");
            Console.WriteLine("reference date is : " + today.ToLongDateString());
            Console.WriteLine("");

            /* 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("QLNet price/yld (%)  {0:0.00} / {1:0.00} ",
                              callableBond.cleanPrice(),
                              100.0 * callableBond.yield(bondDayCounter,
                                                         Compounding.Compounded,
                                                         frequency,
                                                         accuracy,
                                                         maxIterations));
            Console.WriteLine("Bloomberg price/yld (%) 96,50 / 5,47");
            Console.WriteLine("");
            //

            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("QLNet price/yld (%)  {0:0.00} / {1:0.00} ",
                              callableBond.cleanPrice(),
                              100.0 * callableBond.yield(bondDayCounter,
                                                         Compounding.Compounded,
                                                         frequency,
                                                         accuracy,
                                                         maxIterations));

            Console.WriteLine("Bloomberg price/yld (%)  95,68 / 5,66");
            Console.WriteLine("");

            //

            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("QLNet price/yld (%)  {0:0.00} / {1:0.00} ",
                              callableBond.cleanPrice(),
                              100.0 * callableBond.yield(bondDayCounter,
                                                         Compounding.Compounded,
                                                         frequency,
                                                         accuracy,
                                                         maxIterations));

            Console.WriteLine("Bloomberg price/yld (%) 92,34 / 6,49");
            Console.WriteLine("");
            //

            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("QLNet price/yld (%)  {0:0.00} / {1:0.00} ",
                              callableBond.cleanPrice(),
                              100.0 * callableBond.yield(bondDayCounter,
                                                         Compounding.Compounded,
                                                         frequency,
                                                         accuracy,
                                                         maxIterations));

            Console.WriteLine("Bloomberg price/yld (%) 87,16 / 7,83");
            Console.WriteLine("");
            //

            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("QLNet price/yld (%)  {0:0.00} / {1:0.00} ",
                              callableBond.cleanPrice(),
                              100.0 * callableBond.yield(bondDayCounter,
                                                         Compounding.Compounded,
                                                         frequency,
                                                         accuracy,
                                                         maxIterations));

            Console.WriteLine("Bloomberg price/yld (%) 77,31 / 10,65");
        }
Example #2
0
        public void testConversions()
        {
            InterestRateData[] cases =
            {
                // data from "Option Pricing Formulas", Haug, pag.181-182
                // Rate,Compounding,        Frequency,   Time, Compounding2,      Frequency2,  Rate2, precision
                new InterestRateData(0.0800, Compounding.Compounded,           Frequency.Quarterly,            1.00, Compounding.Continuous,           Frequency.Annual,           0.0792, 4),
                new InterestRateData(0.1200, Compounding.Continuous,           Frequency.Annual,               1.00, Compounding.Compounded,           Frequency.Annual,           0.1275, 4),
                new InterestRateData(0.0800, Compounding.Compounded,           Frequency.Quarterly,            1.00, Compounding.Compounded,           Frequency.Annual,           0.0824, 4),
                new InterestRateData(0.0700, Compounding.Compounded,           Frequency.Quarterly,            1.00, Compounding.Compounded,           Frequency.Semiannual,       0.0706, 4),
                // undocumented, but reasonable :)
                new InterestRateData(0.0100, Compounding.Compounded,           Frequency.Annual,               1.00, Compounding.Simple,               Frequency.Annual,           0.0100, 4),
                new InterestRateData(0.0200, Compounding.Simple,               Frequency.Annual,               1.00, Compounding.Compounded,           Frequency.Annual,           0.0200, 4),
                new InterestRateData(0.0300, Compounding.Compounded,           Frequency.Semiannual,           0.50, Compounding.Simple,               Frequency.Annual,           0.0300, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Annual,               0.50, Compounding.Compounded,           Frequency.Semiannual,       0.0400, 4),
                new InterestRateData(0.0500, Compounding.Compounded,           Frequency.EveryFourthMonth,  1.0 / 3, Compounding.Simple,               Frequency.Annual,           0.0500, 4),
                new InterestRateData(0.0600, Compounding.Simple,               Frequency.Annual,            1.0 / 3, Compounding.Compounded,           Frequency.EveryFourthMonth, 0.0600, 4),
                new InterestRateData(0.0500, Compounding.Compounded,           Frequency.Quarterly,            0.25, Compounding.Simple,               Frequency.Annual,           0.0500, 4),
                new InterestRateData(0.0600, Compounding.Simple,               Frequency.Annual,               0.25, Compounding.Compounded,           Frequency.Quarterly,        0.0600, 4),
                new InterestRateData(0.0700, Compounding.Compounded,           Frequency.Bimonthly,         1.0 / 6, Compounding.Simple,               Frequency.Annual,           0.0700, 4),
                new InterestRateData(0.0800, Compounding.Simple,               Frequency.Annual,            1.0 / 6, Compounding.Compounded,           Frequency.Bimonthly,        0.0800, 4),
                new InterestRateData(0.0900, Compounding.Compounded,           Frequency.Monthly,          1.0 / 12, Compounding.Simple,               Frequency.Annual,           0.0900, 4),
                new InterestRateData(0.1000, Compounding.Simple,               Frequency.Annual,           1.0 / 12, Compounding.Compounded,           Frequency.Monthly,          0.1000, 4),

                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.25, Compounding.Simple,               Frequency.Annual,           0.0300, 4),
                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.25, Compounding.Simple,               Frequency.Semiannual,       0.0300, 4),
                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.25, Compounding.Simple,               Frequency.Quarterly,        0.0300, 4),
                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.50, Compounding.Simple,               Frequency.Annual,           0.0300, 4),
                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.50, Compounding.Simple,               Frequency.Semiannual,       0.0300, 4),
                new InterestRateData(0.0300, Compounding.SimpleThenCompounded, Frequency.Semiannual,           0.75, Compounding.Compounded,           Frequency.Semiannual,       0.0300, 4),

                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.25, Compounding.SimpleThenCompounded, Frequency.Quarterly,        0.0400, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.25, Compounding.SimpleThenCompounded, Frequency.Semiannual,       0.0400, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.25, Compounding.SimpleThenCompounded, Frequency.Annual,           0.0400, 4),

                new InterestRateData(0.0400, Compounding.Compounded,           Frequency.Quarterly,            0.50, Compounding.SimpleThenCompounded, Frequency.Quarterly,        0.0400, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.50, Compounding.SimpleThenCompounded, Frequency.Semiannual,       0.0400, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.50, Compounding.SimpleThenCompounded, Frequency.Annual,           0.0400, 4),

                new InterestRateData(0.0400, Compounding.Compounded,           Frequency.Quarterly,            0.75, Compounding.SimpleThenCompounded, Frequency.Quarterly,        0.0400, 4),
                new InterestRateData(0.0400, Compounding.Compounded,           Frequency.Semiannual,           0.75, Compounding.SimpleThenCompounded, Frequency.Semiannual,       0.0400, 4),
                new InterestRateData(0.0400, Compounding.Simple,               Frequency.Semiannual,           0.75, Compounding.SimpleThenCompounded, Frequency.Annual,           0.0400, 4)
            };

            Rounding     roundingPrecision;
            double       r3;
            double       r2;
            Date         d1 = Date.Today;
            Date         d2;
            InterestRate ir;
            InterestRate ir2;
            InterestRate ir3;
            InterestRate expectedIR;
            double       compoundf;
            double       error;
            double       disc;

            for (int i = 0; i < cases.Length - 1; i++)
            {
                ir = new InterestRate(cases[i].r, new Actual360(), cases[i].comp, cases[i].freq);
                d2 = d1 + new Period((int)(360 * cases[i].t + 0.5), TimeUnit.Days);
                roundingPrecision = new Rounding(cases[i].precision);

                // check that the compound factor is the inverse of the discount factor
                compoundf = ir.compoundFactor(d1, d2);
                disc      = ir.discountFactor(d1, d2);
                error     = Math.Abs(disc - 1.0 / compoundf);
                if (error > 1e-15)
                {
                    QAssert.Fail(ir + "  1.0/compound_factor: " + 1.0 / compoundf);
                }

                // check that the equivalent InterestRate with *same* daycounter,
                // compounding, and frequency is the *same* InterestRate
                //ir2 = ir.equivalentRate(d1, d2, ir.dayCounter(), ir.compounding(), ir.frequency());
                ir2   = ir.equivalentRate(ir.dayCounter(), ir.compounding(), ir.frequency(), d1, d2);
                error = Math.Abs(ir.rate() - ir2.rate());
                if (error > 1e-15)
                {
                    QAssert.Fail("original interest rate: " + ir + " equivalent interest rate: " + ir2 + " rate error: " + error);
                }
                if (ir.dayCounter() != ir2.dayCounter())
                {
                    QAssert.Fail("day counter error original interest rate: " + ir + " equivalent interest rate: " + ir2);
                }
                if (ir.compounding() != ir2.compounding())
                {
                    QAssert.Fail("compounding error original interest rate: " + ir + " equivalent interest rate: " + ir2);
                }
                if (ir.frequency() != ir2.frequency())
                {
                    QAssert.Fail("frequency error original interest rate: " + ir + " equivalent interest rate: " + ir2);
                }

                // check that the equivalent rate with *same* daycounter,
                // compounding, and frequency is the *same* rate
                //r2 = ir.equivalentRate(d1, d2, ir.dayCounter(), ir.compounding(), ir.frequency()).rate();
                r2    = ir.equivalentRate(ir.dayCounter(), ir.compounding(), ir.frequency(), d1, d2).value();
                error = Math.Abs(ir.rate() - r2);
                if (error > 1e-15)
                {
                    QAssert.Fail("original rate: " + ir + " equivalent rate: " + r2 + " error: " + error);
                }

                // check that the equivalent InterestRate with *different*
                // compounding, and frequency is the *expected* InterestRate
                //ir3 = ir.equivalentRate(d1, d2, ir.dayCounter(), cases[i].comp2, cases[i].freq2);
                ir3        = ir.equivalentRate(ir.dayCounter(), cases[i].comp2, cases[i].freq2, d1, d2);
                expectedIR = new InterestRate(cases[i].expected, ir.dayCounter(), cases[i].comp2, cases[i].freq2);
                r3         = roundingPrecision.Round(ir3.rate());
                error      = Math.Abs(r3 - expectedIR.rate());
                if (error > 1.0e-17)
                {
                    QAssert.Fail("original interest rate: " + ir + " calculated equivalent interest rate: " + ir3 + " truncated equivalent rate: " + r3 + " expected equivalent interest rate: " + expectedIR + " rate error: " + error);
                }
                if (ir3.dayCounter() != expectedIR.dayCounter())
                {
                    QAssert.Fail("day counter error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR);
                }
                if (ir3.compounding() != expectedIR.compounding())
                {
                    QAssert.Fail("compounding error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR);
                }
                if (ir3.frequency() != expectedIR.frequency())
                {
                    QAssert.Fail("frequency error original interest rate: " + ir3 + " equivalent interest rate: " + expectedIR);
                }

                // check that the equivalent rate with *different*
                // compounding, and frequency is the *expected* rate
                //r3 = ir.equivalentRate(d1, d2, ir.dayCounter(), cases[i].comp2, cases[i].freq2).rate();
                r3    = ir.equivalentRate(ir.dayCounter(), cases[i].comp2, cases[i].freq2, d1, d2).value();
                r3    = roundingPrecision.Round(r3);
                error = Math.Abs(r3 - cases[i].expected);
                if (error > 1.0e-17)
                {
                    QAssert.Fail("calculated equivalent rate: " + r3 + " expected equivalent rate: " + cases[i].expected + " error: " + error);
                }
            }
        }
Example #3
0
        public void testBrazilianCached()
        {
            //("Testing Brazilian public bond prices against cached values...");

            CommonVars vars = new CommonVars();

            double faceAmount = 1000.0;
            double redemption = 100.0;
            Date   issueDate  = new Date(1, Month.January, 2007);

            Date today = new Date(6, Month.June, 2007);

            Settings.setEvaluationDate(today);

            // NTN-F maturity dates
            InitializedList <Date> maturityDates = new InitializedList <Date>(6);

            maturityDates[0] = new Date(1, Month.January, 2008);
            maturityDates[1] = new Date(1, Month.January, 2010);
            maturityDates[2] = new Date(1, Month.July, 2010);
            maturityDates[3] = new Date(1, Month.January, 2012);
            maturityDates[4] = new Date(1, Month.January, 2014);
            maturityDates[5] = new Date(1, Month.January, 2017);

            // NTN-F yields
            InitializedList <double> yields = new InitializedList <double>(6);

            yields[0] = 0.114614;
            yields[1] = 0.105726;
            yields[2] = 0.105328;
            yields[3] = 0.104283;
            yields[4] = 0.103218;
            yields[5] = 0.102948;

            // NTN-F prices
            InitializedList <double> prices = new InitializedList <double>(6);

            prices[0] = 1034.63031372;
            prices[1] = 1030.09919487;
            prices[2] = 1029.98307160;
            prices[3] = 1028.13585068;
            prices[4] = 1028.33383817;
            prices[5] = 1026.19716497;

            int settlementDays = 1;

            vars.faceAmount = 1000.0;

            // The tolerance is high because Andima truncate yields
            double tolerance = 1.0e-4;

            InitializedList <InterestRate> couponRates = new InitializedList <InterestRate>(1);

            couponRates[0] = new InterestRate(0.1, new Thirty360(), Compounding.Compounded, Frequency.Annual);

            for (int bondIndex = 0; bondIndex < maturityDates.Count; bondIndex++)
            {
                // plain
                InterestRate yield = new InterestRate(yields[bondIndex], new Business252(new Brazil()),
                                                      Compounding.Compounded, Frequency.Annual);

                Schedule schedule = new Schedule(new Date(1, Month.January, 2007),
                                                 maturityDates[bondIndex], new Period(Frequency.Semiannual),
                                                 new Brazil(Brazil.Market.Settlement),
                                                 BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted,
                                                 DateGeneration.Rule.Backward, false);


                FixedRateBond bond = new FixedRateBond(settlementDays,
                                                       faceAmount,
                                                       schedule,
                                                       couponRates,
                                                       BusinessDayConvention.Following,
                                                       redemption,
                                                       issueDate);

                double cachedPrice = prices[bondIndex];

                double price = vars.faceAmount * (bond.cleanPrice(yield.rate(),
                                                                  yield.dayCounter(),
                                                                  yield.compounding(),
                                                                  yield.frequency(),
                                                                  today) + bond.accruedAmount(today)) / 100;
                if (Math.Abs(price - cachedPrice) > tolerance)
                {
                    Assert.Fail("failed to reproduce cached price:\n"
                                + "    calculated: " + price + "\n"
                                + "    expected:   " + cachedPrice + "\n"
                                + "    error:      " + (price - cachedPrice) + "\n"
                                );
                }
            }
        }