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

#include <qle/pricingengines/baroneadesiwhaleyengine.hpp>

+ Inheritance diagram for BaroneAdesiWhaleyApproximationEngine:
+ Collaboration diagram for BaroneAdesiWhaleyApproximationEngine:

Public Member Functions

 BaroneAdesiWhaleyApproximationEngine (const QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > &)
 
void calculate () const override
 

Static Public Member Functions

static QuantLib::Real criticalPrice (const QuantLib::ext::shared_ptr< QuantLib::StrikedTypePayoff > &payoff, QuantLib::DiscountFactor riskFreeDiscount, QuantLib::DiscountFactor dividendDiscount, QuantLib::Real variance, QuantLib::Real tolerance=1e-6)
 

Private Attributes

QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process_
 

Detailed Description

Barone-Adesi and Whaley pricing engine for American options (1987) This QuantExt class is a copy of the class with the same name in QuantLib v1.14 with the following change

Definition at line 58 of file baroneadesiwhaleyengine.hpp.

Constructor & Destructor Documentation

◆ BaroneAdesiWhaleyApproximationEngine()

BaroneAdesiWhaleyApproximationEngine ( const QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > &  )

Definition at line 47 of file baroneadesiwhaleyengine.cpp.

49 : process_(process) {
50 registerWith(process_);
51}
QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process_

Member Function Documentation

◆ criticalPrice()

Real criticalPrice ( const QuantLib::ext::shared_ptr< QuantLib::StrikedTypePayoff > &  payoff,
QuantLib::DiscountFactor  riskFreeDiscount,
QuantLib::DiscountFactor  dividendDiscount,
QuantLib::Real  variance,
QuantLib::Real  tolerance = 1e-6 
)
static

Definition at line 54 of file baroneadesiwhaleyengine.cpp.

