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

Black-formula bond option engine. More...

#include <qle/pricingengines/blackbondoptionengine.hpp>

+ Inheritance diagram for BlackBondOptionEngine:
+ Collaboration diagram for BlackBondOptionEngine:

Public Member Functions

 BlackBondOptionEngine (const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility, const Handle< YieldTermStructure > &underlyingReferenceCurve, const Handle< DefaultProbabilityTermStructure > &defaultCurve=Handle< DefaultProbabilityTermStructure >(), const Handle< Quote > &recoveryRate=Handle< Quote >(), const Handle< Quote > &securitySpread=Handle< Quote >(), Period timestepPeriod=1 *Months)
 volatility is the quoted fwd yield volatility, not price vol More...
 
void calculate () const override
 

Private Attributes

Handle< YieldTermStructure > discountCurve_
 
Handle< SwaptionVolatilityStructurevolatility_
 
Handle< YieldTermStructure > underlyingReferenceCurve_
 
Handle< DefaultProbabilityTermStructure > defaultCurve_
 
Handle< Quote > recoveryRate_
 
Handle< Quote > securitySpread_
 
Period timestepPeriod_
 

Detailed Description

Black-formula bond option engine.

Definition at line 36 of file blackbondoptionengine.hpp.

Constructor & Destructor Documentation

◆ BlackBondOptionEngine()

BlackBondOptionEngine ( const Handle< YieldTermStructure > &  discountCurve,
const Handle< SwaptionVolatilityStructure > &  volatility,
const Handle< YieldTermStructure > &  underlyingReferenceCurve,
const Handle< DefaultProbabilityTermStructure > &  defaultCurve = Handle<DefaultProbabilityTermStructure>(),
const Handle< Quote > &  recoveryRate = Handle<Quote>(),
const Handle< Quote > &  securitySpread = Handle<Quote>(),
Period  timestepPeriod = 1 * Months 
)

volatility is the quoted fwd yield volatility, not price vol

Definition at line 33 of file blackbondoptionengine.cpp.

39 : discountCurve_(discountCurve), volatility_(volatility), underlyingReferenceCurve_(underlyingReferenceCurve),
40 defaultCurve_(defaultCurve), recoveryRate_(recoveryRate), securitySpread_(securitySpread),
41 timestepPeriod_(timestepPeriod) {
42 registerWith(discountCurve_);
43 registerWith(volatility_);
44 registerWith(underlyingReferenceCurve_);
45 registerWith(defaultCurve_);
46 registerWith(recoveryRate_);
47 registerWith(securitySpread_);
48}
Handle< YieldTermStructure > discountCurve_
Handle< SwaptionVolatilityStructure > volatility_
Handle< DefaultProbabilityTermStructure > defaultCurve_
Handle< YieldTermStructure > underlyingReferenceCurve_

Member Function Documentation

◆ calculate()

void calculate ( ) const
override

Definition at line 50 of file blackbondoptionengine.cpp.

