private double calculateSinglePeriodAccrualOnDefault(
            CdsCoupon coupon,
            double effectiveStart,
            double[] integrationPoints,
            YieldTermStructure yieldCurve,
            PiecewiseconstantHazardRate creditCurve)
        {
            double start = Math.Max(coupon.getEffStart(), effectiveStart);

            if (start >= coupon.getEffEnd())
            {
                return(0.0); //this coupon has already expired
            }

            double[] knots = DoublesScheduleGenerator.truncateSetInclusive(start, coupon.getEffEnd(), integrationPoints);

            double t   = knots[0];
            double ht0 = creditCurve.getRT_(t);
            double rt0 = yieldCurve.getRT_(t);
            double b0  = Math.Exp(-rt0 - ht0); // this is the risky discount factor

            double t0     = t - coupon.getEffStart() + _omega;
            double pv     = 0.0;
            int    nItems = knots.Length;

            for (int j = 1; j < nItems; ++j)
            {
                t = knots[j];
                double ht1 = creditCurve.getRT_(t);
                double rt1 = yieldCurve.getRT_(t);
                double b1  = Math.Exp(-rt1 - ht1);

                double dt = knots[j] - knots[j - 1];

                double dht  = ht1 - ht0;
                double drt  = rt1 - rt0;
                double dhrt = dht + drt;

                double tPV;
                double t1 = t - coupon.getEffStart() + _omega;
                if (Math.Abs(dhrt) < 1e-5)
                {
                    tPV = dht * b0 * (t0 * Maths.Epsilon.epsilon(-dhrt) + dt * Maths.Epsilon.epsilonP(-dhrt));
                }
                else
                {
                    tPV = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1));
                }
                t0  = t1;
                pv += tPV;
                ht0 = ht1;
                rt0 = rt1;
                b0  = b1;
            }
            return(coupon.getYFRatio() * pv);
        }
Beispiel #2
0
            private double calculateSinglePeriodAccrualOnDefault(int paymentIndex, PiecewiseconstantHazardRate creditCurve)
            {
                double[] knots = _premLegIntPoints[paymentIndex];
                if (knots == null)
                {
                    return(0.0);
                }
                double[] df       = _premDF[paymentIndex];
                double[] deltaT   = _premDt[paymentIndex];
                double[] rt       = _rt[paymentIndex];
                double   accRate  = _accRate[paymentIndex];
                double   accStart = _offsetAccStart[paymentIndex];

                double t   = knots[0];
                double ht0 = creditCurve.getRT_(t);
                double rt0 = rt[0];
                double b0  = df[0] * Math.Exp(-ht0);

                double t0     = t - accStart + _omega;
                double pv     = 0.0;
                int    nItems = knots.Length;

                for (int j = 1; j < nItems; ++j)
                {
                    t = knots[j];
                    double ht1 = creditCurve.getRT_(t);
                    double rt1 = rt[j];
                    double b1  = df[j] * Math.Exp(-ht1);
                    double dt  = deltaT[j - 1];

                    double dht  = ht1 - ht0;
                    double drt  = rt1 - rt0;
                    double dhrt = dht + drt + 1e-50; // to keep consistent with ISDA c code

                    double tPV;

                    if (Math.Abs(dhrt) < 1e-5)
                    {
                        tPV = dht * dt * b0 * Epsilon.epsilonP(-dhrt);
                    }
                    else
                    {
                        tPV = dht * dt / dhrt * ((b0 - b1) / dhrt - b1);
                    }
                }

                return(accRate * pv);
            }
Beispiel #3
0
        public virtual double[] pvAndSense(PiecewiseconstantHazardRate creditCurve)
        {
            double pv      = _riskLessValue * Math.Exp(-creditCurve.getRT_(_effEnd));
            double pvSense = -pv *creditCurve.getSingleNodeRTSensitivity(_effEnd, _creditCurveKnot);

            return(new double[] { pv, pvSense });
        }
