boost::shared_ptr<SmileSection> SwaptionVolCube2::smileSectionImpl(const Date& optionDate, const Period& swapTenor) const { calculate(); Rate atmForward = atmStrike(optionDate, swapTenor); Volatility atmVol = atmVol_->volatility(optionDate, swapTenor, atmForward); Time optionTime = timeFromReference(optionDate); Real exerciseTimeSqrt = std::sqrt(optionTime); std::vector<Real> strikes, stdDevs; strikes.reserve(nStrikes_); stdDevs.reserve(nStrikes_); Time length = swapLength(swapTenor); for (Size i=0; i<nStrikes_; ++i) { strikes.push_back(atmForward + strikeSpreads_[i]); stdDevs.push_back(exerciseTimeSqrt*( atmVol + volSpreadsInterpolator_[i](length, optionTime))); } Real shift = atmVol_->shift(optionTime,length); return boost::shared_ptr<SmileSection>(new InterpolatedSmileSection<Linear>(optionTime, strikes, stdDevs, atmForward, Linear(), Actual365Fixed(), volatilityType(), shift)); }
inline Volatility SwaptionVolatilityStructure::volatilityImpl(const Date& optionDate, const Period& swapTenor, Rate strike) const { return volatilityImpl(timeFromReference(optionDate), swapLength(swapTenor), strike); }
inline Real SwaptionVolatilityStructure::shift(Time optionTime, const Period& swapTenor, bool extrapolate) const { checkSwapTenor(swapTenor, extrapolate); checkRange(optionTime, extrapolate); Time length = swapLength(swapTenor); return shiftImpl(optionTime, length); }
inline Volatility SwaptionVolatilityStructure::volatility(Time optionTime, const Period& swapTenor, Rate strike, bool extrapolate) const { checkSwapTenor(swapTenor, extrapolate); checkRange(optionTime, extrapolate); checkStrike(strike, extrapolate); Time length = swapLength(swapTenor); return volatilityImpl(optionTime, length, strike); }
inline Time SwaptionVolatilityStructure::maxSwapLength() const { return swapLength(maxSwapTenor()); }
// 4. default implementation of Date-based xxxImpl methods // relying on the equivalent Time-based methods inline boost::shared_ptr<SmileSection> SwaptionVolatilityStructure::smileSectionImpl(const Date& optionDate, const Period& swapT) const { return smileSectionImpl(timeFromReference(optionDate), swapLength(swapT)); }
//@} //! \name Other inspectors //@{ //! returns the lower indexes of surrounding volatility matrix corners std::pair<Size,Size> locate(const Date& optionDate, const Period& swapTenor) const { return locate(timeFromReference(optionDate), swapLength(swapTenor)); }
std::vector<Real> SwaptionVolCube1::spreadVolInterpolation( const Date& atmOptionDate, const Period& atmSwapTenor) const { Time atmOptionTime = timeFromReference(atmOptionDate); Time atmTimeLength = swapLength(atmSwapTenor); std::vector<Real> result; const std::vector<Time>& optionTimes(sparseParameters_.optionTimes()); const std::vector<Time>& swapLengths(sparseParameters_.swapLengths()); const std::vector<Date>& optionDates = sparseParameters_.optionDates(); const std::vector<Period>& swapTenors = sparseParameters_.swapTenors(); std::vector<Real>::const_iterator optionTimesPreviousNode, swapLengthsPreviousNode; optionTimesPreviousNode = std::lower_bound(optionTimes.begin(), optionTimes.end(), atmOptionTime); Size optionTimesPreviousIndex = optionTimesPreviousNode - optionTimes.begin(); if (optionTimesPreviousIndex >0) optionTimesPreviousIndex --; swapLengthsPreviousNode = std::lower_bound(swapLengths.begin(), swapLengths.end(), atmTimeLength); Size swapLengthsPreviousIndex = swapLengthsPreviousNode - swapLengths.begin(); if (swapLengthsPreviousIndex >0) swapLengthsPreviousIndex --; std::vector< std::vector<boost::shared_ptr<SmileSection> > > smiles; std::vector<boost::shared_ptr<SmileSection> > smilesOnPreviousExpiry; std::vector<boost::shared_ptr<SmileSection> > smilesOnNextExpiry; QL_REQUIRE(optionTimesPreviousIndex+1 < sparseSmiles_.size(), "optionTimesPreviousIndex+1 >= sparseSmiles_.size()"); QL_REQUIRE(swapLengthsPreviousIndex+1 < sparseSmiles_[0].size(), "swapLengthsPreviousIndex+1 >= sparseSmiles_[0].size()"); smilesOnPreviousExpiry.push_back( sparseSmiles_[optionTimesPreviousIndex][swapLengthsPreviousIndex]); smilesOnPreviousExpiry.push_back( sparseSmiles_[optionTimesPreviousIndex][swapLengthsPreviousIndex+1]); smilesOnNextExpiry.push_back( sparseSmiles_[optionTimesPreviousIndex+1][swapLengthsPreviousIndex]); smilesOnNextExpiry.push_back( sparseSmiles_[optionTimesPreviousIndex+1][swapLengthsPreviousIndex+1]); smiles.push_back(smilesOnPreviousExpiry); smiles.push_back(smilesOnNextExpiry); std::vector<Real> optionsNodes(2); optionsNodes[0] = optionTimes[optionTimesPreviousIndex]; optionsNodes[1] = optionTimes[optionTimesPreviousIndex+1]; std::vector<Date> optionsDateNodes(2); optionsDateNodes[0] = optionDates[optionTimesPreviousIndex]; optionsDateNodes[1] = optionDates[optionTimesPreviousIndex+1]; std::vector<Real> swapLengthsNodes(2); swapLengthsNodes[0] = swapLengths[swapLengthsPreviousIndex]; swapLengthsNodes[1] = swapLengths[swapLengthsPreviousIndex+1]; std::vector<Period> swapTenorNodes(2); swapTenorNodes[0] = swapTenors[swapLengthsPreviousIndex]; swapTenorNodes[1] = swapTenors[swapLengthsPreviousIndex+1]; Rate atmForward = atmStrike(atmOptionDate, atmSwapTenor); Matrix atmForwards(2, 2, 0.0); Matrix atmVols(2, 2, 0.0); for (Size i=0; i<2; i++) { for (Size j=0; j<2; j++) { atmForwards[i][j] = atmStrike(optionsDateNodes[i], swapTenorNodes[j]); // atmVols[i][j] = smiles[i][j]->volatility(atmForwards[i][j]); atmVols[i][j] = atmVol_->volatility( optionsDateNodes[i], swapTenorNodes[j], atmForwards[i][j]); /* With the old implementation the interpolated spreads on ATM volatilities were null even if the spreads on ATM volatilities to be interpolated were non-zero. The new implementation removes this behaviour, but introduces a small ERROR in the cube: even if no spreads are applied on any cube ATM volatility corresponding to quoted smile sections (that is ATM volatilities in sparse cube), the cube ATM volatilities corresponding to not quoted smile sections (that is ATM volatilities in dense cube) are no more exactly the quoted values, but that ones PLUS the linear interpolation of the fit errors on the ATM volatilities in sparse cube whose spreads are used in the calculation. A similar imprecision is introduced to the volatilities in dense cube whith moneyness near to 1. (See below how spreadVols are calculated). The extent of this error depends on the quality of the fit: in case of good fits it is negligibile. */ } } for (Size k=0; k<nStrikes_; k++){ const Real strike = std::max(atmForward + strikeSpreads_[k],MINSTRIKE); const Real moneyness = atmForward/strike; Matrix strikes(2,2,0.); Matrix spreadVols(2,2,0.); for (Size i=0; i<2; i++){ for (Size j=0; j<2; j++){ strikes[i][j] = atmForwards[i][j]/moneyness; spreadVols[i][j] = smiles[i][j]->volatility(strikes[i][j]) - atmVols[i][j]; } } Cube localInterpolator(optionsDateNodes, swapTenorNodes, optionsNodes, swapLengthsNodes, 1); localInterpolator.setLayer(0, spreadVols); localInterpolator.updateInterpolators(); result.push_back(localInterpolator(atmOptionTime, atmTimeLength)[0]); } return result; }
inline Real SwaptionVolatilityStructure::shiftImpl(const Date &optionDate, const Period &swapTenor) const { return shiftImpl(timeFromReference(optionDate), swapLength(swapTenor)); }