Real LinearTsrPricer::strikeFromVegaRatio(Real ratio, Option::Type optionType, Real referenceStrike) const { Real a, b, min, max, k; if (optionType == Option::Call) { a = swapRateValue_; min = referenceStrike; b = max = k = std::min(smileSection_->maxStrike(), adjustedUpperBound_); } else { a = min = k = std::max(smileSection_->minStrike(), adjustedLowerBound_); b = swapRateValue_; max = referenceStrike; } VegaRatioHelper h(&*smileSection_, smileSection_->vega(swapRateValue_) * ratio); Brent solver; try { k = solver.solve(h, 1.0E-5, (a + b) / 2.0, a, b); } catch (...) { // use default value set above } return std::min(std::max(k, min), max); }
Spread CallableBond::OAS(Real cleanPrice, const Handle<YieldTermStructure>& engineTS, const DayCounter& dayCounter, Compounding compounding, Frequency frequency, Date settlement, Real accuracy, Size maxIterations, Spread guess) { if (settlement == Date()) settlement = settlementDate(); Real dirtyPrice = cleanPrice + accruedAmount(settlement); boost::function<Real(Real)> f = NPVSpreadHelper(*this); OASHelper obj(f, dirtyPrice); Brent solver; solver.setMaxEvaluations(maxIterations); Real step = 0.001; Spread oas=solver.solve(obj, accuracy, guess, step); return continuousToConv(oas, *this, engineTS, dayCounter, compounding, frequency); }
Real LinearTsrPricer::strikeFromPrice(Real price, Option::Type optionType, Real referenceStrike) const { Real a, b, min, max, k; if (optionType == Option::Call) { a = swapRateValue_; min = referenceStrike; b = max = k = std::min(smileSection_->maxStrike(), settings_.upperRateBound_); } else { a = min = k = std::max(smileSection_->minStrike(), settings_.lowerRateBound_); b = swapRateValue_; max = referenceStrike; } PriceHelper h(&*smileSection_, optionType, price); Brent solver; try { k = solver.solve(h, 1.0E-5, swapRateValue_, a, b); } catch (...) { // use default value set above } return std::min(std::max(k, min), max); }
Volatility CalibrationHelper::impliedVolatility(Real targetValue, Real accuracy, Size maxEvaluations, Volatility minVol, Volatility maxVol) const { ImpliedVolatilityHelper f(*this,targetValue); Brent solver; solver.setMaxEvaluations(maxEvaluations); return solver.solve(f,accuracy,volatility_->value(),minVol,maxVol); }
double Gamma2Distribution::cumulativeInv(const double p, const bool fast, const long bins) { if(fast) { if(!fastCalculated_ || bins!=fastBins_) { fastBins_=bins; preCalculateCumulativeInv(); } return fastCumulativeInv(p); } G2InvHelper hlp(this,p); Brent b; return b.solve(hlp,INVG2ACC,1.0,0.1); }
std::vector<Volatility> OptionletStripper2::spreadsVolImplied() const { Brent solver; std::vector<Volatility> result(nOptionExpiries_); Volatility guess = 0.0001, minSpread = -0.1, maxSpread = 0.1; for (Size j=0; j<nOptionExpiries_; ++j) { ObjectiveFunction f(stripper1_, caps_[j], atmCapFloorPrices_[j]); solver.setMaxEvaluations(maxEvaluations_); Volatility root = solver.solve(f, accuracy_, guess, minSpread, maxSpread); result[j] = root; } return result; }
Volatility CallableBond::impliedVolatility( Real targetValue, const Handle<YieldTermStructure>& discountCurve, Real accuracy, Size maxEvaluations, Volatility minVol, Volatility maxVol) const { calculate(); QL_REQUIRE(!isExpired(), "instrument expired"); Volatility guess = 0.5*(minVol + maxVol); blackDiscountCurve_.linkTo(*discountCurve, false); ImpliedVolHelper f(*this,targetValue); Brent solver; solver.setMaxEvaluations(maxEvaluations); return solver.solve(f, accuracy, guess, minVol, maxVol); }
Volatility HestonBlackVolSurface::blackVolImpl(Time t, Real strike) const { const boost::shared_ptr<HestonProcess> process = hestonModel_->process(); const DiscountFactor df = process->riskFreeRate()->discount(t, true); const DiscountFactor div = process->dividendYield()->discount(t, true); const Real spotPrice = process->s0()->value(); const Real fwd = spotPrice * process->dividendYield()->discount(t, true) / process->riskFreeRate()->discount(t, true); const PlainVanillaPayoff payoff( fwd > strike ? Option::Put : Option::Call, strike); const Real kappa = hestonModel_->kappa(); const Real theta = hestonModel_->theta(); const Real rho = hestonModel_->rho(); const Real sigma = hestonModel_->sigma(); const Real v0 = hestonModel_->v0(); const AnalyticHestonEngine::ComplexLogFormula cpxLogFormula = AnalyticHestonEngine::Gatheral; const AnalyticHestonEngine* const hestonEnginePtr = 0; Real npv; Size evaluations; AnalyticHestonEngine::doCalculation( df, div, spotPrice, strike, t, kappa, theta, sigma, v0, rho, payoff, integration_, cpxLogFormula, hestonEnginePtr, npv, evaluations); if (npv <= 0.0) return std::sqrt(theta); Brent solver; solver.setMaxEvaluations(10000); const Volatility guess = std::sqrt(theta); const Real accuracy = std::numeric_limits<Real>::epsilon(); const boost::function<Real(Real)> f = boost::bind( &blackValue, payoff.optionType(), strike, fwd, t, _1, df, npv); return solver.solve(f, accuracy, guess, 0.01); }
Real InverseNonCentralChiSquareDistribution::operator()(Real x) const { // first find the right side of the interval Real upper = guess_; Size evaluations = maxEvaluations_; while (nonCentralDist_(upper) < x && evaluations > 0) { upper*=2.0; --evaluations; } // use a brent solver for the rest Brent solver; solver.setMaxEvaluations(evaluations); return solver.solve(compose(std::bind2nd(std::minus<Real>(),x), nonCentralDist_), accuracy_, 0.75*upper, (evaluations == maxEvaluations_)? 0.0: 0.5*upper, upper); }
Volatility CdsOption::impliedVolatility( Real targetValue, const Handle<YieldTermStructure>& termStructure, const Handle<DefaultProbabilityTermStructure>& probability, Real recoveryRate, Real accuracy, Size maxEvaluations, Volatility minVol, Volatility maxVol) const { calculate(); QL_REQUIRE(!isExpired(), "instrument expired"); Volatility guess = 0.10; ImpliedVolHelper f(*this, probability, recoveryRate, termStructure, targetValue); Brent solver; solver.setMaxEvaluations(maxEvaluations); return solver.solve(f, accuracy, guess, minVol, maxVol); }
Volatility ImpliedVolatilityHelper::calculate( const Instrument& instrument, const PricingEngine& engine, SimpleQuote& volQuote, Real targetValue, Real accuracy, Natural maxEvaluations, Volatility minVol, Volatility maxVol) { instrument.setupArguments(engine.getArguments()); engine.getArguments()->validate(); PriceError f(engine, volQuote, targetValue); Brent solver; solver.setMaxEvaluations(maxEvaluations); Volatility guess = (minVol+maxVol)/2.0; Volatility result = solver.solve(f, accuracy, guess, minVol, maxVol); return result; }
Real CmsSpreadOption::impliedCorrelation(boost::shared_ptr<CmsPricer> pricer,const Period& pillar, const double price,Size preCalculatedFixings,double preCalculatedPrice) { Brent b; double res=b.solve(CmsSpreadOptionImplCorrHelper(this,pricer,pillar,price,preCalculatedFixings,preCalculatedPrice),CORRACC,pricer->correlationTermStructure()->correlation(pillar,true),CORRSTEP); double res2=atan(res)*2.0/M_PI; pricer->correlationTermStructure()->setPillarCorrelation(pillar,res2); return res2; /*CmsSpreadOptionImplCorrHelper h(boost::shared_ptr<CmsSpreadOption>(this),pricer,pillar,price); double x1=-MAXCORR,x2=MAXCORR,x; double f1=h(x1); double f2=h(x2); double f; double step=MAXCORR+MAXCORR; if(f1*f2>=0.0) QL_FAIL("No sign change for correlations -1,1"); do { x=(x1+x2)/2.0; f=h(x); if(f1*f<0.0) x2=x; else x1=x; step/=step; pricer->correlationTermStructure()->setPillarCorrelation(pillar,x); } while(step>CORRACC); pricer->correlationTermStructure()->setPillarCorrelation(pillar,x); return x;*/ }
void IterativeBootstrap<Curve>::calculate() const { Size n = ts_->instruments_.size(); // ensure rate helpers are sorted std::sort(ts_->instruments_.begin(), ts_->instruments_.end(), detail::BootstrapHelperSorter()); // check that there is no instruments with the same maturity for (Size i=1; i<n; ++i) { Date m1 = ts_->instruments_[i-1]->latestDate(), m2 = ts_->instruments_[i]->latestDate(); QL_REQUIRE(m1 != m2, "two instruments have the same maturity ("<< m1 <<")"); } // check that there is no instruments with invalid quote for (Size i=0; i<n; ++i) QL_REQUIRE(ts_->instruments_[i]->quote()->isValid(), io::ordinal(i+1) << " instrument (maturity: " << ts_->instruments_[i]->latestDate() << ") has an invalid quote"); // setup instruments for (Size i=0; i<n; ++i) { // don't try this at home! // This call creates instruments, and removes "const". // There is a significant interaction with observability. ts_->instruments_[i]->setTermStructure(const_cast<Curve*>(ts_)); } // calculate dates and times ts_->dates_ = std::vector<Date>(n+1); ts_->times_ = std::vector<Time>(n+1); ts_->dates_[0] = Traits::initialDate(ts_); ts_->times_[0] = ts_->timeFromReference(ts_->dates_[0]); for (Size i=0; i<n; ++i) { ts_->dates_[i+1] = ts_->instruments_[i]->latestDate(); ts_->times_[i+1] = ts_->timeFromReference(ts_->dates_[i+1]); } // set initial guess only if the current curve cannot be used as guess if (validCurve_) { QL_ENSURE(ts_->data_.size() == n+1, "dimension mismatch: expected " << n+1 << ", actual " << ts_->data_.size()); } else { ts_->data_ = std::vector<Rate>(n+1); ts_->data_[0] = Traits::initialValue(ts_); for (Size i=0; i<n; ++i) ts_->data_[i+1] = Traits::initialGuess(); } Brent solver; Size maxIterations = Traits::maxIterations(); for (Size iteration=0; ; ++iteration) { std::vector<Rate> previousData = ts_->data_; // restart from the previous interpolation if (validCurve_) { ts_->interpolation_ = ts_->interpolator_.interpolate( ts_->times_.begin(), ts_->times_.end(), ts_->data_.begin()); } for (Size i=1; i<n+1; ++i) { // calculate guess before extending interpolation // to ensure that any extrapolation is performed // using the curve bootstrapped so far and no more boost::shared_ptr<typename Traits::helper> instrument = ts_->instruments_[i-1]; Rate guess = 0.0; if (validCurve_ || iteration>0) { guess = ts_->data_[i]; } else if (i==1) { guess = Traits::initialGuess(); } else { // most traits extrapolate guess = Traits::guess(ts_, ts_->dates_[i]); } // bracket Real min = Traits::minValueAfter(i, ts_->data_); Real max = Traits::maxValueAfter(i, ts_->data_); if (guess<=min || guess>=max) guess = (min+max)/2.0; if (!validCurve_ && iteration == 0) { // extend interpolation a point at a time try { ts_->interpolation_ = ts_->interpolator_.interpolate( ts_->times_.begin(), ts_->times_.begin()+i+1, ts_->data_.begin()); } catch(...) { if (!Interpolator::global) throw; // no chance to fix it in a later iteration // otherwise, if the target interpolation is // not usable yet ts_->interpolation_ = Linear().interpolate( ts_->times_.begin(), ts_->times_.begin()+i+1, ts_->data_.begin()); } } // required because we just changed the data // is it really required? ts_->interpolation_.update(); try { BootstrapError<Curve> error(ts_, instrument, i); Real r = solver.solve(error,ts_->accuracy_,guess,min,max); // redundant assignment (as it has been already performed // by BootstrapError in solve procedure), but safe ts_->data_[i] = r; } catch (std::exception &e) { validCurve_ = false; QL_FAIL(io::ordinal(iteration+1) << " iteration: " "failed at " << io::ordinal(i) << " instrument" ", maturity " << ts_->dates_[i] << ", reference date " << ts_->dates_[0] << ": " << e.what()); } } if (!Interpolator::global) break; // no need for convergence loop else if (!validCurve_ && iteration == 0) { // ensure the target interpolation is used ts_->interpolation_ = ts_->interpolator_.interpolate(ts_->times_.begin(), ts_->times_.end(), ts_->data_.begin()); // at least one more iteration is needed to check convergence continue; } // exit conditions Real improvement = 0.0; for (Size i=1; i<n+1; ++i) improvement=std::max(improvement, std::fabs(ts_->data_[i]-previousData[i])); if (improvement<=ts_->accuracy_) // convergence reached break; QL_REQUIRE(iteration+1 < maxIterations, "convergence not reached after " << iteration+1 << " iterations; last improvement " << improvement << ", required accuracy " << ts_->accuracy_); } validCurve_ = true; }
void InterpolatedYoYOptionletStripper<Interpolator1D>:: initialize(const boost::shared_ptr<YoYCapFloorTermPriceSurface> &s, const boost::shared_ptr<YoYInflationCapFloorEngine> &p, const Real slope) const { YoYCapFloorTermPriceSurface_ = s; p_ = p; lag_ = YoYCapFloorTermPriceSurface_->observationLag(); frequency_ = YoYCapFloorTermPriceSurface_->frequency(); indexIsInterpolated_ = YoYCapFloorTermPriceSurface_->indexIsInterpolated(); Natural fixingDays_ = YoYCapFloorTermPriceSurface_->fixingDays(); Natural settlementDays = 0; // always Calendar cal = YoYCapFloorTermPriceSurface_->calendar(); BusinessDayConvention bdc = YoYCapFloorTermPriceSurface_->businessDayConvention(); DayCounter dc = YoYCapFloorTermPriceSurface_->dayCounter(); // switch from caps to floors when out of floors Rate maxFloor = YoYCapFloorTermPriceSurface_->floorStrikes().back(); YoYInflationCapFloor::Type useType = YoYInflationCapFloor::Floor; Period TPmin = YoYCapFloorTermPriceSurface_->maturities().front(); // create a "fake index" based on Generic, this should work // provided that the lag and frequency are correct RelinkableHandle<YoYInflationTermStructure> hYoY( YoYCapFloorTermPriceSurface_->YoYTS()); boost::shared_ptr<YoYInflationIndex> anIndex( new YYGenericCPI(frequency_, false, false, lag_, Currency(), hYoY)); // strip each K separatly for (Size i=0; i<YoYCapFloorTermPriceSurface_->strikes().size(); i++) { Rate K = YoYCapFloorTermPriceSurface_->strikes()[i]; if (K > maxFloor) useType = YoYInflationCapFloor::Cap; // solve for the initial point on the vol curve Brent solver; Real solverTolerance_ = 1e-7; // these are VOLATILITY guesses (always +) Real lo = 0.00001, hi = 0.08; Real guess = (hi+lo)/2.0; Real found; Real priceToMatch = (useType == YoYInflationCapFloor::Cap ? YoYCapFloorTermPriceSurface_->capPrice(TPmin, K) : YoYCapFloorTermPriceSurface_->floorPrice(TPmin, K)); try{ found = solver.solve( ObjectiveFunction(useType, slope, K, lag_, fixingDays_, anIndex, YoYCapFloorTermPriceSurface_, p_, priceToMatch), solverTolerance_, guess, lo, hi ); } catch( std::exception &e) { QL_FAIL("failed to find solution here because: " << e.what()); } // ***create helpers*** Real notional = 10000; // work in bps std::vector<boost::shared_ptr<BootstrapHelper<YoYOptionletVolatilitySurface> > > helperInstruments; std::vector<boost::shared_ptr<YoYOptionletHelper> > helpers; for (Size j = 0; j < YoYCapFloorTermPriceSurface_->maturities().size(); j++){ Period Tp = YoYCapFloorTermPriceSurface_->maturities()[j]; Real nextPrice = (useType == YoYInflationCapFloor::Cap ? YoYCapFloorTermPriceSurface_->capPrice(Tp, K) : YoYCapFloorTermPriceSurface_->floorPrice(Tp, K)); Handle<Quote> quote1(boost::shared_ptr<Quote>( new SimpleQuote( nextPrice ))); // helper should be an integer number of periods away, // this is enforced by rounding Size nT = (Size)floor(s->timeFromReference(s->yoyOptionDateFromTenor(Tp))+0.5); helpers.push_back(boost::shared_ptr<YoYOptionletHelper>( new YoYOptionletHelper(quote1, notional, useType, lag_, dc, cal, fixingDays_, anIndex, K, nT, p_))); boost::shared_ptr<ConstantYoYOptionletVolatility> yoyVolBLACK( new ConstantYoYOptionletVolatility(found, settlementDays, cal, bdc, dc, lag_, frequency_, false, // -100% to +300% -1.0,3.0)); helpers[j]->setTermStructure( // gets underlying pointer & removes const const_cast<ConstantYoYOptionletVolatility*>( yoyVolBLACK.get())); helperInstruments.push_back(helpers[j]); } // ***bootstrap*** // this is the artificial vol at zero so that first section works Real Tmin = s->timeFromReference(s->yoyOptionDateFromTenor(TPmin)); Volatility baseYoYVolatility = found - slope * Tmin * found; Rate eps = std::max(K, 0.02) / 1000.0; Rate minStrike = K-eps; Rate maxStrike = K+eps; boost::shared_ptr< PiecewiseYoYOptionletVolatilityCurve<Interpolator1D> > testPW( new PiecewiseYoYOptionletVolatilityCurve<Interpolator1D>( settlementDays, cal, bdc, dc, lag_, frequency_, indexIsInterpolated_, minStrike, maxStrike, baseYoYVolatility, helperInstruments) ); testPW->recalculate(); volCurves_.push_back(testPW); } }
NoArbSabrModel::NoArbSabrModel(const Real expiryTime, const Real forward, const Real alpha, const Real beta, const Real nu, const Real rho) : expiryTime_(expiryTime), externalForward_(forward), alpha_(alpha), beta_(beta), nu_(nu), rho_(rho), forward_(forward), numericalForward_(forward) { using namespace ext::placeholders; QL_REQUIRE(expiryTime > 0.0 && expiryTime <= detail::NoArbSabrModel::expiryTime_max, "expiryTime (" << expiryTime << ") out of bounds"); QL_REQUIRE(forward > 0.0, "forward (" << forward << ") must be positive"); QL_REQUIRE(beta >= detail::NoArbSabrModel::beta_min && beta <= detail::NoArbSabrModel::beta_max, "beta (" << beta << ") out of bounds"); Real sigmaI = alpha * std::pow(forward, beta - 1.0); QL_REQUIRE(sigmaI >= detail::NoArbSabrModel::sigmaI_min && sigmaI <= detail::NoArbSabrModel::sigmaI_max, "sigmaI = alpha*forward^(beta-1.0) (" << sigmaI << ") out of bounds, alpha=" << alpha << " beta=" << beta << " forward=" << forward); QL_REQUIRE(nu >= detail::NoArbSabrModel::nu_min && nu <= detail::NoArbSabrModel::nu_max, "nu (" << nu << ") out of bounds"); QL_REQUIRE(rho >= detail::NoArbSabrModel::rho_min && rho <= detail::NoArbSabrModel::rho_max, "rho (" << rho << ") out of bounds"); // determine a region sufficient for integration in the normal case fmin_ = fmax_ = forward_; for (Real tmp = p(fmax_); tmp > std::max(detail::NoArbSabrModel::i_accuracy / std::max(1.0, fmax_ - fmin_), detail::NoArbSabrModel::density_threshold); tmp = p(fmax_)) { fmax_ *= 2.0; } for (Real tmp = p(fmin_); tmp > std::max(detail::NoArbSabrModel::i_accuracy / std::max(1.0, fmax_ - fmin_), detail::NoArbSabrModel::density_threshold); tmp = p(fmin_)) { fmin_ *= 0.5; } fmin_ = std::max(detail::NoArbSabrModel::strike_min, fmin_); QL_REQUIRE(fmax_ > fmin_, "could not find a reasonable integration domain"); integrator_ = ext::make_shared<GaussLobattoIntegral>( detail::NoArbSabrModel::i_max_iterations, detail::NoArbSabrModel::i_accuracy); detail::D0Interpolator d0(forward_, expiryTime_, alpha_, beta_, nu_, rho_); absProb_ = d0(); try { Brent b; Real start = std::sqrt(externalForward_ - detail::NoArbSabrModel::strike_min); Real tmp = b.solve(ext::bind(&NoArbSabrModel::forwardError, this, _1), detail::NoArbSabrModel::forward_accuracy, start, std::min(detail::NoArbSabrModel::forward_search_step, start / 2.0)); forward_ = tmp * tmp + detail::NoArbSabrModel::strike_min; } catch (Error&) { // fall back to unadjusted forward forward_ = externalForward_; } Real d = forwardError(std::sqrt(forward_ - detail::NoArbSabrModel::strike_min)); numericalForward_ = d + externalForward_; }
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_ << ")"); }
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); } }
void AnalyticCompoundOptionEngine::calculate() const { QL_REQUIRE(strikeDaughter()>0.0, "Daughter strike must be positive"); QL_REQUIRE(strikeMother()>0.0, "Mother strike must be positive"); QL_REQUIRE(spot() >= 0.0, "negative or null underlying given"); /* Solver Setup ***************************************************/ Date helpDate(process_->riskFreeRate()->referenceDate()); Date helpMaturity=helpDate+(maturityDaughter()-maturityMother())*Days; Real vol =process_->blackVolatility()->blackVol(helpMaturity, strikeDaughter()); Time helpTimeToMat=process_->time(helpMaturity); vol=vol*std::sqrt(helpTimeToMat); DiscountFactor dividendDiscount = process_->dividendYield()->discount(helpMaturity); DiscountFactor riskFreeDiscount = process_->riskFreeRate()->discount(helpMaturity); boost::shared_ptr<ImpliedSpotHelper> f( new ImpliedSpotHelper(dividendDiscount, riskFreeDiscount, vol, payoffDaughter(), strikeMother())); Brent solver; solver.setMaxEvaluations(1000); Real accuracy = 1.0e-6; Real X=0.0; Real sSolved=0.0; sSolved=solver.solve(*f, accuracy, strikeDaughter(), 1.0e-6, strikeDaughter()*1000.0); X=transformX(sSolved); // transform stock to return as in Wystup's book /* Solver Setup Finished*****************************************/ Real phi=typeDaughter(); // -1 or 1 Real w=typeMother(); // -1 or 1 Real rho=std::sqrt(residualTimeMother()/residualTimeDaughter()); BivariateCumulativeNormalDistributionDr78 N2(w*rho) ; DiscountFactor ddD=dividendDiscountDaughter(); DiscountFactor rdD=riskFreeDiscountDaughter(); //DiscountFactor ddM=dividendDiscountMother(); DiscountFactor rdM=riskFreeDiscountMother(); Real XmSM=X-stdDeviationMother(); Real S=spot(); Real dP=dPlus(); Real dPT12=dPlusTau12(sSolved); Real vD=volatilityDaughter(); Real dM=dMinus(); Real strD=strikeDaughter(); Real strM=strikeMother(); Real rTM=residualTimeMother(); Real rTD=residualTimeDaughter(); Real rD=riskFreeRateDaughter(); Real dD=dividendRateDaughter(); Real tempRes=0.0; Real tempDelta=0.0; Real tempGamma=0.0; Real tempVega=0.0; Real tempTheta=0.0; Real N2XmSM=N2(-phi*w*XmSM,phi*dP); Real N2X=N2(-phi*w*X,phi*dM); Real NeX=N_(-phi*w*e(X)); Real NX=N_(-phi*w*X); Real NT12=N_(phi*dPT12); Real ndP=n_(dP); Real nXm=n_(XmSM); Real invMTime=1/std::sqrt(rTM); Real invDTime=1/std::sqrt(rTD); tempRes=phi*w*S*ddD*N2XmSM-phi*w*strD*rdD*N2X-w*strM*rdM*NX; tempDelta=phi*w*ddD*N2XmSM; tempGamma=(ddD/(vD*S))*(invMTime*nXm*NT12+w*invDTime*ndP*NeX); tempVega=ddD*S*((1/invMTime)*nXm*NT12+w*(1/invDTime)*ndP*NeX); tempTheta+=phi*w*dD*S*ddD*N2XmSM-phi*w*rD*strD*rdD*N2X-w*rD*strM*rdM*NX; tempTheta-=0.5*vD*S*ddD*(invMTime*nXm*NT12+w*invDTime*ndP*NeX); results_.value=tempRes; results_.delta=tempDelta; results_.gamma=tempGamma; results_.vega=tempVega; results_.theta=tempTheta; }
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_ << ")"); }