Class FrtbDrcSecuritisation

java.lang.Object
com.quantfinlib.risk.FrtbDrcSecuritisation

public final class FrtbDrcSecuritisation extends Object
FRTB standardised default risk charge (DRC) for securitisations, both outside the correlation trading portfolio (non-CTP, MAR22.27 to MAR22.35) and inside it (CTP, MAR22.36 to MAR22.45). Styled after BCBS d457 (January 2019) and the consolidated Basel Framework chapter MAR22; risk weights from the securitisation framework CRE40 to CRE42. Not certified.

Plain idea. A tranche's loss on default is already priced in its banking-book risk weight, so the jump-to-default amount is simply the tranche's market value. Longs and shorts net only within exactly the same tranche of the same pool (or index series). Inside a bucket the risk-weighted shorts offset the longs only to the extent of the hedge benefit ratio, as for non-securitisations. Non-CTP buckets are summed with no diversification. CTP buckets (one per index) may go negative and are aggregated with a second 50% haircut on net-short buckets.

Formal statement.

 gross JTD = market value (signed)                 MAR22.27, MAR22.36, MAR22.37
 nth-to-default: A = (N-1)/names, D = N/names      MAR22.38
 net JTD  = Σ JTD_i·min(max(M_i, 0.25), 1)  within one tranche   MAR22.29, 22.30, 22.39
 non-CTP: DRC_b = max(Σ RW·long − HBR_b·Σ RW·|short|, 0); DRC = Σ_b DRC_b   MAR22.33, 22.35
 CTP:     DRC_b = Σ RW·long − HBR_ctp·Σ RW·|short|   (no floor)            MAR22.44
          DRC_ctp = max(Σ_b (max(DRC_b,0) + 0.5·min(DRC_b,0)), 0)          MAR22.45

Risk weights. MAR22.34(1) and MAR22.42 take the banking-book securitisation risk weight with the maturity component set to zero, ie a one-year tranche maturity. This class implements SEC-SA (CRE41.1 to CRE41.22) and the one-year columns of SEC-ERBA (CRE42.2 to CRE42.14). SEC-IRBA (CRE44) needs the bank's own K_IRB for the pool, so a weight from it is taken as caller input in FrtbDrcSecuritisation.NetJtd.riskWeight(). The banking-book weight is a multiple of exposure that includes the 8% capital ratio (1250% means a deduction). Turning it into a DRC weight per unit of JTD is done here by multiplying by 8% (drcRiskWeight(double)). Evidence class: library reading. MAR22.34 and MAR22.42 (checked against the consolidated Basel Framework text on disk) do not print the factor. The reading rests on three printed rules: RBC20.1(3) sets total capital at 8.0% of RWA, so a banking-book weight is RWA per unit of exposure; MAR20.1 turns market-risk capital into RWA by x12.5, the inverse of 8%, so DRC weights are capital per unit of JTD; and CRE41.4 footnote 211 converts a risk weight to capital as "risk weight times 8%" inside K_SA. It is also the only factor that makes the 1250% weight a charge equal to the full market value, which matches the fair-value cap of MAR22.34(3). Non-tranched CTP products use the non-securitisation weights of MAR22.24 (FrtbDrc.CreditQuality), MAR22.43.