57 {
58
59 // Calculation of seed value, Si
60 Real n = 2.0 * std::log(dividendDiscount / riskFreeDiscount) / (variance);
61 Real m = -2.0 * std::log(riskFreeDiscount) / (variance);
62 Real bT = std::log(dividendDiscount / riskFreeDiscount);
63
64 Real qu, Su, h, Si;
65 switch (payoff->optionType()) {
66 case Option::Call:
67 qu = (-(n - 1.0) + std::sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
68 Su = payoff->strike() / (1.0 - 1.0 / qu);
69 h = -(bT + 2.0 * std::sqrt(variance)) * payoff->strike() / (Su - payoff->strike());
70 Si = payoff->strike() + (Su - payoff->strike()) * (1.0 - std::exp(h));
71 break;
72 case Option::Put:
73 qu = (-(n - 1.0) - std::sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
74 Su = payoff->strike() / (1.0 - 1.0 / qu);
75 h = (bT - 2.0 * std::sqrt(variance)) * payoff->strike() / (payoff->strike() - Su);
76 Si = Su + (payoff->strike() - Su) * std::exp(h);
77 break;
78 default:
79 QL_FAIL("unknown option type");
80 }
81
82 Real maxIterations = 100;
83 // Newton Raphson algorithm for finding critical price Si
84 Real Q, LHS, RHS, bi;
85 Real forwardSi = Si * dividendDiscount / riskFreeDiscount;
86 Real d1 = (std::log(forwardSi / payoff->strike()) + 0.5 * variance) / std::sqrt(variance);
87 CumulativeNormalDistribution cumNormalDist;
88 Real K = (!close(riskFreeDiscount, 1.0, 1000))
89 ? -2.0 * std::log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount))
90 : 2.0 / variance;
91 Real temp = blackFormula(payoff->optionType(), payoff->strike(), forwardSi, std::sqrt(variance)) * riskFreeDiscount;
92 Real i;
93 switch (payoff->optionType()) {
94 case Option::Call:
95 Q = (-(n - 1.0) + std::sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2;
96 LHS = Si - payoff->strike();
97 RHS = temp + (1 - dividendDiscount * cumNormalDist(d1)) * Si / Q;
98 bi = dividendDiscount * cumNormalDist(d1) * (1 - 1 / Q) +
99 (1 - dividendDiscount * cumNormalDist.derivative(d1) / std::sqrt(variance)) / Q;
100 i = 0;
101 while ((std::fabs(LHS - RHS) / payoff->strike()) > tolerance) {
102 if (i > maxIterations)
103 QL_FAIL("Failed to find critical price after " << maxIterations << "iterations.");
104 Si = (payoff->strike() + RHS - bi * Si) / (1 - bi);
105 forwardSi = Si * dividendDiscount / riskFreeDiscount;
106 d1 = (std::log(forwardSi / payoff->strike()) + 0.5 * variance) / std::sqrt(variance);
107 LHS = Si - payoff->strike();
108 Real temp2 =
109 blackFormula(payoff->optionType(), payoff->strike(), forwardSi, std::sqrt(variance)) * riskFreeDiscount;
110 RHS = temp2 + (1 - dividendDiscount * cumNormalDist(d1)) * Si / Q;
111 bi = dividendDiscount * cumNormalDist(d1) * (1 - 1 / Q) +
112 (1 - dividendDiscount * cumNormalDist.derivative(d1) / std::sqrt(variance)) / Q;
113 i++;
114 }
115 break;
116 case Option::Put:
117 Q = (-(n - 1.0) - std::sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2;
118 LHS = payoff->strike() - Si;
119 RHS = temp - (1 - dividendDiscount * cumNormalDist(-d1)) * Si / Q;
120 bi = -dividendDiscount * cumNormalDist(-d1) * (1 - 1 / Q) -
121 (1 + dividendDiscount * cumNormalDist.derivative(-d1) / std::sqrt(variance)) / Q;
122 i = 0;
123 while ((std::fabs(LHS - RHS) / payoff->strike()) > tolerance) {
124 if (i > maxIterations)
125 QL_FAIL("Failed to find critical price after " << maxIterations << "iterations.");
126 Si = (payoff->strike() - RHS + bi * Si) / (1 + bi);
127 forwardSi = Si * dividendDiscount / riskFreeDiscount;
128 d1 = (std::log(forwardSi / payoff->strike()) + 0.5 * variance) / std::sqrt(variance);
129 LHS = payoff->strike() - Si;
130 Real temp2 =
131 blackFormula(payoff->optionType(), payoff->strike(), forwardSi, std::sqrt(variance)) * riskFreeDiscount;
132 RHS = temp2 - (1 - dividendDiscount * cumNormalDist(-d1)) * Si / Q;
133 bi = -dividendDiscount * cumNormalDist(-d1) * (1 - 1 / Q) -
134 (1 + dividendDiscount * cumNormalDist.derivative(-d1) / std::sqrt(variance)) / Q;
135 i++;
136 }
137 break;
138 default:
139 QL_FAIL("unknown option type");
140 }
141
142 return Si;
143}
RandomVariable variance(const RandomVariable &r)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculate()

void calculate ( ) const
override

Definition at line 145 of file baroneadesiwhaleyengine.cpp.

145 {
146
147 QL_REQUIRE(arguments_.exercise->type() == Exercise::American, "not an American Option");
148
149 ext::shared_ptr<AmericanExercise> ex = ext::dynamic_pointer_cast<AmericanExercise>(arguments_.exercise);
150 QL_REQUIRE(ex, "non-American exercise given");
151 QL_REQUIRE(!ex->payoffAtExpiry(), "payoff at expiry not handled");
152
153 ext::shared_ptr<StrikedTypePayoff> payoff = ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
154 QL_REQUIRE(payoff, "non-striked payoff given");
155
156 Real variance = process_->blackVolatility()->blackVariance(ex->lastDate(), payoff->strike());
157 DiscountFactor dividendDiscount = process_->dividendYield()->discount(ex->lastDate());
158 DiscountFactor riskFreeDiscount = process_->riskFreeRate()->discount(ex->lastDate());
159 Real spot = process_->stateVariable()->value();
160 QL_REQUIRE(spot > 0.0, "negative or null underlying given");
161 Real forwardPrice = spot * dividendDiscount / riskFreeDiscount;
162 BlackCalculator black(payoff, forwardPrice, std::sqrt(variance), riskFreeDiscount);
163 bool useEuropeanPrice = (payoff->optionType() == Option::Call &&
164 ((dividendDiscount >= 1.0 && riskFreeDiscount < 1.0) ||
165 (riskFreeDiscount >= 1.0 && (dividendDiscount / riskFreeDiscount) >= 1.0))) ||
166 (dividendDiscount < 1.0 && riskFreeDiscount >= 1.0 && payoff->optionType() == Option::Put);
167
168 Real tolerance = 1e-6;
169 Real Sk = 0.0;
170 // try to determine the critical price for the american option
171 // in some case, for example r < 0, q < 0 it is never optimal to exercise early
172 // and we cannot solve for critical price, the newton raphson solver diverges
173 // with Si turning negative
174 try {
175 Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance);
176 } catch (...) {
177 // failed to calculate the critical price
178 useEuropeanPrice = true;
179 }
180
181 if (useEuropeanPrice) {
182 // early exercise never optimal
183 results_.value = black.value();
184 results_.delta = black.delta(spot);
185 results_.deltaForward = black.deltaForward();
186 results_.elasticity = black.elasticity(spot);
187 results_.gamma = black.gamma(spot);
188
189 DayCounter rfdc = process_->riskFreeRate()->dayCounter();
190 DayCounter divdc = process_->dividendYield()->dayCounter();
191 DayCounter voldc = process_->blackVolatility()->dayCounter();
192 Time t = rfdc.yearFraction(process_->riskFreeRate()->referenceDate(), arguments_.exercise->lastDate());
193 results_.rho = black.rho(t);
194
195 t = divdc.yearFraction(process_->dividendYield()->referenceDate(), arguments_.exercise->lastDate());
196 results_.dividendRho = black.dividendRho(t);
197
198 t = voldc.yearFraction(process_->blackVolatility()->referenceDate(), arguments_.exercise->lastDate());
199 results_.vega = black.vega(t);
200 results_.theta = black.theta(spot, t);
201 results_.thetaPerDay = black.thetaPerDay(spot, t);
202
203 results_.strikeSensitivity = black.strikeSensitivity();
204 results_.itmCashProbability = black.itmCashProbability();
205 } else {
206 // early exercise can be optimal
207 CumulativeNormalDistribution cumNormalDist;
208 Real forwardSk = Sk * dividendDiscount / riskFreeDiscount;
209 Real d1 = (std::log(forwardSk / payoff->strike()) + 0.5 * variance) / std::sqrt(variance);
210 Real n = 2.0 * std::log(dividendDiscount / riskFreeDiscount) / variance;
211 Real K = (!close(riskFreeDiscount, 1.0, 1000))
212 ? -2.0 * std::log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount))
213 : 2.0 / variance;
214 Real Q, a;
215 switch (payoff->optionType()) {
216 case Option::Call:
217 Q = (-(n - 1.0) + std::sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0;
218 a = (Sk / Q) * (1.0 - dividendDiscount * cumNormalDist(d1));
219 if (spot < Sk) {
220 results_.value = black.value() + a * std::pow((spot / Sk), Q);
221 } else {
222 results_.value = spot - payoff->strike();
223 }
224 break;
225 case Option::Put:
226 Q = (-(n - 1.0) - std::sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0;
227 a = -(Sk / Q) * (1.0 - dividendDiscount * cumNormalDist(-d1));
228 if (spot > Sk) {
229 results_.value = black.value() + a * std::pow((spot / Sk), Q);
230 } else {
231 results_.value = payoff->strike() - spot;
232 }
233 break;
234 default:
235 QL_FAIL("unknown option type");
236 }
237 } // end of "early exercise can be optimal"
238}
const Instrument::results * results_
Definition: cdsoption.cpp:81
static QuantLib::Real criticalPrice(const QuantLib::ext::shared_ptr< QuantLib::StrikedTypePayoff > &payoff, QuantLib::DiscountFactor riskFreeDiscount, QuantLib::DiscountFactor dividendDiscount, QuantLib::Real variance, QuantLib::Real tolerance=1e-6)
RandomVariable black(const RandomVariable &omega, const RandomVariable &t, const RandomVariable &strike, const RandomVariable &forward, const RandomVariable &impliedVol)
Swap::arguments * arguments_
+ Here is the call graph for this function:

Member Data Documentation

◆ process_

QuantLib::ext::shared_ptr<QuantLib::GeneralizedBlackScholesProcess> process_
private

Definition at line 68 of file baroneadesiwhaleyengine.hpp.