예제 #1
0
        //  Calculate DayCountFraction. Allows for ACT/ACT-ICMA, Act/365, Act/365f and 30I/360 so far.
        //  Period runs from startDate to settleDate, endDate is period end date for ACT/ACT.
        public static double CalcDcf(DateTime periodStartDate, DateTime periodEndDate, DateTime settleDate, DayCountType dctType)
        {
            double fraction = 0.0;

            if (settleDate.Date < periodStartDate.Date)
                return 0.0;

            switch (dctType)
            {
                case DayCountType.ActActIcma:
                    if (settleDate.Date >= periodEndDate.Date)
                    {
                        fraction = 1.0;
                    }
                    else
                    {
                        fraction = (settleDate - periodStartDate).TotalDays / (periodEndDate - periodStartDate).TotalDays;
                    }
                    break;
                //  This is 30/360 as defined in 4.16(f) of 2006 ISDA definitions
                case DayCountType.F30360:
                    fraction = 360.0 * (settleDate.Year - periodStartDate.Year) + 30.0 * (settleDate.Month - periodStartDate.Month);
                    fraction -= Math.Min(periodStartDate.Day, 30);
                    if (settleDate.Day == 31 && periodStartDate.Day == 30)
                    {
                        fraction += 30;
                    }
                    else
                    {
                        fraction += settleDate.Day;
                    }
                    fraction /= 360.0;
                    break;
                //  This is 30E360, 4.16(g) ISDA 2006.
                case DayCountType.E30360:
                    fraction = 360.0 * (settleDate.Year - periodStartDate.Year) + 30.0 * (settleDate.Month - periodStartDate.Month);
                    fraction += Math.Min(settleDate.Day, 30) - Math.Min(periodStartDate.Day, 30);
                    fraction /= 360.0;
                    break;
                //  This is 30I360, 4.16(h) ISDA 2006.
                case DayCountType.I30360:
                    fraction = 360.0 * (settleDate.Year - periodStartDate.Year) + 30.0 * (settleDate.Month - periodStartDate.Month);
                    fraction += Math.Min(settleDate.Day, 30) - Math.Min(periodStartDate.Day, 30);
                    if (settleDate.Month == 2 && settleDate.Date != periodEndDate.Date)
                    {
                        fraction += 1.0;
                    }
                    fraction /= 360.0;
                    break;
                //  This is 30E+/360.
                case DayCountType.EPlus30360:
                    fraction = 360.0 * (settleDate.Year - periodStartDate.Year) + 30.0 * (settleDate.Month - periodStartDate.Month);
                    fraction += Math.Min(settleDate.Day, 30) - Math.Min(periodStartDate.Day, 30);
                    if (settleDate.Day == 31)
                    {
                        fraction += 1;
                    }
                    fraction /= 360.0;
                    break;
                //  This is Act/365 Fixed, 4.16(d) ISDA 2006.
                case DayCountType.Act365:
                    fraction = (settleDate - periodStartDate).TotalDays / 365.0;
                    break;
                //  This is Act/360, 4.16(e) ISDA 2006.
                case DayCountType.Act360:
                    fraction = (settleDate - periodStartDate).TotalDays / 360.0;
                    break;
                //  This is the coupon accrual convention for UK and JP bond, adjusted so that the full period accrual is exactly 0.5 for semi-annual coupons.
                case DayCountType.Act365F:
                    //  Explicitly assume two coupons per year.
                    if ((settleDate - periodStartDate).TotalDays > 182)
                    {
                        fraction = 0.5 - (periodEndDate - settleDate).TotalDays / 365.0;
                    }
                    else
                    {
                        fraction = (settleDate - periodStartDate).TotalDays / 365.0;
                    }
                    break;
            }

            return fraction;
        }
예제 #2
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);
        }
예제 #3
0
 //  Discount margin pricer for floater bonds
 public static double DiscountMarginFRN(DateTime settleDate, DateTime bondEffectiveDate, DateTime bondFirstCpnDate, DateTime bondMaturityDate, double bondMargin, 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, double lastCpnLibor = 0.0, int blendIndex = 0)
 {
     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);
     int i = 0;
     while (settleDate >= schedule[i] && i < schedule.Count - 1)
         i++;
     double dcf = (i == 0 ? 0.0 : CalcDcf(schedule[i - 1], schedule[i], settleDate, bondDctType));
     double accrual = (lastCpnLibor + bondMargin / 100.0) * dcf;
     double floatPv = SwapAnalytics.PriceFloatLeg(bondEffectiveDate, bondMaturityDate, bondMargin, bondDctType, bondFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndex)[0];
     return SwapAnalytics.CalcSwapMargin(bondEffectiveDate, bondMaturityDate, -1.0 + (bondCleanPrice + accrual) / 100.0 - floatPv, floatDctType, floatFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndex);
 }
