Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | List of all members
BlackIndexCdsOptionEngine Class Reference

#include <qle/pricingengines/blackindexcdsoptionengine.hpp>

+ Inheritance diagram for BlackIndexCdsOptionEngine:
+ Collaboration diagram for BlackIndexCdsOptionEngine:

Public Member Functions

 IndexCdsOptionBaseEngine (const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &probability, QuantLib::Real recovery, const Handle< YieldTermStructure > &discountSwapCurrency, const Handle< YieldTermStructure > &discountTradeCollateral, const QuantLib::Handle< QuantExt::CreditVolCurve > &volatility)
 Constructor taking a default probability term structure bootstrapped from the index spreads. More...
 
 IndexCdsOptionBaseEngine (const std::vector< QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > > &probabilities, const std::vector< QuantLib::Real > &recoveries, const Handle< YieldTermStructure > &discountSwapCurrency, const Handle< YieldTermStructure > &discountTradeCollateral, const QuantLib::Handle< QuantExt::CreditVolCurve > &volatility, QuantLib::Real indexRecovery=QuantLib::Null< QuantLib::Real >())
 
- Public Member Functions inherited from IndexCdsOptionBaseEngine
 IndexCdsOptionBaseEngine (const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &probability, QuantLib::Real recovery, const Handle< YieldTermStructure > &discountSwapCurrency, const Handle< YieldTermStructure > &discountTradeCollateral, const QuantLib::Handle< QuantExt::CreditVolCurve > &volatility)
 Constructor taking a default probability term structure bootstrapped from the index spreads. More...
 
 IndexCdsOptionBaseEngine (const std::vector< QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > > &probabilities, const std::vector< QuantLib::Real > &recoveries, const Handle< YieldTermStructure > &discountSwapCurrency, const Handle< YieldTermStructure > &discountTradeCollateral, const QuantLib::Handle< QuantExt::CreditVolCurve > &volatility, QuantLib::Real indexRecovery=QuantLib::Null< QuantLib::Real >())
 
const std::vector< QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > > & probabilities () const
 
const std::vector< QuantLib::Real > & recoveries () const
 
const QuantLib::Handle< QuantLib::YieldTermStructure > discountSwapCurrency () const
 
const QuantLib::Handle< QuantLib::YieldTermStructure > discountTradeCollateral () const
 
const QuantLib::Handle< QuantExt::CreditVolCurvevolatility () const
 
void calculate () const override
 

Private Member Functions

void doCalc () const override
 Engine specific calculation. More...
 
void spreadStrikeCalculate (QuantLib::Real fep) const
 
void priceStrikeCalculate (QuantLib::Real fep) const
 
QuantLib::Real forwardRiskyAnnuityStrike () const
 

Additional Inherited Members

- Protected Member Functions inherited from IndexCdsOptionBaseEngine
void registerWithMarket ()
 Register with market data. More...
 
QuantLib::Real fep () const
 Calculate the discounted value of the front end protection. More...
 
- Protected Attributes inherited from IndexCdsOptionBaseEngine
std::vector< QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > > probabilities_
 Store inputs. More...
 
std::vector< QuantLib::Real > recoveries_
 
QuantLib::Handle< QuantLib::YieldTermStructure > discountSwapCurrency_
 
QuantLib::Handle< QuantLib::YieldTermStructure > discountTradeCollateral_
 
QuantLib::Handle< QuantExt::CreditVolCurvevolatility_
 
QuantLib::Real indexRecovery_
 Assumed index recovery used in the flat strike spread curve calculation if provided. More...
 
std::vector< QuantLib::Real > notionals_
 Store the underlying index CDS notional(s) during calculation. More...
 

Detailed Description

Black index CDS option engine

Prices index CDS option instruments quoted in terms of strike spread or strike price. If the strike is in terms of spread, it is assumed that the volatility structure's strike dimension, if there is one, is in terms of spread also. This is the standard quotation convention for investment grade index families like CDX IG and ITraxx Europe. If the strike is in terms of price, it is assumed that the volatility structure's strike dimension, if there is one, is in terms of price also. This is the standard quotation convention for high yield index families like CDX HY and CDX EM.

The valuation of the index CDS options with strike price is a reasonably straightforward application of Black's formula. The approach is outlined for example in Mark-to-market Credit Index Option Pricing and Credit Volatility Index, John Yang and Lukasz Dobrek, 23 June 2015, Section 1.1. Here, we calculate the front end protection (FEP) adjusted forward price as opposed to deriving it from the market quotes of payer and receiver CDS options with the same strike.

The valuation of the index CDS options with strike spread follows the approach outlined in Modelling Single-name and Multi-name Credit Derivatives, Dominic O'Kane, 2008, Section 11.7. This is also the approach outlined in Credit Index Option, ICE, 2018.