50 {
51
52 QL_REQUIRE(!discountCurve_.empty(), "BlackBondOptionEngine::calculate(): empty discount curve");
53
54 QL_REQUIRE(arguments_.putCallSchedule.size() == 1, "BlackBondOptionEngine: can only handle European options");
55 Date exerciseDate = arguments_.putCallSchedule.front()->date();
56
57 QL_REQUIRE(!underlyingReferenceCurve_.empty(), "BlackBondOptionEngine::calculate(): empty reference curve");
58
59 auto fwdBondEngine = QuantLib::ext::make_shared<DiscountingRiskyBondEngine>(
61 auto bondNpvResults = fwdBondEngine->calculateNpv(exerciseDate, arguments_.underlying->settlementDate(exerciseDate),
62 arguments_.underlying->cashflows());
63
64 for (auto& cfRes : bondNpvResults.cashflowResults) {
65 cfRes.legNumber = 0;
66 cfRes.type = "Underlying_Bond__" + cfRes.type;
67 }
68
69 Real fwdNpv = bondNpvResults.npv;
70
71
72 Real knockOutProbability = defaultCurve_.empty() ? 0.0 : 1.0 - defaultCurve_->survivalProbability(exerciseDate);
73
74 // adjust forward if option does not knock out (option is on the recovery value if bond defaults before expiry)
75 if (!arguments_.knocksOutOnDefault) {
76 fwdNpv = (1.0 - knockOutProbability) * fwdNpv + knockOutProbability *
77 (recoveryRate_.empty() ? 0.0 : recoveryRate_->value()) *
78 arguments_.underlying->notional(exerciseDate);
79 }
80
81 // hard code yield compounding convention to annual
82 Rate fwdYtm = CashFlows::yield(arguments_.underlying->cashflows(), fwdNpv, volatility_->dayCounter(), Compounded,
83 Annual, false, exerciseDate, exerciseDate);
84 InterestRate fwdRate(fwdYtm, volatility_->dayCounter(), Compounded, Annual);
85 Time fwdDur = CashFlows::duration(arguments_.underlying->cashflows(), fwdRate, Duration::Modified, false,
86 exerciseDate, exerciseDate);
87
88 QL_REQUIRE(arguments_.putCallSchedule.size() == 1, "BlackBondOptionEngine: only European bond options allowed");
89
90 // read atm yield vol
91 Real underlyingLength = volatility_->swapLength(exerciseDate, arguments_.underlying->cashflows().back()->date());
92 Volatility yieldVol = volatility_->volatility(exerciseDate, underlyingLength, fwdYtm);
93
94 // compute price vol from yield vol
95 Volatility fwdPriceVol;
96 Real shift = 0.0;
97 if (volatility_->volatilityType() == VolatilityType::Normal)
98 fwdPriceVol = yieldVol * fwdDur;
99 else {
100 if (close_enough(volatility_->shift(exerciseDate, underlyingLength), 0.0)) {
101 QL_REQUIRE(fwdYtm > 0, "BlackBondOptionEngine: input yield vols are lognormal, but yield is not positive ("
102 << fwdYtm << ")");
103 fwdPriceVol = yieldVol * fwdDur * fwdYtm;
104 } else {
105 shift = volatility_->shift(exerciseDate, underlyingLength);
106 QL_REQUIRE(fwdYtm > -shift, "BlackBondOptionEngine: input yield vols are shifted lognormal "
107 << shift << ", but yield (" << fwdYtm << ") is not greater than -shift ("
108 << -shift);
109 fwdPriceVol = yieldVol * fwdDur * (fwdYtm + shift);
110 }
111 }
112
113 QL_REQUIRE(fwdPriceVol >= 0.0, "BlackBondOptionEngine: negative forward price vol ("
114 << fwdPriceVol << "), yieldVol=" << yieldVol << ", fwdDur=" << fwdDur
115 << ", fwdYtm=" << fwdYtm << ", shift="
116 << (volatility_->volatilityType() == VolatilityType::Normal
117 ? 0.0
118 : volatility_->shift(exerciseDate, underlyingLength)));
119
120 // strike could be a price or yield
121 Real cashStrike;
122 if (arguments_.putCallSchedule.front()->isBondPrice()) {
123 // adjust cashCashStrike if given as clean
124 // note that the accruedAmount is on the basis of notional 100, as cleanPrice and dirtyPrice
125 cashStrike = arguments_.putCallSchedule.front()->price().amount();
126 if (arguments_.putCallSchedule.front()->price().type() == QuantLib::Bond::Price::Clean)
127 cashStrike += arguments_.underlying->accruedAmount(exerciseDate) / 100;
128 } else {
129 // for a yield get the strike using npv calculation, yield should always be dirty price
130 // so no adjustment needed
131 InterestRate yield = arguments_.putCallSchedule.front()->yield();
132 cashStrike = CashFlows::npv(arguments_.underlying->cashflows(), yield, false, exerciseDate, exerciseDate);
133 }
134
135 Real optionValue = blackFormula(
136 arguments_.putCallSchedule[0]->type() == Callability::Call ? Option::Call : Option::Put, cashStrike, fwdNpv,
137 fwdPriceVol * std::sqrt(volatility_->timeFromReference(exerciseDate)), discountCurve_->discount(exerciseDate));
138
139 // correct for knock out probability
140 if (arguments_.knocksOutOnDefault && !defaultCurve_.empty())
141 optionValue *= 1.0 - knockOutProbability;
142
143 CashFlowResults optionFlow;
144 optionFlow.payDate = exerciseDate;
145 optionFlow.legNumber = 1;
146 optionFlow.type = "ExpectedOptionPayoff";
147 optionFlow.amount = optionValue / discountCurve_->discount(exerciseDate);
148 optionFlow.discountFactor = discountCurve_->discount(exerciseDate);
149 optionFlow.presentValue = optionValue;
150
151 bondNpvResults.cashflowResults.push_back(optionFlow);
152
153 results_.additionalResults["knockOutProbability"] = knockOutProbability;
154 results_.additionalResults["cashFlowResults"] = bondNpvResults.cashflowResults;
155 results_.additionalResults["CashStrike"] = cashStrike;
156 results_.additionalResults["FwdCashPrice"] = fwdNpv;
157 results_.additionalResults["PriceVol"] = fwdPriceVol;
158 results_.additionalResults["timeToExpiry"] = volatility_->timeFromReference(exerciseDate);
159 results_.additionalResults["optionValue"] = optionValue;
160 results_.additionalResults["yieldVol"] = yieldVol;
161 results_.additionalResults["yieldVolShift"] = shift;
162 results_.additionalResults["fwdDuration"] = fwdDur;
163 results_.additionalResults["fwdYieldToMaturity"] = fwdYtm;
164
165 results_.additionalResults["AccruedAtExercise"] = arguments_.underlying->accruedAmount(exerciseDate)/100;
166 // results_.additionalResults["CleanBondPrice"] = arguments_.underlying->cleanPrice();
167 // results_.additionalResults["DirtyBondPrice"] = arguments_.underlying->dirtyPrice();
168 if (!arguments_.knocksOutOnDefault) {
169 results_.additionalResults["ExpectedBondRecovery"] = knockOutProbability *
170 (recoveryRate_.empty() ? 0.0 : recoveryRate_->value()) *
171 arguments_.underlying->notional(exerciseDate);
172 }
173 results_.value = optionValue;
174}
const Instrument::results * results_
Definition: cdsoption.cpp:81
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Swap::arguments * arguments_
+ Here is the call graph for this function:

Member Data Documentation

◆ discountCurve_

Handle<YieldTermStructure> discountCurve_
private

Definition at line 48 of file blackbondoptionengine.hpp.

◆ volatility_

Handle<SwaptionVolatilityStructure> volatility_
private

Definition at line 49 of file blackbondoptionengine.hpp.

◆ underlyingReferenceCurve_

Handle<YieldTermStructure> underlyingReferenceCurve_
private

Definition at line 50 of file blackbondoptionengine.hpp.

◆ defaultCurve_

Handle<DefaultProbabilityTermStructure> defaultCurve_
private

Definition at line 51 of file blackbondoptionengine.hpp.

◆ recoveryRate_

Handle<Quote> recoveryRate_
private

Definition at line 52 of file blackbondoptionengine.hpp.

◆ securitySpread_

Handle<Quote> securitySpread_
private

Definition at line 53 of file blackbondoptionengine.hpp.

◆ timestepPeriod_

Period timestepPeriod_
private

Definition at line 54 of file blackbondoptionengine.hpp.