35#ifndef QL_BOOST_HAS_TANH_SINH
41 class QdPlusBoundaryEvaluator {
43 QdPlusBoundaryEvaluator(
46 q(dy), dr(
std::exp(-
r * tau)), dq(
std::exp(-
q * tau)),
47 ddr((
std::abs(
r*tau) > 1e-5)?
Real(
r/(1-dr)) :
Real(1/(tau*(1-0.5*
r*tau*(1-
r*tau/3))))),
48 omega(2 * (
r -
q) / sigma2),
50 (-(omega - 1) -
std::sqrt(
squared(omega - 1) + 8 * ddr / sigma2))),
51 lambdaPrime(2 * ddr*ddr /
52 (sigma2 *
std::sqrt(
squared(omega - 1) + 8 * ddr / sigma2))),
53 alpha(2 * dr / (sigma2 * (2 * lambda + omega - 1))),
54 beta(
alpha * (ddr + lambdaPrime / (2 * lambda + omega - 1)) - lambda),
55 xMax(QdPlusAmericanEngine::xMax(strike,
r,
q)),
71 return (1-dq*Phi_dp)*
S + (lambda+c0)*(K-
S-npv);
78 return 1 - dq*Phi_dp + dq/
v*phi_dp +
beta*(1-dq*Phi_dp)
85 const Real gamma = phi_dp*dq/(
v*
S);
86 const Real colour = gamma*(
q + (
r-
q)*dp/v + (1-dp*dm)/(2*tau));
88 return dq*(phi_dp/(
S*
v) - phi_dp*dp/(
S*v*v))
89 + beta*gamma + alpha/dr*colour;
92 Real xmin()
const {
return xMin; }
93 Real xmax()
const {
return xMax; }
94 Size evaluations()
const {
return nrEvaluations; }
97 void preCalculate(
Real S)
const {
100 dp = std::log(
S*dq/(K*dr))/
v + 0.5*
v;
106 npv = dr*K*Phi_dm -
S*dq*Phi_dp;
107 theta =
r*K*dr*Phi_dm -
q*
S*dq*Phi_dp - sigma2*
S/(2*
v)*dq*phi_dp;
108 charm = -dq*(phi_dp*((
r-
q)/v - dm/(2*tau)) +q*Phi_dp);
111 const CumulativeNormalDistribution Phi;
112 const NormalDistribution phi;
118 const Real omega, lambda, lambdaPrime,
alpha,
beta, xMax, xMin;
119 mutable Size nrEvaluations = 0;
120 mutable Real sc, dp, dm, Phi_dp, Phi_dm, phi_dp;
127 const Real xmax, ext::shared_ptr<Interpolation> q_z)
128 :
T_(
T), S_(
S), K_(K), xmax_(xmax),
129 r_(
r), q_(
q),
vol_(vol), q_z_(
std::move(q_z)) {}
134 const Real q = (*q_z_)(2*std::sqrt(std::max(0.0,
T_-
t)/
T_) - 1,
true);
135 const Real b_t = xmax_*std::exp(-std::sqrt(std::max(0.0,
q)));
137 const Real dr = std::exp(-r_*
t);
138 const Real dq = std::exp(-q_*
t);
144 const Real dp = std::log(S_*dq/(b_t*dr))/
v + 0.5*
v;
145 r = 2*z*(r_*K_*dr*Phi_(-dp+
v) - q_*S_*dq*Phi_(-dp));
151 r = z*(r_*K_*dr - q_*S_*dq);
152 else if (b_t*dr > S_*dq)
153 r = 2*z*(r_*K_*dr - q_*S_*dq);
162 ext::shared_ptr<GeneralizedBlackScholesProcess> process)
163 : process_(
std::move(process)) {
169 "not an American option");
172 ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
175 const Real spot = process_->x0();
176 QL_REQUIRE(spot >= 0.0,
"negative underlying given");
178 const auto maturity = arguments_.exercise->lastDate();
179 const Time T = process_->time(maturity);
180 const Real S = process_->x0();
182 const Rate r = -std::log(process_->riskFreeRate()->discount(maturity))/
T;
183 const Rate q = -std::log(process_->dividendYield()->discount(maturity))/
T;
184 const Volatility vol = process_->blackVolatility()->blackVol(
T, K);
186 QL_REQUIRE(
S >= 0,
"zero or positive underlying value is required");
187 QL_REQUIRE(K >= 0,
"zero or positive strike is required");
188 QL_REQUIRE(vol >= 0,
"zero or positive volatility is required");
191 results_.value = calculatePutWithEdgeCases(
S, K,
r,
q, vol,
T);
193 results_.value = calculatePutWithEdgeCases(K,
S,
q,
r, vol,
T);
195 QL_FAIL(
"unknown option type");
205 return std::max(K, K*std::exp(-
r*
T));
207 if (
r <= 0.0 &&
r <=
q)
210 vol*std::sqrt(
T), std::exp(-
r*
T)).value());
212 if (
close(vol, 0.0)) {
213 const auto intrinsic = [&](
Real t) ->
Real {
214 return std::max(0.0, K*std::exp(-
r*
t)-
S*std::exp(-
q*
t));
216 const Real npv0 = intrinsic(0.0);
217 const Real npvT = intrinsic(
T);
221 if (extremT > 0.0 && extremT <
T)
222 return std::max({npv0, npvT, intrinsic(extremT)});
224 return std::max(npv0, npvT);
227 return calculatePut(
S, K,
r,
q, vol,
T);
235 if (
r > 0.0 &&
q > 0.0)
236 return K*std::min(1.0,
r/
q);
237 else if (
r > 0.0 &&
q <= 0.0)
239 else if (
r == 0.0 &&
q < 0.0)
241 else if (
r == 0.0 &&
q >= 0.0)
243 else if (r < 0.0 && q >= 0.0)
245 else if (
r < 0.0 &&
q <
r)
247 else if (
r < 0.0 &&
r <=
q &&
q < 0.0)
254 ext::shared_ptr<GeneralizedBlackScholesProcess> process,
255 Size interpolationPoints,
258 : detail::QdPutCallParityEngine(
std::move(process)),
259 interpolationPoints_(interpolationPoints),
260 solverType_(solverType),
262 maxIter_((maxIter ==
Null<
Size>()) ? (
264 || solverType ==
Brent || solverType==
Ridder)? 100 : 10)
267 template <
class Solver>
269 const QdPlusBoundaryEvaluator& eval,
272 solver.setMaxEvaluations(maxIter);
273 solver.setLowerBound(eval.xmin());
275 const Real fxmin = eval(eval.xmin());
276 Real xmax = std::max(0.5*(eval.xmax() +
S), eval.xmax());
277 while (eval(xmax)*fxmin > 0.0 && eval.evaluations() <
maxIter_)
281 guess = 0.5*(xmax +
S);
284 guess = std::nextafter(xmax,
Real(-1));
285 else if (guess <= eval.xmin())
288 return solver.solve(eval,
eps_, guess, eval.xmin(), xmax);
296 return std::pair<Size, Real>(
299 const QdPlusBoundaryEvaluator eval(
S, K,
r,
q, vol, tau,
T);
315 bool resultCloseEnough;
318 const Real xmin = eval.xmin();
323 const Real fPrime = eval.derivative(x);
324 const Real lf = fx*eval.fprime2(x)/(fPrime*fPrime);
326 ?
Real(1/(1 - 0.5*lf)*fx/fPrime)
327 :
Real((1 + 0.5*lf/(1-lf))*fx/fPrime);
329 x = std::max(xmin, x - step);
330 resultCloseEnough = std::fabs(x-xOld) < 0.5*
eps_;
332 while (!resultCloseEnough && eval.evaluations() <
maxIter_);
334 if (!resultCloseEnough && !
close(std::fabs(fx), 0.0)) {
340 QL_FAIL(
"unknown solver type");
343 return std::pair<Size, Real>(eval.evaluations(), x);
346 ext::shared_ptr<ChebyshevInterpolation>
352 return ext::make_shared<ChebyshevInterpolation>(
367 if (
r < 0.0 &&
q <
r)
368 QL_FAIL(
"double-boundary case q<r<0 for a put option is given");
370 const ext::shared_ptr<Interpolation> q_z
376#ifdef QL_BOOST_HAS_TANH_SINH
380 (aov, 0.0, std::sqrt(
T));
384 "negative early exercise value " << addOn);
386 const Real europeanValue = std::max(
391 vol*std::sqrt(
T), std::exp(-
r*
T)).value()
394 return europeanValue + std::max(0.0, addOn);
Black-formula calculator class.
ext::shared_ptr< SimpleQuote > vol_
const Instrument::results * results_
chebyshev interpolation between discrete Chebyshev nodes
Black 1976 calculator class.
Integral of a one-dimensional function.
template class providing a null value for a given type.
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
ext::shared_ptr< ChebyshevInterpolation > getPutExerciseBoundary(Real S, Real K, Rate r, Rate q, Volatility vol, Time T) const
static Real xMax(Real K, Rate r, Rate q)
const SolverType solverType_
const Size interpolationPoints_
std::pair< Size, Real > putExerciseBoundaryAtTau(Real S, Real K, Rate r, Rate q, Volatility vol, Time T, Time tau) const
Real calculatePut(Real S, Real K, Rate r, Rate q, Volatility vol, Time T) const override
QdPlusAmericanEngine(ext::shared_ptr< GeneralizedBlackScholesProcess >, Size interpolationPoints=8, SolverType solverType=Halley, Real eps=1e-6, Size maxIter=Null< Size >())
Real buildInSolver(const QdPlusBoundaryEvaluator &eval, Solver solver, Real S, Real strike, Size maxIter, Real guess=Null< Real >()) const
Real operator()(Real z) const
QdPlusAddOnValue(Time T, Real S, Real K, Rate r, Rate q, Volatility vol, Real xmax, ext::shared_ptr< Interpolation > q_z)
void calculate() const override
QdPutCallParityEngine(ext::shared_ptr< GeneralizedBlackScholesProcess > process)
const ext::shared_ptr< GeneralizedBlackScholesProcess > process_
Real calculatePutWithEdgeCases(Real S, Real K, Rate r, Rate q, Volatility vol, Time T) const
floating-point comparisons
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Option exercise classes and payoff function.
integral of a one-dimensional function using the adaptive Gauss-Lobatto integral
Real Time
continuous quantity with 1-year units
Real DiscountFactor
discount factor between dates
Real Volatility
volatility
std::size_t Size
size of a container
ext::shared_ptr< QuantLib::Payoff > payoff
functionals and combinators not included in the STL
bool close(const Quantity &m1, const Quantity &m2, Size n)
bool close_enough(const Quantity &m1, const Quantity &m2, Size n)
ext::shared_ptr< YieldTermStructure > q
ext::shared_ptr< YieldTermStructure > r
ext::shared_ptr< BlackVolTermStructure > v