Beispiel #4
0
            public double rpv01(PiecewiseconstantHazardRate creditCurve, CdsPriceType cleanOrDirty)
            {
                double pv = 0.0;

                for (int i = 0; i < _nPayments; i++)
                {
                    CdsCoupon c = _cds.getCoupon(i);
                    double    q = Math.Exp(-creditCurve.getRT_(c.getEffEnd()));
                    pv += c.getYearFrac() * _paymentDF[i] * q;
                }

                if (_cds.isPayAccOnDefault())
                {
                    double accPV = 0.0;
                    for (int i = 0; i < _nPayments; i++)
                    {
                        accPV += calculateSinglePeriodAccrualOnDefault(i, creditCurve);
                    }
                    pv += accPV;
                }

                pv /= _valuationDF;

                if (cleanOrDirty == CdsPriceType.CLEAN)
                {
                    pv -= _cds.getAccruedYearFraction();
                }
                return(pv);
            }
Beispiel #5
0
            public double protectionLeg(PiecewiseconstantHazardRate creditCurve)
            {
                double ht0 = creditCurve.getRT_(_proLegIntPoints[0]);
                double rt0 = _proYieldCurveRT[0];
                double b0  = _proDF[0] * Math.Exp(-ht0);

                double pv = 0.0;

                for (int i = 1; i < _nProPoints; ++i)
                {
                    double ht1  = creditCurve.getRT_(_proLegIntPoints[i]);
                    double rt1  = _proYieldCurveRT[i];
                    double b1   = _proDF[i] * Math.Exp(-ht1);
                    double dht  = ht1 - ht0;
                    double drt  = rt1 - rt0;
                    double dhrt = dht + drt;

                    // this is equivalent to the ISDA code without explicitly calculating the time step - it also handles the limit
                    double dPV;
                    if (Math.Abs(dhrt) < 1e-5)
                    {
                        dPV = dht * b0 * Epsilon.epsilon(-dhrt);
                    }
                    else
                    {
                        dPV = (b0 - b1) * dht / dhrt;
                    }
                    pv += dPV;
                    ht0 = ht1;
                    rt0 = rt1;
                    b0  = b1;
                }
                pv *= _lgdDF; // multiply by LGD and adjust to valuation date

                return(pv);
            }
        /**
         * Compute the present value of the protection leg with a notional of 1, which is given by the integral
         * $\frac{1-R}{P(T_{v})} \int_{T_a} ^{T_b} P(t) \frac{dQ(t)}{dt} dt$ where $P(t)$ and $Q(t)$ are the discount
         * and survival curves respectively, $T_a$ and $T_b$ are the start and end of the protection respectively,
         * $T_v$ is the valuation time (all measured from $t = 0$, 'today') and $R$ is the recovery rate.
         *
         * @param cds  the analytic description of a CDS traded at a certain time
         * @param yieldCurve  the yield (or discount) curve
         * @param creditCurve  the credit (or survival) curve
         * @param valuationTime  the valuation time, if time is zero, leg is valued today,
         *  value often quoted for cash-settlement date
         * @return the value of the protection leg (on a unit notional)
         */
        public double annuity(
            CDS cds,
            YieldTermStructure yieldCurve,
            PiecewiseconstantHazardRate creditCurve,
            CdsPriceType cleanOrDirty,
            double valuationTime)
        {
            double pv    = dirtyAnnuity(cds, yieldCurve, creditCurve);
            double valDF = Math.Exp(-yieldCurve.getRT_(valuationTime));

            if (cleanOrDirty == CdsPriceType.CLEAN)
            {
                double csTime    = cds.getCashSettleTime();
                double protStart = cds.getEffectiveProtectionStart();
                double csDF      = valuationTime == csTime ? valDF :Math.Exp(-yieldCurve.getRT_(csTime));
                double q         = protStart == 0 ? 1.0 : Math.Exp(-creditCurve.getRT_(protStart));
                double acc       = cds.getAccruedYearFraction();
                pv -= acc * csDF * q; //subtract the accrued risky discounted to today
            }

            pv /= valDF; //roll forward to valuation date
            return(pv);
        }
        /**
         * The value of the full (or dirty) annuity (or RPV01 - the premium leg per unit of coupon) today (t=0).
         * The cash flows from premium payments and accrual-on-default are risky discounted to t=0
         * The actual value of the leg is this multiplied by the notional and the fractional coupon
         * (i.e. coupon in basis points divided by 10,000).
         * <p>
         * This is valid for both spot and forward starting CDS.
         *
         * @param cds  the analytic description of a CDS traded at a certain time
         * @param yieldCurve  the yield (or discount) curve
         * @param creditCurve  the credit (or survival) curve
         * @return the full (or dirty) annuity valued today. <b>Note</b> what is usually quoted is the clean annuity
         */
        public double dirtyAnnuity(CDS cds,
                                   YieldTermStructure yt, PiecewiseconstantHazardRate hazard)
        {
            DateTime        tradedate      = cds.tradedate;
            List <DateTime> Jumps          = yt.jumpDates_;
            DateTime        settlementDate = tradedate.AddDays(0);
            double          recoveryrate   = cds.Recovery;
            DateTime        Stepindate     = tradedate.AddDays(1);
            double          coupon         = cds.PremiumRate;

            OMLib.Conventions.DayCount.Actual360 dc = new OMLib.Conventions.DayCount.Actual360();

            CdsCoupon[] cf       = cds.getCoupons();
            double      notional = cds.Notional;

            double ita      = (double)365 / 360;
            double totalNPV = 0.0;

            for (int i = 0; i < cf.Length; ++i)
            {
                totalNPV += cf[i].getYearFrac() * notional * Math.Exp(-hazard.getRT_(cf[i].getEffEnd()))
                            * Math.Exp(-yt.getRT_(cf[i].getPaymentTime()));
            }

            double start = cds.getNumPayments() == 1 ? cds.getEffectiveProtectionStart() : cds.getAccStart();

            double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(start, cds.getProtectionEnd(), yt, hazard);
            double   accPV = 0.0;

            for (int i = 0; i < cf.Length; ++i)
            {
                accPV += calculateSinglePeriodAccrualOnDefault(cf[i], cds.getEffectiveProtectionStart(), integrationSchedule, yt, hazard);
            }
            totalNPV += accPV;

            return(totalNPV);
        }
