All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.pricer.credit.IsdaHomogenousCdsIndexProductPricer Maven / Gradle / Ivy

There is a newer version: 2.12.46
Show newest version
/*
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.pricer.credit;

import java.time.LocalDate;

import com.google.common.collect.ImmutableMap;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.tuple.Triple;
import com.opengamma.strata.market.curve.CurveInfoType;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.common.PriceType;
import com.opengamma.strata.product.credit.ResolvedCds;
import com.opengamma.strata.product.credit.ResolvedCdsIndex;

/**
 * Pricer for CDS portfolio index based on ISDA standard model. 
 * 

* The CDS index is priced as a single name CDS using a single credit curve rather than * credit curves of constituent single names. *

* {@code CreditRatesProvider} must contain the index credit curve as well as * the information on the relevant recovery rate and index factor. *

* This pricer invokes the implementation in {@link IsdaCdsProductPricer}. */ public class IsdaHomogenousCdsIndexProductPricer { /** * Default implementation. */ public static final IsdaHomogenousCdsIndexProductPricer DEFAULT = new IsdaHomogenousCdsIndexProductPricer(AccrualOnDefaultFormula.ORIGINAL_ISDA); /** * The pricer for single name CDS. */ private final IsdaCdsProductPricer underlyingPricer; /** * Constructor specifying the formula to use for the accrued on default calculation. * * @param formula the formula */ public IsdaHomogenousCdsIndexProductPricer(AccrualOnDefaultFormula formula) { this.underlyingPricer = new IsdaCdsProductPricer(formula); } //------------------------------------------------------------------------- /** * Gets the accrual-on-default formula used in this pricer. * * @return the formula */ public AccrualOnDefaultFormula getAccrualOnDefaultFormula() { return underlyingPricer.getAccrualOnDefaultFormula(); } //------------------------------------------------------------------------- /** * Calculates the price of the CDS index product, which is the minus of the present value per unit notional. *

* This method can calculate the clean or dirty price, see {@link PriceType}. * If calculating the clean price, the accrued interest is calculated based on the step-in date. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param priceType the price type * @param refData the reference data * @return the price */ public double price( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, PriceType priceType, ReferenceData refData) { ResolvedCds cds = cdsIndex.toSingleNameCds(); return underlyingPricer.price(cds, ratesProvider, referenceDate, priceType, refData); } /** * Calculates the price sensitivity of the product. *

* The price sensitivity of the product is the sensitivity of price to the underlying curves. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the present value sensitivity */ public PointSensitivityBuilder priceSensitivity( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { ResolvedCds cds = cdsIndex.toSingleNameCds(); return underlyingPricer.priceSensitivity(cds, ratesProvider, referenceDate, refData); } //------------------------------------------------------------------------- /** * Calculates the present value of the CDS index product. *

* The present value of the product is based on {@code referenceDate}. * This is typically the valuation date, or cash settlement date if the product is associated with a {@code Trade}. *

* This method can calculate the clean or dirty present value, see {@link PriceType}. * If calculating the clean value, the accrued interest is calculated based on the step-in date. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param priceType the price type * @param refData the reference data * @return the present value */ public CurrencyAmount presentValue( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, PriceType priceType, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return CurrencyAmount.of(cdsIndex.getCurrency(), 0d); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = underlyingPricer.recoveryRate(cds, ratesProvider); Triple rates = reduceDiscountFactors(cds, ratesProvider); double protectionLeg = (1d - recoveryRate) * underlyingPricer.protectionFull(cds, rates.getFirst(), rates.getSecond(), referenceDate, effectiveStartDate); double rpv01 = underlyingPricer.riskyAnnuity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate, priceType); double amount = cds.getBuySell().normalize(cds.getNotional()) * rates.getThird() * (protectionLeg - rpv01 * cds.getFixedRate()); return CurrencyAmount.of(cds.getCurrency(), amount); } /** * Calculates the present value sensitivity of the product. *

* The present value sensitivity of the product is the sensitivity of present value to the underlying curves. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the present value sensitivity */ public PointSensitivityBuilder presentValueSensitivity( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return PointSensitivityBuilder.none(); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = underlyingPricer.recoveryRate(cds, ratesProvider); Triple rates = reduceDiscountFactors(cds, ratesProvider); double signedNotional = cds.getBuySell().normalize(cds.getNotional()); PointSensitivityBuilder protectionLegSensi = underlyingPricer.protectionLegSensitivity( cds, rates.getFirst(), rates.getSecond(), referenceDate, effectiveStartDate, recoveryRate); protectionLegSensi = protectionLegSensi.multipliedBy(signedNotional * rates.getThird()); PointSensitivityBuilder riskyAnnuitySensi = underlyingPricer.riskyAnnuitySensitivity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate); riskyAnnuitySensi = riskyAnnuitySensi.multipliedBy(-cds.getFixedRate() * signedNotional * rates.getThird()); return protectionLegSensi.combinedWith(riskyAnnuitySensi); } //------------------------------------------------------------------------- /** * Calculates the par spread of the CDS index product. *

