34#include <boost/math/special_functions/fpclassify.hpp>
35#include <boost/math/special_functions/atanh.hpp>
36#include <boost/math/special_functions/sign.hpp>
43 QL_REQUIRE(displacement >= 0.0,
"displacement ("
45 <<
") must be non-negative");
47 "strike + displacement (" << strike <<
" + " << displacement
48 <<
") must be non-negative");
49 QL_REQUIRE(forward + displacement > 0.0,
"forward + displacement ("
52 <<
") must be positive");
65 checkParameters(strike, forward, displacement);
67 "stdDev (" << stdDev <<
") must be non-negative");
69 "discount (" << discount <<
") must be positive");
71 auto sign =
Integer(optionType);
74 return std::max((forward-strike) * sign,
Real(0.0)) * discount;
76 forward = forward + displacement;
77 strike = strike + displacement;
84 Real d1 = std::log(forward/strike)/stdDev + 0.5*stdDev;
85 Real d2 = d1 - stdDev;
87 Real nd1 = phi(sign * d1);
88 Real nd2 = phi(sign * d2);
89 Real result = discount * sign * (forward*nd1 - strike*nd2);
91 "negative value (" << result <<
") for " <<
92 stdDev <<
" stdDev, " <<
93 optionType <<
" option, " <<
94 strike <<
" strike , " <<
95 forward <<
" forward");
105 payoff->strike(), forward, stdDev, discount, displacement);
115 checkParameters(strike, forward, displacement);
117 "stdDev (" << stdDev <<
") must be non-negative");
119 "discount (" << discount <<
") must be positive");
121 auto sign =
Integer(optionType);
124 return sign * std::max(1.0 * boost::math::sign((forward - strike) * sign), 0.0) * discount;
126 forward = forward + displacement;
127 strike = strike + displacement;
132 Real d1 = std::log(forward/strike)/stdDev + 0.5*stdDev;
134 return sign * phi(sign * d1) * discount;
144 payoff->strike(), forward, stdDev, discount, displacement);
154 checkParameters(strike, forward, displacement);
156 "blackPrice (" << blackPrice <<
") must be non-negative");
158 "discount (" << discount <<
") must be positive");
161 forward = forward + displacement;
162 strike = strike + displacement;
165 stdDev = blackPrice/discount*std::sqrt(2.0 *
M_PI)/forward;
168 Real moneynessDelta =
Integer(optionType) * (forward-strike);
169 Real moneynessDelta_2 = moneynessDelta/2.0;
170 Real temp = blackPrice/discount - moneynessDelta_2;
171 Real moneynessDelta_PI = moneynessDelta*moneynessDelta/
M_PI;
172 Real temp2 = temp*temp-moneynessDelta_PI;
178 temp2 = std::sqrt(temp2);
180 temp *= std::sqrt(2.0 *
M_PI);
181 stdDev = temp/(forward+strike);
184 "stdDev (" << stdDev <<
") must be non-negative");
189 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
195 payoff->strike(), forward, blackPrice, discount, displacement);
205 checkParameters(strike, forward, displacement);
207 "blackPrice (" << blackPrice <<
") must be non-negative");
208 QL_REQUIRE(blackAtmPrice >= 0.0,
"blackAtmPrice ("
210 <<
") must be non-negative");
211 QL_REQUIRE(discount > 0.0,
"discount (" << discount
212 <<
") must be positive");
216 forward = forward + displacement;
217 strike = strike + displacement;
218 blackPrice /= discount;
219 blackAtmPrice /= discount;
224 blackFormula(optionType, strike, forward, s0, 1.0, 0.0);
225 Real dc = blackPrice - priceAtmVol;
227 if (
close(dc, 0.0)) {
235 Real tmp = d1 * d1 + 2.0 * d2 * dc;
236 if (std::fabs(d2) > 1E-10 && tmp >= 0.0)
237 ds = (-d1 + std::sqrt(tmp)) / d2;
239 if(std::fabs(d1) > 1E-10)
244 QL_ENSURE(stdDev >= 0.0,
"stdDev (" << stdDev
245 <<
") must be non-negative");
250 const ext::shared_ptr<PlainVanillaPayoff> &
payoff,
257 payoff->optionType(),
payoff->strike(), forward, blackPrice,
258 blackAtmPrice, discount, displacement);
263 return 0.5*(1.0+boost::math::sign(x)
264 *std::sqrt(1.0-std::exp(-
M_2_PI*x*x)));
272 checkParameters(K,
F, displacement);
274 "blackPrice (" << marketValue <<
") must be non-negative");
275 QL_REQUIRE(df > 0.0,
"discount (" << df <<
") must be positive");
277 F =
F + displacement;
278 K = K + displacement;
281 const Real ey2 = ey*ey;
282 const Real y = std::log(ey);
290 const Real B = 4.0*(
b + 1/
b)
291 - 2*K/
F*(a + 1.0/a)*(ey2 + 1 - R2);
294 const Real beta = 2*C/(B+std::sqrt(B*B+4*A*C));
298 const Real M0 = K*df*(
300 : 0.5-ey*Af(-std::sqrt(2*
y)));
302 if (marketValue <= M0)
303 return std::sqrt(gamma+
y)-std::sqrt(gamma-
y);
305 return std::sqrt(gamma+
y)+std::sqrt(gamma-
y);
308 const Real M0 = K*df*(
310 : Af(std::sqrt(-2*
y)) - 0.5*ey);
312 if (marketValue <= M0)
313 return std::sqrt(gamma-
y)-std::sqrt(gamma+
y);
315 return std::sqrt(gamma+
y)+std::sqrt(gamma-
y);
320 const ext::shared_ptr<PlainVanillaPayoff> &
payoff,
326 F, marketValue, df, displacement);
329 class BlackImpliedStdDevHelper {
334 Real undiscountedBlackPrice,
335 Real displacement = 0.0)
336 : halfOptionType_(0.5 *
Integer(optionType)),
337 signedStrike_(
Integer(optionType) * (strike+displacement)),
338 signedForward_(
Integer(optionType) * (forward+displacement)),
339 undiscountedBlackPrice_(undiscountedBlackPrice)
341 checkParameters(strike, forward, displacement);
343 "undiscounted Black price (" <<
344 undiscountedBlackPrice <<
") must be non-negative");
345 signedMoneyness_ =
Integer(optionType) * std::log((forward+displacement)/(strike+displacement));
348 Real operator()(
Real stdDev)
const {
349 #if defined(QL_EXTRA_SAFETY_CHECKS)
351 "stdDev (" << stdDev <<
") must be non-negative");
354 return std::max(signedForward_-signedStrike_,
Real(0.0))
355 - undiscountedBlackPrice_;
356 Real temp = halfOptionType_*stdDev;
357 Real d = signedMoneyness_/stdDev;
358 Real signedD1 =
d + temp;
359 Real signedD2 =
d - temp;
360 Real result = signedForward_ * N_(signedD1)
361 - signedStrike_ * N_(signedD2);
363 return std::max(
Real(0.0), result) - undiscountedBlackPrice_;
365 Real derivative(
Real stdDev)
const {
366 #if defined(QL_EXTRA_SAFETY_CHECKS)
368 "stdDev (" << stdDev <<
") must be non-negative");
370 Real signedD1 = signedMoneyness_/stdDev + halfOptionType_*stdDev;
371 return signedForward_*N_.derivative(signedD1);
374 Real halfOptionType_;
375 Real signedStrike_, signedForward_;
376 Real undiscountedBlackPrice_, signedMoneyness_;
377 CumulativeNormalDistribution N_;
391 checkParameters(strike, forward, displacement);
394 "discount (" << discount <<
") must be positive");
397 "option price (" << blackPrice <<
") must be non-negative");
399 Real otherOptionPrice = blackPrice -
Integer(optionType) * (forward-strike)*discount;
402 " price (" << otherOptionPrice <<
403 ") implied by put-call parity. No solution exists for " <<
404 optionType <<
" strike " << strike <<
405 ", forward " << forward <<
406 ", price " << blackPrice <<
407 ", deflator " << discount);
414 blackPrice = otherOptionPrice;
418 blackPrice = otherOptionPrice;
421 strike = strike + displacement;
422 forward = forward + displacement;
426 optionType, strike, forward, blackPrice, discount, displacement);
429 "stdDev guess (" << guess <<
") must be non-negative");
430 BlackImpliedStdDevHelper
f(optionType, strike, forward,
431 blackPrice/discount);
434 Real minSdtDev = 0.0, maxStdDev = 24.0;
435 Real stdDev = solver.
solve(
f, accuracy, guess, minSdtDev, maxStdDev);
437 "stdDev (" << stdDev <<
") must be non-negative");
442 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
451 forward, blackPrice, discount, displacement, guess, accuracy, maxIterations);
457 return CumulativeNormalDistribution()(x/
v + 0.5*
v);
460 return std::exp(-x)*CumulativeNormalDistribution()(x/
v - 0.5*
v);
463 const Real ax = 2*std::fabs(x);
465 return (v2-ax)/(v2+ax);
468 return cs+Nm(x,
v)+w*Np(x,
v);
471 const Real q =
F(
v,x,cs,w)/(1+w);
476 const Real k = MaddockInverseCumulativeNormal()(
q);
478 return k + std::sqrt(k*k + 2*std::fabs(x));
495 "discount (" << discount <<
") must be positive");
498 "option price (" << blackPrice <<
") must be non-negative");
500 strike = strike + displacement;
501 forward = forward + displacement;
505 optionType, strike, forward,
506 blackPrice, discount, displacement);
510 "stdDev guess (" << guess <<
") must be non-negative");
513 Real x = std::log(forward/strike);
515 ?
Real(blackPrice / (forward*discount))
516 : (blackPrice/ (forward*discount) + 1.0 - strike/forward);
518 QL_REQUIRE(cs >= 0.0,
"normalized call price (" << cs
519 <<
") must be positive");
523 cs = forward/strike*cs + 1.0 - forward/strike;
524 QL_REQUIRE(cs >= 0.0,
"negative option price from in-out duality");
529 Real dv, vk, vkp1 = guess;
533 const Real alphaK = (1+w)/(1+phi(x,vk));
534 vkp1 = alphaK*G(vk,x,cs,w) + (1-alphaK)*vk;
535 dv = std::fabs(vkp1 - vk);
536 }
while (dv > accuracy && ++nIter < maxIterations);
538 QL_REQUIRE(dv <= accuracy,
"max iterations exceeded");
539 QL_REQUIRE(vk >= 0.0,
"stdDev (" << vk <<
") must be non-negative");
545 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
557 forward, blackPrice, discount, displacement,
558 guess, omega, accuracy, maxIterations);
567 checkParameters(strike, forward, displacement);
569 auto sign =
Integer(optionType);
572 return (forward * sign > strike * sign ? 1.0 : 0.0);
574 forward = forward + displacement;
575 strike = strike + displacement;
578 Real d2 = std::log(forward/strike)/stdDev - 0.5*stdDev;
580 return phi(sign * d2);
584 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
589 payoff->strike(), forward, stdDev , displacement);
598 checkParameters(strike, forward, displacement);
600 auto sign =
Integer(optionType);
603 return (forward * sign < strike * sign ? 1.0 : 0.0);
605 forward = forward + displacement;
606 strike = strike + displacement;
609 Real d1 = std::log(forward/strike)/stdDev + 0.5*stdDev;
611 return phi(sign * d1);
615 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
620 payoff->strike(), forward, stdDev , displacement);
634 displacement)*std::sqrt(expiry);
643 checkParameters(strike, forward, displacement);
645 "stdDev (" << stdDev <<
") must be non-negative");
647 "discount (" << discount <<
") must be positive");
649 forward = forward + displacement;
650 strike = strike + displacement;
652 if (stdDev==0.0 || strike==0.0)
655 Real d1 = std::log(forward/strike)/stdDev + .5*stdDev;
656 return discount * forward *
661 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
667 stdDev, discount, displacement);
676 checkParameters(strike, forward, displacement);
678 "stdDev (" << stdDev <<
") must be non-negative");
680 "discount (" << discount <<
") must be positive");
682 forward = forward + displacement;
683 strike = strike + displacement;
685 if (stdDev==0.0 || strike==0.0)
688 Real d1 = std::log(forward/strike)/stdDev + .5*stdDev;
689 Real d1p = -std::log(forward/strike)/(stdDev*stdDev) + .5;
690 return discount * forward *
695 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
701 stdDev, discount, displacement);
711 "stdDev (" << stdDev <<
") must be non-negative");
713 "discount (" << discount <<
") must be positive");
714 Real d = (forward-strike) *
Integer(optionType), h =
d / stdDev;
716 return discount*std::max(
d, 0.0);
720 "negative value (" << result <<
") for " <<
721 stdDev <<
" stdDev, " <<
722 optionType <<
" option, " <<
723 strike <<
" strike , " <<
724 forward <<
" forward");
729 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
734 payoff->strike(), forward, stdDev, discount);
741 "stdDev (" << stdDev <<
") must be non-negative");
743 "discount (" << discount <<
") must be positive");
744 auto sign =
Integer(optionType);
746 return sign * std::max(1.0 * boost::math::sign((forward - strike) * sign), 0.0) * discount;
747 Real d = (forward - strike) * sign, h =
d / stdDev;
749 return sign * phi(h) * discount;
753 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
759 payoff->strike(), forward, stdDev, discount);
764 const static Real A0 = 3.994961687345134e-1;
765 const static Real A1 = 2.100960795068497e+1;
766 const static Real A2 = 4.980340217855084e+1;
767 const static Real A3 = 5.988761102690991e+2;
768 const static Real A4 = 1.848489695437094e+3;
769 const static Real A5 = 6.106322407867059e+3;
770 const static Real A6 = 2.493415285349361e+4;
771 const static Real A7 = 1.266458051348246e+4;
773 const static Real B0 = 1.000000000000000e+0;
774 const static Real B1 = 4.990534153589422e+1;
775 const static Real B2 = 3.093573936743112e+1;
776 const static Real B3 = 1.495105008310999e+3;
777 const static Real B4 = 1.323614537899738e+3;
778 const static Real B5 = 1.598919697679745e+4;
779 const static Real B6 = 2.392008891720782e+4;
780 const static Real B7 = 3.608817108375034e+3;
781 const static Real B8 = -2.067719486400926e+2;
782 const static Real B9 = 1.174240599306013e+1;
785 "eta (" << eta <<
") must be non-negative");
787 const Real num = A0 + eta * (A1 + eta * (A2 + eta * (A3 + eta * (A4 + eta
788 * (A5 + eta * (A6 + eta * A7))))));
790 const Real den = B0 + eta * (B1 + eta * (B2 + eta * (B3 + eta * (B4 + eta
791 * (B5 + eta * (B6 + eta * (B7 + eta * (B8 + eta * B9))))))));
793 return std::sqrt(eta) * (num / den);
807 "tte (" << tte <<
") must be positive");
809 Real forwardPremium = bachelierPrice/discount;
811 Real straddlePremium;
813 straddlePremium = 2.0 * forwardPremium - (forward - strike);
815 straddlePremium = 2.0 * forwardPremium + (forward - strike);
818 Real nu = (forward - strike) / straddlePremium;
820 "nu (" <<
nu <<
") must be <= 1.0");
822 "nu (" <<
nu <<
") must be >= -1.0");
827 Real eta = (std::fabs(
nu) < SQRT_QL_EPSILON) ? 1.0 :
Real(
nu / boost::math::atanh(
nu));
831 Real impliedBpvol = std::sqrt(
M_PI / (2 * tte)) * straddlePremium * heta;
843 "stdDev (" << stdDev <<
") must be non-negative");
845 "discount (" << discount <<
") must be positive");
850 Real d1 = (forward - strike)/stdDev;
856 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
870 "stdDev (" << stdDev <<
") must be non-negative");
871 Real d = (forward - strike) *
Integer(optionType), h =
d / stdDev;
873 return std::max(
d, 0.0);
875 Real result = phi(h);
880 const ext::shared_ptr<PlainVanillaPayoff>&
payoff,
884 payoff->strike(), forward, stdDev);
Cumulative normal distribution function.
Real derivative(Real x) const
Normal distribution function.
Real derivative(Real x) const
template class providing a null value for a given type.
void setMaxEvaluations(Size evaluations)
Real solve(const F &f, Real accuracy, Real guess, Real step) const
#define QL_ENSURE(condition, message)
throw an error if the given post-condition is not verified
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
ext::function< Real(Real)> b
unsigned QL_INTEGER Natural
positive integer
QL_INTEGER Integer
integer number
std::size_t Size
size of a container
ext::shared_ptr< QuantLib::Payoff > payoff
functionals and combinators not included in the STL
Real blackFormulaAssetItmProbability(Option::Type optionType, Real strike, Real forward, Real stdDev, Real displacement)
Real blackFormulaCashItmProbability(Option::Type optionType, Real strike, Real forward, Real stdDev, Real displacement)
Real blackFormulaImpliedStdDevLiRS(Option::Type optionType, Real strike, Real forward, Real blackPrice, Real discount, Real displacement, Real guess, Real w, Real accuracy, Natural maxIterations)
Real blackFormulaImpliedStdDevChambers(Option::Type optionType, Real strike, Real forward, Real blackPrice, Real blackAtmPrice, Real discount, Real displacement)
Real bachelierBlackFormulaAssetItmProbability(Option::Type optionType, Real strike, Real forward, Real stdDev)
Real bachelierBlackFormulaStdDevDerivative(Rate strike, Rate forward, Real stdDev, Real discount)
Real bachelierBlackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount)
Real blackFormulaImpliedStdDev(Option::Type optionType, Real strike, Real forward, Real blackPrice, Real discount, Real displacement, Real guess, Real accuracy, Natural maxIterations)
bool close(const Quantity &m1, const Quantity &m2, Size n)
Real blackFormulaForwardDerivative(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount, Real displacement)
Real bachelierBlackFormulaForwardDerivative(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount)
Real blackFormulaVolDerivative(Rate strike, Rate forward, Real stdDev, Real expiry, Real discount, Real displacement)
Real blackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount, Real displacement)
bool close_enough(const Quantity &m1, const Quantity &m2, Size n)
Real bachelierBlackFormulaImpliedVol(Option::Type optionType, Real strike, Real forward, Real tte, Real bachelierPrice, Real discount)
Real blackFormulaStdDevSecondDerivative(Rate strike, Rate forward, Real stdDev, Real discount, Real displacement)
Real blackFormulaImpliedStdDevApproximation(Option::Type optionType, Real strike, Real forward, Real blackPrice, Real discount, Real displacement)
Real blackFormulaImpliedStdDevApproximationRS(Option::Type type, Real K, Real F, Real marketValue, Real df, Real displacement)
Real blackFormulaStdDevDerivative(Rate strike, Rate forward, Real stdDev, Real discount, Real displacement)
Safe (bracketed) Newton 1-D solver.
normal, cumulative and inverse cumulative distributions
ext::shared_ptr< YieldTermStructure > q
ext::shared_ptr< BlackVolTermStructure > v