Real BlackIborCouponPricer::optionletPrice(Option::Type optionType, Real effStrike) const { Date fixingDate = coupon_->fixingDate(); if (fixingDate <= Settings::instance().evaluationDate()) { // the amount is determined Real a, b; if (optionType==Option::Call) { a = coupon_->indexFixing(); b = effStrike; } else { a = effStrike; b = coupon_->indexFixing(); } return std::max(a - b, 0.0)* accrualPeriod_*discount_; } else { // not yet determined, use Black model QL_REQUIRE(!capletVolatility().empty(), "missing optionlet volatility"); Real stdDev = std::sqrt(capletVolatility()->blackVariance(fixingDate, effStrike)); Real shift = capletVolatility()->displacement(); bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal; Rate fixing = shiftedLn ? blackFormula(optionType, effStrike, adjustedFixing(), stdDev, 1.0, shift) : bachelierBlackFormula(optionType, effStrike, adjustedFixing(), stdDev, 1.0); return fixing * accrualPeriod_ * discount_; } }
Real HullWhiteSmileSection::volatilityImpl(Rate strike) const { boost::shared_ptr<VanillaSwap> underlying = MakeVanillaSwap(swapLength_,swapIndex_->iborIndex(),strike) .withEffectiveDate(swapIndex_->valueDate(optionDate_)) .withFixedLegCalendar(swapIndex_->fixingCalendar()) .withFixedLegDayCount(swapIndex_->dayCounter()) .withFixedLegTenor(swapIndex_->fixedLegTenor()) .withFixedLegConvention(swapIndex_->fixedLegConvention()) .withFixedLegTerminationDateConvention(swapIndex_->fixedLegConvention()) .withType(VanillaSwap::Payer); underlying->setPricingEngine(discountingEngine_); boost::shared_ptr<Exercise> exercise(new EuropeanExercise(optionDate_)); Swaption swaption(underlying,exercise); swaption.setPricingEngine(jamshidianEngine_); Real npv = swaption.NPV(); Real annuity = fabs(underlying->fixedLegBPS() * 10000.0); //Real blackNpv=blackFormula(Option::Call,strike,atm,0.30*sqrt(exerciseTime())); //std::cout << std::setprecision(8) << "SECTION atm=" << atm_ << " annuity=" << annuity << " npv=" << npv << " strike= " << strike << std::endl; Real vol=0.0; try { vol=blackFormulaImpliedStdDev(Option::Call,strike,atm_,npv,annuity) / sqrt(exerciseTime()); } catch(QuantLib::Error) { //std::cout << " can not imply vol for npv " << npv << " black(300%)=" << blackFormula(Option::Call,strike,atm_,sqrt(exerciseTime())*3.00,annuity) << // " black(1%)=" << blackFormula(Option::Call,strike,atm_,sqrt(exerciseTime())*0.01,annuity) << std::endl; if(npv>blackFormula(Option::Call,strike,atm_,3.00,annuity)) vol =3.0/sqrt(exerciseTime()); else vol=0.0; } return vol; }
Real YoYInflationBlackCapFloorEngine::optionletImpl(Option::Type type, Rate strike, Rate forward, Real stdDev, Real d) const { return blackFormula(type, strike, forward, stdDev, d); }
Real HestonModelHelper::blackPrice(Real volatility) const { calculate(); const Real stdDev = volatility * std::sqrt(maturity()); return blackFormula( type_, strikePrice_ * termStructure_->discount(tau_), s0_->value() * dividendYield_->discount(tau_), stdDev); }
Real blackFormula(const boost::shared_ptr<PlainVanillaPayoff>& payoff, Real forward, Real stdDev, Real discount, Real displacement) { return blackFormula(payoff->optionType(), payoff->strike(), forward, stdDev, discount, displacement); }
Real YoYInflationUnitDisplacedBlackCapFloorEngine::optionletImpl( Option::Type type, Rate strike, Rate forward, Real stdDev, Real d) const { // could use displacement parameter in blackFormula but this is clearer return blackFormula(type, strike+1.0, forward+1.0, stdDev, d); }
Real Generalized_HullWhite::discountBondOption(Option::Type type, Real strike, Time maturity, Time bondMaturity) const { Real _a = a(); Real v; v = std::sqrt(Vp(0, maturity, bondMaturity)); Real f = termStructure()->discount(bondMaturity); Real k = termStructure()->discount(maturity)*strike; return blackFormula(type, k, f, v); }
Real SmileSection::optionPrice(Rate strike, Option::Type type, Real discount) const { Real atm = atmLevel(); QL_REQUIRE(atm != Null<Real>(), "smile section must provide atm level to compute option price"); // if lognormal or shifted lognormal, // for strike at -shift, return option price even if outside // minstrike, maxstrike interval if (volatilityType() == ShiftedLognormal) return blackFormula(type,strike,atm, fabs(strike+shift()) < QL_EPSILON ? 0.2 : sqrt(variance(strike)),discount,shift()); else return bachelierBlackFormula(type,strike,atm,sqrt(variance(strike)),discount); }
Real HullWhite::discountBondOption(Option::Type type, Real strike, Time maturity, Time bondMaturity) const { Real _a = a(); Real v; if (_a < std::sqrt(QL_EPSILON)) { v = sigma()*B(maturity, bondMaturity)* std::sqrt(maturity); } else { v = sigma()*B(maturity, bondMaturity)* std::sqrt(0.5*(1.0 - std::exp(-2.0*_a*maturity))/_a); } Real f = termStructure()->discount(bondMaturity); Real k = termStructure()->discount(maturity)*strike; return blackFormula(type, k, f, v); }
Real HullWhite::discountBondOption(Option::Type type, Real strike, Time maturity, Time bondStart, Time bondMaturity) const { Real _a = a(); Real v; if (_a < std::sqrt(QL_EPSILON)) { v = sigma()*B(bondStart, bondMaturity)* std::sqrt(maturity); } else { v = sigma()/(_a*sqrt(2.0*_a)) * sqrt ( exp(-2.0*_a*(bondStart-maturity))-exp(-2.0*_a*bondStart) -2.0*(exp(-_a*(bondStart+bondMaturity-2.0*maturity))-exp(-_a*(bondStart+bondMaturity))) +exp(-2.0*_a*(bondMaturity-maturity))-exp(-2.0*_a*bondMaturity)); } Real f = termStructure()->discount(bondMaturity); Real k = termStructure()->discount(bondStart)*strike; return blackFormula(type, k, f, v); }
Real LiborForwardModel::discountBondOption(Option::Type type, Real strike, Time maturity, Time bondMaturity) const { const std::vector<Time> & accrualStartTimes = process_->accrualStartTimes(); const std::vector<Time> & accrualEndTimes = process_->accrualEndTimes(); QL_REQUIRE( accrualStartTimes.front()<= maturity && accrualStartTimes.back() >= maturity, "capet maturity does not fit to the process"); const Size i = std::lower_bound(accrualStartTimes.begin(), accrualStartTimes.end(), maturity) - accrualStartTimes.begin(); QL_REQUIRE( i<process_->size() && std::fabs(maturity - accrualStartTimes[i]) < 100*std::numeric_limits<Real>::epsilon() && std::fabs(bondMaturity - accrualEndTimes[i]) < 100*std::numeric_limits<Real>::epsilon(), "irregular fixings are not (yet) supported"); const Real tenor = accrualEndTimes[i] - accrualStartTimes[i]; const Real forward = process_->initialValues()[i]; const Real capRate = (1.0/strike - 1.0)/tenor; const Volatility var = covarProxy_ ->integratedCovariance(i, i, process_->fixingTimes()[i]); const DiscountFactor dis = process_->index()->forwardingTermStructure()->discount(bondMaturity); const Real black = blackFormula( (type==Option::Put ? Option::Call : Option::Put), capRate, forward, std::sqrt(var)); const Real npv = dis * tenor * black; return npv / (1.0 + capRate*tenor); }
CapPseudoDerivative::CapPseudoDerivative(boost::shared_ptr<MarketModel> inputModel, Real strike, Size startIndex, Size endIndex, Real firstDF) : firstDF_(firstDF) { QL_REQUIRE(startIndex < endIndex, "for a cap pseudo derivative the start of the cap must be before the end"); QL_REQUIRE( endIndex <= inputModel->numberOfRates(), "for a cap pseudo derivative the end of the cap must before the end of the rates"); Size numberCaplets = endIndex-startIndex; Size numberRates = inputModel->numberOfRates(); Size factors = inputModel->numberOfFactors(); LMMCurveState curve(inputModel->evolution().rateTimes()); curve.setOnForwardRates(inputModel->initialRates()); const Matrix& totalCovariance(inputModel->totalCovariance(inputModel->numberOfSteps()-1)); std::vector<Real> displacedImpliedVols(numberCaplets); std::vector<Real> annuities(numberCaplets); std::vector<Real> initialRates(numberCaplets); std::vector<Real> expiries(numberCaplets); Real capPrice =0.0; Real guess=0.0; Real minVol = 1e10; Real maxVol =0.0; for (Size j = startIndex; j < endIndex; ++j) { Size capletIndex = j - startIndex; Time resetTime = inputModel->evolution().rateTimes()[j]; expiries[capletIndex] = resetTime; Real sd = std::sqrt(totalCovariance[j][j]); displacedImpliedVols[capletIndex] = std::sqrt(totalCovariance[j][j]/resetTime); Real forward = inputModel->initialRates()[j]; initialRates[capletIndex] = forward; Real annuity = curve.discountRatio(j+1,0)* inputModel->evolution().rateTaus()[j]*firstDF_; annuities[capletIndex] = annuity; Real displacement = inputModel->displacements()[j]; guess+= displacedImpliedVols[capletIndex]*(forward+displacement)/forward; minVol =std::min(minVol, displacedImpliedVols[capletIndex]); maxVol =std::max(maxVol, displacedImpliedVols[capletIndex]*(forward+displacement)/forward); Real capletPrice = blackFormula(Option::Call, strike, forward, sd, annuity, displacement ); capPrice += capletPrice; } guess/=numberCaplets; for (Size step =0; step < inputModel->evolution().numberOfSteps(); ++step) { Matrix thisDerivative(numberRates,factors,0.0); for (Size rate =std::max(inputModel->evolution().firstAliveRate()[step],startIndex); rate < endIndex; ++rate) { for (Size f=0; f < factors; ++f) { Real expiry = inputModel->evolution().rateTimes()[rate]; Real volDerivative = inputModel->pseudoRoot(step)[rate][f] /(displacedImpliedVols[rate-startIndex]*expiry); Real capletVega = blackFormulaVolDerivative(strike,inputModel->initialRates()[rate], displacedImpliedVols[rate-startIndex]*std::sqrt(expiry), expiry, annuities[rate-startIndex], inputModel->displacements()[rate]); // note that the cap derivative is equal to one of the caplet ones so we lose a loop thisDerivative[rate][f] = volDerivative*capletVega; } } priceDerivatives_.push_back(thisDerivative); } QuickCap capPricer(strike, annuities, initialRates, expiries,capPrice); Size maxEvaluations = 1000; Real accuracy = 1E-6; Brent solver; solver.setMaxEvaluations(maxEvaluations); impliedVolatility_ = solver.solve(capPricer,accuracy,guess,minVol*0.99,maxVol*1.01); vega_ = capPricer.vega(impliedVolatility_); for (Size step =0; step < inputModel->evolution().numberOfSteps(); ++step) { Matrix thisDerivative(numberRates,factors,0.0); for (Size rate =std::max(inputModel->evolution().firstAliveRate()[step],startIndex); rate < endIndex; ++rate) { for (Size f=0; f < factors; ++f) { thisDerivative[rate][f] = priceDerivatives_[step][rate][f]/vega_; } } volatilityDerivatives_.push_back(thisDerivative); } }
Real value(const Option::Type type, const Real strike, const Real atmForward, const Real stdDev, const Real annuity, const Real displacement) { return blackFormula(type, strike, atmForward, stdDev, annuity, displacement); }
void JuQuadraticApproximationEngine::calculate() const { QL_REQUIRE(arguments_.exercise->type() == Exercise::American, "not an American Option"); boost::shared_ptr<AmericanExercise> ex = boost::dynamic_pointer_cast<AmericanExercise>(arguments_.exercise); QL_REQUIRE(ex, "non-American exercise given"); QL_REQUIRE(!ex->payoffAtExpiry(), "payoff at expiry not handled"); boost::shared_ptr<StrikedTypePayoff> payoff = boost::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff); QL_REQUIRE(payoff, "non-striked payoff given"); Real variance = process_->blackVolatility()->blackVariance( ex->lastDate(), payoff->strike()); DiscountFactor dividendDiscount = process_->dividendYield()->discount( ex->lastDate()); DiscountFactor riskFreeDiscount = process_->riskFreeRate()->discount( ex->lastDate()); Real spot = process_->stateVariable()->value(); QL_REQUIRE(spot > 0.0, "negative or null underlying given"); Real forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black(payoff, forwardPrice, std::sqrt(variance), riskFreeDiscount); if (dividendDiscount>=1.0 && payoff->optionType()==Option::Call) { // early exercise never optimal results_.value = black.value(); results_.delta = black.delta(spot); results_.deltaForward = black.deltaForward(); results_.elasticity = black.elasticity(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_->riskFreeRate()->dayCounter(); DayCounter divdc = process_->dividendYield()->dayCounter(); DayCounter voldc = process_->blackVolatility()->dayCounter(); Time t = rfdc.yearFraction(process_->riskFreeRate()->referenceDate(), arguments_.exercise->lastDate()); results_.rho = black.rho(t); t = divdc.yearFraction(process_->dividendYield()->referenceDate(), arguments_.exercise->lastDate()); results_.dividendRho = black.dividendRho(t); t = voldc.yearFraction(process_->blackVolatility()->referenceDate(), arguments_.exercise->lastDate()); results_.vega = black.vega(t); results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); } else { // early exercise can be optimal CumulativeNormalDistribution cumNormalDist; NormalDistribution normalDist; Real tolerance = 1e-6; Real Sk = BaroneAdesiWhaleyApproximationEngine::criticalPrice( payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); Real forwardSk = Sk * dividendDiscount / riskFreeDiscount; Real alpha = -2.0*std::log(riskFreeDiscount)/(variance); Real beta = 2.0*std::log(dividendDiscount/riskFreeDiscount)/ (variance); Real h = 1 - riskFreeDiscount; Real phi; switch (payoff->optionType()) { case Option::Call: phi = 1; break; case Option::Put: phi = -1; break; default: QL_FAIL("unknown option type"); } //it can throw: to be fixed // FLOATING_POINT_EXCEPTION Real temp_root = std::sqrt ((beta-1)*(beta-1) + (4*alpha)/h); Real lambda = (-(beta-1) + phi * temp_root) / 2; Real lambda_prime = - phi * alpha / (h*h * temp_root); Real black_Sk = blackFormula(payoff->optionType(), payoff->strike(), forwardSk, std::sqrt(variance)) * riskFreeDiscount; Real hA = phi * (Sk - payoff->strike()) - black_Sk; Real d1_Sk = (std::log(forwardSk/payoff->strike()) + 0.5*variance) /std::sqrt(variance); Real d2_Sk = d1_Sk - std::sqrt(variance); Real part1 = forwardSk * normalDist(d1_Sk) / (alpha * std::sqrt(variance)); Real part2 = - phi * forwardSk * cumNormalDist(phi * d1_Sk) * std::log(dividendDiscount) / std::log(riskFreeDiscount); Real part3 = + phi * payoff->strike() * cumNormalDist(phi * d2_Sk); Real V_E_h = part1 + part2 + part3; Real b = (1-h) * alpha * lambda_prime / (2*(2*lambda + beta - 1)); Real c = - ((1 - h) * alpha / (2 * lambda + beta - 1)) * (V_E_h / (hA) + 1 / h + lambda_prime / (2*lambda + beta - 1)); Real temp_spot_ratio = std::log(spot / Sk); Real chi = temp_spot_ratio * (b * temp_spot_ratio + c); if (phi*(Sk-spot) > 0) { results_.value = black.value() + hA * std::pow((spot/Sk), lambda) / (1 - chi); } else { results_.value = phi * (spot - payoff->strike()); } Real temp_chi_prime = (2 * b / spot) * std::log(spot/Sk); Real chi_prime = temp_chi_prime + c / spot; Real chi_double_prime = 2*b/(spot*spot) - temp_chi_prime / spot - c / (spot*spot); results_.delta = phi * dividendDiscount * cumNormalDist (phi * d1_Sk) + (lambda / (spot * (1 - chi)) + chi_prime / ((1 - chi)*(1 - chi))) * (phi * (Sk - payoff->strike()) - black_Sk) * std::pow((spot/Sk), lambda); results_.gamma = phi * dividendDiscount * normalDist (phi*d1_Sk) / (spot * std::sqrt(variance)) + (2 * lambda * chi_prime / (spot * (1 - chi) * (1 - chi)) + 2 * chi_prime * chi_prime / ((1 - chi) * (1 - chi) * (1 - chi)) + chi_double_prime / ((1 - chi) * (1 - chi)) + lambda * (1 - lambda) / (spot * spot * (1 - chi))) * (phi * (Sk - payoff->strike()) - black_Sk) * std::pow((spot/Sk), lambda); } // end of "early exercise can be optimal" }
void BlackCapFloorEngine::calculate() const { Real value = 0.0; Real vega = 0.0; Size optionlets = arguments_.startDates.size(); std::vector<Real> values(optionlets, 0.0); std::vector<Real> vegas(optionlets, 0.0); std::vector<Real> stdDevs(optionlets, 0.0); CapFloor::Type type = arguments_.type; Date today = vol_->referenceDate(); Date settlement = discountCurve_->referenceDate(); for (Size i=0; i<optionlets; ++i) { Date paymentDate = arguments_.endDates[i]; // handling of settlementDate, npvDate and includeSettlementFlows // should be implemented. // For the time being just discard expired caplets if (paymentDate > settlement) { DiscountFactor d = arguments_.nominals[i] * arguments_.gearings[i] * discountCurve_->discount(paymentDate) * arguments_.accrualTimes[i]; Rate forward = arguments_.forwards[i]; Date fixingDate = arguments_.fixingDates[i]; Time sqrtTime = 0.0; if (fixingDate > today) sqrtTime = std::sqrt(vol_->timeFromReference(fixingDate)); if (type == CapFloor::Cap || type == CapFloor::Collar) { Rate strike = arguments_.capRates[i]; if (sqrtTime>0.0) { stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate, strike)); vegas[i] = blackFormulaStdDevDerivative(strike, forward, stdDevs[i], d, displacement_) * sqrtTime; } // include caplets with past fixing date values[i] = blackFormula(Option::Call, strike, forward, stdDevs[i], d, displacement_); } if (type == CapFloor::Floor || type == CapFloor::Collar) { Rate strike = arguments_.floorRates[i]; Real floorletVega = 0.0; if (sqrtTime>0.0) { stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate, strike)); floorletVega = blackFormulaStdDevDerivative(strike, forward, stdDevs[i], d, displacement_) * sqrtTime; } Real floorlet = blackFormula(Option::Put, strike, forward, stdDevs[i], d, displacement_); if (type == CapFloor::Floor) { values[i] = floorlet; vegas[i] = floorletVega; } else { // a collar is long a cap and short a floor values[i] -= floorlet; vegas[i] -= floorletVega; } } value += values[i]; vega += vegas[i]; } } results_.value = value; results_.additionalResults["vega"] = vega; results_.additionalResults["optionletsPrice"] = values; results_.additionalResults["optionletsVega"] = vegas; results_.additionalResults["optionletsAtmForward"] = arguments_.forwards; if (type != CapFloor::Collar) results_.additionalResults["optionletsStdDev"] = stdDevs; }
void KahaleSmileSection::compute() { std::pair<Size,Size> afIdx = ssutils_->arbitragefreeIndices(); leftIndex_ = afIdx.first; rightIndex_ = afIdx.second; if(deleteArbitragePoints_) { while(leftIndex_>1 || rightIndex_<k_.size()-1) { ssutils_ = boost::shared_ptr<SmileSectionUtils>(new SmileSectionUtils(*source_,moneynessGrid_,f_)); std::pair<Size,Size> afIdx = ssutils_->arbitragefreeIndices(); leftIndex_ = afIdx.first; rightIndex_ = afIdx.second; QL_REQUIRE(rightIndex_>leftIndex_, "arbitrage free region must at least contain two points (only index is " << leftIndex_ << ")"); if(leftIndex_>1) { moneynessGrid_.erase(moneynessGrid_.begin()+leftIndex_-1); k_.erase(k_.begin()+leftIndex_-1); c_.erase(c_.begin()+leftIndex_-1); leftIndex_--; rightIndex_--; } if(rightIndex_<k_.size()-1) { moneynessGrid_.erase(moneynessGrid_.begin()+rightIndex_+1); k_.erase(k_.begin()+rightIndex_+1); c_.erase(c_.begin()+rightIndex_+1); rightIndex_--; } } } cFunctions_ = std::vector<boost::shared_ptr<cFunction> >(rightIndex_-leftIndex_+2); // extrapolation in the leftmost interval Brent brent; bool success; Real secl = 0.0; do { success=true; try { Real k1 = k_[leftIndex_]; Real c1 = c_[leftIndex_]; Real c0 = c_[0]; secl = (c_[leftIndex_]-c_[0]) / (k_[leftIndex_]-k_[0]); Real sec = (c_[leftIndex_+1]-c_[leftIndex_]) / (k_[leftIndex_+1]-k_[leftIndex_]); Real c1p; if(interpolate_) c1p=(secl+sec)/2; else { c1p=(blackFormula(Option::Call, k1+gap_, f_, sqrt(source_->variance(k1+gap_)))- blackFormula(Option::Call, k1, f_, sqrt(source_->variance(k1))))/gap_; QL_REQUIRE(secl < c1p && c1p <= 0.0,"dummy"); // can not extrapolate so throw exception which is caught below } sHelper1 sh1(k1,c0,c1,c1p); Real s = brent.solve(sh1,QL_KAHALE_ACC,0.20,0.00,QL_KAHALE_SMAX); // numerical parameters hardcoded here sh1(s); boost::shared_ptr<cFunction> cFct1(new cFunction(sh1.f_,s,0.0,sh1.b_)); cFunctions_[0]=cFct1; } catch(...) { leftIndex_++; success=false; } } while(!success && leftIndex_ < rightIndex_); QL_REQUIRE(leftIndex_ < rightIndex_, "can not extrapolate to left, right index of af region reached (" << rightIndex_ << ")"); // interpolation Real cp0 = 0.0, cp1 = 0.0; if(interpolate_) { for(Size i = leftIndex_; i<rightIndex_; i++) { Real k0 = k_[i]; Real k1 = k_[i+1]; Real c0 = c_[i]; Real c1 = c_[i+1]; Real sec = (c_[i+1]-c_[i]) / (k_[i+1]-k_[i]); if(i==leftIndex_) cp0 = leftIndex_ > 0 ? (secl + sec) / 2.0 : sec; Real secr; if(i==rightIndex_-1) secr=0.0; else secr = (c_[i+2]-c_[i+1]) / (k_[i+2]-k_[i+1]); cp1 = (sec+secr) / 2.0; aHelper ah(k0,k1,c0,c1,cp0,cp1); Real a; try { a = brent.solve(ah,QL_KAHALE_ACC,0.5*(cp1+(1.0+cp0)),cp1+QL_KAHALE_EPS,1.0+cp0-QL_KAHALE_EPS); // numerical parameters hardcoded here } catch(...) { // from theory there must exist a zero. if the solver does not find it, it most // probably lies close one of the interval bounds. Just choose the better bound // and relax the accuracy. This does not matter in practice usually. Real la = std::fabs(ah(cp1+QL_KAHALE_EPS)); Real ra = std::fabs(ah(1.0+cp0-QL_KAHALE_EPS)); if( la < QL_KAHALE_ACC_RELAX || ra < QL_KAHALE_ACC_RELAX) { // tolerance hardcoded here a = la < ra ? cp1+QL_KAHALE_EPS : 1.0+cp0-QL_KAHALE_EPS; } else QL_FAIL("can not interpolate at index " << i); } ah(a); boost::shared_ptr<cFunction> cFct(new cFunction(ah.f_,ah.s_,a,ah.b_)); cFunctions_[leftIndex_ > 0 ? i-leftIndex_+1 : 0]=cFct; cp0=cp1; } } // extrapolation of right wing do { success=true; try { Real k0 = k_[rightIndex_]; Real c0 = c_[rightIndex_]; Real cp0; if(interpolate_) cp0 = 0.5*(c_[rightIndex_]-c_[rightIndex_-1])/(k_[rightIndex_]-k_[rightIndex_-1]); else { cp0=(blackFormula(Option::Call, k0, f_, sqrt(source_->variance(k0)))- blackFormula(Option::Call, k0-gap_, f_, sqrt(source_->variance(k0-gap_))))/gap_; } boost::shared_ptr<cFunction> cFct; if(exponentialExtrapolation_) { cFct = boost::shared_ptr<cFunction>(new cFunction(-cp0/c0 ,std::log(c0)-cp0/c0*k0)); } else { sHelper sh(k0,c0,cp0); Real s; s = brent.solve(sh,QL_KAHALE_ACC,0.20,0.0,QL_KAHALE_SMAX); // numerical parameters hardcoded here sh(s); cFct = boost::shared_ptr<cFunction>(new cFunction(sh.f_,s,0.0,0.0)); } cFunctions_[rightIndex_-leftIndex_+1]=cFct; } catch (...) { rightIndex_--; success=false; } } while(!success && rightIndex_ > leftIndex_); QL_REQUIRE(leftIndex_ < rightIndex_, "can not extrapolate to right, left index of af region reached (" << leftIndex_ << ")"); }
void BlackSwaptionEngine::calculate() const { static const Spread basisPoint = 1.0e-4; Date exerciseDate = arguments_.exercise->date(0); // the part of the swap preceding exerciseDate should be truncated // to avoid taking into account unwanted cashflows VanillaSwap swap = *arguments_.swap; Rate strike = swap.fixedRate(); // using the discounting curve // swap.iborIndex() might be using a different forwarding curve swap.setPricingEngine(boost::shared_ptr<PricingEngine>(new DiscountingSwapEngine(discountCurve_, false))); Rate atmForward = swap.fairRate(); // Volatilities are quoted for zero-spreaded swaps. // Therefore, any spread on the floating leg must be removed // with a corresponding correction on the fixed leg. if (swap.spread()!=0.0) { Spread correction = swap.spread() * std::fabs(swap.floatingLegBPS()/swap.fixedLegBPS()); strike -= correction; atmForward -= correction; results_.additionalResults["spreadCorrection"] = correction; } else { results_.additionalResults["spreadCorrection"] = 0.0; } results_.additionalResults["strike"] = strike; results_.additionalResults["atmForward"] = atmForward; // using the discounting curve swap.setPricingEngine(boost::shared_ptr<PricingEngine>( new DiscountingSwapEngine(discountCurve_, false))); Real annuity; switch(arguments_.settlementType) { case Settlement::Physical: { annuity = std::fabs(swap.fixedLegBPS())/basisPoint; break; } case Settlement::Cash: { const Leg& fixedLeg = swap.fixedLeg(); boost::shared_ptr<FixedRateCoupon> firstCoupon = boost::dynamic_pointer_cast<FixedRateCoupon>(fixedLeg[0]); DayCounter dayCount = firstCoupon->dayCounter(); Real fixedLegCashBPS = CashFlows::bps(fixedLeg, InterestRate(atmForward, dayCount, Compounded, Annual), false, discountCurve_->referenceDate()) ; annuity = std::fabs(fixedLegCashBPS/basisPoint); break; } default: QL_FAIL("unknown settlement type"); } results_.additionalResults["annuity"] = annuity; // the swap length calculation might be improved using the value date // of the exercise date Time swapLength = vol_->swapLength(exerciseDate, arguments_.floatingPayDates.back()); results_.additionalResults["swapLength"] = swapLength; Real variance = vol_->blackVariance(exerciseDate, swapLength, strike); Real stdDev = std::sqrt(variance); results_.additionalResults["stdDev"] = stdDev; Option::Type w = (arguments_.type==VanillaSwap::Payer) ? Option::Call : Option::Put; results_.value = blackFormula(w, strike, atmForward, stdDev, annuity, displacement_); Time exerciseTime = vol_->timeFromReference(exerciseDate); results_.additionalResults["vega"] = std::sqrt(exerciseTime) * blackFormulaStdDevDerivative(strike, atmForward, stdDev, annuity, displacement_); }
void VannaVolgaDoubleBarrierEngine::calculate() const { const Real sigmaShift_vega = 0.001; const Real sigmaShift_volga = 0.0001; const Real spotShift_delta = 0.0001 * spotFX_->value(); const Real sigmaShift_vanna = 0.0001; Handle<Quote> x0Quote( //used for shift boost::make_shared<SimpleQuote>(spotFX_->value())); Handle<Quote> atmVolQuote( //used for shift boost::make_shared<SimpleQuote>(atmVol_->value())); boost::shared_ptr<BlackVolTermStructure> blackVolTS = boost::make_shared<BlackConstantVol>( Settings::instance().evaluationDate(), NullCalendar(), atmVolQuote, Actual365Fixed()); boost::shared_ptr<BlackScholesMertonProcess> stochProcess = boost::make_shared<BlackScholesMertonProcess>( x0Quote, foreignTS_, domesticTS_, Handle<BlackVolTermStructure>(blackVolTS)); boost::shared_ptr<PricingEngine> engineBS = boost::make_shared<AnalyticDoubleBarrierEngine>(stochProcess, series_); BlackDeltaCalculator blackDeltaCalculatorAtm( Option::Call, atmVol_->deltaType(), x0Quote->value(), domesticTS_->discount(T_), foreignTS_->discount(T_), atmVol_->value() * sqrt(T_)); Real atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_->atmType()); Real call25Vol = vol25Call_->value(); Real put25Vol = vol25Put_->value(); BlackDeltaCalculator blackDeltaCalculatorPut25( Option::Put, vol25Put_->deltaType(), x0Quote->value(), domesticTS_->discount(T_), foreignTS_->discount(T_), put25Vol * sqrt(T_)); Real put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25); BlackDeltaCalculator blackDeltaCalculatorCall25( Option::Call, vol25Call_->deltaType(), x0Quote->value(), domesticTS_->discount(T_), foreignTS_->discount(T_), call25Vol * sqrt(T_)); Real call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25); //here use vanna volga interpolated smile to price vanilla std::vector<Real> strikes; std::vector<Real> vols; strikes.push_back(put25Strike); vols.push_back(put25Vol); strikes.push_back(atmStrike); vols.push_back(atmVol_->value()); strikes.push_back(call25Strike); vols.push_back(call25Vol); VannaVolga vannaVolga(x0Quote->value(), foreignTS_->discount(T_), foreignTS_->discount(T_), T_); Interpolation interpolation = vannaVolga.interpolate(strikes.begin(), strikes.end(), vols.begin()); interpolation.enableExtrapolation(); const boost::shared_ptr<StrikedTypePayoff> payoff = boost::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff); Real strikeVol = interpolation(payoff->strike()); //vannila option price Real vanillaOption = blackFormula(payoff->optionType(), payoff->strike(), x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), strikeVol * sqrt(T_), domesticTS_->discount(T_)); //already out if((x0Quote->value() > arguments_.barrier[1] || x0Quote->value() < arguments_.barrier[0]) && arguments_.barrierType[0] == Barrier::DownOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //already in else if((x0Quote->value() > arguments_.barrier[1] || x0Quote->value() < arguments_.barrier[0]) && arguments_.barrierType[0] == Barrier::DownIn) { results_.value = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } else { //set up BS barrier option pricing //only calculate out barrier option price // in barrier price = vanilla - out barrier boost::shared_ptr<StrikedTypePayoff> payoff = boost::static_pointer_cast<StrikedTypePayoff> (arguments_.payoff); DoubleBarrierOption doubleBarrierOption(arguments_.barrierType, arguments_.barrier, arguments_.rebate, payoff, arguments_.exercise); doubleBarrierOption.setPricingEngine(engineBS); //BS price Real priceBS = doubleBarrierOption.NPV(); Real priceAtmCallBS = blackFormula(Option::Call,atmStrike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), atmVol_->value() * sqrt(T_), domesticTS_->discount(T_)); Real price25CallBS = blackFormula(Option::Call,call25Strike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), atmVol_->value() * sqrt(T_), domesticTS_->discount(T_)); Real price25PutBS = blackFormula(Option::Put,put25Strike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), atmVol_->value() * sqrt(T_), domesticTS_->discount(T_)); //market price Real priceAtmCallMkt = blackFormula(Option::Call,atmStrike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), atmVol_->value() * sqrt(T_), domesticTS_->discount(T_)); Real price25CallMkt = blackFormula(Option::Call,call25Strike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), call25Vol * sqrt(T_), domesticTS_->discount(T_)); Real price25PutMkt = blackFormula(Option::Put,put25Strike, x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_), put25Vol * sqrt(T_), domesticTS_->discount(T_)); //Analytical Black Scholes formula NormalDistribution norm; Real d1atm = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/atmStrike) + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_)); Real vegaAtm_Analytical = x0Quote->value() * norm(d1atm) * sqrt(T_) * foreignTS_->discount(T_); Real vannaAtm_Analytical = vegaAtm_Analytical/x0Quote->value() *(1.0 - d1atm/(atmVolQuote->value()*sqrt(T_))); Real volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value(); Real d125call = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/call25Strike) + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_)); Real vega25Call_Analytical = x0Quote->value() * norm(d125call) * sqrt(T_) * foreignTS_->discount(T_); Real vanna25Call_Analytical = vega25Call_Analytical/x0Quote->value() *(1.0 - d125call/(atmVolQuote->value()*sqrt(T_))); Real volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value(); Real d125Put = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/put25Strike) + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_)); Real vega25Put_Analytical = x0Quote->value() * norm(d125Put) * sqrt(T_) * foreignTS_->discount(T_); Real vanna25Put_Analytical = vega25Put_Analytical/x0Quote->value() *(1.0 - d125Put/(atmVolQuote->value()*sqrt(T_))); Real volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value(); //BS vega boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vega); doubleBarrierOption.recalculate(); Real vegaBarBS = (doubleBarrierOption.NPV() - priceBS)/sigmaShift_vega; boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() - sigmaShift_vega);//setback //BS volga //vegaBar2 //base NPV boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_volga); doubleBarrierOption.recalculate(); Real priceBS2 = doubleBarrierOption.NPV(); //shifted npv boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vega); doubleBarrierOption.recalculate(); Real vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2)/sigmaShift_vega; Real volgaBarBS = (vegaBarBS2 - vegaBarBS)/sigmaShift_volga; boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() - sigmaShift_volga - sigmaShift_vega);//setback //BS Delta //base delta boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//shift forth doubleBarrierOption.recalculate(); Real priceBS_delta1 = doubleBarrierOption.NPV(); boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); Real priceBS_delta2 = doubleBarrierOption.NPV(); boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//set back Real deltaBar1 = (priceBS_delta1 - priceBS_delta2)/(2.0*spotShift_delta); //shifted vanna boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vanna);//shift sigma //shifted delta boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//shift forth doubleBarrierOption.recalculate(); priceBS_delta1 = doubleBarrierOption.NPV(); boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); priceBS_delta2 = doubleBarrierOption.NPV(); boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//set back Real deltaBar2 = (priceBS_delta1 - priceBS_delta2)/(2.0*spotShift_delta); Real vannaBarBS = (deltaBar2 - deltaBar1)/sigmaShift_vanna; boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() - sigmaShift_vanna);//set back //Matrix Matrix A(3,3,0.0); //analytical A[0][0] = vegaAtm_Analytical; A[0][1] = vega25Call_Analytical; A[0][2] = vega25Put_Analytical; A[1][0] = vannaAtm_Analytical; A[1][1] = vanna25Call_Analytical; A[1][2] = vanna25Put_Analytical; A[2][0] = volgaAtm_Analytical; A[2][1] = volga25Call_Analytical; A[2][2] = volga25Put_Analytical; Array b(3,0.0); b[0] = vegaBarBS; b[1] = vannaBarBS; b[2] = volgaBarBS; Array q = inverse(A) * b; Real H = arguments_.barrier[1]; Real L = arguments_.barrier[0]; Real theta_tilt_minus = ((domesticTS_->zeroRate(T_, Continuous) - foreignTS_->zeroRate(T_, Continuous))/atmVol_->value() - atmVol_->value()/2.0)*std::sqrt(T_); Real h = 1.0/atmVol_->value() * std::log(H/x0Quote->value())/std::sqrt(T_); Real l = 1.0/atmVol_->value() * std::log(L/x0Quote->value())/std::sqrt(T_); CumulativeNormalDistribution cnd; Real doubleNoTouch = 0.0; for(int j = -series_; j< series_; j++ ) { Real e_minus = 2*j*(h-l) - theta_tilt_minus; doubleNoTouch += std::exp(-2.0*j*theta_tilt_minus*(h-l))*(cnd(h+e_minus) - cnd(l+e_minus)) - std::exp(-2.0*j*theta_tilt_minus*(h-l)+2.0*theta_tilt_minus*h)*(cnd(h-2.0*h+e_minus) - cnd(l-2.0*h+e_minus)); } Real p_survival = doubleNoTouch; Real lambda = p_survival ; Real adjust = q[0]*(priceAtmCallMkt - priceAtmCallBS) + q[1]*(price25CallMkt - price25CallBS) + q[2]*(price25PutMkt - price25PutBS); Real outPrice = priceBS + lambda*adjust;// Real inPrice; //adapt Vanilla delta if(adaptVanDelta_ == true) { outPrice += lambda*(bsPriceWithSmile_ - vanillaOption); //capfloored by (0, vanilla) outPrice = std::max(0.0, std::min(bsPriceWithSmile_, outPrice)); inPrice = bsPriceWithSmile_ - outPrice; } else { //capfloored by (0, vanilla) outPrice = std::max(0.0, std::min(vanillaOption , outPrice)); inPrice = vanillaOption - outPrice; } if(arguments_.barrierType[0] == Barrier::DownOut) results_.value = outPrice; else results_.value = inPrice; results_.additionalResults["VanillaPrice"] = vanillaOption; results_.additionalResults["BarrierInPrice"] = inPrice; results_.additionalResults["BarrierOutPrice"] = outPrice; results_.additionalResults["lambda"] = lambda; } }
void KahaleSmileSection::compute() { std::pair<Size, Size> afIdx = ssutils_->arbitragefreeIndices(); leftIndex_ = afIdx.first; rightIndex_ = afIdx.second; cFunctions_ = std::vector<boost::shared_ptr<cFunction> >( rightIndex_ - leftIndex_ + 2); // extrapolation in the leftmost interval Brent brent; bool success; Real secl = 0.0; do { success = true; try { Real k1 = k_[leftIndex_]; Real c1 = c_[leftIndex_]; Real c0 = c_[0]; secl = (c_[leftIndex_] - c_[0]) / (k_[leftIndex_] - k_[0]); Real sec = (c_[leftIndex_ + 1] - c_[leftIndex_]) / (k_[leftIndex_ + 1] - k_[leftIndex_]); Real c1p; if (interpolate_) c1p = (secl + sec) / 2; else { c1p = (blackFormula(Option::Call, k1 + gap_, f_, sqrt(source_->variance(k1 + gap_))) - blackFormula(Option::Call, k1, f_, sqrt(source_->variance(k1)))) / gap_; QL_REQUIRE(secl < c1p && c1p <= 0.0, "dummy"); // can not extrapolate so throw exception which is caught // below } sHelper1 sh1(k1, c0, c1, c1p); Real s = brent.solve(sh1, QL_KAHALE_ACC, 0.20, 0.00, QL_KAHALE_SMAX); // numerical parameters // hardcoded here sh1(s); boost::shared_ptr<cFunction> cFct1( new cFunction(sh1.f_, s, 0.0, sh1.b_)); cFunctions_[0] = cFct1; // sanity check - in rare cases we can get digitials // which are not monotonic or greater than 1.0 // due to numerical effects. Move to the next index in // these cases. Real dig = digitalOptionPrice(k1 / 2.0); QL_REQUIRE(dig >= -c1p && dig <= 1.0, "dummy"); } catch (...) { leftIndex_++; success = false; } } while (!success && leftIndex_ < rightIndex_); QL_REQUIRE( leftIndex_ < rightIndex_, "can not extrapolate to left, right index of af region reached (" << rightIndex_ << ")"); // interpolation Real cp0 = 0.0, cp1 = 0.0; if (interpolate_) { for (Size i = leftIndex_; i < rightIndex_; i++) { Real k0 = k_[i]; Real k1 = k_[i + 1]; Real c0 = c_[i]; Real c1 = c_[i + 1]; Real sec = (c_[i + 1] - c_[i]) / (k_[i + 1] - k_[i]); if (i == leftIndex_) cp0 = leftIndex_ > 0 ? (secl + sec) / 2.0 : sec; Real secr; if (i == rightIndex_ - 1) secr = 0.0; else secr = (c_[i + 2] - c_[i + 1]) / (k_[i + 2] - k_[i + 1]); cp1 = (sec + secr) / 2.0; aHelper ah(k0, k1, c0, c1, cp0, cp1); Real a; bool valid = false; try { a = brent.solve( ah, QL_KAHALE_ACC, 0.5 * (cp1 + (1.0 + cp0)), cp1 + QL_KAHALE_EPS, 1.0 + cp0 - QL_KAHALE_EPS); // numerical parameters hardcoded here valid = true; } catch (...) { // delete the right point of the interval where we try to // interpolate moneynessGrid_.erase(moneynessGrid_.begin() + (i + 1)); k_.erase(k_.begin() + (i + 1)); c_.erase(c_.begin() + (i + 1)); cFunctions_.erase(cFunctions_.begin() + (i + 1)); rightIndex_--; i--; } if (valid) { ah(a); boost::shared_ptr<cFunction> cFct( new cFunction(ah.f_, ah.s_, a, ah.b_)); cFunctions_[leftIndex_ > 0 ? i - leftIndex_ + 1 : 0] = cFct; cp0 = cp1; } } } // extrapolation of right wing do { success = true; try { Real k0 = k_[rightIndex_]; Real c0 = c_[rightIndex_]; Real cp0; if (interpolate_) cp0 = 0.5 * (c_[rightIndex_] - c_[rightIndex_ - 1]) / (k_[rightIndex_] - k_[rightIndex_ - 1]); else { cp0 = (blackFormula(Option::Call, k0, f_, sqrt(source_->variance(k0))) - blackFormula(Option::Call, k0 - gap_, f_, sqrt(source_->variance(k0 - gap_)))) / gap_; } boost::shared_ptr<cFunction> cFct; if (exponentialExtrapolation_) { QL_REQUIRE(-cp0 / c0 > 0.0, "dummy"); // this is caught // below cFct = boost::shared_ptr<cFunction>( new cFunction(-cp0 / c0, std::log(c0) - cp0 / c0 * k0)); } else { sHelper sh(k0, c0, cp0); Real s; s = brent.solve(sh, QL_KAHALE_ACC, 0.20, 0.0, QL_KAHALE_SMAX); // numerical parameters // hardcoded here sh(s); cFct = boost::shared_ptr<cFunction>( new cFunction(sh.f_, s, 0.0, 0.0)); } cFunctions_[rightIndex_ - leftIndex_ + 1] = cFct; } catch (...) { rightIndex_--; success = false; } } while (!success && rightIndex_ > leftIndex_); QL_REQUIRE( leftIndex_ < rightIndex_, "can not extrapolate to right, left index of af region reached (" << leftIndex_ << ")"); }