예제 #4
0
        //  Calculate MMS. Assumes startDate > today, so won't handle historic first fixing / first fixed period.
        public static double CalcMMS(DateTime startDate, DateTime maturityDate, DayCountType dctType, long fixedFreq, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double[] stubExpiries, double[] stubTenors, double[,] stubValues, double[] fixingTenors, double[] fixings, DateTime firstCpnDate, int blendIndex = 0)
        {
            bool bLongFirstCpn = firstCpnDate > discCurveDates[0] && firstCpnDate > startDate.AddDays(1).AddMonths((int)fixedFreq).AddDays(-1);
            List<DateTime> schedule = GenerateSchedule(startDate, maturityDate, fixedFreq);
            List<DateTime> floatSchedule = GenerateSchedule(startDate, maturityDate, floatFreq);
            List<DateTime> adjSchedule = GenerateAdjustedSchedule(schedule, holidays, (dctType == DayCountType.I30360 ? "MF" : "F"));
            List<DateTime> adjFloatSchedule = GenerateAdjustedSchedule(floatSchedule, holidays, (dctType == DayCountType.I30360 ? "MF" : "F"));
            double[] interpTimes = new double[adjSchedule.Count];
            double[] interpFloatTimes = new double[adjFloatSchedule.Count];
            int i;

            // Use adjusted schedule dates for payments.
            interpTimes[0] = (startDate - discCurveDates[0]).TotalDays;
            interpFloatTimes[0] = (startDate - discCurveDates[0]).TotalDays;
            // Convert dates to times for spline interpolator
            for (i = 1; i < adjSchedule.Count - (bLongFirstCpn ? 1 : 0); i++)
            {
                interpTimes[i] = (adjSchedule[i + (bLongFirstCpn ? 1 : 0)] - discCurveDates[0]).TotalDays;
            }
            for (i = 1; i < adjFloatSchedule.Count; i++)
            {
                interpFloatTimes[i] = (adjFloatSchedule[i] - discCurveDates[0]).TotalDays;
            }

            List<double> floatDiscDfs = InterpDfSpline(interpFloatTimes, discCurveDates, discDfs);
            List<double> floatFcstDfs = (blendIndex > 0 ? InterpDfBlend(interpFloatTimes, fcstCurveDates, fcstDfs, blendIndex) : InterpDfSpline(interpFloatTimes, fcstCurveDates, fcstDfs));
            //  First float period is unusual: possibilities include fixing already known or stub period
            double floatPv = (floatFcstDfs[0] / floatFcstDfs[1] - 1.0) * floatDiscDfs[1];
            if (fixings != null && startDate < NextBusDay(NextBusDay(fcstCurveDates[0].AddDays(1), holidays).AddDays(1), holidays))
            {
                //  Known fixings: interpolate.
                i = 0;
                while (i < fixingTenors.Length - 1 && fixingTenors[i] < interpFloatTimes[1]) i++;
                double liborFix = (i == 0 ? fixings[0] : ((fixingTenors[i] - interpFloatTimes[1]) * fixings[i - 1] + (interpFloatTimes[1] - fixingTenors[i - 1]) * fixings[i]) / (fixingTenors[i] - fixingTenors[i - 1]));
                floatPv = liborFix * (adjFloatSchedule[1] - startDate).TotalDays / 360.0;
            }
            //  If basis spreads have been provided for the stub period then use them
            else if (stubValues != null && adjFloatSchedule[0] < startDate)
            {
                double factor = ALGLIB.splineSmoothWrapper.biLinearInterp((startDate - fcstCurveDates[0]).TotalDays, (adjFloatSchedule[1] - startDate).TotalDays, stubExpiries, stubTenors, stubValues);
                floatPv -= factor * (adjFloatSchedule[1] - startDate).TotalDays / 360.0;
            }
            for (i = 2; i < adjFloatSchedule.Count; i++)
            {
                floatPv += (floatFcstDfs[i - 1] / floatFcstDfs[i] - 1.0) * floatDiscDfs[i];
            }
            List<double> bondDiscDfs = InterpDfSpline(interpTimes, discCurveDates, discDfs);
            double fixedPv01 = 0.0;
            for (i = 1 + (bLongFirstCpn ? 1 : 0); i < adjSchedule.Count; i++)
            {
                double factor;
                if (dctType == DayCountType.ActActIcma)
                {
                    if (i > 1 + (bLongFirstCpn ? 1 : 0))
                    {
                        factor = fixedFreq / 12.0;
                    }
                    else if (bLongFirstCpn)
                    {
                        factor = fixedFreq / 12.0 * (1.0 + (adjSchedule[1] - startDate).TotalDays / (adjSchedule[1] - adjSchedule[0]).TotalDays);
                    }
                    else
                    {
                        factor = fixedFreq / 12.0 * (adjSchedule[1] - startDate).TotalDays / (adjSchedule[1] - adjSchedule[0]).TotalDays;
                    }
                }
                else
                {
                    factor = CalcDcf((i == 1 + (bLongFirstCpn ? 1 : 0) ? startDate : adjSchedule[i - 1]), adjSchedule[i], adjSchedule[i], dctType);
                }
                fixedPv01 += factor * bondDiscDfs[i - (bLongFirstCpn ? 1 : 0)];
            }

            return floatPv / fixedPv01;
        }