Class IsdaCdsModel

java.lang.Object
com.quantfinlib.credit.IsdaCdsModel

public final class IsdaCdsModel extends Object
The ISDA CDS Standard Model: the pricing conventions every post-2009 standard CDS is quoted and settled with, beside the textbook quarterly grid of CdsPricer. Sources: ISDA CDS Standard Model v1.8.2 (the open-source JPMorgan library, functions JpmcdsCdsContingentLegPV and JpmcdsFeeLegPV); "ISDA CDS Standard Model proposed numerical fix" (Markit, 2009); Markit, "The CDS Big Bang" (2009) for the contract conventions; O'Kane, Modelling Single-name and Multi-name Credit Derivatives (Wiley 2008), ch. 6.

Contract conventions (all implemented here):

  • Coupon dates are the IMM dates 20 Mar, 20 Jun, 20 Sep, 20 Dec. Maturity rolls semi-annually (ISDA, December 2015 rule): a trade dated on or after 20 Mar and before 20 Sep matures on 20 Jun of year + tenor; on or after 20 Sep, 20 Dec of year + tenor; before 20 Mar, 20 Dec of year - 1 + tenor. Maturity is not adjusted.
  • Step-in date T+1 calendar day; cash settlement T+3 business days; accrual starts on the last IMM date, business-day adjusted (following), on or before the step-in date.
  • Quarterly periods. Accrual and payment dates are adjusted following; the last accrual period ends on maturity + 1 day (the maturity date itself is protected and accrues) and pays on the adjusted maturity. Accrual is ACT/360.
  • Business days are Monday to Friday by default (the 2.1 behaviour). Pass IsdaCdsModel.Conventions with a holiday calendar (for example HolidayCalendars.calendar(Market.NEW_YORK_FED, ...)) to adjust accrual, payment and settlement dates for holidays too.

Model (all times ACT/365F from the trade date, curves as IsdaCurve, so hazard lambda and forward f are constant on each segment between the merged node times of the discount and survival curves). On a segment of length dt starting with survival Q0 and discount P0, with k = lambda + f:

  protection:  integral h Q P dt                  = lambda Q0 P0 (1 - e^{-k dt}) / k
  accrual on default, accrued c(t) = a (t - tS):
               integral a (t - tS) h Q P dt       = a lambda Q0 P0 [ (t0 - tS) (1 - e^{-k dt}) / k
                                                     + (1 - e^{-k dt} (1 + k dt)) / k^2 ]

(the second is the integration by parts of the first; small k uses the Taylor series). Protection runs from the trade date (step-in minus one day) to maturity and pays 1 - R at default. Each premium period pays ACT/360(start, end) at the payment date if the name survives to end - 1 day, plus the accrued coupon at default, the accrual rate being a = ACT/360 days / ACT/365 span on the window [start - 1 day, end - 1 day] (the v1.8.2 observation-offset convention). Present values are divided by P at the cash-settlement date, so they are amounts paid on that date.

Quoting: cleanUpfront = protection - coupon * (RPV01_dirty - accrued) is the points upfront the buyer pays per unit notional; dirtyUpfront = protection - coupon * RPV01_dirty is the cash that changes hands at settlement. The quoted (conventional) spread is the par spread of the flat hazard curve that reprices the upfront, with recovery 40% for standard contracts and fixed coupon 100bp or 500bp.

