Real bachelierBlackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount) { QL_REQUIRE(stdDev>=0.0, "stdDev (" << stdDev << ") must be non-negative"); QL_REQUIRE(discount>0.0, "discount (" << discount << ") must be positive"); Real d = (forward-strike)*optionType, h = d/stdDev; if (stdDev==0.0) return discount*std::max(d, 0.0); CumulativeNormalDistribution phi; Real result = discount*(stdDev*phi.derivative(h) + d*phi(h)); QL_ENSURE(result>=0.0, "negative value (" << result << ") for " << stdDev << " stdDev, " << optionType << " option, " << strike << " strike , " << forward << " forward"); return result; }
AmericanPayoffAtExpiry::AmericanPayoffAtExpiry( Real spot, DiscountFactor discount, DiscountFactor dividendDiscount, Real variance, const boost::shared_ptr<StrikedTypePayoff>& payoff) : spot_(spot), discount_(discount), dividendDiscount_(dividendDiscount), variance_(variance) { QL_REQUIRE(spot_>0.0, "positive spot_ value required"); forward_ = spot_ * dividendDiscount_ / discount_; QL_REQUIRE(discount_>0.0, "positive discount required"); QL_REQUIRE(dividendDiscount_>0.0, "positive dividend discount_ required"); QL_REQUIRE(variance_>=0.0, "negative variance_ not allowed"); stdDev_ = std::sqrt(variance_); Option::Type type = payoff->optionType(); strike_ = payoff->strike(); mu_ = std::log(dividendDiscount_/discount_)/variance_ - 0.5; // binary cash-or-nothing payoff? boost::shared_ptr<CashOrNothingPayoff> coo = boost::dynamic_pointer_cast<CashOrNothingPayoff>(payoff); if (coo) { K_ = coo->cashPayoff(); DKDstrike_ = 0.0; } // binary asset-or-nothing payoff? boost::shared_ptr<AssetOrNothingPayoff> aoo = boost::dynamic_pointer_cast<AssetOrNothingPayoff>(payoff); if (aoo) { K_ = forward_; DKDstrike_ = 0.0; mu_ += 1.0; } log_H_S_ = std::log(strike_/spot_); Real n_d1, n_d2; Real cum_d1_, cum_d2_; if (variance_>=QL_EPSILON) { D1_ = log_H_S_/stdDev_ + mu_*stdDev_; D2_ = D1_ - 2.0*mu_*stdDev_; CumulativeNormalDistribution f; cum_d1_ = f(D1_); cum_d2_ = f(D2_); n_d1 = f.derivative(D1_); n_d2 = f.derivative(D2_); } else { if (log_H_S_>0) { cum_d1_= 1.0; cum_d2_= 1.0; } else { cum_d1_= 0.0; cum_d2_= 0.0; } n_d1 = 0.0; n_d2 = 0.0; } switch (type) { // up-and-in cash-(at-hit)-or-nothing option // a.k.a. american call with cash-or-nothing payoff case Option::Call: if (strike_>spot_) { alpha_ = 1.0-cum_d2_;// N(-d2) DalphaDd1_ = - n_d2; // -n( d2) beta_ = 1.0-cum_d1_;// N(-d1) DbetaDd2_ = - n_d1; // -n( d1) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; // down-and-in cash-(at-hit)-or-nothing option // a.k.a. american put with cash-or-nothing payoff case Option::Put: if (strike_<spot_) { alpha_ = cum_d2_;// N(d2) DalphaDd1_ = n_d2; // n(d2) beta_ = cum_d1_;// N(d1) DbetaDd2_ = n_d1; // n(d1) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; default: QL_FAIL("invalid option type"); } inTheMoney_ = (type==Option::Call && strike_<spot_) || (type==Option::Put && strike_>spot_); if (inTheMoney_) { Y_ = 1.0; X_ = 1.0; DYDstrike_ = 0.0; DXDstrike_ = 0.0; } else { Y_ = 1.0; X_ = std::pow(Real(strike_/spot_), Real(2.0*mu_)); // DXDstrike_ = ......; } }
AmericanPayoffAtExpiry::AmericanPayoffAtExpiry( Real spot, DiscountFactor discount, DiscountFactor dividendDiscount, Real variance, const boost::shared_ptr<StrikedTypePayoff>& payoff, bool knock_in) : spot_(spot), discount_(discount), dividendDiscount_(dividendDiscount), variance_(variance), knock_in_(knock_in) { QL_REQUIRE(spot_>0.0, "positive spot value required"); QL_REQUIRE(discount_>0.0, "positive discount required"); QL_REQUIRE(dividendDiscount_>0.0, "positive dividend discount required"); QL_REQUIRE(variance_>=0.0, "negative variance not allowed"); stdDev_ = std::sqrt(variance_); Option::Type type = payoff->optionType(); strike_ = payoff->strike(); forward_ = spot_ * dividendDiscount_ / discount_; mu_ = std::log(dividendDiscount_/discount_)/variance_ - 0.5; // binary cash-or-nothing payoff? boost::shared_ptr<CashOrNothingPayoff> coo = boost::dynamic_pointer_cast<CashOrNothingPayoff>(payoff); if (coo) { K_ = coo->cashPayoff(); } // binary asset-or-nothing payoff? boost::shared_ptr<AssetOrNothingPayoff> aoo = boost::dynamic_pointer_cast<AssetOrNothingPayoff>(payoff); if (aoo) { K_ = forward_; mu_ += 1.0; } log_H_S_ = std::log(strike_/spot_); double log_S_H_ = std::log(spot_/strike_); double eta; double phi; switch (type) { case Option::Call: if (knock_in_) { // up-and-in cash-(at-expiry)-or-nothing option // a.k.a. american call with cash-or-nothing payoff eta = -1.0; phi = 1.0; } else { // up-and-out cash-(at-expiry)-or-nothing option eta = -1.0; phi = -1.0; } break; case Option::Put: if (knock_in_) { // down-and-in cash-(at-expiry)-or-nothing option // a.k.a. american put with cash-or-nothing payoff eta = 1.0; phi = -1.0; } else { // down-and-out cash-(at-expiry)-or-nothing option eta = 1.0; phi = 1.0; } break; default: QL_FAIL("invalid option type"); } if (variance_>=QL_EPSILON) { D1_ = phi*(log_S_H_/stdDev_ + mu_*stdDev_); D2_ = eta*(log_H_S_/stdDev_ + mu_*stdDev_); CumulativeNormalDistribution f; cum_d1_ = f(D1_); cum_d2_ = f(D2_); n_d1_ = f.derivative(D1_); n_d2_ = f.derivative(D2_); } else { if (log_S_H_ * phi >0) cum_d1_= 1.0; else cum_d1_= 0.0; if (log_H_S_ * eta >0) cum_d2_= 1.0; else cum_d2_= 0.0; n_d1_ = 0.0; n_d2_ = 0.0; } switch (type) { case Option::Call: if (strike_<=spot_) { if (knock_in_) { // up-and-in cash-(at-expiry)-or-nothing option // a.k.a. american call with cash-or-nothing payoff cum_d1_ = 0.5; cum_d2_ = 0.5; } else { // up-and-out cash-(at-expiry)-or-nothing option // already knocked out cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } break; case Option::Put: if (strike_>=spot_) { if (knock_in_) { // down-and-in cash-(at-expiry)-or-nothing option // a.k.a. american put with cash-or-nothing payoff cum_d1_ = 0.5; cum_d2_ = 0.5; } else { // down-and-out cash-(at-expiry)-or-nothing option // already knocked out cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } break; default: QL_FAIL("invalid option type"); } inTheMoney_ = (type==Option::Call && strike_<spot_) || (type==Option::Put && strike_>spot_); if (inTheMoney_) { X_ = 1.0; Y_ = 1.0; } else { X_ = 1.0; if (cum_d2_ == 0.0) Y_ = 0.0; // check needed on some extreme cases else Y_ = std::pow(Real(strike_/spot_), Real(2.0*mu_)); } if (!knock_in_) Y_ *= -1.0; }
void BlackCalculator::initialize(const shared_ptr<StrikedTypePayoff>& p) { QL_REQUIRE(strike_>=0.0, "strike (" << strike_ << ") must be non-negative"); QL_REQUIRE(forward_>0.0, "forward (" << forward_ << ") must be positive"); //QL_REQUIRE(displacement_>=0.0, // "displacement (" << displacement_ << ") must be non-negative"); QL_REQUIRE(stdDev_>=0.0, "stdDev (" << stdDev_ << ") must be non-negative"); QL_REQUIRE(discount_>0.0, "discount (" << discount_ << ") must be positive"); if (stdDev_>=QL_EPSILON) { if (close(strike_, 0.0)) { d1_ = QL_MAX_REAL; d2_ = QL_MAX_REAL; cum_d1_ = 1.0; cum_d2_ = 1.0; n_d1_ = 0.0; n_d2_ = 0.0; } else { d1_ = std::log(forward_/strike_)/stdDev_ + 0.5*stdDev_; d2_ = d1_-stdDev_; CumulativeNormalDistribution f; cum_d1_ = f(d1_); cum_d2_ = f(d2_); n_d1_ = f.derivative(d1_); n_d2_ = f.derivative(d2_); } } else { if (close(forward_, strike_)) { d1_ = 0; d2_ = 0; cum_d1_ = 0.5; cum_d2_ = 0.5; n_d1_ = M_SQRT_2 * M_1_SQRTPI; n_d2_ = M_SQRT_2 * M_1_SQRTPI; } else if (forward_>strike_) { d1_ = QL_MAX_REAL; d2_ = QL_MAX_REAL; cum_d1_ = 1.0; cum_d2_ = 1.0; n_d1_ = 0.0; n_d2_ = 0.0; } else { d1_ = QL_MIN_REAL; d2_ = QL_MIN_REAL; cum_d1_ = 0.0; cum_d2_ = 0.0; n_d1_ = 0.0; n_d2_ = 0.0; } } x_ = strike_; DxDstrike_ = 1.0; // the following one will probably disappear as soon as // super-share will be properly handled DxDs_ = 0.0; // this part is always executed. // in case of plain-vanilla payoffs, it is also the only part // which is executed. switch (p->optionType()) { case Option::Call: alpha_ = cum_d1_;// N(d1) DalphaDd1_ = n_d1_;// n(d1) beta_ = -cum_d2_;// -N(d2) DbetaDd2_ = - n_d2_;// -n(d2) break; case Option::Put: alpha_ = -1.0+cum_d1_;// -N(-d1) DalphaDd1_ = n_d1_;// n( d1) beta_ = 1.0-cum_d2_;// N(-d2) DbetaDd2_ = - n_d2_;// -n( d2) break; default: QL_FAIL("invalid option type"); } // now dispatch on type. Calculator calc(*this); p->accept(calc); }