30#include <ql/pricingengines/blackformula.hpp>
31#include <ql/math/functional.hpp>
32#include <ql/math/solvers1d/newtonsafe.hpp>
33#include <ql/math/distributions/normaldistribution.hpp>
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");
46 QL_REQUIRE(strike + displacement >= 0.0,
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);
66 QL_REQUIRE(stdDev>=0.0,
67 "stdDev (" << stdDev <<
") must be non-negative");
68 QL_REQUIRE(discount>0.0,
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);
90 QL_ENSURE(result>=0.0,
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);
116 QL_REQUIRE(stdDev>=0.0,
117 "stdDev (" << stdDev <<
") must be non-negative");
118 QL_REQUIRE(discount>0.0,
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);
155 QL_REQUIRE(blackPrice>=0.0,
156 "blackPrice (" << blackPrice <<
") must be non-negative");
157 QL_REQUIRE(discount>0.0,
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);
183 QL_ENSURE(stdDev>=0.0,
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);
206 QL_REQUIRE(blackPrice >= 0.0,
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;
221 Real s0 = M_SQRT2 * M_SQRTPI * blackAtmPrice /
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);
273 QL_REQUIRE(marketValue >= 0.0,
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);
283 const Real alpha = marketValue/(K*df);
287 const Real a = std::exp((1.0-M_2_PI)*y);
289 const Real b = std::exp(M_2_PI*y);
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));
295 const Real gamma = -M_PI_2*std::log(beta);
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,
325 payoff->optionType(), payoff->strike(),
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);
342 QL_REQUIRE(undiscountedBlackPrice>=0.0,
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)
350 QL_REQUIRE(stdDev>=0.0,
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)
367 QL_REQUIRE(stdDev>=0.0,
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);
393 QL_REQUIRE(discount>0.0,
394 "discount (" << discount <<
") must be positive");
396 QL_REQUIRE(blackPrice>=0.0,
397 "option price (" << blackPrice <<
") must be non-negative");
399 Real otherOptionPrice = blackPrice -
Integer(optionType) * (forward-strike)*discount;
400 QL_REQUIRE(otherOptionPrice>=0.0,
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);
428 QL_REQUIRE(guess>=0.0,
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);
436 QL_ENSURE(stdDev>=0.0,
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));
494 QL_REQUIRE(discount>0.0,
495 "discount (" << discount <<
") must be positive");
497 QL_REQUIRE(blackPrice>=0.0,
498 "option price (" << blackPrice <<
") must be non-negative");
500 strike = strike + displacement;
501 forward = forward + displacement;
505 optionType, strike, forward,
506 blackPrice, discount, displacement);
509 QL_REQUIRE(guess>=0.0,
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,
556 payoff->optionType(), payoff->strike(),
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);
644 QL_REQUIRE(stdDev>=0.0,
645 "stdDev (" << stdDev <<
") must be non-negative");
646 QL_REQUIRE(discount>0.0,
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);
677 QL_REQUIRE(stdDev>=0.0,
678 "stdDev (" << stdDev <<
") must be non-negative");
679 QL_REQUIRE(discount>0.0,
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);
710 QL_REQUIRE(stdDev>=0.0,
711 "stdDev (" << stdDev <<
") must be non-negative");
712 QL_REQUIRE(discount>0.0,
713 "discount (" << discount <<
") must be positive");
714 Real d = (forward-strike) *
Integer(optionType), h = d / stdDev;
716 return discount*std::max(d, 0.0);
719 QL_ENSURE(result>=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);
740 QL_REQUIRE(stdDev>=0.0,
741 "stdDev (" << stdDev <<
") must be non-negative");
742 QL_REQUIRE(discount>0.0,
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;
842 QL_REQUIRE(stdDev>=0.0,
843 "stdDev (" << stdDev <<
") must be non-negative");
844 QL_REQUIRE(discount>0.0,
845 "discount (" << discount <<
") must be positive");
850 Real d1 = (forward - strike)/stdDev;
856 const ext::shared_ptr<PlainVanillaPayoff>& payoff,
869 QL_REQUIRE(stdDev>=0.0,
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
unsigned QL_INTEGER Natural
positive integer
QL_INTEGER Integer
integer number
std::size_t Size
size of a container
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)