Half-day accrual on default (optional, IsdaCdsModel.Conventions.halfDayAccrual): the plain idea is that a default can happen at any time during the default day, on average at mid-day, and the protection seller is owed the coupon accrued up to that moment, which the integral above, counting from the start of the accrual day, sets half a day short. With the option on, the accrued at default becomes a (t - tS + 1/730), adding a / 730 * integral h Q P dt per period (O'Kane 2008, sec. 6.6; R. White, "The Pricing and Risk Management of Credit Default Swaps, with a Focus on the ISDA Model", OpenGamma 2013, on the accrual-on-default term). Off by default, which keeps the 2.1 numbers. The ISDA Standard Model itself includes the half-day term: the official RFR test grids below match only with it on.

Verification against the official ISDA CDS Standard Model RFR test grids (cdsmodel.com, USD SOFR 2021-04-26 and 2022-06-21, 7164 rows each; published reference): with half-day accrual on, a weekends-only calendar for the 2021 grid and the New York calendar for the 2022 grid, cash settlement amounts agree within 1e-6 USD on 10mm notional and clean upfronts within 1e-13. With half-day accrual off the clean upfront is off by up to 4.6e-5 (about 460 USD on 10mm). The grids are copyright and not in the repository; IsdaGridVerificationTest runs when QFL_ISDA_GRID_DIR points at CSVs made by scripts/isda-grid-to-csv.py, and the swap-curve bootstrap it needs lives in that test. The in-repo tests also pin the closed-form segment integrals against Gauss-Legendre quadrature to 1e-10 and the holiday and half-day variants against an independent re-implementation. Research lane, deterministic.

  • Field Details

    • STANDARD_RECOVERY

      public static final double STANDARD_RECOVERY
      Standard recovery rate for senior unsecured quoting.
      See Also:
    • COUPON_100BP

      public static final double COUPON_100BP
      Standard investment-grade coupon, 100bp.
      See Also:
    • COUPON_500BP

      public static final double COUPON_500BP
      Standard high-yield coupon, 500bp.
      See Also:
  • Method Details

    • standardContract

      public static IsdaCdsModel.Contract standardContract(LocalDate tradeDate, int tenorYears, double coupon, double recovery)
      A standard contract: rolled maturity, IMM accrual start, T+1 step-in and T+3 business-day settlement.
      Parameters:
      tradeDate - trade date
      tenorYears - tenor in years, 1 to 30
      coupon - fixed coupon (usually COUPON_100BP or COUPON_500BP)
      recovery - recovery rate (usually STANDARD_RECOVERY)
      Returns:
      the contract
    • standardContract

      public static IsdaCdsModel.Contract standardContract(LocalDate tradeDate, int tenorYears, double coupon, double recovery, BusinessCalendar calendar)
      A standard contract with settlement and accrual start adjusted on a holiday calendar.
      Parameters:
      tradeDate - trade date
      tenorYears - tenor in years, 1 to 30
      coupon - fixed coupon
      recovery - recovery rate
      calendar - business-day calendar
      Returns:
      the contract
    • standardMaturity

      public static LocalDate standardMaturity(LocalDate tradeDate, int tenorYears)
      Standard maturity under the semi-annual roll (20 Jun / 20 Dec).
      Parameters:
      tradeDate - trade date
      tenorYears - tenor in years, ≥ 0
      Returns:
      unadjusted maturity date
    • nextImmDate

      public static LocalDate nextImmDate(LocalDate date)
      The first IMM date (20 Mar/Jun/Sep/Dec) strictly after date.
      Parameters:
      date - any date
      Returns:
      the next IMM date
    • previousImmDate

      public static LocalDate previousImmDate(LocalDate date)
      The last IMM date on or before date (unadjusted).
      Parameters:
      date - any date
      Returns:
      the previous IMM date
    • accrualStartDate

      public static LocalDate accrualStartDate(LocalDate stepInDate)
      Accrual start for a step-in date: the last adjusted IMM date on or before it.
      Parameters:
      stepInDate - step-in date
      Returns:
      adjusted accrual start
    • accrualStartDate

      public static LocalDate accrualStartDate(LocalDate stepInDate, BusinessCalendar calendar)
      Accrual start on a holiday calendar: the last IMM date, adjusted following on calendar, on or before the step-in date.
      Parameters:
      stepInDate - step-in date
      calendar - business-day calendar
      Returns:
      adjusted accrual start
    • adjustFollowing

      public static LocalDate adjustFollowing(LocalDate date)
      Moves a weekend date to the following Monday.
    • addBusinessDays

      public static LocalDate addBusinessDays(LocalDate date, int days)
      Adds Monday-to-Friday business days.
    • schedule

      public static List<IsdaCdsModel.Period> schedule(IsdaCdsModel.Contract c)
      The coupon schedule from the accrual start to maturity.
      Parameters:
      c - the contract
      Returns:
      the periods in order
    • schedule

      public static List<IsdaCdsModel.Period> schedule(IsdaCdsModel.Contract c, BusinessCalendar calendar)
      The coupon schedule with accrual and payment dates adjusted following on a holiday calendar.
      Parameters:
      c - the contract
      calendar - business-day calendar
      Returns:
      the periods in order
    • protectionLeg

      public static double protectionLeg(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Protection leg value per unit notional, paid at cash settlement: (1 - R) integral_0^tM h Q P dt / P(t_cs).
      Parameters:
      c - the contract
      discount - discount curve, base date = trade date
      survival - survival curve, base date = trade date
      Returns:
      the protection leg PV
    • riskyAnnuityDirty

      public static double riskyAnnuityDirty(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Dirty risky annuity: the premium leg per unit coupon, including the full current coupon and accrual on default, paid at cash settlement.
      Parameters:
      c - the contract
      discount - discount curve
      survival - survival curve
      Returns:
      dirty RPV01 in years
    • riskyAnnuityDirty

      public static double riskyAnnuityDirty(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival, IsdaCdsModel.Conventions conventions)
      Dirty risky annuity under explicit conventions (holiday calendar, half-day accrual on default).
      Parameters:
      c - the contract
      discount - discount curve
      survival - survival curve
      conventions - model conventions
      Returns:
      dirty RPV01 in years
    • accruedYearFraction

      public static double accruedYearFraction(IsdaCdsModel.Contract c)
      Accrued premium fraction at step-in: ACT/360(accrual start, step-in).
      Parameters:
      c - the contract
      Returns:
      accrued year fraction
    • riskyAnnuityClean

      public static double riskyAnnuityClean(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Clean risky annuity: dirty minus the accrued fraction.
    • riskyAnnuityClean

      public static double riskyAnnuityClean(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival, IsdaCdsModel.Conventions conventions)
      Clean risky annuity under explicit conventions.
    • parSpread

      public static double parSpread(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Par spread: protection over clean risky annuity.
    • parSpread

      public static double parSpread(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival, IsdaCdsModel.Conventions conventions)
      Par spread under explicit conventions.
    • cleanUpfront

      public static double cleanUpfront(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Points upfront per unit notional paid by the protection buyer (clean).
    • cleanUpfront

      public static double cleanUpfront(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival, IsdaCdsModel.Conventions conventions)
      Clean points upfront under explicit conventions.
    • dirtyUpfront

      public static double dirtyUpfront(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival)
      Cash settlement amount per unit notional paid by the buyer (dirty).
    • dirtyUpfront

      public static double dirtyUpfront(IsdaCdsModel.Contract c, IsdaCurve discount, IsdaCurve survival, IsdaCdsModel.Conventions conventions)
      Dirty upfront under explicit conventions.
    • quotedSpreadToUpfront

      public static double quotedSpreadToUpfront(IsdaCdsModel.Contract c, double quotedSpread, IsdaCurve discount)
      Converts a quoted (conventional) spread to clean points upfront at the contract's coupon: flat hazard calibrated so the par spread is the quote, using the contract's recovery.
      Parameters:
      c - the contract (coupon and recovery)
      quotedSpread - conventional spread, decimal, > 0
      discount - discount curve
      Returns:
      clean upfront per unit notional
    • quotedSpreadToUpfront

      public static double quotedSpreadToUpfront(IsdaCdsModel.Contract c, double quotedSpread, IsdaCurve discount, IsdaCdsModel.Conventions conventions)
      Quoted spread to clean upfront under explicit conventions.
    • upfrontToQuotedSpread

      public static double upfrontToQuotedSpread(IsdaCdsModel.Contract c, double cleanUpfront, IsdaCurve discount)
      Converts clean points upfront to the quoted spread: flat hazard that reprices the upfront, then its par spread.
      Parameters:
      c - the contract (coupon and recovery)
      cleanUpfront - points upfront per unit notional
      discount - discount curve
      Returns:
      the quoted spread, decimal
    • upfrontToQuotedSpread

      public static double upfrontToQuotedSpread(IsdaCdsModel.Contract c, double cleanUpfront, IsdaCurve discount, IsdaCdsModel.Conventions conventions)
      Clean upfront to quoted spread under explicit conventions.
    • flatHazardForParSpread

      public static double flatHazardForParSpread(IsdaCdsModel.Contract c, double parSpread, IsdaCurve discount)
      The flat hazard whose par spread equals parSpread.
      Parameters:
      c - the contract
      parSpread - target spread, decimal, > 0
      discount - discount curve
      Returns:
      the hazard per year
    • flatHazardForParSpread

      public static double flatHazardForParSpread(IsdaCdsModel.Contract c, double parSpread, IsdaCurve discount, IsdaCdsModel.Conventions conventions)
      Flat hazard for a par spread under explicit conventions.
    • bootstrap

      public static IsdaCurve bootstrap(LocalDate tradeDate, int[] tenorYears, double[] parSpreads, double recovery, IsdaCurve discount)
      Bootstraps a piecewise-constant hazard curve from par spreads of standard contracts traded today, one node per maturity.
      Parameters:
      tradeDate - trade date
      tenorYears - ascending tenors in years
      parSpreads - par spreads, decimal, > 0
      recovery - recovery rate
      discount - discount curve
      Returns:
      the survival curve
    • bootstrap

      public static IsdaCurve bootstrap(LocalDate tradeDate, int[] tenorYears, double[] parSpreads, double recovery, IsdaCurve discount, IsdaCdsModel.Conventions conventions)
      Bootstraps under explicit conventions; the standard contracts are built on the conventions' calendar.
      Parameters:
      tradeDate - trade date
      tenorYears - ascending tenors in years
      parSpreads - par spreads, decimal, > 0
      recovery - recovery rate
      discount - discount curve
      conventions - model conventions
      Returns:
      the survival curve