25#include <ql/cashflows/lineartsrpricer.hpp>
26#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
27#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
28#include <ql/time/daycounters/actual365fixed.hpp>
32#include <ql/math/optimization/levenbergmarquardt.hpp>
33#include <ql/math/optimization/problem.hpp>
34#include <ql/math/optimization/projectedconstraint.hpp>
35#include <ql/math/optimization/projection.hpp>
36#include <ql/models/calibrationhelper.hpp>
37#include <ql/quotes/simplequote.hpp>
38#include <ql/termstructures/volatility/optionlet/optionletvolatilitystructure.hpp>
40#include <ql/time/calendars/target.hpp>
41#include <ql/time/daycounters/actual360.hpp>
49class CorrelationCurve::CalibrationFunction :
public CostFunction {
51 CalibrationFunction(vector<Handle<Quote>>& correlations,
const vector<QuantLib::ext::shared_ptr<QuantExt::CmsCapHelper>>& h,
52 const vector<Real>& weights,
const Projection& projection)
53 :
correlations_(correlations), instruments_(h), weights_(weights), projection_(projection) {}
55 virtual ~CalibrationFunction() {}
57 virtual Real
value(
const Array& params)
const override {
60 QuantLib::ext::shared_ptr<SimpleQuote> q = QuantLib::ext::dynamic_pointer_cast<SimpleQuote>(*correlations_[i]);
61 q->setValue(params[i]);
62 LOG(
"set corr " << params[i]);
65 for (Size i = 0; i < instruments_.size(); i++) {
66 Real diff = instruments_[i]->calibrationError();
67 value += diff * diff * weights_[i];
69 return std::sqrt(
value);
72 virtual Array values(
const Array& params)
const override {
74 QuantLib::ext::shared_ptr<SimpleQuote> q = QuantLib::ext::dynamic_pointer_cast<SimpleQuote>(*correlations_[i]);
75 q->setValue(params[i]);
77 Array values(instruments_.size());
78 for (Size i = 0; i < instruments_.size(); i++) {
79 values[i] = instruments_[i]->calibrationError() * std::sqrt(weights_[i]);
84 virtual Real finiteDifferenceEpsilon()
const override {
return 1e-6; }
88 mutable vector<QuantLib::ext::shared_ptr<QuantExt::CmsCapHelper>> instruments_;
89 vector<Real> weights_;
90 const Projection projection_;
94 const QuantLib::ext::shared_ptr<CorrelationCurveConfig>& config, Date asof,
const vector<Handle<Quote>>& prices,
95 vector<Handle<Quote>>& correlations, QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure>& curve,
96 map<
string, QuantLib::ext::shared_ptr<SwapIndex>>& swapIndices, map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
97 map<
string, QuantLib::ext::shared_ptr<GenericYieldVolCurve>>& swaptionVolCurves) {
99 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
102 string ccy = config->currency();
103 string swaptionVol =
"SwaptionVolatility/" + ccy +
"/" + config->swaptionVolatility();
104 auto it = swaptionVolCurves.find(swaptionVol);
106 if (it == swaptionVolCurves.end())
107 QL_FAIL(
"The swaption vol curve not found " << swaptionVol);
108 Handle<SwaptionVolatilityStructure> vol(it->second->volTermStructure());
110 string dc =
"Yield/" + ccy +
"/" + config->discountCurve();
111 Handle<YieldTermStructure> yts;
112 auto it2 = yieldCurves.find(dc);
113 if (it2 != yieldCurves.end()) {
114 yts = it2->second->handle();
116 QL_FAIL(
"The discount curve not found, " << dc);
119 Real lower = (vol->volatilityType() == ShiftedLognormal) ? 0.0001 : -2;
120 Real upper = (vol->volatilityType() == ShiftedLognormal) ? 2.0000 : 2;
122 LinearTsrPricer::Settings settings;
123 settings.withRateBound(lower, upper);
126 Handle<Quote> revQuote(QuantLib::ext::shared_ptr<Quote>(
new SimpleQuote(rev)));
128 QuantLib::ext::shared_ptr<QuantLib::CmsCouponPricer> cmsPricer =
129 QuantLib::ext::make_shared<LinearTsrPricer>(vol, revQuote, yts, settings);
132 Handle<QuantExt::CorrelationTermStructure> ch(curve);
133 QuantLib::ext::shared_ptr<FloatingRateCouponPricer> pricer =
134 QuantLib::ext::make_shared<QuantExt::LognormalCmsSpreadPricer>(cmsPricer, ch, yts, 16);
137 auto it3 = swapIndices.find(config->index1());
138 if (it3 == swapIndices.end())
139 QL_FAIL(
"The swap index not found " << config->index1());
141 QuantLib::ext::shared_ptr<SwapIndex> index1 = it3->second;
143 auto it4 = swapIndices.find(config->index2());
144 if (it4 == swapIndices.end())
145 QL_FAIL(
"The swap index not found " << config->index2());
147 QuantLib::ext::shared_ptr<SwapIndex> index2 = it4->second;
149 vector<QuantLib::ext::shared_ptr<QuantExt::CmsCapHelper>> instruments;
151 QuantLib::ext::shared_ptr<Convention> tmp = conventions->get(config->conventions());
152 QL_REQUIRE(tmp,
"no conventions found with id " << config->conventions());
154 QuantLib::ext::shared_ptr<CmsSpreadOptionConvention> conv = QuantLib::ext::dynamic_pointer_cast<CmsSpreadOptionConvention>(tmp);
155 QL_REQUIRE(conv != NULL,
"CMS Correlation curves require CMSSpreadOption convention ");
156 Period forwardStart = conv->forwardStart();
157 Period spotDays = conv->spotDays();
158 Period cmsTenor = conv->swapTenor();
159 Natural fixingDays = conv->fixingDays();
160 Calendar
calendar = conv->calendar();
161 DayCounter dcount = conv->dayCounter();
162 BusinessDayConvention bdc = conv->rollConvention();
163 vector<Period> optionTenors = parseVectorOfValues<Period>(config->optionTenors(), &
parsePeriod);
165 for (Size i = 0; i < prices.size(); i++) {
166 QuantLib::ext::shared_ptr<QuantExt::CmsCapHelper> inst = QuantLib::ext::make_shared<QuantExt::CmsCapHelper>(
167 asof, index1, index2, yts, prices[i], correlations[i], optionTenors[i], forwardStart, spotDays, cmsTenor,
168 fixingDays,
calendar, dcount, bdc, pricer, cmsPricer);
169 instruments.push_back(inst);
173 EndCriteria endCriteria(1000, 500, 1E-8, 1E-8, 1E-8);
174 BoundaryConstraint constraint(-1, 1);
175 vector<Real> weights(prices.size(), 1);
177 Array prms(prices.size(), 0);
178 vector<bool> all(prms.size(),
false);
179 Projection proj(prms, all);
180 ProjectedConstraint pc(constraint, proj);
182 QuantLib::ext::shared_ptr<OptimizationMethod> method = QuantLib::ext::make_shared<LevenbergMarquardt>(1E-8, 1E-8, 1E-8);
186 Problem prob(c, pc, proj.project(prms));
188 method->minimize(prob, endCriteria);
190 Array result(prob.currentValue());
195 map<
string, QuantLib::ext::shared_ptr<SwapIndex>>& swapIndices,
196 map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
197 map<
string, QuantLib::ext::shared_ptr<GenericYieldVolCurve>>& swaptionVolCurves) {
201 const QuantLib::ext::shared_ptr<CorrelationCurveConfig>& config =
205 QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure> corr;
209 corr = QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure>(
215 "Unsupported correlation curve building dimension");
222 vector<pair<Real, Handle<Quote>>> quotePairs;
226 "CorrelationCurve: Wildcards only supported for curve dimension ATM");
227 LOG(
"Have single quote with pattern " << wildcard->pattern());
230 for (
const auto& md : loader.
get(*wildcard, asof)) {
232 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
234 auto q = QuantLib::ext::dynamic_pointer_cast<CorrelationQuote>(md);
235 if (q && q->quoteType() == config->quoteType()) {
237 TLOG(
"The quote " << q->name() <<
" matched the pattern");
239 Date expiryDate =
getDateFromDateOrPeriod(q->expiry(), asof, config->calendar(), config->businessDayConvention());
240 if (expiryDate > asof) {
242 quotePairs.push_back(make_pair(config->dayCounter().yearFraction(asof, expiryDate), q->quote()));
243 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," << fixed
244 << setprecision(9) << q->quote()->value() <<
")");
249 if (quotePairs.size() == 0) {
250 Real corr = config->index1() == config->index2() ? 1.0 : 0.0;
251 WLOG(
"CorrelationCurve: No quotes found for correlation curve: "
252 << config->curveID() <<
", continuing with correlation " << corr <<
".");
253 corr_ = QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure>(
259 vector<Period> optionTenors = parseVectorOfValues<Period>(config->optionTenors(), &
parsePeriod);
260 quotePairs.resize(optionTenors.size());
264 for (
auto& q : config->quotes()) {
265 if (loader.
has(q, asof)) {
266 QuantLib::ext::shared_ptr<CorrelationQuote> c =
267 QuantLib::ext::dynamic_pointer_cast<CorrelationQuote>(loader.
get(q, asof));
269 Size i = std::find(optionTenors.begin(), optionTenors.end(),
parsePeriod(c->expiry())) -
270 optionTenors.begin();
271 QL_REQUIRE(i < optionTenors.size(),
"CorrelationCurve: correlation tenor not found for " << q);
276 Date d = config->calendar().advance(asof, optionTenors[i], config->businessDayConvention());
277 time = config->dayCounter().yearFraction(asof, d);
279 quotePairs[i] = make_pair(time, c->quote());
281 TLOG(
"CorrelationCurve: Added quote " << c->name() <<
", tenor " << optionTenors[i] <<
", with value "
282 << fixed << setprecision(9) << c->quote()->value() );
290 QL_FAIL(
"could not build correlation curve");
293 vector<Handle<Quote>> corrs;
296 std::sort(quotePairs.begin(), quotePairs.end());
298 vector<Handle<Quote>> quotes;
299 for (Size i = 0; i < quotePairs.size(); i++) {
301 corrs.push_back(quotePairs[i].second);
303 Handle<Quote> q(QuantLib::ext::make_shared<SimpleQuote>(0));
306 quotes.push_back(quotePairs[i].second);
307 times.push_back(quotePairs[i].first);
312 LOG(
"building " << (flat ?
"flat" :
"interpolated curve") <<
" correlation termstructure");
315 corr = QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure>(
318 corr->enableExtrapolation(config->extrapolate());
321 corr = QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure>(
323 config->calendar()));
330 yieldCurves, swaptionVolCurves);
332 QL_FAIL(
"price calibration only supported for CMSSpread correlations");
336 LOG(
"Returning correlation surface for config " <<
spec);
339 }
catch (std::exception& e) {
340 QL_FAIL(
"correlation curve building failed for curve " <<
spec.
curveConfigID() <<
" on date "
341 << io::iso_date(asof) <<
": " << e.what());
343 QL_FAIL(
"correlation curve building failed: unknown error");
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > & correlations_
friend class CalibrationFunction
const CorrelationCurveSpec & spec() const
QuantLib::ext::shared_ptr< QuantExt::CorrelationTermStructure > corr_
CorrelationCurve()
Default constructor.
void calibrateCMSSpreadCorrelations(const QuantLib::ext::shared_ptr< CorrelationCurveConfig > &config, Date asof, const vector< Handle< Quote > > &prices, vector< Handle< Quote > > "es, QuantLib::ext::shared_ptr< QuantExt::CorrelationTermStructure > &curve, map< string, QuantLib::ext::shared_ptr< SwapIndex > > &swapIndices, map< string, QuantLib::ext::shared_ptr< YieldCurve > > &yieldCurves, map< string, QuantLib::ext::shared_ptr< GenericYieldVolCurve > > &swaptionVolCurves)
Correlation curve description.
Container class for all Curve Configurations.
const std::string & curveConfigID() const
Market data loader base class.
virtual QuantLib::ext::shared_ptr< MarketDatum > get(const std::string &name, const QuantLib::Date &d) const
get quote by its unique name, throws if not existent, override in derived classes for performance
virtual bool has(const std::string &name, const QuantLib::Date &d) const
Default implementation, returns false if get throws or returns a null pointer.
SafeStack< ValueType > value
Currency and instrument specific conventions/defaults.
Date getDateFromDateOrPeriod(const string &token, Date asof, QuantLib::Calendar cal, QuantLib::BusinessDayConvention bdc)
Get a date from a date string or period.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define WLOG(text)
Logging Macro (Level = Warning)
#define TLOG(text)
Logging Macro (Level = Data)
#define DLOGGERSTREAM(text)
boost::optional< Wildcard > getUniqueWildcard(const C &c)
checks if at most one element in C has a wild card and returns it in this case
Serializable Credit Default Swap.
vector< string > curveConfigs
utilities for wildcard handling