* The par spread is a coupon rate such that the clean PV is 0. * The result is represented in decimal form. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the par spread */ public double parSpread( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { ResolvedCds cds = cdsIndex.toSingleNameCds(); return underlyingPricer.parSpread(cds, ratesProvider, referenceDate, refData); } /** * Calculates the par spread sensitivity of the product. *

* The par spread sensitivity of the product is the sensitivity of par spread to the underlying curves. * The resulting sensitivity is based on the currency of the CDS index product. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the par spread */ public PointSensitivityBuilder parSpreadSensitivity( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { ResolvedCds cds = cdsIndex.toSingleNameCds(); return underlyingPricer.parSpreadSensitivity(cds, ratesProvider, referenceDate, refData); } //------------------------------------------------------------------------- /** * Calculates the risky PV01 of the CDS index product. *

* RPV01 is defined as minus of the present value sensitivity to coupon rate. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param priceType the price type * @param refData the reference date * @return the RPV01 */ public CurrencyAmount rpv01( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, PriceType priceType, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return CurrencyAmount.of(cdsIndex.getCurrency(), 0d); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); Triple rates = reduceDiscountFactors(cds, ratesProvider); double riskyAnnuity = underlyingPricer.riskyAnnuity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate, priceType); double amount = cds.getBuySell().normalize(cds.getNotional()) * riskyAnnuity * rates.getThird(); return CurrencyAmount.of(cds.getCurrency(), amount); } //------------------------------------------------------------------------- /** * Calculates the risky annuity, which is RPV01 per unit notional. *

* Zero is returned if the CDS index already expired. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param priceType the price type * @param refData the reference data * @return the risky annuity */ public double riskyAnnuity( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, PriceType priceType, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return 0d; } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cdsIndex.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cdsIndex.calculateEffectiveStartDate(stepinDate); Triple rates = reduceDiscountFactors(cds, ratesProvider); return underlyingPricer.riskyAnnuity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate, priceType); } /** * Calculates the risky annuity sensitivity of the product. *

* The risky annuity sensitivity of the product is the sensitivity of risky annuity to the underlying curves. * The resulting sensitivity is based on the currency of the CDS index product. *

* Empty sensitivity is returned if the CDS index already expired. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the risky annuity sensitivity */ public PointSensitivityBuilder riskyAnnuitySensitivity( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return PointSensitivityBuilder.none(); } LocalDate stepinDate = cdsIndex.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cdsIndex.calculateEffectiveStartDate(stepinDate); ResolvedCds cds = cdsIndex.toSingleNameCds(); Triple rates = reduceDiscountFactors(cds, ratesProvider); return underlyingPricer.riskyAnnuitySensitivity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate); } //------------------------------------------------------------------------- /** * Calculates the recovery01 of the CDS index product. *

