const Disposable<Array> Gaussian1dNonstandardSwaptionEngine::initialGuess( const Date &expiry) const { Size fixedIdx = std::upper_bound(arguments_.fixedResetDates.begin(), arguments_.fixedResetDates.end(), expiry - 1) - arguments_.fixedResetDates.begin(); Array initial(3); Real nominalSum = 0.0, weightedRate = 0.0, ind = 0.0; for (Size i = fixedIdx; i < arguments_.fixedResetDates.size(); i++) { nominalSum += arguments_.fixedNominal[i]; Real rate = arguments_.fixedRate[i]; if (close(rate, 0.0)) rate = 0.03; // this value is at least better than zero weightedRate += arguments_.fixedNominal[i] * rate; if (arguments_.fixedNominal[i] > 1E-8) // exclude zero nominal periods ind += 1.0; } Real nominalAvg = nominalSum / ind; QL_REQUIRE(nominalSum > 0.0, "sum of nominals on fixed leg must be positive (" << nominalSum << ")"); weightedRate /= nominalSum; initial[0] = nominalAvg; initial[1] = model_->termStructure()->timeFromReference(underlyingLastDate()) - model_->termStructure()->timeFromReference(expiry); initial[2] = weightedRate; return initial; }
Disposable<std::vector<boost::shared_ptr<CalibrationHelper> > > BasketGeneratingEngine::calibrationBasket( const boost::shared_ptr<Exercise> &exercise, boost::shared_ptr<SwapIndex> standardSwapBase, boost::shared_ptr<SwaptionVolatilityStructure> swaptionVolatility, const CalibrationBasketType basketType) const { QL_REQUIRE( !standardSwapBase->forwardingTermStructure().empty(), "standard swap base forwarding term structure must not be empty."); QL_REQUIRE( !standardSwapBase->exogenousDiscount() || !standardSwapBase->discountingTermStructure().empty(), "standard swap base discounting term structure must not be empty."); std::vector<boost::shared_ptr<CalibrationHelper> > result; int minIdxAlive = std::upper_bound(exercise->dates().begin(), exercise->dates().end(), Settings::instance().evaluationDate()) - exercise->dates().begin(); boost::shared_ptr<RebatedExercise> rebEx = boost::dynamic_pointer_cast<RebatedExercise>(exercise); for (Size i = minIdxAlive; i < exercise->dates().size(); i++) { Date expiry = exercise->date(i); Real rebate = 0.0; Date rebateDate = expiry; if (rebEx != NULL) { rebate = rebEx->rebate(i); rebateDate = rebEx->rebatePaymentDate(i); } boost::shared_ptr<SwaptionHelper> helper; switch (basketType) { case Naive: { Real swapLength = swaptionVolatility->dayCounter().yearFraction( standardSwapBase->valueDate(expiry), underlyingLastDate()); boost::shared_ptr<SmileSection> sec = swaptionVolatility->smileSection( expiry, static_cast<Size>(swapLength * 12.0 + 0.5) * Months, true); Real atmStrike = sec->atmLevel(); Real atmVol; if (atmStrike == Null<Real>()) atmVol = sec->volatility(0.03); else atmVol = sec->volatility(atmStrike); helper = boost::shared_ptr<SwaptionHelper>(new SwaptionHelper( expiry, underlyingLastDate(), Handle<Quote>(boost::make_shared<SimpleQuote>(atmVol)), standardSwapBase->iborIndex(), standardSwapBase->fixedLegTenor(), standardSwapBase->dayCounter(), standardSwapBase->iborIndex()->dayCounter(), standardSwapBase->exogenousDiscount() ? standardSwapBase->discountingTermStructure() : standardSwapBase->forwardingTermStructure(), CalibrationHelper::RelativePriceError, Null<Real>(), 1.0)); break; } case MaturityStrikeByDeltaGamma: { // determine the npv, first and second order derivatives at // $y=0$ of the underlying swap const Real h = 0.0001; // finite difference step in $y$, make // this a parameter of the engine ? Real zSpreadDsc = oas_.empty() ? 1.0 : exp(-oas_->value() * onefactormodel_->termStructure() ->dayCounter() .yearFraction(expiry, rebateDate)); Real npvm = underlyingNpv(expiry, -h) + rebate * onefactormodel_->zerobond(rebateDate, expiry, -h, discountCurve_) * zSpreadDsc; Real npv = underlyingNpv(expiry, 0.0) + rebate * onefactormodel_->zerobond( rebateDate, expiry, 0, discountCurve_) * zSpreadDsc; Real npvp = underlyingNpv(expiry, h) + rebate * onefactormodel_->zerobond(rebateDate, expiry, h, discountCurve_) * zSpreadDsc; Real delta = (npvp - npvm) / (2.0 * h); Real gamma = (npvp - 2.0 * npv + npvm) / (h * h); QL_REQUIRE(npv * npv + delta * delta + gamma * gamma > 0.0, "(npv,delta,gamma) must have a positive norm"); // debug output // std::cout << "EXOTIC npv " << npv << " delta " << delta // << " gamma " << gamma << std::endl; // Real xtmp = -5.0; // std::cout // << "********************************************EXERCISE " // << expiry << " ******************" << std::endl; // std::cout << "globalExoticNpv;"; // while (xtmp <= 5.0 + QL_EPSILON) { // std::cout << underlyingNpv(expiry, xtmp) << ";"; // xtmp += 0.1; // } // std::cout << std::endl; // end debug output // play safe, we restrict the maximum maturity so to easily fit // in the date class restriction Real maxMaturity = swaptionVolatility->dayCounter().yearFraction( expiry, Date::maxDate() - 365); boost::shared_ptr<MatchHelper> matchHelper_; matchHelper_ = boost::shared_ptr<MatchHelper>(new MatchHelper( underlyingType(), npv, delta, gamma, onefactormodel_, standardSwapBase, expiry, maxMaturity, h)); // Optimize Array initial = initialGuess(expiry); QL_REQUIRE(initial.size() == 3, "initial guess must have size 3 (but is " << initial.size() << ")"); EndCriteria ec(1000, 200, 1E-8, 1E-8, 1E-8); // make these // criteria and the // optimizer itself // parameters of // the method ? Constraint constraint = NoConstraint(); Problem p(*matchHelper_, constraint, initial); LevenbergMarquardt lm; EndCriteria::Type ret = lm.minimize(p, ec); QL_REQUIRE(ret != EndCriteria::None && ret != EndCriteria::Unknown && ret != EndCriteria::MaxIterations, "optimizer returns error (" << ret << ")"); Array solution = p.currentValue(); Real maturity = fabs(solution[1]); Size years = (Size)std::floor(maturity); maturity -= (Real)years; maturity *= 12.0; Size months = (Size)std::floor(maturity + 0.5); if (years == 0 && months == 0) months = 1; // ensure a maturity of at least one months // maturity -= (Real)months; maturity *= 365.25; // Size days = (Size)std::floor(maturity); Period matPeriod = years * Years + months * Months; //+days*Days; // we have to floor the strike of the calibration instrument, // see warning in the header solution[2] = std::max(solution[2], 0.00001); // floor at 0.1bp // also the calibrated nominal may be zero, so we floor it, too solution[0] = std::max(solution[0], 0.000001); // float at 0.01bp helper = boost::shared_ptr<SwaptionHelper>(new SwaptionHelper( expiry, matPeriod, Handle<Quote>(boost::make_shared<SimpleQuote>( swaptionVolatility->volatility( expiry, matPeriod, solution[2], true))), standardSwapBase->iborIndex(), standardSwapBase->fixedLegTenor(), standardSwapBase->dayCounter(), standardSwapBase->iborIndex()->dayCounter(), standardSwapBase->exogenousDiscount() ? standardSwapBase->discountingTermStructure() : standardSwapBase->forwardingTermStructure(), CalibrationHelper::RelativePriceError, solution[2], fabs(solution[0]))); break; } default: QL_FAIL("Calibration basket type not known (" << basketType << ")"); } result.push_back(helper); } return result; }