1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*
4  Copyright (C) 2009 Chris Kenyon
5 
6  This file is part of QuantLib, a free-software/open-source library
7  for financial quantitative analysts and developers - http://quantlib.org/
8 
9  QuantLib is free software: you can redistribute it and/or modify it
10  under the terms of the QuantLib license.  You should have received a
11  copy of the license along with this program; if not, please email
12  <quantlib-dev@lists.sf.net>. The license is also available online at
13  <http://quantlib.org/license.shtml>.
14 
15  This program is distributed in the hope that it will be useful, but WITHOUT
16  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  FOR A PARTICULAR PURPOSE.  See the license for more details.
18  */
19 
20 
21 
22 #include <ql/cashflows/inflationcoupon.hpp>
23 #include <ql/cashflows/cashflowvectors.hpp>
24 #include <ql/time/daycounters/thirty360.hpp>
25 
26 #include <ql/cashflows/cpicoupon.hpp>
27 #include <ql/cashflows/cpicouponpricer.hpp>
28 
29 
30 namespace QuantLib {
31 
32     CPICoupon::
CPICoupon(Real baseCPI,const Date & paymentDate,Real nominal,const Date & startDate,const Date & endDate,Natural fixingDays,const ext::shared_ptr<ZeroInflationIndex> & zeroIndex,const Period & observationLag,CPI::InterpolationType observationInterpolation,const DayCounter & dayCounter,Real fixedRate,Spread spread,const Date & refPeriodStart,const Date & refPeriodEnd,const Date & exCouponDate)33     CPICoupon(
34               Real baseCPI, // user provided
35               const Date& paymentDate,
36               Real nominal,
37               const Date& startDate,
38               const Date& endDate,
39               Natural fixingDays,
40               const ext::shared_ptr<ZeroInflationIndex>& zeroIndex,
41               const Period& observationLag,
42               CPI::InterpolationType observationInterpolation,
43               const DayCounter& dayCounter,
44               Real fixedRate, // aka gearing
45               Spread spread,
46               const Date& refPeriodStart,
47               const Date& refPeriodEnd,
48               const Date& exCouponDate)
49     : InflationCoupon(paymentDate, nominal, startDate, endDate,
50                       fixingDays, zeroIndex, observationLag,
51                       dayCounter, refPeriodStart, refPeriodEnd, exCouponDate),
52         baseCPI_(baseCPI), fixedRate_(fixedRate), spread_(spread),
53         observationInterpolation_(observationInterpolation) {
54 
55             QL_REQUIRE(std::fabs(baseCPI_) > 1e-16,
56                        "|baseCPI_| < 1e-16, future divide-by-zero problem");
57         }
58 
59 
accept(AcyclicVisitor & v)60     void CPICoupon::accept(AcyclicVisitor& v) {
61         Visitor<CPICoupon>* v1 =
62         dynamic_cast<Visitor<CPICoupon>*>(&v);
63         if (v1 != 0)
64             v1->visit(*this);
65         else
66             InflationCoupon::accept(v);
67     }
68 
69 
checkPricerImpl(const ext::shared_ptr<InflationCouponPricer> & pricer) const70     bool CPICoupon::checkPricerImpl(
71             const ext::shared_ptr<InflationCouponPricer>&pricer) const {
72         return static_cast<bool>(
73                         ext::dynamic_pointer_cast<CPICouponPricer>(pricer));
74     }
75 
76 
indexFixing(const Date & d) const77     Rate CPICoupon::indexFixing(const Date &d) const {
78         // you may want to modify the interpolation of the index
79         // this gives you the chance
80 
81         Rate I1;
82         // what interpolation do we use? Index / flat / linear
83         if (observationInterpolation() == CPI::AsIndex) {
84             I1 = cpiIndex()->fixing(d);
85 
86         } else {
87             // work out what it should be
88             std::pair<Date,Date> dd = inflationPeriod(d, cpiIndex()->frequency());
89             Real indexStart = cpiIndex()->fixing(dd.first);
90             if (observationInterpolation() == CPI::Linear) {
91                 Real indexEnd = cpiIndex()->fixing(dd.second+Period(1,Days));
92                 // linear interpolation
93                 I1 = indexStart + (indexEnd - indexStart) * (d - dd.first)
94                 / (Real)( (dd.second+Period(1,Days)) - dd.first); // can't get to next period's value within current period
95             } else {
96                 // no interpolation, i.e. flat = constant, so use start-of-period value
97                 I1 = indexStart;
98             }
99 
100         }
101         return I1;
102     }
103 
104 
105 
106 
baseDate() const107     Date CPICashFlow::baseDate() const {
108         // you may not have a valid date
109         QL_FAIL("no base date specified");
110     }
111 
baseFixing() const112     Real CPICashFlow::baseFixing() const {
113         return baseFixing_;
114     }
115 
amount() const116     Real CPICashFlow::amount() const {
117         Real I0 = baseFixing();
118         Real I1;
119 
120         // what interpolation do we use? Index / flat / linear
121         if (interpolation() == CPI::AsIndex ) {
122             I1 = index()->fixing(fixingDate());
123         } else {
124             // work out what it should be
125             //std::cout << fixingDate() << " and " << frequency() << std::endl;
126             //std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency());
127             //std::cout << fixingDate() << " and " << dd.first << " " << dd.second << std::endl;
128             // work out what it should be
129             std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency());
130             Real indexStart = index()->fixing(dd.first);
131             if (interpolation() == CPI::Linear) {
132                 Real indexEnd = index()->fixing(dd.second+Period(1,Days));
133                 // linear interpolation
134                 //std::cout << indexStart << " and " << indexEnd << std::endl;
135                 I1 = indexStart + (indexEnd - indexStart) * (fixingDate() - dd.first)
136                 / ( (dd.second+Period(1,Days)) - dd.first); // can't get to next period's value within current period
137             } else {
138                 // no interpolation, i.e. flat = constant, so use start-of-period value
139                 I1 = indexStart;
140             }
141 
142         }
143 
144 
145         if (growthOnly())
146             return notional() * (I1 / I0 - 1.0);
147         else
148             return notional() * (I1 / I0);
149     }
150 
151 
CPILeg(const Schedule & schedule,const ext::shared_ptr<ZeroInflationIndex> & index,const Real baseCPI,const Period & observationLag)152     CPILeg::CPILeg(const Schedule& schedule, const ext::shared_ptr<ZeroInflationIndex>& index,
153                    const Real baseCPI, const Period& observationLag) :
154     schedule_(schedule), index_(index),
155     baseCPI_(baseCPI), observationLag_(observationLag),
156     paymentDayCounter_(Thirty360()),
157     paymentAdjustment_(ModifiedFollowing),
158     paymentCalendar_(schedule.calendar()),
159     fixingDays_(std::vector<Natural>(1,0)),
160     observationInterpolation_(CPI::AsIndex),
161     subtractInflationNominal_(true),
162     spreads_(std::vector<Real>(1,0))
163     {}
164 
165 
withObservationInterpolation(CPI::InterpolationType interp)166     CPILeg& CPILeg::withObservationInterpolation(CPI::InterpolationType interp) {
167         observationInterpolation_ = interp;
168         return *this;
169     }
170 
171 
withFixedRates(Real fixedRate)172     CPILeg& CPILeg::withFixedRates(Real fixedRate) {
173         fixedRates_ = std::vector<Real>(1,fixedRate);
174         return *this;
175     }
176 
withFixedRates(const std::vector<Real> & fixedRates)177     CPILeg& CPILeg::withFixedRates(const std::vector<Real>& fixedRates) {
178         fixedRates_ =   fixedRates;
179         return *this;
180     }
181 
withNotionals(Real notional)182     CPILeg& CPILeg::withNotionals(Real notional) {
183         notionals_ = std::vector<Real>(1,notional);
184         return *this;
185     }
186 
withNotionals(const std::vector<Real> & notionals)187     CPILeg& CPILeg::withNotionals(const std::vector<Real>& notionals) {
188         notionals_ = notionals;
189         return *this;
190     }
191 
withSubtractInflationNominal(bool growthOnly)192     CPILeg& CPILeg::withSubtractInflationNominal(bool growthOnly) {
193         subtractInflationNominal_ = growthOnly;
194         return *this;
195     }
196 
withPaymentDayCounter(const DayCounter & dayCounter)197     CPILeg& CPILeg::withPaymentDayCounter(const DayCounter& dayCounter) {
198         paymentDayCounter_ = dayCounter;
199         return *this;
200     }
201 
withPaymentAdjustment(BusinessDayConvention convention)202     CPILeg& CPILeg::withPaymentAdjustment(BusinessDayConvention convention) {
203         paymentAdjustment_ = convention;
204         return *this;
205     }
206 
withPaymentCalendar(const Calendar & cal)207     CPILeg& CPILeg::withPaymentCalendar(const Calendar& cal) {
208         paymentCalendar_ = cal;
209         return *this;
210     }
211 
withFixingDays(Natural fixingDays)212     CPILeg& CPILeg::withFixingDays(Natural fixingDays) {
213         fixingDays_ = std::vector<Natural>(1,fixingDays);
214         return *this;
215     }
216 
withFixingDays(const std::vector<Natural> & fixingDays)217     CPILeg& CPILeg::withFixingDays(const std::vector<Natural>& fixingDays) {
218         fixingDays_ = fixingDays;
219         return *this;
220     }
221 
withSpreads(Spread spread)222     CPILeg& CPILeg::withSpreads(Spread spread) {
223         spreads_ = std::vector<Spread>(1,spread);
224         return *this;
225     }
226 
withSpreads(const std::vector<Spread> & spreads)227     CPILeg& CPILeg::withSpreads(const std::vector<Spread>& spreads) {
228         spreads_ = spreads;
229         return *this;
230     }
231 
withCaps(Rate cap)232     CPILeg& CPILeg::withCaps(Rate cap) {
233         caps_ = std::vector<Rate>(1,cap);
234         return *this;
235     }
236 
withCaps(const std::vector<Rate> & caps)237     CPILeg& CPILeg::withCaps(const std::vector<Rate>& caps) {
238         caps_ = caps;
239         return *this;
240     }
241 
withFloors(Rate floor)242     CPILeg& CPILeg::withFloors(Rate floor) {
243         floors_ = std::vector<Rate>(1,floor);
244         return *this;
245     }
246 
withFloors(const std::vector<Rate> & floors)247     CPILeg& CPILeg::withFloors(const std::vector<Rate>& floors) {
248         floors_ = floors;
249         return *this;
250     }
251 
withExCouponPeriod(const Period & period,const Calendar & cal,BusinessDayConvention convention,bool endOfMonth)252     CPILeg& CPILeg::withExCouponPeriod(
253                         const Period& period,
254                         const Calendar& cal,
255                         BusinessDayConvention convention,
256                         bool endOfMonth) {
257         exCouponPeriod_ = period;
258         exCouponCalendar_ = cal;
259         exCouponAdjustment_ = convention;
260         exCouponEndOfMonth_ = endOfMonth;
261         return *this;
262     }
263 
264 
operator Leg() const265     CPILeg::operator Leg() const {
266 
267         QL_REQUIRE(!notionals_.empty(), "no notional given");
268         Size n = schedule_.size()-1;
269         Leg leg;
270         leg.reserve(n+1);   // +1 for notional, we always have some sort ...
271         if (n>0) {
272             QL_REQUIRE(!fixedRates_.empty() || !spreads_.empty(),
273                        "no fixedRates or spreads given");
274 
275             Date refStart, start, refEnd, end;
276 
277             for (Size i=0; i<n; ++i) {
278                 refStart = start = schedule_.date(i);
279                 refEnd   =   end = schedule_.date(i+1);
280                 Date paymentDate = paymentCalendar_.adjust(end, paymentAdjustment_);
281 
282                 Date exCouponDate;
283                 if (exCouponPeriod_ != Period())
284                 {
285                     exCouponDate = exCouponCalendar_.advance(paymentDate,
286                                                                 -exCouponPeriod_,
287                                                                 exCouponAdjustment_,
288                                                                 exCouponEndOfMonth_);
289                 }
290 
291                 if (i==0   && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) {
292                     BusinessDayConvention bdc = schedule_.businessDayConvention();
293                     refStart = schedule_.calendar().adjust(end - schedule_.tenor(), bdc);
294                 }
295                 if (i==n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) {
296                     BusinessDayConvention bdc = schedule_.businessDayConvention();
297                     refEnd = schedule_.calendar().adjust(start + schedule_.tenor(), bdc);
298                 }
299                 if (detail::get(fixedRates_, i, 1.0) == 0.0) { // fixed coupon
300                     leg.push_back(ext::shared_ptr<CashFlow>
301                                   (new FixedRateCoupon
302                                    (paymentDate, detail::get(notionals_, i, 0.0),
303                                     detail::effectiveFixedRate(spreads_,caps_,floors_,i),
304                                     paymentDayCounter_, start, end, refStart, refEnd, exCouponDate)));
305                 } else { // zero inflation coupon
306                     if (detail::noOption(caps_, floors_, i)) { // just swaplet
307                         ext::shared_ptr<CPICoupon> coup;
308 
309                         coup = ext::shared_ptr<CPICoupon>
310                             (new CPICoupon(baseCPI_,    // all have same base for ratio
311                                      paymentDate,
312                                      detail::get(notionals_, i, 0.0),
313                                      start, end,
314                                      detail::get(fixingDays_, i, 0.0),
315                                      index_, observationLag_,
316                                      observationInterpolation_,
317                                      paymentDayCounter_,
318                                      detail::get(fixedRates_, i, 0.0),
319                                      detail::get(spreads_, i, 0.0),
320                                      refStart, refEnd, exCouponDate));
321 
322                         // in this case you can set a pricer
323                         // straight away because it only provides computation - not data
324                         ext::shared_ptr<CPICouponPricer> pricer =
325                             ext::make_shared<CPICouponPricer>(Handle<CPIVolatilitySurface>(),
326                                                               Handle<YieldTermStructure>());
327                         coup->setPricer(pricer);
328                         leg.push_back(ext::dynamic_pointer_cast<CashFlow>(coup));
329 
330                     } else  {     // cap/floorlet
331                         QL_FAIL("caps/floors on CPI coupons not implemented.");
332                     }
333                 }
334             }
335         }
336 
337         // in CPI legs you always have a notional flow of some sort
338         Date paymentDate = paymentCalendar_.adjust(schedule_.date(n), paymentAdjustment_);
339         Date fixingDate = paymentDate - observationLag_;
340         ext::shared_ptr<CashFlow> xnl(new CPICashFlow
341                           (detail::get(notionals_, n, 0.0), index_,
342                            Date(), // is fake, i.e. you do not have one
343                            baseCPI_, fixingDate, paymentDate,
344                            subtractInflationNominal_, observationInterpolation_,
345                            index_->frequency())
346                          );
347         leg.push_back(xnl);
348 
349 
350         return leg;
351     }
352 
353 
354 
355 
356 
357 
358 } // namespace RiskLib
359 
360