Worked example (published reference, MAR22.45): CTP index buckets of +100 (CDX North America IG) and −100 (Major Sovereign) give a total of 100 − 0.5·100 = 50.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    The 11 asset classes of MAR22.31(2)(a), plus the corporates bucket of MAR22.31(1).
    static enum 
    Long-term rating grades of SEC-ERBA Tables 2 and 4 (CRE42.4, CRE42.13).
    static enum 
    Short-term rating grades of SEC-ERBA Tables 1 and 3 (CRE42.2, CRE42.12).
    static final record 
    A net JTD of one tranche (signed, positive long) and its DRC risk weight per unit of JTD.
    static final record 
    A non-CTP bucket (MAR22.31, MAR22.32).
    static final record 
    A net tranche JTD tagged with its bucket key (a FrtbDrcSecuritisation.NonCtpBucket, or an index name for the CTP).
    static enum 
    The four regions of MAR22.31(2)(b).
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final double
    Largest pool share, by EAD, with unknown delinquency status that still allows SEC-SA (CRE41.9, CRE41.10).
    static final double
    The 1250% banking-book risk weight (deduction), as a multiple.
  • Method Summary

    Modifier and Type
    Method
    Description
    Groups positions by bucket key in first-seen order.
    static double[]
    checkSingleNameDecomposition(double securitisationValue, double[] singleNameEquivalents, boolean exotic, double tolerance)
    Checks a valuation-model decomposition into single-name equivalents (MAR22.39(2)): allowed for vanilla securitisations only (exotics such as CDO-squared are prohibited) and the single-name amounts must be consistent with the undecomposed value.
    static double
    ctpAggregate(List<Double> bucketCharges)
    CTP aggregation max(Σ_b (max(DRC_b, 0) + 0.5·min(DRC_b, 0)), 0) (MAR22.45).
    static double
    CTP index-bucket charge with the portfolio-wide HBR_ctp and no floor (MAR22.44): Σ RW·long − HBR_ctp·Σ RW·|short|; may be negative.
    static <K> double
    Total CTP DRC: one bucket per index, bespokes in the bucket of their index (MAR22.40, MAR22.41); HBR_ctp from all CTP positions together (MAR22.44(1)); aggregated by MAR22.45.
    static double[]
    decomposeIndex(double jtd, double[] attach, double[] detach)
    Decomposes a non-tranched index position (or a basket of its names) into replicating tranches that span the whole structure, for offsetting against tranche positions (MAR22.28 non-CTP; MAR22.39(3) CTP).
    static double
    drcRiskWeight(double bankingBookRiskWeight)
    Converts a banking-book securitisation risk weight (a multiple, 12.5 = 1250%) to a DRC weight: ×8% (see class note).
    static double
    erbaRiskWeight(FrtbDrcSecuritisation.ErbaRating rating, boolean senior, double thickness, boolean stc)
    SEC-ERBA banking-book risk weight as a multiple, one-year tranche maturity (MAR22.34(1), so the maturity interpolation of CRE42.5(1) is not needed).
    static double
    grossJtd(double signedMarketValue)
    Gross JTD of a securitisation position or of a CTP single-name or index hedge: its signed market value, no LGD applied (MAR22.27, MAR22.36, MAR22.37).
    static double
    HBR = Σ long / (Σ long + Σ |short|), unweighted (MAR22.23); 0 for an empty set.
    static double
    juniorUnratedFloor(double secSaRiskWeight, double nextSeniorRatedRiskWeight)
    Second sentence of CRE41.15: an unrated junior tranche in a deal whose more senior tranches are rated, where no rating can be inferred for the junior one, takes no less than the weight of the next more senior rated tranche.
    static double
    kA(double kSa, double w)
    K_A = (1 − W)·K_SA + 0.5·W (CRE41.8).
    static double
    kAPartlyUnknownW(double kAKnown, double eadKnown, double eadUnknown)
    K_A when W is unknown for part of the pool (CRE41.9): K_A = (EAD_known / EAD_total) * K_A^known + EAD_unknown / EAD_total, so the unknown subpool is charged at 100%.
    static double
    kSsfa(double kA, double attach, double detach, double p)
    K_SSFA = (e^{a·u} − e^{a·l}) / (a·(u − l)) with a = −1/(p·K_A), u = D − K_A, l = max(A − K_A, 0) (CRE41.11).
    static double
    netTranche(double[] grossJtd, double[] maturityYears)
    Net JTD of one specific tranche (same pool or index series and the same attachment and detachment points) across maturities, with the one-year scaling and three-month floor of MAR22.15 to MAR22.18 (MAR22.30, MAR22.39).
    static double
    Non-CTP bucket charge, max(Σ RW·long − HBR_b·Σ RW·|short|, 0) (MAR22.33).
    static <K> double
    Non-CTP total: simple sum of bucket charges (MAR22.35).
    static double
    nthToDefaultAttachment(int n, int totalNames)
    Attachment point (N − 1) / names of an nth-to-default product (MAR22.38(1)).
    static double
    nthToDefaultDetachment(int n, int totalNames)
    Detachment point N / names of an nth-to-default product (MAR22.38(2)).
    static double[]
    offsetByReplication(double attach, double detach, double jtd, double[] pieceAttach, double[] pieceDetach, double[] pieceJtd)
    Offset by replication within one index series or pool (MAR22.30(1), MAR22.39(3)).
    static boolean
    secSaPermittedWithUnknownW(double eadKnown, double eadUnknown)
    Whether SEC-SA may be used when the delinquency status is unknown for part of the pool: true when unknown EAD / total EAD is at most 5% (CRE41.9).
    static double
    secSaRiskWeight(double kA, double attach, double detach, boolean resecuritisation, boolean stc, boolean senior)
    SEC-SA banking-book risk weight as a multiple (CRE41.11 to CRE41.22). p = 1 (CRE41.12), 1.5 for a resecuritisation (CRE41.16(3)), 0.5 for STC (CRE41.21).

    Methods inherited from class Object

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

    • RW_1250

      public static final double RW_1250
      The 1250% banking-book risk weight (deduction), as a multiple.
      See Also:
    • MAX_UNKNOWN_W_SHARE

      public static final double MAX_UNKNOWN_W_SHARE
      Largest pool share, by EAD, with unknown delinquency status that still allows SEC-SA (CRE41.9, CRE41.10).
      See Also:
  • Method Details

    • grossJtd

      public static double grossJtd(double signedMarketValue)
      Gross JTD of a securitisation position or of a CTP single-name or index hedge: its signed market value, no LGD applied (MAR22.27, MAR22.36, MAR22.37).
    • nthToDefaultAttachment

      public static double nthToDefaultAttachment(int n, int totalNames)
      Attachment point (N − 1) / names of an nth-to-default product (MAR22.38(1)).
    • nthToDefaultDetachment

      public static double nthToDefaultDetachment(int n, int totalNames)
      Detachment point N / names of an nth-to-default product (MAR22.38(2)).
    • netTranche

      public static double netTranche(double[] grossJtd, double[] maturityYears)
      Net JTD of one specific tranche (same pool or index series and the same attachment and detachment points) across maturities, with the one-year scaling and three-month floor of MAR22.15 to MAR22.18 (MAR22.30, MAR22.39). Different tranches, pools, series or index families must not be passed together (MAR22.29, MAR22.39(4)); a replication by decomposition goes through decomposeIndex(double, double[], double[]), offsetByReplication(double, double, double, double[], double[], double[]) or checkSingleNameDecomposition(double, double[], boolean, double) first (MAR22.28, MAR22.30(1)-(3), MAR22.39(2)-(3)).
    • decomposeIndex

      public static double[] decomposeIndex(double jtd, double[] attach, double[] detach)
      Decomposes a non-tranched index position (or a basket of its names) into replicating tranches that span the whole structure, for offsetting against tranche positions (MAR22.28 non-CTP; MAR22.39(3) CTP). Tranche i gets jtd * (D_i - A_i). The split by thickness is a library reading of "decomposed proportionately": a full capital structure loses exactly what the index loses, and a notional share equal to thickness is the split under which the tranches sum back to the index. Names treated this way must be taken out of the non-securitisation DRC (MAR22.28, last sentence); that is the caller's job.
      Parameters:
      jtd - signed JTD of the index position, finite
      attach - attachment points, ascending, first 0
      detach - detachment points, detach[i] = attach[i + 1], last 1
      Returns:
      one replicating JTD per tranche, same order
    • offsetByReplication

      public static double[] offsetByReplication(double attach, double detach, double jtd, double[] pieceAttach, double[] pieceDetach, double[] pieceJtd)
      Offset by replication within one index series or pool (MAR22.30(1), MAR22.39(3)). A tranche [A, D] with JTD jtd is split over the sub-tranches that tile it exactly, in proportion to thickness, and added to the JTD already held in each. A perfect replication leaves zeros. The text's example (MAR22.39(3)) is a long 10-15% tranche against shorts in 10-12% and 12-15%. The ordinary netting of netTranche(double[], double[]) then applies per sub-tranche (MAR22.30(3)).
      Parameters:
      attach - A of the tranche being replicated
      detach - D of the tranche being replicated
      jtd - signed JTD of that tranche, finite
      pieceAttach - sub-tranche attachments, ascending, first = A
      pieceDetach - sub-tranche detachments, contiguous, last = D
      pieceJtd - signed JTD already held in each sub-tranche
      Returns:
      residual signed JTD per sub-tranche
    • checkSingleNameDecomposition

      public static double[] checkSingleNameDecomposition(double securitisationValue, double[] singleNameEquivalents, boolean exotic, double tolerance)
      Checks a valuation-model decomposition into single-name equivalents (MAR22.39(2)): allowed for vanilla securitisations only (exotics such as CDO-squared are prohibited) and the single-name amounts must be consistent with the undecomposed value. The text gives no number for "consistent", so the tolerance is a caller input.
      Parameters:
      securitisationValue - undecomposed value of the securitisation
      singleNameEquivalents - unconditional minus conditional-on-default value per name
      exotic - true for CDO-squared and other exotic structures
      tolerance - allowed absolute gap between the sum and the value, at least 0
      Returns:
      a copy of the single-name equivalents
      Throws:
      IllegalArgumentException - if exotic or the sum misses by more than tolerance
    • drcRiskWeight

      public static double drcRiskWeight(double bankingBookRiskWeight)
      Converts a banking-book securitisation risk weight (a multiple, 12.5 = 1250%) to a DRC weight: ×8% (see class note).
    • kA

      public static double kA(double kSa, double w)
      K_A = (1 − W)·K_SA + 0.5·W (CRE41.8).
    • secSaPermittedWithUnknownW

      public static boolean secSaPermittedWithUnknownW(double eadKnown, double eadUnknown)
      Whether SEC-SA may be used when the delinquency status is unknown for part of the pool: true when unknown EAD / total EAD is at most 5% (CRE41.9). When false the exposure takes 1250% (CRE41.10).
      Parameters:
      eadKnown - EAD of subpool 1 (W known), at least 0
      eadUnknown - EAD of subpool 2 (W unknown), at least 0; not both 0
      Returns:
      true if SEC-SA is still available
    • kAPartlyUnknownW

      public static double kAPartlyUnknownW(double kAKnown, double eadKnown, double eadUnknown)
      K_A when W is unknown for part of the pool (CRE41.9): K_A = (EAD_known / EAD_total) * K_A^known + EAD_unknown / EAD_total, so the unknown subpool is charged at 100%. K_A^known is kA(double, double) computed on subpool 1 alone.
      Parameters:
      kAKnown - K_A of subpool 1, in [0, 1]
      eadKnown - EAD of subpool 1, at least 0
      eadUnknown - EAD of subpool 2, at least 0; not both 0
      Returns:
      the adjusted K_A
      Throws:
      IllegalArgumentException - if the unknown share exceeds 5%; CRE41.10 then requires 1250%, which the caller applies
    • juniorUnratedFloor

      public static double juniorUnratedFloor(double secSaRiskWeight, double nextSeniorRatedRiskWeight)
      Second sentence of CRE41.15: an unrated junior tranche in a deal whose more senior tranches are rated, where no rating can be inferred for the junior one, takes no less than the weight of the next more senior rated tranche.
      Parameters:
      secSaRiskWeight - the junior tranche's SEC-SA weight, a multiple
      nextSeniorRatedRiskWeight - weight of the next more senior rated tranche, a multiple
      Returns:
      max of the two
    • kSsfa

      public static double kSsfa(double kA, double attach, double detach, double p)
      K_SSFA = (e^{a·u} − e^{a·l}) / (a·(u − l)) with a = −1/(p·K_A), u = D − K_A, l = max(A − K_A, 0) (CRE41.11).
    • secSaRiskWeight

      public static double secSaRiskWeight(double kA, double attach, double detach, boolean resecuritisation, boolean stc, boolean senior)
      SEC-SA banking-book risk weight as a multiple (CRE41.11 to CRE41.22). p = 1 (CRE41.12), 1.5 for a resecuritisation (CRE41.16(3)), 0.5 for STC (CRE41.21). D ≤ K_A gives 1250% (CRE41.13(1)); A ≥ K_A gives 12.5·K_SSFA (CRE41.13(2)); otherwise the weighted average of CRE41.13(3). Floors: 15% (CRE41.15), 100% for a resecuritisation (CRE41.18), STC 10% senior and 15% non-senior (CRE41.22). For the unknown-W case pass K_A from kAPartlyUnknownW(double, double, double) (CRE41.9); for an unrated junior tranche apply juniorUnratedFloor(double, double) (CRE41.15).
      Parameters:
      kA - K_A from kA(double, double)
      attach - A in [0, 1)
      detach - D in (A, 1]
    • erbaRiskWeight

      public static double erbaRiskWeight(FrtbDrcSecuritisation.ErbaRating rating, boolean senior, double thickness, boolean stc)
      SEC-ERBA banking-book risk weight as a multiple, one-year tranche maturity (MAR22.34(1), so the maturity interpolation of CRE42.5(1) is not needed). Non-senior tranches: table weight × (1 − min(T, 50%)) with T = D − A (CRE42.5(2)), then, outside STC, floored at the senior weight of the same rating (CRE42.7). Floors 15%, or STC 10% senior and 15% non-senior (CRE42.7, CRE42.14).
      Parameters:
      thickness - T = D − A, ignored for senior tranches
    • byBucket

      public static <K> Map<K, List<FrtbDrcSecuritisation.NetJtd>> byBucket(List<FrtbDrcSecuritisation.Position<K>> positions)
      Groups positions by bucket key in first-seen order.
    • hedgeBenefitRatio

      public static double hedgeBenefitRatio(List<FrtbDrcSecuritisation.NetJtd> positions)
      HBR = Σ long / (Σ long + Σ |short|), unweighted (MAR22.23); 0 for an empty set.
    • nonCtpBucketCharge

      public static double nonCtpBucketCharge(List<FrtbDrcSecuritisation.NetJtd> bucket)
      Non-CTP bucket charge, max(Σ RW·long − HBR_b·Σ RW·|short|, 0) (MAR22.33).
    • nonCtpTotal

      public static <K> double nonCtpTotal(List<FrtbDrcSecuritisation.Position<K>> positions)
      Non-CTP total: simple sum of bucket charges (MAR22.35).
    • ctpBucketCharge

      public static double ctpBucketCharge(List<FrtbDrcSecuritisation.NetJtd> bucket, double hbrCtp)
      CTP index-bucket charge with the portfolio-wide HBR_ctp and no floor (MAR22.44): Σ RW·long − HBR_ctp·Σ RW·|short|; may be negative.
    • ctpAggregate

      public static double ctpAggregate(List<Double> bucketCharges)
      CTP aggregation max(Σ_b (max(DRC_b, 0) + 0.5·min(DRC_b, 0)), 0) (MAR22.45).
    • ctpTotal

      public static <K> double ctpTotal(List<FrtbDrcSecuritisation.Position<K>> positions)
      Total CTP DRC: one bucket per index, bespokes in the bucket of their index (MAR22.40, MAR22.41); HBR_ctp from all CTP positions together (MAR22.44(1)); aggregated by MAR22.45.