/src/quantlib/ql/experimental/inflation/yoycapfloortermpricesurface.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2009 Chris Kenyon |
5 | | Copyright (C) 2009 Bernd Engelmann |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <http://quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | /*! \file yoycapfloortermpricesurface.hpp |
22 | | \brief yoy inflation cap and floor term-price structure |
23 | | */ |
24 | | |
25 | | #ifndef quantlib_yoy_capfloor_term_price_surface_hpp |
26 | | #define quantlib_yoy_capfloor_term_price_surface_hpp |
27 | | |
28 | | #include <ql/indexes/inflationindex.hpp> |
29 | | #include <ql/termstructures/inflation/piecewiseyoyinflationcurve.hpp> |
30 | | #include <ql/termstructures/inflation/inflationhelpers.hpp> |
31 | | #include <ql/experimental/inflation/polynomial2Dspline.hpp> |
32 | | #include <cmath> |
33 | | |
34 | | namespace QuantLib { |
35 | | |
36 | | //! Abstract base class, inheriting from InflationTermStructure |
37 | | /*! Since this can create a yoy term structure it does take |
38 | | a YoY index. |
39 | | |
40 | | \todo deal with index interpolation. |
41 | | */ |
42 | | class YoYCapFloorTermPriceSurface : public TermStructure { |
43 | | public: |
44 | | YoYCapFloorTermPriceSurface(Natural fixingDays, |
45 | | const Period& yyLag, |
46 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
47 | | CPI::InterpolationType interpolation, |
48 | | Handle<YieldTermStructure> nominal, |
49 | | const DayCounter& dc, |
50 | | const Calendar& cal, |
51 | | const BusinessDayConvention& bdc, |
52 | | const std::vector<Rate>& cStrikes, |
53 | | const std::vector<Rate>& fStrikes, |
54 | | const std::vector<Period>& cfMaturities, |
55 | | const Matrix& cPrice, |
56 | | const Matrix& fPrice); |
57 | | |
58 | | /*! \deprecated Use the overload that passes an interpolation type instead. |
59 | | Deprecated in version 1.36. |
60 | | */ |
61 | | [[deprecated("Use the overload that passes an interpolation type instead")]] |
62 | | YoYCapFloorTermPriceSurface(Natural fixingDays, |
63 | | const Period& yyLag, |
64 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
65 | | Rate baseRate, |
66 | | Handle<YieldTermStructure> nominal, |
67 | | const DayCounter& dc, |
68 | | const Calendar& cal, |
69 | | const BusinessDayConvention& bdc, |
70 | | const std::vector<Rate>& cStrikes, |
71 | | const std::vector<Rate>& fStrikes, |
72 | | const std::vector<Period>& cfMaturities, |
73 | | const Matrix& cPrice, |
74 | | const Matrix& fPrice); |
75 | | |
76 | | bool indexIsInterpolated() const; |
77 | | virtual Period observationLag() const; |
78 | | virtual Frequency frequency() const; |
79 | | |
80 | | //! atm yoy swaps from put-call parity on cap/floor data |
81 | | /*! uses interpolation (on surface price data), yearly maturities. */ |
82 | | virtual std::pair<std::vector<Time>, std::vector<Rate> > |
83 | | atmYoYSwapTimeRates() const = 0; |
84 | | virtual std::pair<std::vector<Date>, std::vector<Rate> > |
85 | | atmYoYSwapDateRates() const = 0; |
86 | | |
87 | | //! derived from yoy swap rates |
88 | | virtual ext::shared_ptr<YoYInflationTermStructure> YoYTS() const = 0; |
89 | | //! index yoy is based on |
90 | 0 | ext::shared_ptr<YoYInflationIndex> yoyIndex() const { return yoyIndex_; } |
91 | | |
92 | | //! inspectors |
93 | | /*! \note you don't know if price() is a cap or a floor |
94 | | without checking the YoYSwapATM level. |
95 | | \note atm cap/floor prices are generally |
96 | | inaccurate because they are from extrapolation |
97 | | and intersection. |
98 | | */ |
99 | | //@{ |
100 | 0 | virtual BusinessDayConvention businessDayConvention() const {return bdc_;} |
101 | 0 | virtual Natural fixingDays() const {return fixingDays_;} |
102 | | virtual Date baseDate() const = 0; |
103 | | virtual Real price(const Date& d, Rate k) const = 0; |
104 | | virtual Real capPrice(const Date& d, Rate k) const = 0; |
105 | | virtual Real floorPrice(const Date& d, Rate k) const = 0; |
106 | | virtual Rate atmYoYSwapRate(const Date &d, |
107 | | bool extrapolate = true) const = 0; |
108 | | virtual Rate atmYoYRate(const Date &d, |
109 | | const Period &obsLag = Period(-1,Days), |
110 | | bool extrapolate = true) const = 0; |
111 | | |
112 | | virtual Real price(const Period& d, Rate k) const; |
113 | | virtual Real capPrice(const Period& d, Rate k) const; |
114 | | virtual Real floorPrice(const Period& d, Rate k) const; |
115 | | virtual Rate atmYoYSwapRate(const Period &d, |
116 | | bool extrapolate = true) const; |
117 | | virtual Rate atmYoYRate(const Period &d, |
118 | | const Period &obsLag = Period(-1,Days), |
119 | | bool extrapolate = true) const; |
120 | | |
121 | 0 | virtual std::vector<Rate> strikes() const {return cfStrikes_;} |
122 | 0 | virtual std::vector<Rate> capStrikes() const {return cStrikes_;} |
123 | 0 | virtual std::vector<Rate> floorStrikes() const {return fStrikes_;} |
124 | 0 | virtual std::vector<Period> maturities() const {return cfMaturities_;} |
125 | 0 | virtual Rate minStrike() const {return cfStrikes_.front();}; |
126 | 0 | virtual Rate maxStrike() const {return cfStrikes_.back();}; |
127 | 0 | virtual Date minMaturity() const {return referenceDate()+cfMaturities_.front();}// \TODO deal with index interpolation |
128 | 0 | virtual Date maxMaturity() const {return referenceDate()+cfMaturities_.back();} |
129 | | //@} |
130 | | |
131 | | virtual Date yoyOptionDateFromTenor(const Period& p) const; |
132 | | |
133 | | protected: |
134 | 0 | virtual bool checkStrike(Rate K) { |
135 | 0 | return ( minStrike() <= K && K <= maxStrike() ); |
136 | 0 | } |
137 | 0 | virtual bool checkMaturity(const Date& d) { |
138 | 0 | return ( minMaturity() <= d && d <= maxMaturity() ); |
139 | 0 | } |
140 | | |
141 | | // defaults, mostly used for building yoy-fwd curve from put-call parity |
142 | | // ext::shared_ptr<YieldTermStructure> nominal_; |
143 | | // Period lag_; |
144 | | // Calendar cal_; |
145 | | Natural fixingDays_; |
146 | | BusinessDayConvention bdc_; |
147 | | ext::shared_ptr<YoYInflationIndex> yoyIndex_; |
148 | | Period observationLag_; |
149 | | Handle<YieldTermStructure> nominalTS_; |
150 | | // data |
151 | | std::vector<Rate> cStrikes_; |
152 | | std::vector<Rate> fStrikes_; |
153 | | std::vector<Period> cfMaturities_; |
154 | | mutable std::vector<Real> cfMaturityTimes_; |
155 | | Matrix cPrice_; |
156 | | Matrix fPrice_; |
157 | | bool indexIsInterpolated_; |
158 | | // constructed |
159 | | mutable std::vector<Rate> cfStrikes_; |
160 | | mutable ext::shared_ptr<YoYInflationTermStructure> yoy_; |
161 | | mutable std::pair<std::vector<Time>, std::vector<Rate> > atmYoYSwapTimeRates_; |
162 | | mutable std::pair<std::vector<Date>, std::vector<Rate> > atmYoYSwapDateRates_; |
163 | | }; |
164 | | |
165 | | |
166 | | template<class Interpolator2D, class Interpolator1D> |
167 | | class InterpolatedYoYCapFloorTermPriceSurface |
168 | | : public YoYCapFloorTermPriceSurface { |
169 | | public: |
170 | | InterpolatedYoYCapFloorTermPriceSurface( |
171 | | Natural fixingDays, |
172 | | const Period &yyLag, // observation lag |
173 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
174 | | CPI::InterpolationType interpolation, |
175 | | const Handle<YieldTermStructure> &nominal, |
176 | | const DayCounter &dc, |
177 | | const Calendar &cal, |
178 | | const BusinessDayConvention &bdc, |
179 | | const std::vector<Rate> &cStrikes, |
180 | | const std::vector<Rate> &fStrikes, |
181 | | const std::vector<Period> &cfMaturities, |
182 | | const Matrix &cPrice, |
183 | | const Matrix &fPrice, |
184 | | const Interpolator2D &interpolator2d = Interpolator2D(), |
185 | | const Interpolator1D &interpolator1d = Interpolator1D()); |
186 | | |
187 | | /*! \deprecated Use the overload that passes an interpolation type instead. |
188 | | Deprecated in version 1.36. |
189 | | */ |
190 | | [[deprecated("Use the overload that passes an interpolation type instead")]] |
191 | | InterpolatedYoYCapFloorTermPriceSurface( |
192 | | Natural fixingDays, |
193 | | const Period &yyLag, // observation lag |
194 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
195 | | Rate baseRate, |
196 | | const Handle<YieldTermStructure> &nominal, |
197 | | const DayCounter &dc, |
198 | | const Calendar &cal, |
199 | | const BusinessDayConvention &bdc, |
200 | | const std::vector<Rate> &cStrikes, |
201 | | const std::vector<Rate> &fStrikes, |
202 | | const std::vector<Period> &cfMaturities, |
203 | | const Matrix &cPrice, |
204 | | const Matrix &fPrice, |
205 | | const Interpolator2D &interpolator2d = Interpolator2D(), |
206 | | const Interpolator1D &interpolator1d = Interpolator1D()); |
207 | | |
208 | | //! inflation term structure interface |
209 | | //@{ |
210 | | Date maxDate() const override { return yoy_->maxDate(); } |
211 | | Date baseDate() const override { return yoy_->baseDate(); } |
212 | | //@} |
213 | | Natural fixingDays() const override { return fixingDays_; } |
214 | | |
215 | | //! \name YoYCapFloorTermPriceSurface interface |
216 | | //@{ |
217 | | std::pair<std::vector<Time>, std::vector<Rate> > atmYoYSwapTimeRates() const override { |
218 | | return atmYoYSwapTimeRates_; |
219 | | } |
220 | | std::pair<std::vector<Date>, std::vector<Rate> > atmYoYSwapDateRates() const override { |
221 | | return atmYoYSwapDateRates_; |
222 | | } |
223 | | ext::shared_ptr<YoYInflationTermStructure> YoYTS() const override { return yoy_; } |
224 | | Rate price(const Date& d, Rate k) const override; |
225 | | Real floorPrice(const Date& d, Rate k) const override; |
226 | | Real capPrice(const Date& d, Rate k) const override; |
227 | | Rate atmYoYSwapRate(const Date& d, bool extrapolate = true) const override { |
228 | | return atmYoYSwapRateCurve_(timeFromReference(d),extrapolate); |
229 | | } |
230 | | Rate atmYoYRate(const Date& d, |
231 | | const Period& obsLag = Period(-1, Days), |
232 | | bool extrapolate = true) const override { |
233 | | // work in terms of maturity-of-instruments |
234 | | // so ask for rate with observation lag |
235 | | Period p = (obsLag == Period(-1, Days)) ? observationLag() : obsLag; |
236 | | // Third parameter = force linear interpolation of yoy |
237 | | return yoy_->yoyRate(d, p, false, extrapolate); |
238 | | } |
239 | | //@} |
240 | | |
241 | | //! \name LazyObject interface |
242 | | //@{ |
243 | | void update() override; |
244 | | void performCalculations() const; |
245 | | //@} |
246 | | |
247 | | protected: |
248 | | //! intersection of cap and floor price surfaces at given strikes |
249 | | void intersect() const; |
250 | | class ObjectiveFunction { |
251 | | public: |
252 | | ObjectiveFunction(Time t, const Interpolation2D&, const Interpolation2D&); |
253 | | Real operator()(Rate guess) const; |
254 | | protected: |
255 | | const Time t_; |
256 | | const Interpolation2D &a_, &b_; // work on references |
257 | | }; |
258 | | |
259 | | //! mess of making it, i.e. create instruments from quotes and bootstrap |
260 | | void calculateYoYTermStructure() const; |
261 | | |
262 | | // data for surfaces and curve |
263 | | mutable std::vector<Rate> cStrikesB_; |
264 | | mutable std::vector<Rate> fStrikesB_; |
265 | | mutable Matrix cPriceB_; |
266 | | mutable Matrix fPriceB_; |
267 | | mutable Interpolation2D capPrice_, floorPrice_; |
268 | | mutable Interpolation2D floorPrice2_; |
269 | | mutable Interpolator2D interpolator2d_; |
270 | | mutable Interpolation atmYoYSwapRateCurve_; |
271 | | mutable Interpolator1D interpolator1d_; |
272 | | }; |
273 | | |
274 | | |
275 | | // inline definitions |
276 | | |
277 | 0 | inline bool YoYCapFloorTermPriceSurface::indexIsInterpolated() const { |
278 | 0 | return indexIsInterpolated_; |
279 | 0 | } |
280 | | |
281 | 0 | inline Period YoYCapFloorTermPriceSurface::observationLag() const { |
282 | 0 | return observationLag_; |
283 | 0 | } |
284 | | |
285 | 0 | inline Frequency YoYCapFloorTermPriceSurface::frequency() const { |
286 | 0 | return yoyIndex_->frequency(); |
287 | 0 | } |
288 | | |
289 | | // template definitions |
290 | | |
291 | | #ifndef __DOXYGEN__ |
292 | | |
293 | | template<class I2D, class I1D> |
294 | | InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
295 | | InterpolatedYoYCapFloorTermPriceSurface( |
296 | | Natural fixingDays, |
297 | | const Period &yyLag, |
298 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
299 | | CPI::InterpolationType interpolation, |
300 | | const Handle<YieldTermStructure> &nominal, |
301 | | const DayCounter &dc, |
302 | | const Calendar &cal, |
303 | | const BusinessDayConvention &bdc, |
304 | | const std::vector<Rate> &cStrikes, |
305 | | const std::vector<Rate> &fStrikes, |
306 | | const std::vector<Period> &cfMaturities, |
307 | | const Matrix &cPrice, |
308 | | const Matrix &fPrice, |
309 | | const I2D &interpolator2d, |
310 | | const I1D &interpolator1d) |
311 | | : YoYCapFloorTermPriceSurface(fixingDays, yyLag, yii, |
312 | | interpolation, nominal, dc, cal, bdc, |
313 | | cStrikes, fStrikes, cfMaturities, |
314 | | cPrice, fPrice), |
315 | | interpolator2d_(interpolator2d), interpolator1d_(interpolator1d) { |
316 | | performCalculations(); |
317 | | } |
318 | | |
319 | | template<class I2D, class I1D> |
320 | | InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
321 | | InterpolatedYoYCapFloorTermPriceSurface( |
322 | | Natural fixingDays, |
323 | | const Period &yyLag, |
324 | | const ext::shared_ptr<YoYInflationIndex>& yii, |
325 | | Rate baseRate, |
326 | | const Handle<YieldTermStructure> &nominal, |
327 | | const DayCounter &dc, |
328 | | const Calendar &cal, |
329 | | const BusinessDayConvention &bdc, |
330 | | const std::vector<Rate> &cStrikes, |
331 | | const std::vector<Rate> &fStrikes, |
332 | | const std::vector<Period> &cfMaturities, |
333 | | const Matrix &cPrice, |
334 | | const Matrix &fPrice, |
335 | | const I2D &interpolator2d, |
336 | | const I1D &interpolator1d) |
337 | | : InterpolatedYoYCapFloorTermPriceSurface(fixingDays, yyLag, yii, CPI::AsIndex, |
338 | | nominal, dc, cal, bdc, |
339 | | cStrikes, fStrikes, cfMaturities, |
340 | | cPrice, fPrice, |
341 | | interpolator2d, interpolator1d) {} |
342 | | |
343 | | #endif |
344 | | |
345 | | template<class I2D, class I1D> |
346 | | void InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
347 | | update() { |
348 | | notifyObservers(); |
349 | | } |
350 | | |
351 | | |
352 | | template<class I2D, class I1D> |
353 | | void InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
354 | | performCalculations() const { |
355 | | // calculate all the useful things |
356 | | // ... first the intersection of the cap and floor surfs |
357 | | intersect(); |
358 | | |
359 | | // ... then the yoy term structure, which requires instruments |
360 | | // and a bootstrap |
361 | | calculateYoYTermStructure(); |
362 | | } |
363 | | |
364 | | |
365 | | template<class I2D, class I1D> |
366 | | InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>::ObjectiveFunction:: |
367 | | ObjectiveFunction(const Time t, |
368 | | const Interpolation2D &a, |
369 | | const Interpolation2D &b) |
370 | | : t_(t), a_(a), b_(b) { |
371 | | // do nothing more |
372 | | } |
373 | | |
374 | | |
375 | | template<class I2D, class I1D> |
376 | | Rate InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
377 | | price(const Date &d, const Rate k) const { |
378 | | Rate atm = atmYoYSwapRate(d); |
379 | | return k > atm ? capPrice(d,k): floorPrice(d,k); |
380 | | } |
381 | | |
382 | | |
383 | | template<class I2D, class I1D> |
384 | | Rate InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
385 | | capPrice(const Date &d, const Rate k) const { |
386 | | Time t = timeFromReference(d); |
387 | | return capPrice_(t,k); |
388 | | } |
389 | | |
390 | | |
391 | | template<class I2D, class I1D> |
392 | | Rate InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
393 | | floorPrice(const Date &d, const Rate k) const { |
394 | | Time t = timeFromReference(d); |
395 | | return floorPrice_(t,k); |
396 | | } |
397 | | |
398 | | |
399 | | template<class I2D, class I1D> |
400 | | Real InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>::ObjectiveFunction:: |
401 | | operator()(Rate guess) const { |
402 | | // allow extrapolation because the overlap is typically insufficient |
403 | | // looking for a zero |
404 | | return ( a_(t_,guess,true) - b_(t_,guess,true) ); |
405 | | } |
406 | | |
407 | | |
408 | | template<class I2D, class I1D> |
409 | | void InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
410 | | intersect() const { |
411 | | |
412 | | |
413 | | // TODO: define the constants outside the code |
414 | | const Real maxSearchRange = 0.0201; |
415 | | const Real maxExtrapolationMaturity = 5.01; |
416 | | const Real searchStep = 0.0050; |
417 | | const Real intrinsicValueAddOn = 0.001; |
418 | | |
419 | | std::vector<bool> validMaturity(cfMaturities_.size(),false); |
420 | | |
421 | | cfMaturityTimes_.clear(); |
422 | | for (Size i=0; i<cfMaturities_.size();i++) { |
423 | | cfMaturityTimes_.push_back(timeFromReference( |
424 | | yoyOptionDateFromTenor(cfMaturities_[i]))); |
425 | | } |
426 | | |
427 | | capPrice_ = interpolator2d_.interpolate( |
428 | | cfMaturityTimes_.begin(),cfMaturityTimes_.end(), |
429 | | cStrikes_.begin(), cStrikes_.end(), |
430 | | cPrice_ |
431 | | ); |
432 | | capPrice_.enableExtrapolation(); |
433 | | |
434 | | floorPrice_ = interpolator2d_.interpolate( |
435 | | cfMaturityTimes_.begin(),cfMaturityTimes_.end(), |
436 | | fStrikes_.begin(), fStrikes_.end(), |
437 | | fPrice_ |
438 | | ); |
439 | | floorPrice_.enableExtrapolation(); |
440 | | |
441 | | atmYoYSwapDateRates_.first.clear(); |
442 | | atmYoYSwapDateRates_.second.clear(); |
443 | | atmYoYSwapTimeRates_.first.clear(); |
444 | | atmYoYSwapTimeRates_.second.clear(); |
445 | | Brent solver; |
446 | | Real solverTolerance_ = 1e-7; |
447 | | Real lo,hi,guess; |
448 | | std::vector<Real> minSwapRateIntersection(cfMaturityTimes_.size()); |
449 | | std::vector<Real> maxSwapRateIntersection(cfMaturityTimes_.size()); |
450 | | std::vector<Time> tmpSwapMaturities; |
451 | | std::vector<Rate> tmpSwapRates; |
452 | | for (Size i = 0; i < cfMaturities_.size(); i++) { |
453 | | Time t = cfMaturityTimes_[i]; |
454 | | // determine the sum of discount factors |
455 | | Size numYears = (Size)std::lround(t); |
456 | | Real sumDiscount = 0.0; |
457 | | for (Size j=0; j<numYears; ++j) |
458 | | sumDiscount += nominalTS_->discount(j + 1.0); |
459 | | // determine the minimum value of the ATM swap point |
460 | | Real tmpMinSwapRateIntersection = -1.e10; |
461 | | Real tmpMaxSwapRateIntersection = 1.e10; |
462 | | for (Size j=0; j<fStrikes_.size(); ++j) { |
463 | | Real price = floorPrice_(t,fStrikes_[j]); |
464 | | Real minSwapRate = fStrikes_[j] - price / (sumDiscount * 10000); |
465 | | if (minSwapRate > tmpMinSwapRateIntersection) |
466 | | tmpMinSwapRateIntersection = minSwapRate; |
467 | | } |
468 | | for (Size j=0; j<cStrikes_.size(); ++j) { |
469 | | Real price = capPrice_(t,cStrikes_[j]); |
470 | | Real maxSwapRate = cStrikes_[j] + price / (sumDiscount * 10000); |
471 | | if (maxSwapRate < tmpMaxSwapRateIntersection) |
472 | | tmpMaxSwapRateIntersection = maxSwapRate; |
473 | | } |
474 | | maxSwapRateIntersection[i] = tmpMaxSwapRateIntersection; |
475 | | minSwapRateIntersection[i] = tmpMinSwapRateIntersection; |
476 | | |
477 | | // find the interval where the intersection lies |
478 | | bool trialsExceeded = false; |
479 | | int numTrials = (int)(maxSearchRange / searchStep); |
480 | | if ( floorPrice_(t,fStrikes_.back()) > capPrice_(t,fStrikes_.back()) ) { |
481 | | int counter = 1; |
482 | | bool stop = false; |
483 | | Real strike = 0.0; |
484 | | while (!stop) { |
485 | | strike = fStrikes_.back() - counter * searchStep; |
486 | | if (floorPrice_(t, strike) < capPrice_(t, strike)) |
487 | | stop = true; |
488 | | counter++; |
489 | | if (counter == numTrials + 1) { |
490 | | if (!stop) { |
491 | | stop = true; |
492 | | trialsExceeded = true; |
493 | | } |
494 | | } |
495 | | } |
496 | | lo = strike; |
497 | | hi = strike + searchStep; |
498 | | } else { |
499 | | int counter = 1; |
500 | | bool stop = false; |
501 | | Real strike = 0.0; |
502 | | while (!stop) { |
503 | | strike = fStrikes_.back() + counter * searchStep; |
504 | | if (floorPrice_(t, strike) > capPrice_(t, strike)) |
505 | | stop = true; |
506 | | counter++; |
507 | | if (counter == numTrials + 1) { |
508 | | if (!stop) { |
509 | | stop = true; |
510 | | trialsExceeded = true; |
511 | | } |
512 | | } |
513 | | } |
514 | | lo = strike - searchStep; |
515 | | hi = strike; |
516 | | } |
517 | | |
518 | | guess = (hi+lo)/2.0; |
519 | | Rate kI = -999.999; |
520 | | |
521 | | if (!trialsExceeded) { |
522 | | try{ |
523 | | kI = solver.solve( ObjectiveFunction(t, capPrice_, floorPrice_), solverTolerance_, guess, lo, hi ); |
524 | | } catch( std::exception &e) { |
525 | | QL_FAIL("cap/floor intersection finding failed at t = " << t << ", error msg: "<< e.what()); |
526 | | } |
527 | | // error message if kI is economically nonsensical (only if t is large) |
528 | | if (kI <= minSwapRateIntersection[i]) { |
529 | | if (t > maxExtrapolationMaturity) |
530 | | QL_FAIL("cap/floor intersection finding failed at t = " << t << |
531 | | ", error msg: intersection value is below the arbitrage free lower bound " |
532 | | << minSwapRateIntersection[i]); |
533 | | } |
534 | | else |
535 | | { |
536 | | tmpSwapMaturities.push_back(t); |
537 | | tmpSwapRates.push_back(kI); |
538 | | validMaturity[i] = true; |
539 | | } |
540 | | } |
541 | | else |
542 | | { |
543 | | // error message if t is too large |
544 | | if (t > maxExtrapolationMaturity) |
545 | | QL_FAIL("cap/floor intersection finding failed at t = " << t << |
546 | | ", error msg: no interection found inside the admissible range"); |
547 | | } |
548 | | } |
549 | | |
550 | | // extrapolation of swap rates if necessary |
551 | | //Polynomial2D tmpInterpol; |
552 | | //Interpolation interpol = tmpInterpol.interpolate(tmpSwapMaturities.begin(), tmpSwapMaturities.end(), tmpSwapRates.begin()); |
553 | | //interpol.enableExtrapolation(); |
554 | | int counter = 0; |
555 | | for (Size i=0; i<cfMaturities_.size(); ++i) { |
556 | | if ( !validMaturity[i] ) { |
557 | | atmYoYSwapDateRates_.first.push_back(referenceDate()+cfMaturities_[i]); |
558 | | atmYoYSwapTimeRates_.first.push_back(timeFromReference(referenceDate()+cfMaturities_[i])); |
559 | | // atmYoYSwapRates_->second.push_back(interpol((*cfMaturities_)[i])); |
560 | | // Heuristic: overwrite the the swap rate with a value that guarantees that the |
561 | | // intrinsic value of all options is lower than the price |
562 | | Real newSwapRate = minSwapRateIntersection[i] + intrinsicValueAddOn; |
563 | | if (newSwapRate > maxSwapRateIntersection[i]) |
564 | | newSwapRate = 0.5 * (minSwapRateIntersection[i] + maxSwapRateIntersection[i]); |
565 | | atmYoYSwapTimeRates_.second.push_back(newSwapRate); |
566 | | atmYoYSwapDateRates_.second.push_back(newSwapRate); |
567 | | } else { |
568 | | atmYoYSwapTimeRates_.first.push_back(tmpSwapMaturities[counter]); |
569 | | atmYoYSwapTimeRates_.second.push_back(tmpSwapRates[counter]); |
570 | | atmYoYSwapDateRates_.first.push_back( |
571 | | yoyOptionDateFromTenor(cfMaturities_.at(counter))); |
572 | | atmYoYSwapDateRates_.second.push_back(tmpSwapRates[counter]); |
573 | | counter++; |
574 | | } |
575 | | } |
576 | | |
577 | | // create the swap curve using the factory |
578 | | atmYoYSwapRateCurve_ = |
579 | | interpolator1d_.interpolate(atmYoYSwapTimeRates_.first.begin(), |
580 | | atmYoYSwapTimeRates_.first.end(), |
581 | | atmYoYSwapTimeRates_.second.begin()); |
582 | | } |
583 | | |
584 | | |
585 | | template<class I2D, class I1D> |
586 | | void InterpolatedYoYCapFloorTermPriceSurface<I2D,I1D>:: |
587 | | calculateYoYTermStructure() const { |
588 | | |
589 | | // which yoy-swap points to use in building the yoy-fwd curve? |
590 | | // for now pick every year |
591 | | Size nYears = (Size)std::lround(timeFromReference(referenceDate()+cfMaturities_.back())); |
592 | | |
593 | | std::vector<ext::shared_ptr<BootstrapHelper<YoYInflationTermStructure> > > YYhelpers; |
594 | | for (Size i=1; i<=nYears; i++) { |
595 | | Date maturity = nominalTS_->referenceDate() + Period(i,Years); |
596 | | Handle<Quote> quote(ext::shared_ptr<Quote>( |
597 | | new SimpleQuote( atmYoYSwapRate( maturity ) )));//! |
598 | | auto anInstrument = |
599 | | ext::make_shared<YearOnYearInflationSwapHelper>( |
600 | | quote, observationLag(), maturity, |
601 | | calendar(), bdc_, dayCounter(), |
602 | | yoyIndex(), |
603 | | this->indexIsInterpolated() ? CPI::Linear: CPI::Flat, |
604 | | nominalTS_); |
605 | | YYhelpers.push_back (anInstrument); |
606 | | } |
607 | | |
608 | | Date baseDate = |
609 | | inflationPeriod(nominalTS_->referenceDate() - observationLag(), |
610 | | yoyIndex()->frequency()).first; |
611 | | // usually this base rate is known |
612 | | // however for the data to be self-consistent |
613 | | // we pick this as the end of the curve |
614 | | Rate baseYoYRate = atmYoYSwapRate( referenceDate() );//! |
615 | | |
616 | | // Linear is OK because we have every year |
617 | | auto pYITS = |
618 | | ext::make_shared<PiecewiseYoYInflationCurve<Linear>>( |
619 | | nominalTS_->referenceDate(), baseDate, baseYoYRate, |
620 | | yoyIndex()->frequency(), dayCounter(), YYhelpers); |
621 | | pYITS->recalculate(); |
622 | | yoy_ = pYITS; // store |
623 | | |
624 | | // check that helpers are repriced |
625 | | const Real eps = 1e-5; |
626 | | for (Size i=0; i<YYhelpers.size(); i++) { |
627 | | Rate original = atmYoYSwapRate( yoyOptionDateFromTenor(Period(i+1,Years)) ); |
628 | | QL_REQUIRE(fabs(YYhelpers[i]->impliedQuote() - original) <eps, |
629 | | "could not reprice helper "<< i |
630 | | << ", data " << original |
631 | | << ", implied quote " << YYhelpers[i]->impliedQuote() |
632 | | ); |
633 | | } |
634 | | } |
635 | | |
636 | | } |
637 | | |
638 | | |
639 | | #endif |