36 std::vector<Date> volstepdates,
37 std::vector<Real> volatilities,
39 const std::vector<Date>& swaptionExpiries,
40 const std::vector<Period>& swaptionTenors,
41 const ext::shared_ptr<SwapIndex>& swapIndexBase,
45 sigma_(arguments_[0]), volstepdates_(
std::move(volstepdates)),
46 volatilities_(
std::move(volatilities)), swaptionVol_(swaptionVol),
47 swaptionExpiries_(swaptionExpiries), swaptionTenors_(swaptionTenors),
48 swapIndexBase_(swapIndexBase), iborIndex_(swapIndexBase->iborIndex()) {
50 QL_REQUIRE(swaptionExpiries.size() == swaptionTenors.size(),
51 "number of swaption expiries ("
52 << swaptionExpiries.size()
53 <<
") is differnt from number of swaption tenors ("
54 << swaptionTenors.size() <<
")");
56 "need at least one swaption expiry to calibrate numeraire");
58 "yield term structure handle is empty");
60 "swaption volatility structure is empty");
67 std::vector<Date> volstepdates,
68 std::vector<Real> volatilities,
70 const std::vector<Date>& capletExpiries,
71 ext::shared_ptr<IborIndex> iborIndex,
75 sigma_(arguments_[0]), volstepdates_(
std::move(volstepdates)),
76 volatilities_(
std::move(volatilities)), capletVol_(capletVol),
77 capletExpiries_(capletExpiries), iborIndex_(
std::move(iborIndex)) {
80 "need at least one caplet expiry to calibrate numeraire");
82 "yield term structure handle is empty");
106 "volsteptimes must be strictly increasing ("
125 "there must be n+1 volatilities ("
127 <<
") for n volatility step times ("
158 std::vector<Date>::const_iterator i;
159 std::vector<Period>::const_iterator j;
175 numeraireKnown = i->second.paymentDates_.back();
178 for (
auto j = i->second.paymentDates_.rbegin();
179 j != i->second.paymentDates_.rend() && done; ++j) {
180 if (*j < numeraireKnown) {
187 1,
static_cast<Integer>(((numeraireKnown - *j) / 365.25) * 12.0));
189 ->maturityDate() < numeraireKnown)
198 numeraireKnown = i->first;
223 numInt->enableExtrapolation();
240 <<
") occurs more than once in calibration set");
248 Schedule sched = underlying->fixedSchedule();
252 for (
unsigned int k = 1; k < sched.
size(); k++) {
255 k == 1 ? expiry : sched.
date(k - 1), sched.
date(k)));
265 "caplet expiry (" << expiry
266 <<
") occurs more than once in calibration set");
279 iborIndex_->dayCounter().yearFraction(expiry, endDate));
295 ext::shared_ptr<SmileSection> smileSection;
296 if (i->second.isCaplet_) {
298 i->second.yearFractions_[0] *
300 i->second.atm_ = (
termStructure()->discount(i->first,
true) -
302 i->second.paymentDates_[0],
true)) /
304 smileSection =
capletVol_->smileSection(i->first,
true);
307 for (
unsigned int k = 0; k < i->second.paymentDates_.size();
309 annuity += i->second.yearFractions_[k] *
311 i->second.paymentDates_[k],
true);
313 i->second.annuity_ = annuity;
314 i->second.atm_ = (
termStructure()->discount(i->first,
true) -
316 i->second.paymentDates_.back(),
true)) /
319 i->first, i->second.tenor_,
true);
322 i->second.rawSmileSection_ = ext::shared_ptr<SmileSection>(
325 int forcedLeftIndex = -1;
334 i->second.smileSection_ = ext::make_shared<KahaleSmileSection>(
336 i->second.rawSmileSection_, i->second.atm_,
345 forcedLeftIndex, forcedRightIndex);
348 ext::dynamic_pointer_cast<KahaleSmileSection>(
349 i->second.smileSection_)->coreIndices());
356 *i->second.rawSmileSection_,
361 QL_REQUIRE(i->second.rawSmileSection_->volatilityType() ==
363 "MarkovFunctional: SABR calibration to normal "
364 "input volatilities is not supported");
367 "for sabr calibration at least 4 points are needed (is "
372 v.push_back(i->second.rawSmileSection_->volatility(j));
377 ext::shared_ptr<SabrInterpolatedSmileSection> sabrSection(
379 i->first, i->second.atm_, k,
false,
380 i->second.rawSmileSection_->volatility(
382 v, 0.03, 0.80, 0.50, 0.00,
false,
false,
false,
383 false,
true, ext::shared_ptr<EndCriteria>(),
384 ext::shared_ptr<OptimizationMethod>(),
386 i->second.rawSmileSection_->shift()));
391 i->second.smileSection_ = ext::make_shared<
393 sabrSection, i->second.atm_,
false,
400 forcedLeftIndex, forcedRightIndex);
403 ext::dynamic_pointer_cast<KahaleSmileSection>(
404 i->second.smileSection_)->coreIndices());
409 i->second.smileSection_ =
411 i->second.rawSmileSection_, i->second.atm_);
415 i->second.smileSection_ = i->second.rawSmileSection_;
421 i->second.minRateDigital_ =
422 i->second.smileSection_->digitalOptionPrice(
424 i->second.smileSection_->shift(),
427 i->second.maxRateDigital_ =
428 i->second.smileSection_->digitalOptionPrice(
430 i->second.smileSection_->shift(),
447 int idx =
times_.size() - 2;
451 ext::shared_ptr<CustomSmileSection> mfSec;
453 mfSec = ext::dynamic_pointer_cast<CustomSmileSection>(
454 i->second.smileSection_);
456 "no CustomSmileSection given, this is unexpected...");
460 Array deflatedFinalPayments;
466 for (
unsigned int k = 0; k < i->second.paymentDates_.size(); k++) {
467 deflatedFinalPayments =
469 i->second.paymentDates_[k]),
471 discreteDeflatedAnnuities +=
472 deflatedFinalPayments * i->second.yearFractions_[k];
481 Real digitalsCorrectionFactor = 1.0;
484 digitalsCorrectionFactor);
494 digitalsCorrectionFactor = i->second.annuity_ / digital;
496 digitalsCorrectionFactor;
502 for (
int j =
y_.
size() - 1; j >= 0; j--) {
506 if (j == (
int)(
y_.
size() - 1)) {
513 discreteDeflatedAnnuities[j - 1],
y_[j - 1],
524 discreteDeflatedAnnuities[j - 1],
y_[j - 1],
533 0.0, cc, cb, ca, discreteDeflatedAnnuities[j],
539 "WARNING: integral for digitalPrice is "
541 << j <<
" (" << integral
542 <<
") --- reset it to zero.");
546 digital += integral * numeraire0 * digitalsCorrectionFactor;
550 swapRate = mfSec->inverseDigitalCall(
551 digital, i->second.annuity_);
552 }
else if (digital >= i->second.minRateDigital_) {
554 i->second.rawSmileSection_->shift();
556 }
else if (digital <= i->second.maxRateDigital_) {
561 i->first, i->second, digital, swapRate0,
562 i->second.rawSmileSection_->shift());
564 if (check && j < (
int)
y_.
size() - 1 &&
568 "WARNING: swap rate is decreasing in y for "
570 <<
", j=" << j <<
" (y, swap rate) is ("
572 << j + 1 <<
" it is (" <<
y_[j + 1] <<
","
573 << swapRate0 <<
") --- reset rate to "
574 << swapRate0 <<
" in node j=" << j);
579 1.0 / std::max(
swapRate * discreteDeflatedAnnuities[j] +
580 deflatedFinalPayments[j], 1E-6);
581 (*discreteNumeraire_)[idx][j] =
numeraire * normalization;
588 Real marketDeflatedZerobond =
591 for (
int j =
y_.
size() - 1; j >= 0; j--) {
592 (*discreteNumeraire_)[idx][j] *=
593 modelDeflatedZerobond / marketDeflatedZerobond;
597 modelDeflatedZerobond / marketDeflatedZerobond);
617 for (
Size i = 1; i <
times_.size() - 1; i++) {
641 ext::shared_ptr<SmileSection> sec = calibrationPoint.second.smileSection_;
642 ext::shared_ptr<SmileSection> rawSec = calibrationPoint.second.rawSmileSection_;
644 calibrationPoint.second.atm_);
645 Real shift = sec->shift();
646 std::vector<Real> money = ssutils.
moneyGrid();
647 std::vector<Real> strikes, marketCall, marketPut, modelCall,
648 modelPut, marketVega, marketRawCall, marketRawPut;
649 for (
Size j = 0; j < money.size(); j++) {
650 strikes.push_back(sec->volatilityType() ==
Normal ?
651 Real(calibrationPoint.second.atm_ + money[j]) :
652 money[j] * (calibrationPoint.second.atm_ + shift) -
655 marketRawCall.push_back(rawSec->optionPrice(
656 strikes[j],
Option::Call, calibrationPoint.second.annuity_));
657 marketRawPut.push_back(rawSec->optionPrice(
658 strikes[j],
Option::Put, calibrationPoint.second.annuity_));
663 marketRawCall.push_back(0.0);
664 marketRawPut.push_back(0.0);
666 marketCall.push_back(sec->optionPrice(strikes[j],
Option::Call,
667 calibrationPoint.second.annuity_));
668 marketPut.push_back(sec->optionPrice(strikes[j],
Option::Put,
669 calibrationPoint.second.annuity_));
671 calibrationPoint.second.isCaplet_ ?
675 calibrationPoint.second.tenor_, strikes[j],
678 calibrationPoint.second.isCaplet_ ?
682 calibrationPoint.second.tenor_, strikes[j],
684 marketVega.push_back(sec->vega(strikes[j], calibrationPoint.second.annuity_));
709 Real inverseNormalization =
714 Size i = std::min<Size>(
723 for (
Size j = 0; j <
y.size(); j++) {
734 inverseNormalization / ((tz - ta) / nb + (tb - tz) / na) * dt;
750 Array result(
y.size(), 0.0);
759 for (
Size j = 0; j <
y.size(); j++) {
797 : yts->discount(
T,
true);
800 (yts.
empty() ?
Real(1.0) : (yts->discount(
T) / yts->discount(
t) *
814 const Real digitalPrice,
816 const Real shift)
const {
820 Real solution =
b.solve(
831 const Real strike)
const {
839 out <<
"Markov functional model trace output " << std::endl;
840 out <<
"Model settings" << std::endl;
852 out <<
"Adjustments : "
886 out <<
"Smile moneyness checkpoints: ";
898 out <<
"Messages:" << std::endl;
900 out << message << std::endl;
901 out << std::endl << std::setprecision(16);
902 out <<
"Yield termstructure fit:" << std::endl;
903 out <<
"expiry;tenor;atm;annuity;digitalAdj;ytsAdj;marketzerorate;"
904 "modelzerorate;diff(bp)" << std::endl;
915 out <<
"Volatility smile fit:" << std::endl;
917 std::ostringstream os;
919 std::string p = os.str();
920 out <<
"strike(" << p <<
");marketCallRaw(" << p <<
";marketCall("
921 << p <<
");modelCall(" << p <<
");marketPutRaw(" << p
922 <<
");marketPut(" << p <<
");modelPut(" << p <<
");marketVega("
923 << p <<
")" << (i < m.
expiries_.size() - 1 ?
";" :
"");
935 << (i < m.
expiries_.size() - 1 ?
";" :
"");
944 const bool zeroFixingDays, ext::shared_ptr<IborIndex> iborIdx)
const {
951 Date valueDate = zeroFixingDays ? fixing : iborIdx->valueDate(fixing);
953 iborIdx->valueDate(fixing), iborIdx->tenor(),
954 iborIdx->businessDayConvention(),
955 iborIdx->endOfMonth());
957 Real dcf = iborIdx->dayCounter().yearFraction(valueDate, endDate);
959 return (
zerobond(valueDate, referenceDate,
y) -
961 (dcf *
zerobond(endDate, referenceDate,
y));
968 ext::shared_ptr<SwapIndex> swapIdx)
const {
976 ext::shared_ptr<VanillaSwap> underlying =
underlyingSwap(swapIdx, fixing, tenor);
978 Schedule sched = underlying->fixedSchedule();
980 zeroFixingDays, swapIdx);
985 underlying->paymentConvention()),
992 const Date &fixing,
const Period &tenor,
const Date &referenceDate,
993 const Real y,
const bool zeroFixingDays,
994 ext::shared_ptr<SwapIndex> swapIdx)
const {
1002 ext::shared_ptr<VanillaSwap> underlying =
underlyingSwap(swapIdx, fixing, tenor);
1004 Schedule sched = underlying->fixedSchedule();
1007 for (
unsigned int j = 1; j < sched.
size(); j++) {
1010 sched.
date(j), underlying->paymentConvention()),
1012 swapIdx->dayCounter().yearFraction(
1013 j == 1 && zeroFixingDays ? fixing : sched.
date(j - 1),
1023 const Date& referenceDate,
1025 const bool zeroFixingDays,
1026 const ext::shared_ptr<SwapIndex>& swapIdx)
const {
1031 Time referenceTime =
1037 fixingTime, referenceTime,
y);
1041 for (
Size i = 0; i < yg.
size(); i++) {
1043 zeroFixingDays, swapIdx);
1046 p[i] = annuity * std::max((type ==
Option::Call ? 1.0 : -1.0) *
1058 for (
Size i = 0; i < z.
size() - 1; i++) {
1060 0.0,
payoff.cCoefficients()[i],
payoff.bCoefficients()[i],
1061 payoff.aCoefficients()[i], p[i], z[i], z[i], z[i + 1]);
1068 0.0, 0.0, 0.0, 0.0, p[z.
size() - 2], z[z.
size() - 2],
1069 z[z.
size() - 1], 100.0);
1071 0.0, 0.0, 0.0, 0.0, p[0], z[0], -100.0, z[0]);
1078 z[z.
size() - 2], z[z.
size() - 1], 100.0);
1081 0.0,
payoff.cCoefficients()[0],
1083 p[0], z[0], -100.0, z[0]);
1092 const Date &referenceDate,
const Real y,
const bool zeroFixingDays,
1093 ext::shared_ptr<IborIndex> iborIdx)
const {
1101 Time referenceTime =
1107 fixingTime, referenceTime,
y);
1111 Date valueDate = iborIdx->valueDate(expiry);
1113 valueDate, iborIdx->tenor(), iborIdx->businessDayConvention(),
1116 Real dcf = iborIdx->dayCounter().yearFraction(
1117 zeroFixingDays ? expiry : valueDate, endDate);
1119 for (
Size i = 0; i < yg.
size(); i++) {
1123 p[i] = annuity * std::max((type ==
Option::Call ? 1.0 : -1.0) *
1135 for (
Size i = 0; i < z.
size() - 1; i++) {
1137 0.0,
payoff.cCoefficients()[i],
payoff.bCoefficients()[i],
1138 payoff.aCoefficients()[i], p[i], z[i], z[i], z[i + 1]);
1145 0.0, 0.0, 0.0, 0.0, p[z.
size() - 2], z[z.
size() - 2],
1146 z[z.
size() - 1], 100.0);
1148 0.0, 0.0, 0.0, 0.0, p[0], z[0], -100.0, z[0]);
1155 z[z.
size() - 2], z[z.
size() - 1], 100.0);
1158 0.0,
payoff.cCoefficients()[0],
1160 p[0], z[0], -100.0, z[0]);
smile section that allows for alternate specification of atm level and recentering the source volatil...
smile section that allows for explicit / alternate specification of atm level
Actual/365 (Fixed) day count convention.
1-D array used in linear algebra.
const_iterator end() const
Size size() const
dimension of the array
const_iterator begin() const
Date adjust(const Date &, BusinessDayConvention convention=Following) const
Standard constant parameter .
Cubic interpolation between discrete points.
const std::vector< Real > & cCoefficients() const
const std::vector< Real > & bCoefficients() const
const std::vector< Real > & aCoefficients() const
static Date minDate()
earliest allowed date
static Date endOfMonth(const Date &d)
last day of the month to which the given date belongs
static Date advance(const Date &d, Integer units, TimeUnit)
generalized Gauss-Hermite integration
static Real gaussianShiftedPolynomialIntegral(Real a, Real b, Real c, Real d, Real e, Real h, Real x0, Real x1)
ext::shared_ptr< VanillaSwap > underlyingSwap(const ext::shared_ptr< SwapIndex > &index, const Date &expiry, const Period &tenor) const
Real numeraire(Time t, Real y=0.0, const Handle< YieldTermStructure > &yts=Handle< YieldTermStructure >()) const
Real swapRate(const Date &fixing, const Period &tenor, const Date &referenceDate=Null< Date >(), Real y=0.0, const ext::shared_ptr< SwapIndex > &swapIdx=ext::shared_ptr< SwapIndex >()) const
Array yGrid(Real yStdDevs, int gridPoints, Real T=1.0, Real t=0, Real y=0) const
Real zerobond(Time T, Time t=0.0, Real y=0.0, const Handle< YieldTermStructure > &yts=Handle< YieldTermStructure >()) const
ext::shared_ptr< StochasticProcess1D > stateProcess_
Shared handle to an observable.
bool empty() const
checks if the contained shared pointer points to anything
virtual void calculate() const
std::vector< Date > volstepdates_
Real zerobondImpl(Time T, Time t, Real y, const Handle< YieldTermStructure > &yts) const override
Array zerobondArray(Time T, Time t, const Array &y) const
const ModelOutputs & modelOutputs() const
Handle< SwaptionVolatilityStructure > swaptionVol_
ext::shared_ptr< SwapIndex > swapIndexBase_
Real swapRateInternal(const Date &fixing, const Period &tenor, const Date &referenceDate=Null< Date >(), Real y=0.0, bool zeroFixingDays=false, ext::shared_ptr< SwapIndex > swapIdx=ext::shared_ptr< SwapIndex >()) const
std::vector< std::pair< Size, Size > > forcedArbitrageIndices_
Real swapAnnuityInternal(const Date &fixing, const Period &tenor, const Date &referenceDate=Null< Date >(), Real y=0.0, bool zeroFixingDays=false, ext::shared_ptr< SwapIndex > swapIdx=ext::shared_ptr< SwapIndex >()) const
Handle< OptionletVolatilityStructure > capletVol_
Array deflatedZerobondArray(Time T, Time t, const Array &y) const
const bool capletCalibrated_
void updateNumeraireTabulation() const
void updateSmiles() const
void updateTimes1() const
std::map< Date, CalibrationPoint > calibrationPoints_
ext::shared_ptr< IborIndex > iborIndex_
ext::shared_ptr< Matrix > discreteNumeraire_
Real marketDigitalPrice(const Date &expiry, const CalibrationPoint &p, const Option::Type &type, Real strike) const
MarkovFunctional(const Handle< YieldTermStructure > &termStructure, Real reversion, std::vector< Date > volstepdates, std::vector< Real > volatilities, const Handle< SwaptionVolatilityStructure > &swaptionVol, const std::vector< Date > &swaptionExpiries, const std::vector< Period > &swaptionTenors, const ext::shared_ptr< SwapIndex > &swapIndexBase, MarkovFunctional::ModelSettings modelSettings=ModelSettings())
ModelOutputs modelOutputs_
Real swaptionPriceInternal(const Option::Type &type, const Date &expiry, const Period &tenor, Rate strike, const Date &referenceDate=Null< Date >(), Real y=0.0, bool zeroFixingDays=false, const ext::shared_ptr< SwapIndex > &swapIdx=ext::shared_ptr< SwapIndex >()) const
void updateTimes2() const
Real deflatedZerobond(Time T, Time t=0.0, Real y=0.0) const
void makeCapletCalibrationPoint(const Date &expiry)
std::vector< Period > swaptionTenors_
ModelSettings modelSettings_
std::vector< Date > swaptionExpiries_
std::vector< ext::shared_ptr< Interpolation > > numeraire_
std::vector< Date > capletExpiries_
std::vector< Real > volatilities_
std::vector< std::pair< Size, Size > > arbitrageIndices_
std::vector< Real > times_
Array numeraireArray(Time t, const Array &y) const
void makeSwaptionCalibrationPoint(const Date &expiry, const Period &tenor)
Real marketSwapRate(const Date &expiry, const CalibrationPoint &p, Real digitalPrice, Real guess=0.03, Real shift=0.0) const
Real forwardRateInternal(const Date &fixing, const Date &referenceDate=Null< Date >(), Real y=0.0, bool zeroFixingDays=false, ext::shared_ptr< IborIndex > iborIdx=ext::shared_ptr< IborIndex >()) const
std::vector< Time > volsteptimes_
Real capletPriceInternal(const Option::Type &type, const Date &expiry, Rate strike, const Date &referenceDate=Null< Date >(), Real y=0.0, bool zeroFixingDays=false, ext::shared_ptr< IborIndex > iborIdx=ext::shared_ptr< IborIndex >()) const
Real numeraireImpl(Time t, Real y, const Handle< YieldTermStructure > &yts) const override
const Time & numeraireTime() const
template class providing a null value for a given type.
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
void setParam(Size i, Real x)
const Array & params() const
Piecewise-constant parameter.
Constraint imposing positivity to all arguments
const Calendar & calendar() const
const std::vector< Date > & dates() const
const Date & date(Size i) const
const std::vector< Real > & strikeGrid() const
const std::vector< Real > & moneyGrid() const
const Handle< YieldTermStructure > & termStructure() const
cubic interpolation between discrete points
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
ext::function< Real(Real)> b
Integral of a 1-dimensional function using the Gauss quadratures.
BusinessDayConvention
Business Day conventions.
Real Time
continuous quantity with 1-year units
QL_INTEGER Integer
integer number
std::size_t Size
size of a container
ext::shared_ptr< QuantLib::Payoff > payoff
Arbitrage free smile section using a C^1 inter- and extrapolation method proposed by Kahale,...
Markov Functional 1 Factor Model.
#define QL_MFMESSAGE(o, message)
Real months(const Period &p)
std::ostream & operator<<(std::ostream &out, GFunctionFactory::YieldCurveModel type)
ext::shared_ptr< BlackVolTermStructure > v
Interpolated smile section class.
Smile section base class.
Additional utilities for smile sections.
ext::shared_ptr< SmileSection > smileSection_
std::vector< Date > paymentDates_
std::vector< Real > yearFractions_
std::vector< Real > adjustmentFactors_
std::vector< Real > marketZerorate_
std::vector< Real > digitalsAdjustmentFactors_
std::vector< Period > tenors_
std::vector< Real > modelZerorate_
std::vector< std::string > messages_
std::vector< std::vector< Real > > modelPutPremium_
std::vector< std::vector< Real > > marketPutPremium_
std::vector< Date > expiries_
std::vector< Real > annuity_
std::vector< std::vector< Real > > smileStrikes_
std::vector< std::vector< Real > > modelCallPremium_
std::vector< std::vector< Real > > marketVega_
std::vector< std::vector< Real > > marketCallPremium_
std::vector< std::vector< Real > > marketRawCallPremium_
std::vector< std::vector< Real > > marketRawPutPremium_
std::vector< Real > smileMoneynessCheckpoints_
ext::shared_ptr< CustomSmileFactory > customSmileFactory_
@ SmileExponentialExtrapolation
@ SmileDeleteArbitragePoints