Class CvaApproximator

java.lang.Object
com.quantfinlib.credit.CvaApproximator

public final class CvaApproximator extends Object
Unilateral CVA — the price of the counterparty in every derivative you hold: the expected loss from their default before your trades' cash flows finish arriving. The standard discrete approximation the desks carry:
  CVA = LGD * sum_i EE(t_i) * [ Q(t_{i-1}) - Q(t_i) ] * DF(t_i)
expected exposure at each bucket, times the probability of defaulting in that bucket (read off the CreditCurve's survival function), times the discount factor, times loss-given-default. The three ingredients are separate objects: exposure comes from your pricing/ simulation stack (this class does not know your portfolio — feed it CounterpartyExposureTracker peaks, a swap's expected-exposure hump, or Monte Carlo EE averages), credit comes from the CDS-bootstrapped curve, discount from the YieldCurve.

Approximations: exposure is evaluated at the bucket end and assumed constant across the bucket (O(dt) bias; shrink the grid to shrink it, or use cvaBucketAverage(double, double[], double[], CreditCurve, YieldCurve, double), which averages the bucket and is O(dt^2)). Default and exposure are independent, so there is no wrong-way risk, which understates CVA when exposure grows exactly when the counterparty weakens (the FX-forward-with-an-EM-sovereign classic). Unilateral: your own default (DVA) is not netted. LGD is a constant you pass, usually 1 - recovery on the same convention as the curve's bootstrap, but kept separate because the curve's recovery is a quoting convention while CVA's LGD is a modeling choice. Research lane, deterministic.