Beispiel #8
0
 //-------------------------------------------------------------------------
 public double pv(PiecewiseconstantHazardRate creditCurve)
 {
     return(_riskLessValue * Math.Exp(-creditCurve.getRT_(_effEnd)));
 }
        private double calculateSinglePeriodAccrualOnDefaultCreditSensitivity(
            CdsCoupon coupon,
            double effStart,
            double[] integrationPoints,
            YieldTermStructure yieldCurve,
            PiecewiseconstantHazardRate creditCurve,
            int creditCurveNode)
        {
            double start = Math.Max(coupon.getEffStart(), effStart);

            if (start >= coupon.getEffEnd())
            {
                return(0.0);
            }
            double[] knots = DoublesScheduleGenerator.truncateSetInclusive(start, coupon.getEffEnd(), integrationPoints);

            double t     = knots[0];
            double ht0   = creditCurve.getRT_(t);
            double rt0   = yieldCurve.getRT_(t);
            double p0    = Math.Exp(-rt0);
            double q0    = Math.Exp(-ht0);
            double b0    = p0 * q0; // this is the risky discount factor
            double dqdr0 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode);

            double t0      = t - coupon.getEffStart() + _omega;
            double pvSense = 0.0;
            int    nItems  = knots.Length;

            for (int j = 1; j < nItems; ++j)
            {
                t = knots[j];
                double ht1   = creditCurve.getRT_(t);
                double rt1   = yieldCurve.getRT_(t);
                double p1    = Math.Exp(-rt1);
                double q1    = Math.Exp(-ht1);
                double b1    = p1 * q1;
                double dqdr1 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode);

                double dt = knots[j] - knots[j - 1];

                double dht  = ht1 - ht0;
                double drt  = rt1 - rt0;
                double dhrt = dht + drt + 1e-50; // to keep consistent with ISDA c code

                double tPvSense;
                // TODO once the maths is written up in a white paper, check these formula again,
                // since tests again finite difference could miss some subtle error

                if (_formula == AccrualOnDefaultFormulae.MARKIT_FIX)
                {
                    if (Math.Abs(dhrt) < 1e-5)
                    {
                        double eP     = Maths.Epsilon.epsilonP(-dhrt);
                        double ePP    = Maths.Epsilon.epsilonPP(-dhrt);
                        double dPVdq0 = p0 * dt * ((1 + dht) * eP - dht * ePP);
                        double dPVdq1 = b0 * dt / q1 * (-eP + dht * ePP);
                        tPvSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1;
                    }
                    else
                    {
                        double w1     = (b0 - b1) / dhrt;
                        double w2     = w1 - b1;
                        double w3     = dht / dhrt;
                        double w4     = dt / dhrt;
                        double w5     = (1 - w3) * w2;
                        double dPVdq0 = w4 / q0 * (w5 + w3 * (b0 - w1));
                        double dPVdq1 = w4 / q1 * (w5 + w3 * (b1 * (1 + dhrt) - w1));
                        tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1;
                    }
                }
                else
                {
                    double t1 = t - coupon.getEffStart() + _omega;
                    if (Math.Abs(dhrt) < 1e-5)
                    {
                        double e      = Maths.Epsilon.epsilon(-dhrt);
                        double eP     = Maths.Epsilon.epsilonP(-dhrt);
                        double ePP    = Maths.Epsilon.epsilonPP(-dhrt);
                        double w1     = t0 * e + dt * eP;
                        double w2     = t0 * eP + dt * ePP;
                        double dPVdq0 = p0 * ((1 + dht) * w1 - dht * w2);
                        double dPVdq1 = b0 / q1 * (-w1 + dht * w2);
                        tPvSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1;
                    }
                    else
                    {
                        double w1     = dt / dhrt;
                        double w2     = dht / dhrt;
                        double w3     = (t0 + w1) * b0 - (t1 + w1) * b1;
                        double w4     = (1 - w2) / dhrt;
                        double w5     = w1 / dhrt * (b0 - b1);
                        double dPVdq0 = w4 * w3 / q0 + w2 * ((t0 + w1) * p0 - w5 / q0);
                        double dPVdq1 = w4 * w3 / q1 + w2 * ((t1 + w1) * p1 - w5 / q1);
                        tPvSense = dPVdq0 * dqdr0 - dPVdq1 * dqdr1;
                    }
                    t0 = t1;
                }

                pvSense += tPvSense;
                ht0      = ht1;
                rt0      = rt1;
                p0       = p1;
                q0       = q1;
                b0       = b1;
                dqdr0    = dqdr1;
            }
            return(coupon.getYFRatio() * pvSense);
        }
        /**
         * The sensitivity of the PV of the protection leg to the zero rate of a given node (knot) of the yield curve.
         *
         * @param cds  the analytic description of a CDS traded at a certain time
         * @param yieldCurve  the yield (or discount) curve
         * @param creditCurve  the credit (or survival) curve
         * @param yieldCurveNode  the yield curve node
         * @return the sensitivity (on a unit notional)
         */
        public double protectionLegYieldSensitivity(
            CDS cds,
            YieldTermStructure yieldCurve,
            PiecewiseconstantHazardRate creditCurve,
            int yieldCurveNode)
        {
            if ((yieldCurveNode != 0 && cds.getProtectionEnd() <= yieldCurve.t[yieldCurveNode - 1]) ||
                (yieldCurveNode != creditCurve.getNumberOfKnots() - 1 &&
                 cds.getEffectiveProtectionStart() >= yieldCurve.t[yieldCurveNode + 1]))
            {
                return(0.0); // can't have any sensitivity in this case
            }
            if (cds.getProtectionEnd() <= 0.0)
            { //short cut already expired CDSs
                return(0.0);
            }

            double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(
                cds.getEffectiveProtectionStart(), cds.getProtectionEnd(), yieldCurve, creditCurve);

            double t       = integrationSchedule[0];
            double ht0     = creditCurve.getRT_(t);
            double rt0     = yieldCurve.getRT_(t);
            double dpdr0   = yieldCurve.getSingleNodeDiscountFactorSensitivity(t, yieldCurveNode);
            double q0      = Math.Exp(-ht0);
            double p0      = Math.Exp(-rt0);
            double pvSense = 0.0;
            int    n       = integrationSchedule.Length;

            for (int i = 1; i < n; ++i)
            {
                t = integrationSchedule[i];
                double ht1   = creditCurve.getRT_(t);
                double dpdr1 = yieldCurve.getSingleNodeDiscountFactorSensitivity(t, yieldCurveNode);
                double rt1   = yieldCurve.getRT_(t);
                double q1    = Math.Exp(-ht1);
                double p1    = Math.Exp(-rt1);

                if (dpdr0 == 0.0 && dpdr1 == 0.0)
                {
                    ht0 = ht1;
                    rt0 = rt1;
                    p0  = p1;
                    q0  = q1;
                    continue;
                }

                double hBar  = ht1 - ht0;
                double fBar  = rt1 - rt0;
                double fhBar = hBar + fBar;

                double dPVSense;
                double e      = Maths.Epsilon.epsilon(-fhBar);
                double eP     = Maths.Epsilon.epsilonP(-fhBar);
                double dPVdp0 = q0 * hBar * (e - eP);
                double dPVdp1 = hBar * p0 * q0 / p1 * eP;
                dPVSense = dPVdp0 * dpdr0 + dPVdp1 * dpdr1;

                pvSense += dPVSense;

                ht0   = ht1;
                dpdr0 = dpdr1;
                rt0   = rt1;
                p0    = p1;
                q0    = q1;
            }
            pvSense *= cds.getLGD();

            // Compute the discount factor discounting the upfront payment made on the cash settlement date back to the valuation date
            double df = Math.Exp(-yieldCurve.getRT_(cds.getCashSettleTime()));

            pvSense /= df;

            //TODO this was put in quickly the get the right sensitivity to the first node
            double dfSense = yieldCurve.getSingleNodeDiscountFactorSensitivity(cds.getCashSettleTime(), yieldCurveNode);

            if (dfSense != 0.0)
            {
                double pro = protectionLeg(cds, yieldCurve, creditCurve);
                pvSense -= pro / df * dfSense;
            }

            return(pvSense);
        }
        public double protectionLegCreditSensitivity(
            CDS cds,
            YieldTermStructure yieldCurve,
            PiecewiseconstantHazardRate creditCurve,
            int creditCurveNode)
        {
            if ((creditCurveNode != 0 && cds.getProtectionEnd() <= creditCurve.getTimeAtIndex(creditCurveNode - 1)) ||
                (creditCurveNode != creditCurve.t.Count - 1 &&
                 cds.getEffectiveProtectionStart() >= creditCurve.getTimeAtIndex(creditCurveNode + 1)))
            {
                return(0.0); // can't have any sensitivity in this case
            }
            if (cds.getProtectionEnd() <= 0.0)
            { //short cut already expired CDSs
                return(0.0);
            }

            double[] integrationSchedule = DoublesScheduleGenerator.getIntegrationsPoints(
                cds.getEffectiveProtectionStart(), cds.getProtectionEnd(), yieldCurve, creditCurve);

            double t       = integrationSchedule[0];
            double ht0     = creditCurve.getRT_(t);
            double rt0     = yieldCurve.getRT_(t);
            double dqdr0   = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode);
            double q0      = Math.Exp(-ht0);
            double p0      = Math.Exp(-rt0);
            double pvSense = 0.0;
            int    n       = integrationSchedule.Length;

            for (int i = 1; i < n; ++i)
            {
                t = integrationSchedule[i];
                double ht1   = creditCurve.getRT_(t);
                double dqdr1 = creditCurve.getSingleNodeDiscountFactorSensitivity(t, creditCurveNode);
                double rt1   = yieldCurve.getRT_(t);
                double q1    = Math.Exp(-ht1);
                double p1    = Math.Exp(-rt1);

                if (dqdr0 == 0.0 && dqdr1 == 0.0)
                {
                    ht0 = ht1;
                    rt0 = rt1;
                    p0  = p1;
                    q0  = q1;
                    continue;
                }

                double hBar  = ht1 - ht0;
                double fBar  = rt1 - rt0;
                double fhBar = hBar + fBar;

                double dPVSense;
                if (Math.Abs(fhBar) < 1e-5)
                {
                    double e      = Maths.Epsilon.epsilon(-fhBar);
                    double eP     = Maths.Epsilon.epsilonP(-fhBar);
                    double dPVdq0 = p0 * ((1 + hBar) * e - hBar * eP);
                    double dPVdq1 = -p0 * q0 / q1 * (e - hBar * eP);
                    dPVSense = dPVdq0 * dqdr0 + dPVdq1 * dqdr1;
                }
                else
                {
                    double w = fBar / fhBar * (p0 * q0 - p1 * q1);
                    dPVSense = ((w / q0 + hBar * p0) / fhBar) * dqdr0 - ((w / q1 + hBar * p1) / fhBar) * dqdr1;
                }

                pvSense += dPVSense;

                ht0   = ht1;
                dqdr0 = dqdr1;
                rt0   = rt1;
                p0    = p1;
                q0    = q1;
            }
            pvSense *= cds.getLGD();

            // Compute the discount factor discounting the upfront payment made on the cash settlement date back to the valuation date
            double df = Math.Exp(-yieldCurve.getRT_(cds.getCashSettleTime()));

            pvSense /= df;

            return(pvSense);
        }
        public double calculateSinglePeriodAccrualOnDefault(CDS cds,
                                                            YieldTermStructure yieldCurve,
                                                            PiecewiseconstantHazardRate creditCurve)
        {
            double Acc = 0;

            List <double> Jumps          = yieldCurve.t;
            DateTime      tradedate      = cds.tradedate;
            DateTime      settlementDate = tradedate.AddDays(cds.Cashsettlement);
            double        recoveryrate   = cds.Recovery;
            DateTime      Stepindate     = tradedate.AddDays(1);

            double   notional       = cds.Notional;
            double   coupon         = cds.PremiumRate;
            DateTime lastpayment    = cds.formerpaymentdate;
            DateTime effectiveStart = tradedate.AddDays(1);

            CdsCoupon[] cc = cds.getCoupons();

            for (int i = 0; i < cds.getNumPayments(); ++i)
            {
                //Accured on default
                double t_0 = (i > 0) ? cc[i].getEffStart():0;
                double T   = cc[i].getEffEnd();


                List <double> knots = new List <double>();
                knots.Add(t_0);
                for (int j = 0; j < Jumps.Count; j++)
                {
                    if ((Jumps[j] < T) && (t_0 < Jumps[j]))
                    {
                        knots.Add(Jumps[j]);
                    }
                }
                knots.Add(T);

                double t   = knots[0];
                double ht0 = creditCurve.getRT_(t);
                double rt0 = yieldCurve.getRT_(t);
                double b0  = Math.Exp(-rt0 - ht0); // this is the risky discount factor

                OMLib.Conventions.DayCount.Actual365 dc = new OMLib.Conventions.DayCount.Actual365();
                double t0;
                if (i == 0)
                {
                    t0 = knots[0] - cc[0].getEffStart();
                }
                else
                {
                    t0 = knots[0] - cc[i].getEffStart();
                }
                double pv     = 0.0;
                int    nItems = knots.Count;
                for (int j = 1; j < nItems; ++j)
                {
                    t = knots[j];
                    double ht1 = creditCurve.getRT_(t);
                    double rt1 = yieldCurve.getRT_(t);
                    double b1  = Math.Exp(-rt1 - ht1);

                    double dt = knots[j] - knots[j - 1];

                    double dht  = ht1 - ht0;
                    double drt  = rt1 - rt0;
                    double dhrt = dht + drt + 1e-50;

                    double tPV;
                    double t1;
                    if (i == 0)
                    {
                        t1 = knots[j] - cc[0].getEffStart();
                    }
                    else
                    {
                        t1 = knots[j] - cc[i].getEffStart();
                    }
                    if (Math.Abs(dhrt) < 1e-5)
                    {
                        tPV = dht * b0 * (t0 * (Math.Exp(-dhrt) - 1) / (-dhrt) + dt * ((-dhrt - 1) * (Math.Exp(-dhrt) - 1) - dhrt) / (dhrt * dhrt));
                    }
                    else
                    {
                        tPV = dht / dhrt * (t0 * b0 - t1 * b1 + dt / dhrt * (b0 - b1));
                    }
                    t0  = t1;
                    pv += tPV;
                    ht0 = ht1;
                    rt0 = rt1;
                    b0  = b1;
                }
                Acc += pv;
            }
            return(Acc);
        }
        public double protectionLeg(CDS cds, YieldTermStructure yt, PiecewiseconstantHazardRate hazard,
                                    double valuationTime)
        {
            List <double> Jumps = yt.t;
            List <double> tenor = hazard.t;
            List <double> result = new List <double>();
            int           index = 0, indexj = 0, lastIndex = 0;

            while (index < Jumps.Count || indexj < tenor.Count)
            {
                if (lastIndex > 0)
                {
                    if (index >= Jumps.Count)
                    {
                        if (!DateTime.Equals(result.Last(), tenor[indexj]))
                        {
                            result.Add(tenor[indexj]);
                            lastIndex++;
                        }
                        indexj++;
                        continue;
                    }
                    if (indexj >= tenor.Count)
                    {
                        if (!DateTime.Equals(result.Last(), Jumps[index]))
                        {
                            result.Add(Jumps[index]);
                            lastIndex++;
                        }
                        index++;
                        continue;
                    }
                }
                double smallestVal = tenor.Last();

                // Choose the smaller of a or b
                if (Jumps[index] < tenor[indexj])
                {
                    smallestVal = Jumps[index++];
                }
                else
                {
                    smallestVal = tenor[indexj++];
                }

                // Don't insert duplicates
                if (lastIndex > 0)
                {
                    if (result.Last() != smallestVal)
                    {
                        result.Add(smallestVal);
                        lastIndex++;
                    }
                }
                else
                {
                    result.Add(smallestVal);
                    lastIndex++;
                }
            }
            DateTime tradedate      = cds.tradedate;
            DateTime settlementDate = tradedate.AddDays((int)valuationTime * 365);
            double   recoveryrate   = cds.Recovery;
            DateTime Stepindate     = tradedate.AddDays(1);

            OMLib.Conventions.DayCount.Actual360 dc = new OMLib.Conventions.DayCount.Actual360();
            CdsCoupon[] cf       = cds.getCoupons();
            double      notional = cds.Notional;

            DateTime      t0        = tradedate;
            double        T         = cf.Last().getEffEnd();
            List <double> JumpNodes = new List <double>();

            JumpNodes.Add(0);
            for (int j = 0; j < result.Count; j++)
            {
                if (result[j] < T)
                {
                    JumpNodes.Add(result[j]);
                }
            }
            JumpNodes.Add(T);
            double ht0 = hazard.getRT_(JumpNodes[0]);
            double rt0 = yt.getRT_(JumpNodes[0]);
            double b0  = Math.Exp(-ht0 - rt0); // risky discount factor

            double pv  = 0.0;
            double dPV = 0.0;

            for (int i = 1; i < JumpNodes.Count; ++i)
            {
                double ht1 = hazard.getRT_(JumpNodes[i]);
                double rt1 = yt.getRT_(JumpNodes[i]);
                double b1  = Math.Exp(-ht1 - rt1);

                double dht  = ht1 - ht0;
                double drt  = rt1 - rt0;
                double dhrt = dht + drt;

                // The formula has been modified from ISDA (but is equivalent) to avoid log(exp(x)) and explicitly
                // calculating the time step - it also handles the limit
                if (Math.Abs(dhrt) < 1e-5)
                {
                    dPV = dht * b0 * Maths.Epsilon.epsilon(-dhrt) / (-dhrt);
                }
                else
                {
                    dPV = (b0 - b1) * dht / dhrt;
                }
                pv += dPV;
                ht0 = ht1;
                rt0 = rt1;
                b0  = b1;
            }
            pv = pv * notional * (1 - recoveryrate);
            return(pv / yt.discount(settlementDate));
        }