36#include <ql/exercise.hpp>
37#include <ql/math/comparison.hpp>
38#include <ql/math/distributions/normaldistribution.hpp>
39#include <ql/pricingengines/blackcalculator.hpp>
40#include <ql/pricingengines/blackformula.hpp>
48 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process)
55 DiscountFactor riskFreeDiscount,
56 DiscountFactor dividendDiscount, Real
variance,
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);
65 switch (payoff->optionType()) {
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));
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);
79 QL_FAIL(
"unknown option type");
82 Real maxIterations = 100;
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))
91 Real temp = blackFormula(payoff->optionType(), payoff->strike(), forwardSi, std::sqrt(
variance)) * riskFreeDiscount;
93 switch (payoff->optionType()) {
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;
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();
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;
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;
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;
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;
139 QL_FAIL(
"unknown option type");
147 QL_REQUIRE(
arguments_.exercise->type() == Exercise::American,
"not an American Option");
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");
153 ext::shared_ptr<StrikedTypePayoff> payoff = ext::dynamic_pointer_cast<StrikedTypePayoff>(
arguments_.payoff);
154 QL_REQUIRE(payoff,
"non-striked payoff given");
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);
168 Real tolerance = 1e-6;
178 useEuropeanPrice =
true;
181 if (useEuropeanPrice) {
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());
195 t = divdc.yearFraction(
process_->dividendYield()->referenceDate(),
arguments_.exercise->lastDate());
198 t = voldc.yearFraction(
process_->blackVolatility()->referenceDate(),
arguments_.exercise->lastDate());
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))
215 switch (payoff->optionType()) {
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));
220 results_.value =
black.value() + a * std::pow((spot / Sk), Q);
222 results_.value = spot - payoff->strike();
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));
229 results_.value =
black.value() + a * std::pow((spot / Sk), Q);
231 results_.value = payoff->strike() - spot;
235 QL_FAIL(
"unknown option type");
Barone-Adesi and Whaley approximation engine.
const Instrument::results * results_
QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process_
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)
void calculate() const override
BaroneAdesiWhaleyApproximationEngine(const QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > &)
RandomVariable variance(const RandomVariable &r)
RandomVariable black(const RandomVariable &omega, const RandomVariable &t, const RandomVariable &strike, const RandomVariable &forward, const RandomVariable &impliedVol)
Swap::arguments * arguments_