--- a/ql/pricingengines/blackformula.cpp +++ b/ql/pricingengines/blackformula.cpp @@ -601,7 +601,7 @@ auto sign = Integer(optionType); if (stdDev==0.0) - return (forward * sign < strike * sign ? 1.0 : 0.0); + return (forward * sign > strike * sign ? 1.0 : 0.0); forward = forward + displacement; strike = strike + displacement; @@ -954,9 +954,10 @@ Real stdDev) { QL_REQUIRE(stdDev>=0.0, "stdDev (" << stdDev << ") must be non-negative"); - Real d = (forward - strike) * Integer(optionType), h = d / stdDev; + Real d = (forward - strike) * Integer(optionType); if (stdDev==0.0) - return std::max(d, 0.0); + return (d > 0.0 ? 1.0 : 0.0); + Real h = d / stdDev; CumulativeNormalDistribution phi; Real result = phi(h); return result; --- a/test-suite/blackformula.cpp +++ b/test-suite/blackformula.cpp @@ -442,6 +442,47 @@ assertBachelierBlackFormulaForwardDerivative(Option::Put, strikes, vol); } + +BOOST_AUTO_TEST_CASE(testAssetItmProbabilityAtZeroStdDev) { + BOOST_TEST_MESSAGE("Testing deterministic Black asset ITM probabilities..."); + for (auto type : {Option::Call, Option::Put}) { + const Real sign = Integer(type); + for (Real forward : {80.0, 120.0}) { + for (Real shift : {0.0, 50.0}) { + const Real strike = 100.0; + const Real expected = sign * (forward - strike) > 0.0 ? 1.0 : 0.0; + auto payoff = ext::make_shared(type, strike); + QL_CHECK_SMALL(blackFormulaAssetItmProbability(type, strike, forward, 0.0, shift) - expected, 1e-14); + QL_CHECK_SMALL(blackFormulaAssetItmProbability(payoff, forward, 0.0, shift) - expected, 1e-14); + const Real bump = 1e-4; + const Real slope = (blackFormula(type, strike, forward + bump, 0.0, 1.0, shift) - + blackFormula(type, strike, forward - bump, 0.0, 1.0, shift)) / (2.0 * bump); + QL_CHECK_SMALL(sign * blackFormulaAssetItmProbability(type, strike, forward, 0.0, shift) - slope, 1e-8); + } + } + } +} + +BOOST_AUTO_TEST_CASE(testBachelierAssetItmProbabilityAtZeroStdDev) { + BOOST_TEST_MESSAGE("Testing deterministic Bachelier ITM probabilities..."); + for (auto type : {Option::Call, Option::Put}) { + const Real sign = Integer(type); + for (Real scale : {0.001, 1.0, 1000.0}) { + for (Real forward : {80.0 * scale, 120.0 * scale}) { + const Real strike = 100.0 * scale; + const Real expected = sign * (forward - strike) > 0.0 ? 1.0 : 0.0; + auto payoff = ext::make_shared(type, strike); + QL_CHECK_SMALL(bachelierBlackFormulaAssetItmProbability(type, strike, forward, 0.0) - expected, 1e-14); + QL_CHECK_SMALL(bachelierBlackFormulaAssetItmProbability(payoff, forward, 0.0) - expected, 1e-14); + const Real bump = 1e-4 * scale; + const Real slope = (bachelierBlackFormula(type, strike, forward + bump, 0.0) - + bachelierBlackFormula(type, strike, forward - bump, 0.0)) / (2.0 * bump); + QL_CHECK_SMALL(sign * bachelierBlackFormulaAssetItmProbability(type, strike, forward, 0.0) - slope, 1e-8); + } + } + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()