Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/credit/syntheticcdo.hpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2008 Roland Lichters
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
/*! \file syntheticcdo.hpp
21
    \brief Synthetic Collateralized Debt Obligation and pricing engines
22
*/
23
24
#ifndef quantlib_synthetic_cdo_hpp
25
#define quantlib_synthetic_cdo_hpp
26
27
#include <ql/qldefines.hpp>
28
29
#ifndef QL_PATCH_SOLARIS
30
31
#include <ql/instrument.hpp>
32
#include <ql/default.hpp>
33
#include <ql/optional.hpp>
34
#include <ql/time/schedule.hpp>
35
36
#include <ql/experimental/credit/basket.hpp>
37
#include <ql/cashflows/fixedratecoupon.hpp>
38
39
namespace QuantLib {
40
41
    class YieldTermStructure;
42
43
    //! Synthetic Collateralized Debt Obligation
44
    /*!
45
      The instrument prices a mezzanine CDO tranche with loss given default
46
      between attachment point \f$ D_1\f$ and detachment point
47
      \f$ D_2 > D_1 \f$.
48
49
      For purchased protection, the instrument value is given by the difference
50
      of the protection value \f$ V_1 \f$ and premium value \f$ V_2 \f$,
51
52
      \f[ V = V_1 - V_2. \f]
53
54
      The protection leg is priced as follows:
55
56
      - Build the probability distribution for volume of defaults \f$ L \f$
57
      (before recovery) or Loss Given Default \f$ LGD = (1-r)\,L \f$ at
58
      times/dates \f$ t_i, i=1, ..., N\f$ (premium schedule times with
59
      intermediate steps)
60
61
      - Determine the expected value
62
      \f$ E_i = E_{t_i}\,\left[Pay(LGD)\right] \f$
63
      of the protection payoff \f$ Pay(LGD) \f$  at each time \f$ t_i\f$ where
64
      \f[
65
      Pay(L) = min (D_1, LGD) - min (D_2, LGD) = \left\{
66
      \begin{array}{lcl}
67
      \displaystyle 0 &;& LGD < D_1 \\
68
      \displaystyle LGD - D_1 &;& D_1 \leq LGD \leq D_2 \\
69
      \displaystyle D_2 - D_1 &;& LGD > D_2
70
      \end{array}
71
      \right.
72
      \f]
73
74
      - The protection value is then calculated as
75
      \f[ V_1 \:=\: \sum_{i=1}^N (E_i - E_{i-1}) \cdot  d_i \f]
76
      where \f$ d_i\f$ is the discount factor at time/date \f$ t_i \f$
77
78
      The premium is paid on the protected notional amount, initially
79
      \f$ D_2 - D_1. \f$ This notional amount is reduced by the expected
80
      protection
81
      payments \f$ E_i \f$ at times \f$ t_i, \f$ so that the premium value is
82
      calculated as
83
84
      \f[
85
      V_2 =m \, \cdot \sum_{i=1}^N \,(D_2 - D_1 - E_i) \cdot \Delta_{i-1,i}\,d_i
86
      \f]
87
88
      where \f$ m \f$ is the premium rate, \f$ \Delta_{i-1, i}\f$ is the day
89
      count fraction between date/time \f$ t_{i-1}\f$ and \f$ t_i.\f$
90
91
      The construction of the portfolio loss distribution \f$ E_i \f$ is
92
      based on the probability bucketing algorithm described in
93
94
      <strong>
95
      John Hull and Alan White, "Valuation of a CDO and nth to default CDS
96
      without Monte Carlo simulation", Journal of Derivatives 12, 2, 2004
97
      </strong>
98
99
      The pricing algorithm allows for varying notional amounts and
100
      default termstructures of the underlyings.
101
102
      \todo Investigate and fix cases \f$ E_{i+1} < E_i. \f$
103
    */
104
    class SyntheticCDO : public Instrument {
105
    public:
106
        class arguments;
107
        class results;
108
        class engine;
109
110
        // Review: No accrual settlement flag. No separate upfront payment date.
111
        // Review: Forward start case.
112
        /*! If the notional exceeds the basket inception tranche
113
            notional, the cdo is leveraged by that factor.
114
115
            \todo: allow for extra payment flags, arbitrary upfront
116
                   payment date...
117
        */
118
        SyntheticCDO (const ext::shared_ptr<Basket>& basket,
119
                      Protection::Side side,
120
                      Schedule schedule,
121
                      Rate upfrontRate,
122
                      Rate runningRate,
123
                      const DayCounter& dayCounter,
124
                      BusinessDayConvention paymentConvention,
125
                      ext::optional<Real> notional = ext::nullopt);
126
127
0
        const ext::shared_ptr<Basket>& basket() const { return basket_; }
128
129
        bool isExpired() const override;
130
        Rate fairPremium() const;
131
        Rate fairUpfrontPremium() const;
132
        Rate premiumValue () const;
133
        Rate protectionValue () const;
134
        Real premiumLegNPV() const;
135
        Real protectionLegNPV() const;
136
        /*!
137
          Total outstanding tranche notional, not wiped out
138
        */
139
        Real remainingNotional() const;
140
        /*! The number of times the contract contains the portfolio tranched
141
                notional.
142
        */
143
0
        Real leverageFactor() const {
144
0
            return leverageFactor_;
145
0
        }
146
        //! Last protection date.
147
0
        const Date& maturity() const {
148
0
            return ext::dynamic_pointer_cast<FixedRateCoupon>(
149
0
                normalizedLeg_.back())->accrualEndDate();
150
0
        }
151
        /*! The Gaussian Copula LHP implied correlation that makes the
152
            contract zero value. This is for a flat correlation along
153
            time and portfolio loss level.
154
        */
155
        Real implicitCorrelation(const std::vector<Real>& recoveries,
156
            const Handle<YieldTermStructure>& discountCurve,
157
            Real targetNPV = 0.,
158
            Real accuracy = 1.0e-3) const;
159
160
        /*!
161
          Expected tranche loss for all payment dates
162
         */
163
        std::vector<Real> expectedTrancheLoss() const;
164
        Size error () const;
165
166
        void setupArguments(PricingEngine::arguments*) const override;
167
        void fetchResults(const PricingEngine::results*) const override;
168
169
      private:
170
        void setupExpired() const override;
171
172
        ext::shared_ptr<Basket> basket_;
173
        Protection::Side side_;
174
        Leg normalizedLeg_;
175
176
        Rate upfrontRate_;
177
        Rate runningRate_;
178
        const Real leverageFactor_;
179
        DayCounter dayCounter_;
180
        BusinessDayConvention paymentConvention_;
181
182
        mutable Real premiumValue_;
183
        mutable Real protectionValue_;
184
        mutable Real upfrontPremiumValue_;
185
        mutable Real remainingNotional_;
186
        mutable Size error_;
187
        mutable std::vector<Real> expectedTrancheLoss_;
188
    };
189
190
    class SyntheticCDO::arguments : public virtual PricingEngine::arguments {
191
    public:
192
0
        arguments() : side(Protection::Side(-1)),
193
0
                      upfrontRate(Null<Real>()),
194
0
                      runningRate(Null<Real>()) {}
195
        void validate() const override;
196
197
        ext::shared_ptr<Basket> basket;
198
        Protection::Side side;
199
        Leg normalizedLeg;
200
201
        Rate upfrontRate;
202
        Rate runningRate;
203
        Real leverageFactor;
204
        DayCounter dayCounter;
205
        BusinessDayConvention paymentConvention;
206
    };
207
208
    class SyntheticCDO::results : public Instrument::results {
209
    public:
210
      void reset() override;
211
      Real premiumValue;
212
      Real protectionValue;
213
      Real upfrontPremiumValue;
214
      Real remainingNotional;
215
      Real xMin, xMax;
216
      Size error;
217
      /* Expected tranche losses affecting this tranche coupons. Notice this
218
      number might be below the actual basket losses, since the cdo protection
219
      might start after basket inception (forward start CDO)*/
220
      std::vector<Real> expectedTrancheLoss;
221
    };
222
223
224
    //! CDO base engine
225
    class SyntheticCDO::engine :
226
        public GenericEngine<SyntheticCDO::arguments,
227
                             SyntheticCDO::results> { };
228
229
}
230
231
#endif
232
233
#endif