The ExposureProfile overloads take a simulated profile from MultiCurrencyExposureSimulator instead: CVA and DVA on its stochastic discounting, and cvaWrongWay(ExposureProfile, CreditCurve, double, double, ExposureProfile.Factor) for wrong-way risk through a Gaussian copula between default and a market factor.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final record 
    Bilateral CVA and DVA with first-to-default conditioning.
  • Method Summary

    Modifier and Type
    Method
    Description
    bilateral(ExposureProfile profile, CreditCurve counterparty, double lgd, CreditCurve own, double ownLgd)
    Bilateral CVA/DVA from a simulated profile with first-to-default (Brigo and Capponi, "Bilateral counterparty risk with application to CDSs", 2008; Gregory, The xVA Challenge, 3rd ed., 2015, ch. 14).
    bilateralWrongWay(ExposureProfile profile, CreditCurve counterparty, double lgd, CreditCurve own, double ownLgd, double bCpty, double bOwn, ExposureProfile.Factor driver)
    Bilateral CVA/DVA with wrong-way risk on both names and one path-consistent first-to-default (added in 2.3).
    static double
    cva(double[] expectedExposure, double[] bucketEndYears, CreditCurve counterparty, YieldCurve discount, double lgd)
    Discrete unilateral CVA over the given exposure profile.
    static double
    cva(ExposureProfile profile, CreditCurve counterparty, double lgd)
    CVA from a simulated exposure profile, independent default: LGD * sum_i E[D(0,t_i) E(t_i)] * [Q(t_{i-1}) - Q(t_i)].
    static double
    cvaBucketAverage(double eeAtZero, double[] expectedExposure, double[] bucketEndYears, CreditCurve counterparty, YieldCurve discount, double lgd)
    Discrete unilateral CVA with the discounted exposure averaged over each bucket (trapezoid rule), added in 3.1.
    static double
    cvaWrongWay(ExposureProfile profile, CreditCurve counterparty, double lgd, double rho, ExposureProfile.Factor driver)
    CVA with wrong-way risk through a Gaussian copula between default and a market factor, applied bucket by bucket (Li 2000; Gregory, The xVA Challenge, 3rd ed., 2015, ch. 17; the per-bucket conditioning follows Hull and White, "CVA and wrong-way risk", Financial Analysts Journal 68(5), 2012, where default is tied to the market variable that drives the exposure at each date).
    static double
    dva(ExposureProfile profile, CreditCurve own, double ownLgd)
    DVA from a simulated exposure profile: the same sum over the negative exposure and the bank's own credit curve.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • cva

      public static double cva(double[] expectedExposure, double[] bucketEndYears, CreditCurve counterparty, YieldCurve discount, double lgd)
      Discrete unilateral CVA over the given exposure profile.
      Parameters:
      expectedExposure - EE(t_i) per bucket, ≥ 0 (same currency units as the answer)
      bucketEndYears - bucket end times t_i in years, strictly ascending, all > 0; t_0 = 0 is implicit
      counterparty - the counterparty's bootstrapped credit curve
      discount - risk-free discounting curve
      lgd - loss given default in (0, 1]
      Returns:
      the CVA charge (positive; subtract it from the risk-free PV)
    • cvaBucketAverage

      public static double cvaBucketAverage(double eeAtZero, double[] expectedExposure, double[] bucketEndYears, CreditCurve counterparty, YieldCurve discount, double lgd)
      Discrete unilateral CVA with the discounted exposure averaged over each bucket (trapezoid rule), added in 3.1.

      Plain idea. cva(double[], double[], CreditCurve, YieldCurve, double) charges each bucket's default probability at the exposure of the bucket end. When exposure rises across the bucket that overstates CVA, when it falls it understates it, and the error shrinks only linearly with the bucket width. Averaging the two ends of the bucket removes the first-order term, which is the usual choice on a coarse grid (Gregory, The xVA Challenge, 3rd ed., 2015, section 14.2: the expected exposure in the CVA sum is the average over the interval).

      Formal statement. CVA is the integral LGD * int_0^T DF(t) EE(t) dPD(t). With DEE(t) = DF(t) EE(t) this method computes

        CVA = LGD * sum_i [ DEE(t_{i-1}) + DEE(t_i) ] / 2 * [ Q(t_{i-1}) - Q(t_i) ]
      

      with DEE(0) = eeAtZero. For smooth EE and hazard the error against the integral is O(dt^2) instead of O(dt) (tested against a 200 000-step integral, independent recomputation).

      Worked example. EE(t) = 100 t on (0, 1], one bucket, flat hazard 2 %, zero rates, LGD 1: the integral is 100 * int_0^1 t * 0.02 e^{-0.02 t} dt = 0.9868; the end-point sum gives 100 * (1 - e^{-0.02}) = 1.980 and this method 50 * (1 - e^{-0.02}) = 0.990 (own arithmetic).

      The end-point method is unchanged and stays the default: its numbers are what existing users reconcile against, and its bias was documented. Choose this method for new work on coarse grids.

      Parameters:
      eeAtZero - EE(0), ≥ 0 and finite (0 for a trade at par with no collateral imbalance)
      expectedExposure - EE(t_i) per bucket end, ≥ 0
      bucketEndYears - bucket end times, strictly ascending, all > 0
      counterparty - the counterparty's bootstrapped credit curve
      discount - risk-free discounting curve
      lgd - loss given default in (0, 1]
      Returns:
      the CVA charge (positive)
    • cva

      public static double cva(ExposureProfile profile, CreditCurve counterparty, double lgd)
      CVA from a simulated exposure profile, independent default: LGD * sum_i E[D(0,t_i) E(t_i)] * [Q(t_{i-1}) - Q(t_i)]. The discounting is the simulation's own stochastic discount factor, so no separate curve is passed.
      Parameters:
      profile - simulated netting-set exposure
      counterparty - counterparty credit curve
      lgd - loss given default in (0, 1]
      Returns:
      the CVA charge
    • dva

      public static double dva(ExposureProfile profile, CreditCurve own, double ownLgd)
      DVA from a simulated exposure profile: the same sum over the negative exposure and the bank's own credit curve. Unilateral (no first-to-default conditioning) and positive; bilateral CVA is cva - dva.
      Parameters:
      profile - simulated netting-set exposure
      own - the bank's own credit curve
      ownLgd - own loss given default in (0, 1]
      Returns:
      the DVA benefit
    • cvaWrongWay

      public static double cvaWrongWay(ExposureProfile profile, CreditCurve counterparty, double lgd, double rho, ExposureProfile.Factor driver)
      CVA with wrong-way risk through a Gaussian copula between default and a market factor, applied bucket by bucket (Li 2000; Gregory, The xVA Challenge, 3rd ed., 2015, ch. 17; the per-bucket conditioning follows Hull and White, "CVA and wrong-way risk", Financial Analysts Journal 68(5), 2012, where default is tied to the market variable that drives the exposure at each date). For bucket i the latent credit variable is Z_i = -rho X_i + sqrt(1 - rho^2) eps, eps independent of the market, and default falls in the bucket when Phi^{-1}(PD_{i-1}) < Z_i <= Phi^{-1}(PD_i). X_i is a standard normal driver read path-wise from the simulation:
      • uncollateralised set: X_i = W(t_i) / sqrt(t_i), the standardised factor Brownian at the bucket date;
      • set under a CSA with margin period of risk M: X_i = (W(t_i) - W(t_i - M)) / sqrt(M), the standardised increment over the MPR ending at t_i, which is what drives the collateralised exposure max(V(t_i) - C(t_i - M), 0).

      Conditioning on X_i gives each bucket probability in closed form:

        P(bucket i | X_i) = Phi((Phi^{-1}(PD_i) + rho X_i) / sqrt(1 - rho^2))
                          - Phi((Phi^{-1}(PD_{i-1}) + rho X_i) / sqrt(1 - rho^2))
        CVA = LGD * mean over paths of sum_i D(0,t_i) E(t_i) P(bucket i | X_i)
      

      Since X_i is standard normal, E[P(bucket i | X_i)] = PD_i - PD_{i-1}: the marginal default curve is unchanged, and rho = 0 reproduces cva(ExposureProfile, CreditCurve, double). Positive rho makes default more likely when the driver is high: wrong-way for a payer swap with ExposureProfile.Factor.RATES and for buying foreign currency with ExposureProfile.Factor.FX. Model limits: the bucket probabilities are a marginal construction (each bucket has its own driver, so there is no single joint default time across buckets), and under a CSA with a large threshold the exposure also depends on the factor level, which the increment driver ignores. Before 2.1 the driver was the single terminal factor value for every bucket; that understated wrong-way risk on collateralised sets, where it could even fall below the independent CVA.

      Parameters:
      profile - simulated exposure
      counterparty - counterparty credit curve
      lgd - loss given default in (0, 1]
      rho - copula correlation in (-1, 1)
      driver - market factor that drives default
      Returns:
      the CVA charge
    • bilateral

      public static CvaApproximator.Bilateral bilateral(ExposureProfile profile, CreditCurve counterparty, double lgd, CreditCurve own, double ownLgd)
      Bilateral CVA/DVA from a simulated profile with first-to-default (Brigo and Capponi, "Bilateral counterparty risk with application to CDSs", 2008; Gregory, The xVA Challenge, 3rd ed., 2015, ch. 14).

      Plain idea: a counterparty default only costs the bank if the bank is still alive, and an own default only helps if the counterparty is still alive. Formal statement, with the two defaults independent of each other and of the exposure, and each hazard taken flat inside every profile bucket (so the bucket integral is exact for curves whose hazard is piecewise flat on the bucket grid):

        CVA = LGD_c sum_i DEE_i  * Lc_i / (Lc_i + Lo_i) * (S(t_{i-1}) - S(t_i))
        DVA = LGD_o sum_i DENE_i * Lo_i / (Lc_i + Lo_i) * (S(t_{i-1}) - S(t_i))
        S(t) = Q_c(t) Q_o(t),  L_i = ln(Q(t_{i-1}) / Q(t_i))
      

      Worked example: flat hazards 2 % and 1 %, one bucket (0, 5] with DEE = 10: CVA = 0.6 * 10 * 2/3 * (1 - e^{-0.15}) = 0.557, against the unilateral 0.6 * 10 * (1 - e^{-0.1}) = 0.571 (own arithmetic). As the own hazard goes to zero the CVA leg tends to the unilateral cva(ExposureProfile, CreditCurve, double) (tested). Wrong-way risk is not combined with this method.

      Parameters:
      profile - simulated exposure
      counterparty - counterparty credit curve
      lgd - counterparty LGD in (0, 1]
      own - the bank's own credit curve
      ownLgd - own LGD in (0, 1]
      Returns:
      both legs
    • bilateralWrongWay

      public static CvaApproximator.Bilateral bilateralWrongWay(ExposureProfile profile, CreditCurve counterparty, double lgd, CreditCurve own, double ownLgd, double bCpty, double bOwn, ExposureProfile.Factor driver)
      Bilateral CVA/DVA with wrong-way risk on both names and one path-consistent first-to-default (added in 2.3).

      Plain idea. Each name has a default intensity that rises or falls with the same market driver that moves the exposure. On a path, both names then have one default time each, the earlier one ends the contract, and only that default produces a loss (CVA) or a benefit (DVA). Positive bCpty on a driver that raises the exposure is wrong-way risk and raises the CVA (tested).

      Formal statement. A driven-hazard model in the style of Hull and White, "CVA and wrong-way risk", Financial Analysts Journal 68(5), 2012 (their hazard-rate approach, with the hazard taken flat in each profile bucket). In bucket i, on path p, the integrated hazards are

        Lc_i(p) = exp(alpha_i + bCpty * X_i(p)),   Lo_i(p) = exp(beta_i + bOwn * X_i(p))
      

      with X_i the profile's standardised driver (see cvaWrongWay(ExposureProfile, CreditCurve, double, double, ExposureProfile.Factor)). alpha_i and beta_i are solved bucket by bucket so that the path average of exp(-sum_{j<=i} L_j(p)) equals each name's curve survival Q(t_i): the marginal default curves are matched exactly on the simulated paths. Given the market path the two default times are independent (conditional independence is the stated copula; the names are dependent only through the common driver), so with Sc, So the path survivals to t_{i-1}:

        P(cpty first in bucket i | path) = Sc So Lc / (Lc + Lo) (1 - e^{-(Lc + Lo)})
        CVA = LGD_c mean_p sum_i D E_i(p) P(cpty first in bucket i | p),  DVA likewise with D NE and Lo
      

      With bCpty = bOwn = 0 the hazards are the deterministic bucket hazards and the result equals bilateral(ExposureProfile, CreditCurve, double, CreditCurve, double) to rounding (tested). A brute-force simulation of the two default times agrees within Monte Carlo error (tested, independent computation). Assumptions: hazards flat inside a bucket, default only matters at bucket granularity, no recovery or LGD randomness, and the driver is the profile's per-bucket X_i, so the dependence is on the factor level (uncollateralised) or its MPR increment (CSA). Worked example: a 10y payer swap with bCpty = 0.5 on the rates driver gives a CVA above the independent bilateral value (see BilateralWrongWayCvaTest for the numbers, own test).

      Parameters:
      profile - simulated exposure
      counterparty - counterparty credit curve, survival > 0 on the grid
      lgd - counterparty LGD in (0, 1]
      own - own credit curve, survival > 0 on the grid
      ownLgd - own LGD in (0, 1]
      bCpty - counterparty hazard loading on the driver (finite)
      bOwn - own hazard loading on the driver (finite)
      driver - market factor that drives both hazards
      Returns:
      both legs