* The recovery01 is defined as the present value sensitivity to the recovery rate. * Since the ISDA standard model requires the recovery rate to be constant throughout the lifetime of the CDS index, * one currency amount is returned by this method. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the recovery01 */ public CurrencyAmount recovery01( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { if (isExpired(cdsIndex, ratesProvider)) { return CurrencyAmount.of(cdsIndex.getCurrency(), 0d); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); underlyingPricer.validateRecoveryRates(cds, ratesProvider); Triple rates = reduceDiscountFactors(cds, ratesProvider); double protectionFull = underlyingPricer.protectionFull(cds, rates.getFirst(), rates.getSecond(), referenceDate, effectiveStartDate); double amount = -cds.getBuySell().normalize(cds.getNotional()) * protectionFull * rates.getThird(); return CurrencyAmount.of(cds.getCurrency(), amount); } //------------------------------------------------------------------------- /** * Calculates the jump-to-default of the CDS index product. *

* The jump-to-default is the value of the product in case of immediate default of a constituent single name. *

* Under the homogeneous pool assumption, the jump-to-default values are the same for all of the undefaulted names, * and zero for defaulted names. Thus the resulting object contains a single number. * * @param cdsIndex the product * @param ratesProvider the rates provider * @param referenceDate the reference date * @param refData the reference data * @return the recovery01 */ public JumpToDefault jumpToDefault( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider, LocalDate referenceDate, ReferenceData refData) { StandardId indexId = cdsIndex.getCdsIndexId(); Currency currency = cdsIndex.getCurrency(); if (isExpired(cdsIndex, ratesProvider)) { return JumpToDefault.of(currency, ImmutableMap.of(indexId, 0d)); } ResolvedCds cds = cdsIndex.toSingleNameCds(); LocalDate stepinDate = cds.getStepinDateOffset().adjust(ratesProvider.getValuationDate(), refData); LocalDate effectiveStartDate = cds.calculateEffectiveStartDate(stepinDate); double recoveryRate = underlyingPricer.recoveryRate(cds, ratesProvider); Triple rates = reduceDiscountFactors(cds, ratesProvider); double protectionFull = underlyingPricer.protectionFull( cds, rates.getFirst(), rates.getSecond(), referenceDate, effectiveStartDate); double rpv01 = underlyingPricer.riskyAnnuity( cds, rates.getFirst(), rates.getSecond(), referenceDate, stepinDate, effectiveStartDate, PriceType.CLEAN); double lgd = 1d - recoveryRate; double numTotal = cdsIndex.getLegalEntityIds().size(); double jtd = (lgd - (lgd * protectionFull - cds.getFixedRate() * rpv01)) / numTotal; return JumpToDefault.of(currency, ImmutableMap.of(indexId, cds.getBuySell().normalize(cds.getNotional()) * jtd)); } /** * Calculates the expected loss of the CDS index product. *

* The expected loss is the (undiscounted) expected default settlement value paid by the protection seller. * The resulting value is always positive. * * @param cdsIndex the product * @param ratesProvider the rates provider * @return the expected loss */ public CurrencyAmount expectedLoss( ResolvedCdsIndex cdsIndex, CreditRatesProvider ratesProvider) { if (isExpired(cdsIndex, ratesProvider)) { return CurrencyAmount.of(cdsIndex.getCurrency(), 0d); } ResolvedCds cds = cdsIndex.toSingleNameCds(); double recoveryRate = underlyingPricer.recoveryRate(cds, ratesProvider); Triple rates = reduceDiscountFactors(cds, ratesProvider); double survivalProbability = rates.getSecond().survivalProbability(cds.getProtectionEndDate()); double el = (1d - recoveryRate) * (1d - survivalProbability) * rates.getThird(); return CurrencyAmount.of(cds.getCurrency(), Math.abs(cds.getNotional()) * el); } //------------------------------------------------------------------------- boolean isExpired(ResolvedCdsIndex index, CreditRatesProvider ratesProvider) { return !index.getProtectionEndDate().isAfter(ratesProvider.getValuationDate()); } Triple reduceDiscountFactors( ResolvedCds cds, CreditRatesProvider ratesProvider) { Currency currency = cds.getCurrency(); CreditDiscountFactors discountFactors = ratesProvider.discountFactors(currency); ArgChecker.isTrue(discountFactors.isIsdaCompliant(), "discount factors must be IsdaCompliantZeroRateDiscountFactors"); LegalEntitySurvivalProbabilities survivalProbabilities = ratesProvider.survivalProbabilities(cds.getLegalEntityId(), currency); ArgChecker.isTrue(survivalProbabilities.getSurvivalProbabilities().isIsdaCompliant(), "survival probabilities must be IsdaCompliantZeroRateDiscountFactors"); ArgChecker.isTrue(discountFactors.getDayCount().equals(survivalProbabilities.getSurvivalProbabilities().getDayCount()), "day count conventions of discounting curve and credit curve must be the same"); double indexFactor = ((IsdaCreditDiscountFactors) survivalProbabilities.getSurvivalProbabilities()) .getCurve().getMetadata().getInfo(CurveInfoType.CDS_INDEX_FACTOR); return Triple.of(discountFactors, survivalProbabilities, indexFactor); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy