Exemplo n.º 1
0
        public static double[] SolveYield(Country country, DateTime settleDate, double cleanPrice, DateTime startDate, DateTime firstCpnDate, DateTime maturityDate, double coupon, long freq, int exDivDays = 0, InflationCurve infCurve = null)
        {
            bool bLongFirstCpn = firstCpnDate > settleDate && firstCpnDate > startDate.AddDays(1).AddMonths((int)freq).AddDays(-1);
            DateTime adjSettleDate = (startDate > settleDate ? startDate : settleDate);
            List<DateTime> schedule = GenerateSchedule((bLongFirstCpn && adjSettleDate < firstCpnDate ? startDate : adjSettleDate), maturityDate, freq);
            List<DateTime> adjSchedule = GenerateAdjustedSchedule(schedule, null, "F");
            double[] result = new double[4];
            double guess = coupon / cleanPrice * freq / 12.0, firstCpnSize = 1.0;
            long nCpns = schedule.Count, ctr;

            //  Boundary case
            if (settleDate.Date >= maturityDate.Date)
                return result;

            //  Specific country conventions:
            //  Italy: accrual rounding, semi-annual coupons but yield quoted with annual compounding frequency.
            //  All (US and Eurozone) bar France use US convention - i.e. last coupon period simple yield.
            //  Spain: final principal payment use adjusted payment date.
            //  UK and some others have ex-div periods.
            bool bExDiv = (settleDate.AddDays(exDivDays) >= schedule[(bLongFirstCpn ? 2 : 1)]);
            double dcf = CalcBondAccrualDcf(adjSettleDate, startDate, firstCpnDate, schedule, country);
            //  In case of stub period at start
            if (firstCpnDate > settleDate)
            {
                if (bLongFirstCpn)
                {
                    firstCpnSize = 1.0 + (schedule[1] - startDate).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
                else
                {
                    firstCpnSize = (firstCpnDate - (schedule[0] < startDate ? startDate : schedule[0])).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
            }
            double accrual = 100.0 * coupon * freq / 1200 * (dcf - (bExDiv ? firstCpnSize : 0.0));

            //  Interpolate inflation curve.
            double[] infFactors = InterpolateInflationFactors(adjSchedule, settleDate, infCurve);
            accrual *= infFactors[nCpns];
            cleanPrice *= infFactors[nCpns];

            //  Modify DCF for use in discounting loop. If long first coupon then need to reduce i by 1 in the coupon loop. Do this by incrementing adjusted DCF.
            dcf += (bLongFirstCpn ? 2.0 : 1.0) - firstCpnSize;
            if (country == Country.IT)
            {
                accrual = Math.Round(accrual, 5);
            }           
            for (ctr = 0; ctr < 100; ctr++)
            {
                double pv = -cleanPrice - accrual;
                double dPvdY = 0.0;
                double d2PvdY2 = 0.0;
                double delay = 0.0;
                double tmp;
                //  In final coupon period and US convention
                if (nCpns == 2 && country != Country.FR && country != Country.UK)
                {
                    if (country == Country.ES)
                    {
                        delay = (adjSchedule[1] - schedule[1]).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                    }
                    tmp = 100.0 * infFactors[1] * (1.0 + (bExDiv ? 0.0 : 1.0) * coupon * freq / 1200) / (1.0 + guess * (1.0 + delay - dcf));
                    pv += tmp;
                    dPvdY -= (1 + delay - dcf) * tmp / (1.0 + guess * (1.0 + delay - dcf));
                    d2PvdY2 -= 2.0 * (1 + delay - dcf) * dPvdY / (1.0 + guess * (1.0 + delay - dcf));
                }
                else
                {
                    //  First coupon
                    int i = (bLongFirstCpn ? 2 : 1);
                    tmp = firstCpnSize * 100.0 * infFactors[1] * (bExDiv ? 0.0 : 1.0) * coupon * freq / 1200 / Math.Pow(1.0 + guess, i - dcf);
                    pv += tmp;
                    dPvdY -= (i - dcf) * tmp / (1.0 + guess);
                    d2PvdY2 += (i - dcf) * (i + 1 - dcf) * tmp / ((1.0 + guess) * (1.0 + guess));

                    for (i++; i < nCpns; i++)
                    {
                        tmp = 100.0 * infFactors[i] * coupon * freq / 1200 / Math.Pow(1.0 + guess, i - dcf);
                        pv += tmp;
                        dPvdY -= (i - dcf) * tmp / (1.0 + guess);
                        d2PvdY2 += (i - dcf) * (i + 1 - dcf) * tmp / ((1.0 + guess) * (1.0 + guess));
                    }
                    //  Final Principal
                    if (country == Country.ES)
                    {
                        delay = (adjSchedule[i - 1] - schedule[i - 1]).TotalDays / (schedule[i - 1] - schedule[i - 2]).TotalDays;
                    }
                    tmp = 100.0 * infFactors[i - 1] / Math.Pow(1.0 + guess, i - 1 + delay - dcf);
                    pv += tmp;
                    dPvdY -= (i - 1 + delay - dcf) * tmp / (1.0 + guess);
                    d2PvdY2 += (i - 1 + delay - dcf) * (i + delay - dcf) * tmp / ((1.0 + guess) * (1.0 + guess));
                }
                //  Convergence criterion is Pv within 1.0e-8 after allowing for Price ~ 100
                if (Math.Abs(pv) < 1.0e-8)
                {
                    if (country == Country.IT && nCpns > 2)
                    {
                        tmp = 1.0 + 0.5 * guess * 12 / freq;
                        result[0] = tmp * tmp - 1.0;
                        result[1] = dPvdY / tmp * freq / 12;
                        result[2] = -result[1] / (cleanPrice + accrual);
                        result[3] = 0.0001 * (d2PvdY2 * freq * freq / 144 - 0.5 * result[1]) / tmp / tmp;
                    }
                    else
                    {
                        result[0] = guess * 12 / freq;
                        result[1] = dPvdY * freq / 12;
                        result[2] = -result[1] / (cleanPrice + accrual);
                        result[3] = 0.0001 * d2PvdY2 * freq * freq / 144;
                    }
                    return result;
                }
                else
                {
                    //  Halley's method
                    guess = guess - pv / (dPvdY - pv * d2PvdY2 / (2.0 * dPvdY));
                }
            }

            result[0] = -1.0;
            result[1] = -1.0;
            result[2] = -1.0;
            result[3] = -1.0;
            return result;
        }
Exemplo n.º 2
0
        public static double[] SolveZSpread2(Country country, DateTime settleDate, double cleanPrice, DateTime startDate, DateTime firstCpnDate, DateTime maturityDate, double coupon, long freq, DateTime[] curveDates, double[] dfs, List<DateTime> holidays, int exDivDays = 0, InflationCurve infCurve = null)
        {
            double[] result = new double[2];
            bool bLongFirstCpn = firstCpnDate > settleDate && firstCpnDate > startDate.AddDays(1).AddMonths((int)freq).AddDays(-1);
            DateTime adjSettleDate = startDate > settleDate ? startDate : settleDate;
            List<DateTime> schedule = GenerateSchedule((bLongFirstCpn && adjSettleDate < firstCpnDate ? startDate : adjSettleDate), maturityDate, freq);
            List<DateTime> adjSchedule = GenerateAdjustedSchedule(schedule, holidays, "F");
            double[] interpTimes = new double[adjSchedule.Count];
            List<double> flowZcTimes = new List<double>();
            List<double> flowZcRates = new List<double>();
            double guess = 0.0, firstCpnSize = 1.0;
            long nCpns = schedule.Count, ctr;
            int i;

            //  Boundary case
            if (settleDate.Date >= maturityDate.Date)
                return result;

            bool bExDiv = settleDate.AddDays(exDivDays) >= schedule[bLongFirstCpn ? 2 : 1];
            double dcf = CalcBondAccrualDcf(adjSettleDate, startDate, firstCpnDate, schedule, country);
            //  In case of stub period at start
            if (firstCpnDate > settleDate)
            {
                if (bLongFirstCpn)
                {
                    firstCpnSize = 1.0 + (schedule[1] - startDate).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
                else
                {
                    firstCpnSize = (firstCpnDate - (startDate > schedule[0] ? startDate : schedule[0])).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
            }
            double accrual = 100.0 * coupon * freq / 1200 * (dcf - (bExDiv ? firstCpnSize : 0.0));
            if (country == Country.IT)
            {
                accrual = Math.Round(accrual, 5);
            }
            // Use adjusted schedule dates for payments.
            // Convert dates to times for spline interpolator
            interpTimes[0] = ((settleDate > startDate ? settleDate : startDate) - curveDates[0]).TotalDays;
            for (i = 1; i < adjSchedule.Count; i++)
            {
                interpTimes[i] = (adjSchedule[i] - curveDates[0]).TotalDays;
            }
            List<double> flowDfs = InterpDfSpline(interpTimes, curveDates, dfs);
            //  Calculate base discount factors (before spread) and convert to zero coupon rates. Store rate * freq / 12 for efficiency.
            double dt = 12.0 / freq * interpTimes[0] / 365.0;
            if (dt > 0.0)
            {
                flowZcRates.Add((Math.Pow(1.0 / flowDfs[0], 1.0 / dt) - 1.0));
            }
            else
            {
                flowZcRates.Add(0.0);
            }
            flowZcTimes.Add(dt);
            for (i = 1; i < nCpns; i++)
            {
                if (nCpns == 2)
                {
                    dt = 12.0 / freq * interpTimes[i] / 365.0;
                    flowZcRates.Add((1.0 / flowDfs[i] - 1.0) / dt);
                    flowZcTimes.Add(dt);
                }
                else
                {
                    dt = 12.0 / freq * interpTimes[i] / 365.0;
                    flowZcRates.Add((Math.Pow(1.0 / flowDfs[i], 1.0 / dt) - 1.0));
                    flowZcTimes.Add(dt);
                }
            }
            //  Interpolate inflation curve.
            double[] infFactors = InterpolateInflationFactors(adjSchedule, settleDate, infCurve);
            accrual *= infFactors[nCpns];
            cleanPrice *= infFactors[nCpns];

            //  Iterative solver: Halley's method as faster convergence than Newton-Raphson.
            for (ctr = 0; ctr < 100; ctr++)
            {
                //  Initial payment
                double tmp = (cleanPrice + accrual) * Math.Pow(1.0 + (flowZcRates[0] + guess), -flowZcTimes[0]);
                double pv = -tmp;
                double dPvdZ = flowZcTimes[0] * tmp / (1.0 + flowZcRates[0] + guess);
                double d2PvdZ2 = -flowZcTimes[0] * (1.0 + flowZcTimes[0]) * tmp / ((1.0 + flowZcRates[0] + guess) * (1.0 + flowZcRates[0] + guess));
                if (nCpns == 2 && country != Country.FR && country != Country.UK)
                {
                    tmp = 100.0 * infFactors[1] * (1.0 + (bExDiv ? 0.0 : coupon) * freq / 1200) / (1.0 + (flowZcRates[1] + guess) * flowZcTimes[1]);
                    pv +=  tmp;
                    dPvdZ -= flowZcTimes[1] * tmp / (1.0 + (flowZcRates[1] + guess) * flowZcTimes[1]);
                    d2PvdZ2 += 2.0 * flowZcTimes[1] * flowZcTimes[1] * tmp / ((1.0 + (flowZcRates[1] + guess) * flowZcTimes[1]) * (1.0 + (flowZcRates[1] + guess) * flowZcTimes[1]));
                }
                else
                {
                    //  first coupon
                    i = (bLongFirstCpn ? 2 : 1);
                    tmp = (bExDiv ? 0.0 : firstCpnSize) * 100.0 * infFactors[i] * coupon * freq / 1200 * Math.Pow(1.0 + flowZcRates[i] + guess, -flowZcTimes[i]);
                    pv += tmp;
                    dPvdZ -= flowZcTimes[i] * tmp / (1.0 + flowZcRates[i] + guess);
                    d2PvdZ2 += flowZcTimes[i] * (1.0 + flowZcTimes[i]) * tmp / ((1.0 + flowZcRates[i] + guess) * (1.0 + flowZcRates[i] + guess));

                    for (i++; i < nCpns; i++)
                    {
                        tmp = 100.0 * infFactors[i] * coupon * freq / 1200 * Math.Pow(1.0 + flowZcRates[i] + guess, -flowZcTimes[i]);
                        pv += tmp;
                        dPvdZ -= flowZcTimes[i] * tmp / (1.0 + flowZcRates[i] + guess);
                        d2PvdZ2 += flowZcTimes[i] * (1.0 + flowZcTimes[i]) * tmp / ((1.0 + flowZcRates[i] + guess) * (1.0 + flowZcRates[i] + guess));
                    }
                    //  Final principal
                    tmp = 100.0 * infFactors[i - 1] * Math.Pow(1.0 + flowZcRates[i - 1] + guess, -flowZcTimes[i - 1]);
                    pv += tmp;
                    dPvdZ -= flowZcTimes[i - 1] * tmp / (1.0 + flowZcRates[i - 1] + guess);
                    d2PvdZ2 += flowZcTimes[i - 1] * (1.0 + flowZcTimes[i - 1]) * tmp / ((1.0 + flowZcRates[i - 1] + guess) * (1.0 + flowZcRates[i - 1] + guess));
                }
                //  Convergence criterion is Pv within 1.0e-8 after allowing for Price ~ 100
                if (Math.Abs(pv) < 1.0e-7)
                {
                    result[0] = guess * 12.0 / freq;
                    result[1] = dPvdZ * freq / 12.0;
                    return result;
                }
                else
                {
                    //  Halley's method
                    guess = guess - pv / (dPvdZ - pv * d2PvdZ2 / (2.0 * dPvdZ));
                }
            }

            result[0] = -1.0;
            result[1] = 0.0;
            return result;
        }
Exemplo n.º 3
0
        public static double[] PriceFromYield(Country country, DateTime settleDate, double yield, DateTime startDate, DateTime firstCpnDate, DateTime maturityDate, double coupon, long freq, int exDivDays = 0, InflationCurve infCurve = null)
        {
            bool bLongFirstCpn = firstCpnDate > settleDate && firstCpnDate > startDate.AddDays(1).AddMonths((int)freq).AddDays(-1);
            DateTime adjSettleDate = (startDate > settleDate ? startDate : settleDate);
            List<DateTime> schedule = GenerateSchedule((bLongFirstCpn && adjSettleDate < firstCpnDate ? startDate : adjSettleDate), maturityDate, freq);
            List<DateTime> adjSchedule = GenerateAdjustedSchedule(schedule, null, "F");
            double[] result = new double[2];
            double delay = 0.0, firstCpnSize = 1.0;
            long nCpns = schedule.Count;

            //  Boundary case
            if (settleDate.Date >= maturityDate.Date)
            {
                if (settleDate.Date == maturityDate.Date)
                    result[0] = 100.0 + coupon * freq / 12;

                return result;
            }

            //  Specific country conventions:
            //  Italy: accrual rounding, semi-annual coupons but yield quoted with annual compounding frequency.
            //  All (US and Eurozone) bar France use US convention - i.e. last coupon period simple yield.
            //  Spain: final principal payment use adjusted payment date.

            double dcf = CalcBondAccrualDcf(settleDate, startDate, firstCpnDate, schedule, country);

            //  In case of stub period at start
            if (firstCpnDate > settleDate)
            {
                if (bLongFirstCpn)
                {
                    firstCpnSize = 1.0 + (schedule[1] - startDate).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
                else
                {
                    firstCpnSize = (firstCpnDate - (schedule[0] < startDate ? startDate : schedule[0])).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
            }
            double accrual = 100.0 * coupon * freq / 1200 * dcf;

            //  Interpolate inflation curve.
            double[] infFactors = InterpolateInflationFactors(adjSchedule, settleDate, infCurve);
            accrual *= infFactors[nCpns];

            //  Modify DCF for use in discounting loop. If long first coupon then need to reduce i by 1 in the coupon loop. Do this by incrementing adjusted DCF.
            dcf += (bLongFirstCpn ? 2.0 : 1.0) - firstCpnSize;
            if (country == Country.IT)
            {
                accrual = Math.Round(accrual, 5);
                if (nCpns > 2)
                {
                    yield = 2.0 * (Math.Sqrt(1 + yield) - 1.0);
                }
            }

            double pv = 0.0;
            double dPvdY = 0.0;
            if (nCpns == 2 && country != Country.FR && country != Country.UK)
            {
                if (country == Country.ES)
                {
                    delay = (adjSchedule[1] - schedule[1]).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
                pv += 100.0 * infFactors[1] * (1.0 + coupon * freq / 1200) / (1.0 + yield * freq / 12 * (1.0 + delay - dcf));
                dPvdY -= 100.0 * infFactors[1] * (1 - dcf) * freq / 12 * (1.0 + coupon * freq / 1200) / Math.Pow(1.0 + yield * freq / 12 * (1.0 + delay - dcf), 2);
            }
            else
            {
                int i;
                for (i = (bLongFirstCpn ? 2 : 1); i < nCpns; i++)
                {
                    pv += 100.0 * infFactors[i] * coupon * freq / 1200 / Math.Pow(1.0 + yield * freq / 12, i - dcf);
                    dPvdY -= 100.0 * infFactors[i] * (i - dcf) * freq / 12 * coupon * freq / 1200 / Math.Pow(1.0 + yield * freq / 12, i + 1 - dcf);
                    //  If stub period then first coupon is non-standard size.
                    if (i == (bLongFirstCpn ? 2 : 1))
                    {
                        pv *= firstCpnSize;
                        dPvdY *= firstCpnSize;
                    }
                }
                if (country == Country.ES)
                {
                    delay = (adjSchedule[i - 1] - schedule[i - 1]).TotalDays / (schedule[i - 1] - schedule[i - 2]).TotalDays;
                }
                pv += 100.0 * infFactors[i - 1] / Math.Pow(1.0 + yield * freq / 12, i - 1 + delay - dcf);
                dPvdY -= 100.0 * infFactors[i - 1] * freq / 12 * (i - dcf) / Math.Pow(1.0 + yield * freq / 12, i - 1 + delay - dcf);
            }

            result[0] = (pv - accrual) / infFactors[nCpns];
            result[1] = dPvdY;

            //  Handle exdiv period. First coupon payment comes out of pv calculation and out of accrual.
            if (settleDate.AddDays(exDivDays) >= schedule[1])
            {
                if (nCpns == 2 && country != Country.FR && country != Country.UK)
                {
                    if (country == Country.ES)
                    {
                        delay = (adjSchedule[1] - schedule[1]).TotalDays/(schedule[1] - schedule[0]).TotalDays;
                    }
                    result[0] += coupon * infFactors[1] * freq / 12 * (1.0 - 1.0 / (1.0 + yield * freq / 12 * (1.0 + delay - dcf)));
                    result[1] += (1.0 + delay - dcf) * coupon * infFactors[1] * freq / 12 * freq / 12 / Math.Pow(1.0 + yield * freq / 12 * (1.0 + delay - dcf), 2);
                }
                else
                {
                    result[0] += (bLongFirstCpn ? firstCpnSize : 1.0) * coupon * infFactors[1] * freq / 12 * (1.0 - Math.Pow(1.0 + yield * freq / 12, dcf - (bLongFirstCpn ? 2.0 : 1.0)));
                    result[1] += (bLongFirstCpn ? firstCpnSize * (2.0 - dcf) : 1.0 - dcf) * coupon * infFactors[1] * freq / 12 * freq / 12 * Math.Pow(1.0 + yield * freq / 12, dcf - (bLongFirstCpn ? 2.0 : 1.0));
                }
            }

            return result;
        }
Exemplo n.º 4
0
        public static double[] InterpolateInflationFactors(List<DateTime> schedule, DateTime settleDate, InflationCurve infCurve)
        {
            double[] infFactors;
            int k = 0, i = 0;
            if (schedule != null)
            {
                infFactors = new double[schedule.Count + 1];

                for (i = 0; i < schedule.Count; i++)
                {
                    if (infCurve == null || infCurve.InterpMethod != 1 || schedule[i].AddMonths(-infCurve.InfGap) < infCurve.FcstDates[0])
                        infFactors[i] = 1.0;
                    else
                    {
                        while (k < infCurve.FcstDates.Length - 1 && schedule[i].AddMonths(-infCurve.InfGap).Date > infCurve.FcstDates[k].Date)
                            k++;
                        //  linear interpolation
                        infFactors[i] = ((schedule[i].AddMonths(-infCurve.InfGap) - infCurve.FcstDates[k - 1]).TotalDays * infCurve.FcstIndex[k] + (infCurve.FcstDates[k] - schedule[i].AddMonths(-infCurve.InfGap)).TotalDays * infCurve.FcstIndex[k - 1]) / (infCurve.FcstDates[k] - infCurve.FcstDates[k - 1]).TotalDays;
                        infFactors[i] /= infCurve.InitialFixing;
                    }
                }
            }
            else
                infFactors = new double[1];

            //  Factor for settleDate goes at end
            if (infCurve == null || infCurve.InterpMethod != 1 || settleDate.AddMonths(-infCurve.InfGap) < infCurve.FcstDates[0])
                infFactors[i] = 1.0;
            else
            {
                k = 0;
                while (k < infCurve.FcstDates.Length - 1 && settleDate.AddMonths(-infCurve.InfGap).Date > infCurve.FcstDates[k].Date)
                    k++;
                //  linear interpolation
                infFactors[i] = ((settleDate.AddMonths(-infCurve.InfGap) - infCurve.FcstDates[k - 1]).TotalDays * infCurve.FcstIndex[k] + (infCurve.FcstDates[k] - settleDate.AddMonths(-infCurve.InfGap)).TotalDays * infCurve.FcstIndex[k - 1]) / (infCurve.FcstDates[k] - infCurve.FcstDates[k - 1]).TotalDays;
                infFactors[i] /= infCurve.InitialFixing;
            }

            return infFactors;
        }
Exemplo n.º 5
0
        //  Par-Par Asset Swap pricer
        public static double ParParAssetSwapSpread(DateTime settleDate, DateTime bondEffectiveDate, DateTime bondFirstCpnDate, DateTime bondMaturityDate, double bondCoupon, Country country, DayCountType bondDctType, long bondFreq, double bondCleanPrice, DayCountType floatDctType, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndex = 0, InflationCurve infCurve = null)
        {
            bool bLongFirstCpn = bondFirstCpnDate > settleDate && bondFirstCpnDate > bondEffectiveDate.AddDays(1).AddMonths((int)bondFreq).AddDays(-1);
            DateTime adjSettleDate = (bondEffectiveDate > settleDate ? bondEffectiveDate : settleDate);
            List<DateTime> schedule = GenerateSchedule(bLongFirstCpn && adjSettleDate > bondFirstCpnDate ? bondEffectiveDate : adjSettleDate, bondMaturityDate, bondFreq);
            double dcf = CalcBondAccrualDcf(settleDate, bondEffectiveDate, bondFirstCpnDate, schedule, country);
            double accrual = 100.0 * bondCoupon * bondFreq / 1200 * dcf;

            //  Interpolate inflation curve.
            double[] infFactors = InterpolateInflationFactors(null, settleDate, infCurve);
            double fixedPv = SwapAnalytics.PriceFixedLeg(bondEffectiveDate, bondMaturityDate, bondCoupon, bondDctType, bondFreq, discCurveDates, discDfs, holidays, blendIndex, false, bLongFirstCpn, infCurve, infFactors[0])[0];
            return SwapAnalytics.CalcSwapMargin(bondEffectiveDate, bondMaturityDate, -1.0 + (bondCleanPrice + accrual) / 100.0 - fixedPv / infFactors[0], floatDctType, floatFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndex);
        }
Exemplo n.º 6
0
        public static double PriceFromZSpread(Country country, DateTime settleDate, double spread, DateTime startDate, DateTime firstCpnDate, DateTime maturityDate, double coupon, long freq, DateTime[] curveDates, double[] dfs, List<DateTime> holidays, int exDivDays = 0, InflationCurve infCurve = null)
        {
            bool bLongFirstCpn = firstCpnDate > settleDate && firstCpnDate > startDate.AddDays(1).AddMonths((int)freq).AddDays(-1);
            DateTime adjSettleDate = (startDate > settleDate ? startDate : settleDate);
            List<DateTime> schedule = GenerateSchedule((bLongFirstCpn && adjSettleDate < firstCpnDate ? startDate : adjSettleDate), maturityDate, freq);
            List<DateTime> adjSchedule = GenerateAdjustedSchedule(schedule, holidays, "F");
            double[] interpTimes = new double[adjSchedule.Count];
            List<double> flowZcTimes = new List<double>();
            List<double> flowZcRates = new List<double>();
            double firstCpnSize = 1.0;
            long nCpns = schedule.Count;
            int i;
            bool bExDiv = settleDate.AddDays(exDivDays) >= schedule[(bLongFirstCpn ? 2 : 1)];

            spread *= freq / 12.0;
            double dcf = CalcBondAccrualDcf(settleDate, startDate, firstCpnDate, schedule, country);
            //  In case of stub period at start
            if (firstCpnDate > settleDate)
            {
                if (bLongFirstCpn)
                {
                    firstCpnSize = 1.0 + (schedule[1] - startDate).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
                else
                {
                    firstCpnSize = (firstCpnDate - (startDate > schedule[0] ? startDate : schedule[0])).TotalDays / (schedule[1] - schedule[0]).TotalDays;
                }
            }
            double accrual = 100.0 * coupon * freq / 1200 * (dcf - (bExDiv ? firstCpnSize : 0.0));
            if (country == Country.IT)
            {
                accrual = Math.Round(accrual, 5);
            }
            // Use adjusted schedule dates for payments.
            // Convert dates to times for spline interpolator
            interpTimes[0] = (settleDate - curveDates[0]).TotalDays;
            for (i = 1; i < adjSchedule.Count; i++)
            {
                interpTimes[i] = (adjSchedule[i] - curveDates[0]).TotalDays;
            }
            List<double> flowDfs = InterpDfSpline(interpTimes, curveDates, dfs);
            //  Calculate base discount factors (before spread) and convert to zero coupon rates. Store rate * freq / 12 for efficiency.
            double dt = 12.0 / freq * interpTimes[0] / 365.0;
            if (dt > 0.0)
            {
                flowZcRates.Add((Math.Pow(1.0 / flowDfs[0], 1.0 / dt) - 1.0));
            }
            else
            {
                flowZcRates.Add(0.0);
            }
            flowZcTimes.Add(dt);
            for (i = 1; i < nCpns; i++)
            {
                if (nCpns == 2)
                {
                    dt = 12.0 / freq * interpTimes[i] / 365.0;
                    flowZcRates.Add((1.0 / flowDfs[i] - 1.0) / dt);
                    flowZcTimes.Add(dt);
                }
                else
                {
                    dt = 12.0 / freq * interpTimes[i] / 365.0;
                    flowZcRates.Add((Math.Pow(1.0 / flowDfs[i], 1.0 / dt) - 1.0));
                    flowZcTimes.Add(dt);
                }
            }
            //  Interpolate inflation curve.
            double[] infFactors = InterpolateInflationFactors(adjSchedule, settleDate, infCurve);
            accrual *= infFactors[nCpns];

            //  Initial payment
            double pv = -accrual * Math.Pow(1.0 + (flowZcRates[0] + spread), -flowZcTimes[0]);
            if (nCpns == 2 && country != Country.FR && country != Country.UK)
            {
                pv += 100.0 * infFactors[1] * (1.0 + (bExDiv ? 0.0 : coupon) * freq / 1200) / (1.0 + (flowZcRates[1] + spread) * flowZcTimes[1]);
            }
            else
            {
                //  first coupon
                i = (bLongFirstCpn ? 2 : 1);
                pv += firstCpnSize * 100.0 * infFactors[i] * (bExDiv ? 0.0 : coupon) * freq / 1200 * Math.Pow(1.0 + flowZcRates[i] + spread, -flowZcTimes[i]);

                for (i++; i < nCpns; i++)
                {
                    pv += 100.0 * infFactors[i] * coupon * freq / 1200 * Math.Pow(1.0 + flowZcRates[i] + spread, -flowZcTimes[i]);
                }
                //  Final principal
                pv += 100.0 * infFactors[i - 1] * Math.Pow(1.0 + flowZcRates[i - 1] + spread, -flowZcTimes[i - 1]);
            }

            return pv * Math.Pow(1.0 + (flowZcRates[0] + spread), flowZcTimes[0]) / infFactors[nCpns];
        }
Exemplo n.º 7
0
 public static double SolveZSpread(Country country, DateTime settleDate, double cleanPrice, DateTime startDate, DateTime firstCpnDate, DateTime maturityDate, double coupon, long freq, DateTime[] curveDates, double[] dfs, List<DateTime> holidays, int exDivDays = 0, InflationCurve infCurve = null)
 {
     return SolveZSpread2(country, settleDate, cleanPrice, startDate, firstCpnDate, maturityDate, coupon, freq, curveDates, dfs, holidays, exDivDays, infCurve)[0];
 }