21#include <ql/exercise.hpp>
22#include <ql/pricingengines/blackformula.hpp>
23#include <ql/pricingengines/credit/isdacdsengine.hpp>
24#include <ql/pricingengines/credit/midpointcdsengine.hpp>
25#include <ql/termstructures/credit/flathazardrate.hpp>
26#include <ql/termstructures/yield/flatforward.hpp>
27#include <ql/time/daycounters/actual360.hpp>
48 const Date& exerciseDate =
arguments_.exercise->dates().front();
49 Real exerciseTime =
volatility_->timeFromReference(exerciseDate);
52 results_.additionalResults[
"strikeSpread"] = strike;
53 Real runningSpread = cds.runningSpread();
54 results_.additionalResults[
"runningSpread"] = runningSpread;
58 results_.additionalResults[
"discountToExerciseTradeCollateral"] = discTradeCollToExercise;
59 results_.additionalResults[
"discountToExerciseSwapCurrency"] = discSwapCurrToExercise;
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() <<
")");
70 Real fairSpread = cds.fairSpreadClean();
71 results_.additionalResults[
"forwardSpread"] = fairSpread;
74 Real Fp = fairSpread +
fep * (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) / rpv01 /
75 discTradeCollToExercise / cds.notional();
76 results_.additionalResults[
"fepAdjustedForwardSpread"] = Fp;
82 (strike - runningSpread) *
83 (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) / rpv01;
84 results_.additionalResults[
"adjustedStrikeSpread"] = Kp;
89 Real stdDev =
volatility * std::sqrt(exerciseTime);
91 results_.additionalResults[
"standardDeviation"] = stdDev;
94 Option::Type callPut = cds.side() == Protection::Buyer ? Option::Call : Option::Put;
95 results_.additionalResults[
"callPut"] = callPut == Option::Call ? string(
"Call") : string(
"Put");
98 results_.additionalResults[
"valuationDateNotional"] = cds.notional();
103 "BlackIndexCdsOptionEngine: FEP adjusted forward spread ("
104 << Fp <<
") is not positive, can not calculate a reasonable option price");
107 Kp = std::max(Kp, 0.0);
109 results_.value = discTradeCollToExercise / (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise) *
110 rpv01 * cds.notional() * blackFormula(callPut, Kp, Fp, stdDev, 1.0);
121 const Real& tradeDateNtl =
arguments_.tradeDateNtl;
122 results_.additionalResults[
"valuationDateNotional"] = cds.notional();
123 results_.additionalResults[
"tradeDateNotional"] = tradeDateNtl;
126 Real effStrike = 1.0 - tradeDateNtl / cds.notional() * (1.0 -
arguments_.strike);
127 results_.additionalResults[
"strikePriceDefaultAdjusted"] = effStrike;
130 const Date& exerciseDate =
arguments_.exercise->dates().front();
131 Real exerciseTime =
volatility_->timeFromReference(exerciseDate);
134 results_.additionalResults[
"discountToExerciseTradeCollateral"] = discTradeCollToExercise;
135 results_.additionalResults[
"discountToExerciseSwapCurrency"] = discSwapCurrToExercise;
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);
143 1 - npv / cds.notional() / (Settlement::Cash ? discSwapCurrToExercise : discTradeCollToExercise);
144 results_.additionalResults[
"forwardPrice"] = forwardPrice;
147 Real Fp = forwardPrice -
fep / cds.notional() / discTradeCollToExercise;
148 results_.additionalResults[
"fepAdjustedForwardPrice"] = Fp;
153 Real stdDev =
volatility * std::sqrt(exerciseTime);
155 results_.additionalResults[
"standardDeviation"] = stdDev;
158 Option::Type cp = cds.side() == Protection::Buyer ? Option::Put : Option::Call;
159 results_.additionalResults[
"callPut"] = cp == Option::Put ? string(
"Put") : string(
"Call");
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");
169 results_.value = cds.notional() * blackFormula(cp, effStrike, Fp, stdDev, discTradeCollToExercise);
182 Schedule schedule = MakeSchedule()
183 .from(cds.protectionStartDate())
185 .withCalendar(WeekendsOnly())
186 .withFrequency(Quarterly)
187 .withConvention(Following)
188 .withTerminationDateConvention(Unadjusted)
189 .withRule(DateGeneration::CDS2015);
194 Real accuracy = 1e-8;
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());
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()))));
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());
215 Handle<DefaultProbabilityTermStructure> dph(
216 QuantLib::ext::make_shared<FlatHazardRate>(
discountSwapCurrency_->referenceDate(), hazardRate, Actual365Fixed()));
219 strikeCds->setPricingEngine(
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.");
227 const Date& exerciseDate =
arguments_.exercise->dates().front();
228 Probability spToExercise = dph->survivalProbability(exerciseDate);
230 results_.additionalResults[
"strikeBasedSurvivalToExercise"] = spToExercise;
233 Real rpv01_K_fwd = rpv01_K / spToExercise / discToExercise;
234 results_.additionalResults[
"forwardRiskyAnnuityStrike"] = rpv01_K_fwd;
Black index credit default swap option engine.
const Instrument::results * results_
void spreadStrikeCalculate(QuantLib::Real fep) const
void doCalc() const override
Engine specific calculation.
QuantLib::Real forwardRiskyAnnuityStrike() const
void priceStrikeCalculate(QuantLib::Real fep) const
QuantLib::Handle< QuantLib::YieldTermStructure > discountTradeCollateral_
QuantLib::Real fep() const
Calculate the discounted value of the front end protection.
const QuantLib::Handle< QuantExt::CreditVolCurve > volatility() const
QuantLib::Real indexRecovery_
Assumed index recovery used in the flat strike spread curve calculation if provided.
QuantLib::Handle< QuantLib::YieldTermStructure > discountSwapCurrency_
QuantLib::Handle< QuantExt::CreditVolCurve > volatility_
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real periodToTime(const Period &p)
Swap::arguments * arguments_