Definition at line 48 of file blackindexcdsoptionengine.hpp.

Member Function Documentation

◆ doCalc()

void doCalc ( ) const
overrideprivatevirtual

Engine specific calculation.

Implements IndexCdsOptionBaseEngine.

Definition at line 38 of file blackindexcdsoptionengine.cpp.

38 {
39 // Calculate option value depending on strike type.
40 if (arguments_.strikeType == CdsOption::Spread)
42 else
44}
void spreadStrikeCalculate(QuantLib::Real fep) const
void priceStrikeCalculate(QuantLib::Real fep) const
QuantLib::Real fep() const
Calculate the discounted value of the front end protection.
Swap::arguments * arguments_
+ Here is the call graph for this function:

◆ spreadStrikeCalculate()

void spreadStrikeCalculate ( QuantLib::Real  fep) const
private

Definition at line 46 of file blackindexcdsoptionengine.cpp.

46 {
47
48 const Date& exerciseDate = arguments_.exercise->dates().front();
49 Real exerciseTime = volatility_->timeFromReference(exerciseDate);
50 const auto& cds = *arguments_.swap;
51 const Real& strike = arguments_.strike;
52 results_.additionalResults["strikeSpread"] = strike;
53 Real runningSpread = cds.runningSpread();
54 results_.additionalResults["runningSpread"] = runningSpread;
55
56 DiscountFactor discTradeCollToExercise = discountTradeCollateral_->discount(exerciseDate);
57 DiscountFactor discSwapCurrToExercise = discountSwapCurrency_->discount(exerciseDate);
58 results_.additionalResults["discountToExerciseTradeCollateral"] = discTradeCollToExercise;
59 results_.additionalResults["discountToExerciseSwapCurrency"] = discSwapCurrToExercise;
60
61 // Calculate the risky annuity
62 Real rpv01 = std::abs(cds.couponLegNPV() + cds.accrualRebateNPV()) / (cds.notional() * cds.runningSpread());
63 results_.additionalResults["riskyAnnuity"] = rpv01;
64 QL_REQUIRE(cds.notional() > 0.0 || close_enough(cds.notional(), 0.0),
65 "BlackIndexCdsOptionEngine: notional must not be negative (" << cds.notional() << ")");
66 QL_REQUIRE(rpv01 > 0.0, "BlackIndexCdsOptionEngine: risky annuity must be positive (couponLegNPV="
67 << cds.couponLegNPV() << ", accrualRebateNPV=" << cds.accrualRebateNPV()
68 << ", notional=" << cds.notional() << ", runningSpread=" << cds.runningSpread() << ")");
69
70 Real fairSpread = cds.fairSpreadClean();
71 results_.additionalResults["forwardSpread"] = fairSpread;
72
73 // FEP adjusted forward spread. F^{Adjusted} in O'Kane 2008, Section 11.7. F' in ICE paper (notation is poor).
74 Real Fp = fairSpread + fep * (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) / rpv01 /
75 discTradeCollToExercise / cds.notional();
76 results_.additionalResults["fepAdjustedForwardSpread"] = Fp;
77
78 // Adjusted strike spread. K' in O'Kane 2008, Section 11.7. K' in ICE paper (notation is poor).
79 Real Kp = close_enough(strike, 0.0)
80 ? 0.0
81 : runningSpread + arguments_.tradeDateNtl / cds.notional() * forwardRiskyAnnuityStrike() *
82 (strike - runningSpread) *
83 (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) / rpv01;
84 results_.additionalResults["adjustedStrikeSpread"] = Kp;
85
86 // Read the volatility from the volatility surface
87 Real volatility = volatility_->volatility(exerciseDate, QuantExt::periodToTime(arguments_.indexTerm), strike,
89 Real stdDev = volatility * std::sqrt(exerciseTime);
90 results_.additionalResults["volatility"] = volatility;
91 results_.additionalResults["standardDeviation"] = stdDev;
92
93 // Option type
94 Option::Type callPut = cds.side() == Protection::Buyer ? Option::Call : Option::Put;
95 results_.additionalResults["callPut"] = callPut == Option::Call ? string("Call") : string("Put");
96
97 // NPV. Add the relevant notionals to the additional results also.
98 results_.additionalResults["valuationDateNotional"] = cds.notional();
99 results_.additionalResults["tradeDateNotional"] = arguments_.tradeDateNtl;
100
101 // Check the forward before plugging it into the black formula
102 QL_REQUIRE(Fp > 0.0 || close_enough(stdDev, 0.0),
103 "BlackIndexCdsOptionEngine: FEP adjusted forward spread ("
104 << Fp << ") is not positive, can not calculate a reasonable option price");
105 // The strike spread might get negative through the adjustment above, but economically the strike is
106 // floored at 0.0, so we ensure this here. This lets us compute the black formula as well in all cases.
107 Kp = std::max(Kp, 0.0);
108
109 results_.value = discTradeCollToExercise / (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) *
110 rpv01 * cds.notional() * blackFormula(callPut, Kp, Fp, stdDev, 1.0);
111}
const Instrument::results * results_
Definition: cdsoption.cpp:81
QuantLib::Handle< QuantLib::YieldTermStructure > discountTradeCollateral_
const QuantLib::Handle< QuantExt::CreditVolCurve > volatility() const
QuantLib::Handle< QuantLib::YieldTermStructure > discountSwapCurrency_
QuantLib::Handle< QuantExt::CreditVolCurve > volatility_
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real periodToTime(const Period &p)
Definition: time.cpp:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ priceStrikeCalculate()

void priceStrikeCalculate ( QuantLib::Real  fep) const
private

Definition at line 113 of file blackindexcdsoptionengine.cpp.

113 {
114
115 // Underlying index CDS.
116 const auto& cds = *arguments_.swap;
117
118 // Add some additional entries to additional results.
119 results_.additionalResults["strikePrice"] = arguments_.strike;
120
121 const Real& tradeDateNtl = arguments_.tradeDateNtl;
122 results_.additionalResults["valuationDateNotional"] = cds.notional();
123 results_.additionalResults["tradeDateNotional"] = tradeDateNtl;
124
125 // effective strike (strike is expressed w.r.t. trade date notional by market convention)
126 Real effStrike = 1.0 - tradeDateNtl / cds.notional() * (1.0 - arguments_.strike);
127 results_.additionalResults["strikePriceDefaultAdjusted"] = effStrike;
128
129 // Discount factor to exercise
130 const Date& exerciseDate = arguments_.exercise->dates().front();
131 Real exerciseTime = volatility_->timeFromReference(exerciseDate);
132 DiscountFactor discTradeCollToExercise = discountTradeCollateral_->discount(exerciseDate);
133 DiscountFactor discSwapCurrToExercise = discountSwapCurrency_->discount(exerciseDate);
134 results_.additionalResults["discountToExerciseTradeCollateral"] = discTradeCollToExercise;
135 results_.additionalResults["discountToExerciseSwapCurrency"] = discSwapCurrToExercise;
136
137 // NPV from buyer's perspective gives upfront, as of valuation date, with correct sign.
138 Real npv = cds.side() == Protection::Buyer ? cds.NPV() : -cds.NPV();
139 results_.additionalResults["upfront"] =
140 npv * (arguments_.settlementType == Settlement::Cash ? discTradeCollToExercise / discSwapCurrToExercise : 1.0);
141
142 Real forwardPrice =
143 1 - npv / cds.notional() / (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise);
144 results_.additionalResults["forwardPrice"] = forwardPrice;
145
146 // Front end protection adjusted forward price.
147 Real Fp = forwardPrice - fep / cds.notional() / discTradeCollToExercise;
148 results_.additionalResults["fepAdjustedForwardPrice"] = Fp;
149
150 // Read the volatility from the volatility surface
151 Real volatility = volatility_->volatility(exerciseDate, QuantExt::periodToTime(arguments_.indexTerm), effStrike,
153 Real stdDev = volatility * std::sqrt(exerciseTime);
154 results_.additionalResults["volatility"] = volatility;
155 results_.additionalResults["standardDeviation"] = stdDev;
156
157 // If protection buyer, put on price.
158 Option::Type cp = cds.side() == Protection::Buyer ? Option::Put : Option::Call;
159 results_.additionalResults["callPut"] = cp == Option::Put ? string("Put") : string("Call");
160
161 // Check the inputs to the black formula before applying it
162 QL_REQUIRE(Fp > 0.0 || close_enough(stdDev, 0.0),
163 "BlackIndexCdsOptionEngine: FEP adjusted forward price ("
164 << Fp << ") is not positive, can not calculate a reasonable option price");
165 QL_REQUIRE(effStrike > 0 || close_enough(effStrike, 0.0),
166 "BlackIndexCdsOptionEngine: Effective Strike price ("
167 << effStrike << ") is not positive, can not calculate a reasonable option price");
168
169 results_.value = cds.notional() * blackFormula(cp, effStrike, Fp, stdDev, discTradeCollToExercise);
170}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ forwardRiskyAnnuityStrike()

Real forwardRiskyAnnuityStrike ( ) const
private

Definition at line 172 of file blackindexcdsoptionengine.cpp.

172 {
173
174 // Underlying index CDS.
175 const auto& cds = *arguments_.swap;
176
177 // This method returns RPV01(0; t_e, T, K) / SP(t_e; K). This is the quantity in formula 11.9 of O'Kane 2008.
178 // There is a slight modification in that we divide by the survival probability to t_E using the flat curve at
179 // the strike spread that we create here.
180
181 // Standard index CDS schedule.
182 Schedule schedule = MakeSchedule()
183 .from(cds.protectionStartDate())
184 .to(cds.maturity())
185 .withCalendar(WeekendsOnly())
186 .withFrequency(Quarterly)
187 .withConvention(Following)
188 .withTerminationDateConvention(Unadjusted)
189 .withRule(DateGeneration::CDS2015);
190
191 // Derive hazard rate curve from a single forward starting CDS matching the characteristics of underlying index
192 // CDS with a running spread equal to the strike.
193 const Real& strike = arguments_.strike;
194 Real accuracy = 1e-8;
195
196 auto strikeCds = QuantLib::ext::make_shared<CreditDefaultSwap>(
197 Protection::Buyer, 1 / accuracy, strike, schedule, Following, Actual360(), cds.settlesAccrual(),
198 cds.protectionPaymentTime(), cds.protectionStartDate(), QuantLib::ext::shared_ptr<Claim>(), Actual360(true),
199 true, cds.tradeDate(), cds.cashSettlementDays());
200 // dummy engine
201 strikeCds->setPricingEngine(QuantLib::ext::make_shared<MidPointCdsEngine>(
202 Handle<DefaultProbabilityTermStructure>(
203 QuantLib::ext::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed())),
204 0.0, Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed()))));
205
206 Real hazardRate;
207 try {
208 hazardRate =
209 strikeCds->impliedHazardRate(0.0, discountSwapCurrency_, Actual365Fixed(), indexRecovery_, accuracy);
210 } catch (const std::exception& e) {
211 QL_FAIL("can not imply fair hazard rate for CDS at option strike "
212 << strike << ". Is the strike correct? Exception: " << e.what());
213 }
214
215 Handle<DefaultProbabilityTermStructure> dph(
216 QuantLib::ext::make_shared<FlatHazardRate>(discountSwapCurrency_->referenceDate(), hazardRate, Actual365Fixed()));
217
218 // Calculate the forward risky strike annuity.
219 strikeCds->setPricingEngine(
220 QuantLib::ext::make_shared<QuantExt::MidPointCdsEngine>(dph, indexRecovery_, discountSwapCurrency_));
221 Real rpv01_K = std::abs(strikeCds->couponLegNPV() + strikeCds->accrualRebateNPV()) /
222 (strikeCds->notional() * strikeCds->runningSpread());
223 results_.additionalResults["riskyAnnuityStrike"] = rpv01_K;
224 QL_REQUIRE(rpv01_K > 0.0, "BlackIndexCdsOptionEngine: strike based risky annuity must be positive.");
225
226 // Survival to exercise
227 const Date& exerciseDate = arguments_.exercise->dates().front();
228 Probability spToExercise = dph->survivalProbability(exerciseDate);
229 Real discToExercise = discountSwapCurrency_->discount(exerciseDate);
230 results_.additionalResults["strikeBasedSurvivalToExercise"] = spToExercise;
231
232 // Forward risky annuity strike (divides out the survival probability and discount to exercise)
233 Real rpv01_K_fwd = rpv01_K / spToExercise / discToExercise;
234 results_.additionalResults["forwardRiskyAnnuityStrike"] = rpv01_K_fwd;
235
236 return rpv01_K_fwd;
237}
QuantLib::Real indexRecovery_
Assumed index recovery used in the flat strike spread curve calculation if provided.
+ Here is the caller graph for this function:

◆ IndexCdsOptionBaseEngine() [1/2]

IndexCdsOptionBaseEngine ( const QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > &  probability,
QuantLib::Real  recovery,
const Handle< YieldTermStructure > &  discountSwapCurrency,
const Handle< YieldTermStructure > &  discountTradeCollateral,
const QuantLib::Handle< QuantExt::CreditVolCurve > &  volatility 
)

Constructor taking a default probability term structure bootstrapped from the index spreads.

◆ IndexCdsOptionBaseEngine() [2/2]

IndexCdsOptionBaseEngine ( const std::vector< QuantLib::Handle< QuantLib::DefaultProbabilityTermStructure > > &  probabilities,
const std::vector< QuantLib::Real > &  recoveries,
const Handle< YieldTermStructure > &  discountSwapCurrency,
const Handle< YieldTermStructure > &  discountTradeCollateral,
const QuantLib::Handle< QuantExt::CreditVolCurve > &  volatility,
QuantLib::Real  indexRecovery = QuantLib::Null<QuantLib::Real>() 
)

Constructor taking a vector of default probability term structures bootstrapped from the index constituent spread curves and a vector of associated recovery rates.