1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*
4  Copyright (C) 2006, 2007 Giorgio Facchinetti
5  Copyright (C) 2006, 2007 Mario Pucci
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 
22 #include <ql/cashflows/rangeaccrual.hpp>
23 #include <ql/cashflows/cashflowvectors.hpp>
24 #include <ql/pricingengines/blackformula.hpp>
25 #include <ql/math/distributions/normaldistribution.hpp>
26 #include <ql/time/schedule.hpp>
27 #include <ql/indexes/iborindex.hpp>
28 #include <ql/termstructures/yieldtermstructure.hpp>
29 
30 #include <cmath>
31 
32 namespace QuantLib {
33 
34     //===========================================================================//
35     //                         RangeAccrualFloatersCoupon                        //
36     //===========================================================================//
37 
RangeAccrualFloatersCoupon(const Date & paymentDate,Real nominal,const ext::shared_ptr<IborIndex> & index,const Date & startDate,const Date & endDate,Natural fixingDays,const DayCounter & dayCounter,Real gearing,Rate spread,const Date & refPeriodStart,const Date & refPeriodEnd,const ext::shared_ptr<Schedule> & observationsSchedule,Real lowerTrigger,Real upperTrigger)38     RangeAccrualFloatersCoupon::RangeAccrualFloatersCoupon(
39                 const Date& paymentDate,
40                 Real nominal,
41                 const ext::shared_ptr<IborIndex>& index,
42                 const Date& startDate,                                  // S
43                 const Date& endDate,                                    // T
44                 Natural fixingDays,
45                 const DayCounter& dayCounter,
46                 Real gearing,
47                 Rate spread,
48                 const Date& refPeriodStart,
49                 const Date& refPeriodEnd,
50                 const ext::shared_ptr<Schedule>&  observationsSchedule,
51                 Real lowerTrigger,                                    // l
52                 Real upperTrigger                                     // u
53         )
54     : FloatingRateCoupon(paymentDate, nominal, startDate, endDate,
55                          fixingDays, index, gearing, spread,
56                          refPeriodStart, refPeriodEnd, dayCounter),
57     observationsSchedule_(observationsSchedule),
58     lowerTrigger_(lowerTrigger),
59     upperTrigger_(upperTrigger){
60 
61         QL_REQUIRE(lowerTrigger_<upperTrigger,
62                    "lowerTrigger_>=upperTrigger");
63         QL_REQUIRE(observationsSchedule_->startDate()==startDate,
64                    "incompatible start date");
65         QL_REQUIRE(observationsSchedule_->endDate()==endDate,
66                    "incompatible end date");
67 
68         observationDates_ = observationsSchedule_->dates();
69         observationDates_.pop_back();                       //remove end date
70         observationDates_.erase(observationDates_.begin()); //remove start date
71         observationsNo_ = observationDates_.size();
72 
73         const Handle<YieldTermStructure>& rateCurve =
74             index->forwardingTermStructure();
75         Date referenceDate = rateCurve->referenceDate();
76 
77         startTime_ = dayCounter.yearFraction(referenceDate, startDate);
78         endTime_ = dayCounter.yearFraction(referenceDate, endDate);
79         for(Size i=0;i<observationsNo_;i++) {
80             observationTimes_.push_back(
81                 dayCounter.yearFraction(referenceDate, observationDates_[i]));
82         }
83 
84      }
85 
accept(AcyclicVisitor & v)86     void RangeAccrualFloatersCoupon::accept(AcyclicVisitor& v) {
87         Visitor<RangeAccrualFloatersCoupon>* v1 =
88             dynamic_cast<Visitor<RangeAccrualFloatersCoupon>*>(&v);
89         if (v1 != 0)
90             v1->visit(*this);
91         else
92             FloatingRateCoupon::accept(v);
93     }
94 
priceWithoutOptionality(const Handle<YieldTermStructure> & discountingCurve) const95     Real RangeAccrualFloatersCoupon::priceWithoutOptionality(
96            const Handle<YieldTermStructure>& discountingCurve) const {
97         return accrualPeriod() * (gearing_*indexFixing()+spread_) *
98                nominal() * discountingCurve->discount(date());
99     }
100 
101 
102     //=======================================================================//
103     //                        RangeAccrualPricer                             //
104     //=======================================================================//
105 
initialize(const FloatingRateCoupon & coupon)106     void RangeAccrualPricer::initialize(const FloatingRateCoupon& coupon){
107         coupon_ =  dynamic_cast<const RangeAccrualFloatersCoupon*>(&coupon);
108         QL_REQUIRE(coupon_, "range-accrual coupon required");
109         gearing_ = coupon_->gearing();
110         spread_ = coupon_->spread();
111 
112         Date paymentDate = coupon_->date();
113 
114         ext::shared_ptr<IborIndex> index =
115             ext::dynamic_pointer_cast<IborIndex>(coupon_->index());
116         const Handle<YieldTermStructure>& rateCurve =
117             index->forwardingTermStructure();
118         discount_ = rateCurve->discount(paymentDate);
119         accrualFactor_ = coupon_->accrualPeriod();
120         spreadLegValue_ = spread_ * accrualFactor_* discount_;
121 
122         startTime_ = coupon_->startTime();
123         endTime_ = coupon_->endTime();
124         observationTimes_ = coupon_->observationTimes();
125         lowerTrigger_ = coupon_->lowerTrigger();
126         upperTrigger_ = coupon_->upperTrigger();
127         observationsNo_ = coupon_->observationsNo();
128 
129         const std::vector<Date> &observationDates =
130             coupon_->observationsSchedule()->dates();
131         QL_REQUIRE(observationDates.size()==observationsNo_+2,
132                    "incompatible size of initialValues vector");
133         initialValues_= std::vector<Real>(observationDates.size(),0.);
134 
135         Calendar calendar = index->fixingCalendar();
136         for(Size i=0; i<observationDates.size(); i++) {
137             initialValues_[i]=index->fixing(
138                 calendar.advance(observationDates[i],
139                                  -static_cast<Integer>(coupon_->fixingDays()),
140                                  Days));
141         }
142 
143     }
144 
swapletRate() const145     Real RangeAccrualPricer::swapletRate() const {
146         return swapletPrice()/(accrualFactor_*discount_);
147     }
148 
capletPrice(Rate) const149     Real RangeAccrualPricer::capletPrice(Rate) const {
150         QL_FAIL("RangeAccrualPricer::capletPrice not implemented");
151     }
152 
capletRate(Rate) const153     Rate RangeAccrualPricer::capletRate(Rate) const {
154         QL_FAIL("RangeAccrualPricer::capletRate not implemented");
155     }
156 
floorletPrice(Rate) const157     Real RangeAccrualPricer::floorletPrice(Rate) const {
158         QL_FAIL("RangeAccrualPricer::floorletPrice not implemented");
159     }
160 
floorletRate(Rate) const161     Rate RangeAccrualPricer::floorletRate(Rate) const {
162         QL_FAIL("RangeAccrualPricer::floorletRate not implemented");
163     }
164 
165     //===========================================================================//
166     //                          RangeAccrualPricerByBgm                          //
167     //===========================================================================//
RangeAccrualPricerByBgm(Real correlation,const ext::shared_ptr<SmileSection> & smilesOnExpiry,const ext::shared_ptr<SmileSection> & smilesOnPayment,bool withSmile,bool byCallSpread)168     RangeAccrualPricerByBgm::RangeAccrualPricerByBgm(
169             Real correlation,
170             const  ext::shared_ptr<SmileSection>& smilesOnExpiry,
171             const  ext::shared_ptr<SmileSection>& smilesOnPayment,
172             bool withSmile,
173             bool byCallSpread)
174     : correlation_(correlation),
175       withSmile_(withSmile),
176       byCallSpread_(byCallSpread),
177       smilesOnExpiry_(smilesOnExpiry),
178       smilesOnPayment_(smilesOnPayment),
179       eps_(1.0e-8) {
180 
181     }
swapletPrice() const182     Real RangeAccrualPricerByBgm::swapletPrice() const{
183 
184         Real result = 0.;
185         const Real deflator = discount_*initialValues_[0];
186         for(Size i=0;i<observationsNo_;i++){
187             Real digitalFloater = digitalRangePrice(lowerTrigger_, upperTrigger_,initialValues_[i+1],
188                                                      observationTimes_[i], deflator);
189             result += digitalFloater;
190         }
191         return gearing_ *(result*accrualFactor_/observationsNo_)+ spreadLegValue_;
192     }
193 
driftsOverPeriod(Real U,Real lambdaS,Real lambdaT,Real correlation) const194     std::vector<Real> RangeAccrualPricerByBgm::driftsOverPeriod(Real U,
195                                                                 Real lambdaS,
196                                                                 Real lambdaT,
197                                                                 Real correlation) const{
198         std::vector<Real> result;
199 
200         const Real p = (U-startTime_)/accrualFactor_;
201         const Real q = (endTime_-U)/accrualFactor_;
202         const Real L0T = initialValues_.back();
203 
204         const Real driftBeforeFixing =
205                 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(p*lambdaT*lambdaT + q*lambdaS*lambdaT*correlation) +
206                 q*lambdaS*lambdaS + p*lambdaS*lambdaT*correlation
207                 -0.5*lambda(U,lambdaS,lambdaT)*lambda(U,lambdaS,lambdaT);
208         const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*lambdaT*lambdaT;
209 
210         result.push_back(driftBeforeFixing);
211         result.push_back(driftAfterFixing);
212 
213         return result;
214     }
215 
lambdasOverPeriod(Real U,Real lambdaS,Real lambdaT) const216     std::vector<Real> RangeAccrualPricerByBgm::lambdasOverPeriod(Real U,
217                                                                    Real lambdaS,
218                                                                    Real lambdaT) const{
219         std::vector<Real> result;
220 
221         const Real p = (U-startTime_)/accrualFactor_;
222         const Real q = (endTime_-U)/accrualFactor_;
223 
224         const Real lambdaBeforeFixing = q*lambdaS + p*lambdaT;
225         const Real lambdaAfterFixing = lambdaT;
226 
227         result.push_back(lambdaBeforeFixing);
228         result.push_back(lambdaAfterFixing);
229 
230         return result;
231     }
drift(Real U,Real lambdaS,Real lambdaT,Real correlation) const232     Real RangeAccrualPricerByBgm::drift(Real U,
233                                           Real lambdaS,
234                                           Real lambdaT,
235                                           Real correlation) const{
236         Real result;
237 
238         const Real p = (U-startTime_)/accrualFactor_;
239         const Real q = (endTime_-U)/accrualFactor_;
240         const Real L0T = initialValues_.back();
241 
242         const Real driftBeforeFixing =
243                 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(p*lambdaT*lambdaT + q*lambdaS*lambdaT*correlation) +
244                 q*lambdaS*lambdaS + p*lambdaS*lambdaT*correlation;
245         const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*lambdaT*lambdaT;
246 
247         if(startTime_ > 0){result = driftBeforeFixing;}
248         else {result = driftAfterFixing;}
249 
250         return result;
251     }
252 
lambda(Real U,Real lambdaS,Real lambdaT) const253     Real RangeAccrualPricerByBgm::lambda(Real U,
254                                            Real lambdaS,
255                                            Real lambdaT) const{
256         Real result;
257 
258         const Real p = (U-startTime_)/accrualFactor_;
259         const Real q = (endTime_-U)/accrualFactor_;
260 
261         if(startTime_ > 0){result = q*lambdaS + p*lambdaT;}
262         else {result = lambdaT;}
263 
264         return result;
265     }
266 
267 
derDriftDerLambdaS(Real U,Real lambdaS,Real lambdaT,Real correlation) const268     Real RangeAccrualPricerByBgm::derDriftDerLambdaS(Real U,
269                                                         Real lambdaS,
270                                                         Real lambdaT,
271                                                         Real correlation) const{
272         Real result;
273 
274         const Real p = (U-startTime_)/accrualFactor_;
275         const Real q = (endTime_-U)/accrualFactor_;
276         const Real L0T = initialValues_.back();
277 
278         const Real driftBeforeFixing =
279                 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(q*lambdaT*correlation) +
280                 2*q*lambdaS + p*lambdaT*correlation;
281         const Real driftAfterFixing = 0.;
282 
283         if(startTime_ > 0){result = driftBeforeFixing;}
284         else {result = driftAfterFixing;}
285 
286         return result;
287     }
288 
derLambdaDerLambdaS(Real U) const289     Real RangeAccrualPricerByBgm::derLambdaDerLambdaS(Real U) const {
290 
291         if (startTime_>0) {
292             Real q = (endTime_-U)/accrualFactor_;
293             return q;
294         } else
295             return 0.0;
296 
297     }
298 
derDriftDerLambdaT(Real U,Real lambdaS,Real lambdaT,Real correlation) const299     Real RangeAccrualPricerByBgm::derDriftDerLambdaT(Real U,
300                                                         Real lambdaS,
301                                                         Real lambdaT,
302                                                         Real correlation) const{
303         Real result;
304 
305         const Real p = (U-startTime_)/accrualFactor_;
306         const Real q = (endTime_-U)/accrualFactor_;
307         const Real L0T = initialValues_.back();
308 
309         const Real driftBeforeFixing =
310                 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(2*p*lambdaT + q*lambdaS*correlation) +
311                 + p*lambdaS*correlation;
312         const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*2*lambdaT;
313 
314         if(startTime_ > 0){result = driftBeforeFixing;}
315         else {result = driftAfterFixing;}
316 
317         return result;
318     }
319 
derLambdaDerLambdaT(Real U) const320     Real RangeAccrualPricerByBgm::derLambdaDerLambdaT(Real U) const {
321 
322         if (startTime_>0) {
323             Real p = (U-startTime_)/accrualFactor_;
324             return p;
325         } else
326             return 0.0;
327 
328     }
329 
digitalRangePrice(Real lowerTrigger,Real upperTrigger,Real initialValue,Real expiry,Real deflator) const330     Real RangeAccrualPricerByBgm::digitalRangePrice(Real lowerTrigger,
331                                                       Real upperTrigger,
332                                                       Real initialValue,
333                                                       Real expiry,
334                                                       Real deflator) const{
335             const Real lowerPrice = digitalPrice(lowerTrigger, initialValue, expiry, deflator);
336             const Real upperPrice = digitalPrice(upperTrigger, initialValue, expiry, deflator);
337             const Real result =  lowerPrice - upperPrice;
338             QL_REQUIRE(result >=0.,
339                 "RangeAccrualPricerByBgm::digitalRangePrice:\n digitalPrice("<<upperTrigger<<
340                 "): "<<upperPrice<<" >  digitalPrice("<<lowerTrigger<<"): "<<lowerPrice);
341             return result;
342 
343     }
digitalPrice(Real strike,Real initialValue,Real expiry,Real deflator) const344     Real RangeAccrualPricerByBgm::digitalPrice(Real strike,
345                                         Real initialValue,
346                                         Real expiry,
347                                         Real deflator) const {
348         Real result = deflator;
349         if(strike>eps_/2){
350             if(withSmile_)
351                 result = digitalPriceWithSmile(strike, initialValue, expiry, deflator);
352             else
353                 result = digitalPriceWithoutSmile(strike, initialValue, expiry, deflator);
354         }
355         return result;
356     }
357 
digitalPriceWithoutSmile(Real strike,Real initialValue,Real expiry,Real deflator) const358     Real RangeAccrualPricerByBgm::digitalPriceWithoutSmile(Real strike,
359                                         Real initialValue,
360                                         Real expiry,
361                                         Real deflator) const {
362 
363         Real lambdaS = smilesOnExpiry_->volatility(strike);
364         Real lambdaT = smilesOnPayment_->volatility(strike);
365 
366         std::vector<Real> lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT);
367         const Real variance =
368             startTime_*lambdaU[0]*lambdaU[0]+(expiry-startTime_)*lambdaU[1]*lambdaU[1];
369 
370         Real lambdaSATM = smilesOnExpiry_->volatility(initialValue);
371         Real lambdaTATM = smilesOnPayment_->volatility(initialValue);
372         //drift of Lognormal process (of Libor) "a_U()" nel paper
373         std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);
374         const Real adjustment = (startTime_*muU[0]+(expiry-startTime_)*muU[1]);
375 
376 
377        Real d2 = (std::log(initialValue/strike) + adjustment - 0.5*variance)/std::sqrt(variance);
378 
379        CumulativeNormalDistribution phi;
380        const Real result = deflator*phi(d2);
381 
382        QL_REQUIRE(result > 0.,
383            "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result< 0. Result:"<<result);
384        QL_REQUIRE(result/deflator <= 1.,
385             "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result/deflator > 1. Ratio: "
386             << result/deflator << " result: " << result<< " deflator: " << deflator);
387 
388        return result;
389     }
390 
digitalPriceWithSmile(Real strike,Real initialValue,Real expiry,Real deflator) const391     Real RangeAccrualPricerByBgm::digitalPriceWithSmile(Real strike,
392                                         Real initialValue,
393                                         Real expiry,
394                                         Real deflator) const {
395         Real result;
396         if (byCallSpread_) {
397 
398             // Previous strike
399             const Real previousStrike = strike - eps_/2;
400             Real lambdaS = smilesOnExpiry_->volatility(previousStrike);
401             Real lambdaT = smilesOnPayment_->volatility(previousStrike);
402 
403             //drift of Lognormal process (of Libor) "a_U()" nel paper
404             std::vector<Real> lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT);
405             const Real previousVariance = std::max(startTime_, 0.)*lambdaU[0]*lambdaU[0]+
406                          std::min(expiry-startTime_, expiry)*lambdaU[1]*lambdaU[1];
407 
408             Real lambdaSATM = smilesOnExpiry_->volatility(initialValue);
409             Real lambdaTATM = smilesOnPayment_->volatility(initialValue);
410             std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);
411             const Real previousAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] +
412                                          std::min(expiry-startTime_, expiry)*muU[1]);
413             const Real previousForward = initialValue * previousAdjustment ;
414 
415             // Next strike
416             const Real nextStrike = strike + eps_/2;
417             lambdaS = smilesOnExpiry_->volatility(nextStrike);
418             lambdaT = smilesOnPayment_->volatility(nextStrike);
419 
420             lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT);
421             const Real nextVariance = std::max(startTime_, 0.)*lambdaU[0]*lambdaU[0]+
422                          std::min(expiry-startTime_, expiry)*lambdaU[1]*lambdaU[1];
423             //drift of Lognormal process (of Libor) "a_U()" nel paper
424             muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);
425             const Real nextAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] +
426                                          std::min(expiry-startTime_, expiry)*muU[1]);
427             const Real nextForward = initialValue * nextAdjustment ;
428 
429             result = callSpreadPrice(previousForward,nextForward,previousStrike, nextStrike,
430                                                     deflator, previousVariance, nextVariance);
431 
432         }
433         else{
434             result = digitalPriceWithoutSmile(strike, initialValue, expiry, deflator)+
435                      smileCorrection(strike, initialValue, expiry, deflator);
436         }
437 
438         QL_REQUIRE(result > -std::pow(eps_,.5),
439             "RangeAccrualPricerByBgm::digitalPriceWithSmile: result< 0 Result:"<<result);
440         QL_REQUIRE(result/deflator <=  1.0 + std::pow(eps_,.2),
441             "RangeAccrualPricerByBgm::digitalPriceWithSmile: result/deflator > 1. Ratio: "
442             << result/deflator << " result: " << result<< " deflator: " << deflator);
443 
444         return result;
445     }
446 
smileCorrection(Real strike,Real forward,Real expiry,Real deflator) const447     Real RangeAccrualPricerByBgm::smileCorrection(Real strike,
448                                         Real forward,
449                                         Real expiry,
450                                         Real deflator) const {
451 
452         const Real previousStrike = strike - eps_/2;
453         const Real nextStrike = strike + eps_/2;
454 
455         const Real derSmileS = (smilesOnExpiry_->volatility(nextStrike)-
456                                  smilesOnExpiry_->volatility(previousStrike))/eps_;
457         const Real derSmileT = (smilesOnPayment_->volatility(nextStrike)-
458                                  smilesOnPayment_->volatility(previousStrike))/eps_;
459 
460         Real lambdaS = smilesOnExpiry_->volatility(strike);
461         Real lambdaT = smilesOnPayment_->volatility(strike);
462         //Real lambdaU = lambda(expiry, lambdaS, lambdaT);
463 
464         Real derLambdaDerK = derLambdaDerLambdaS(expiry) * derSmileS +
465                              derLambdaDerLambdaT(expiry) * derSmileT;
466         //Real derDriftDerK = derDriftDerLambdaS(expiry, lambdaS, lambdaT, correlation_)*derSmileS +
467         //                      derDriftDerLambdaT(expiry, lambdaS, lambdaT, correlation_)*derSmileT +
468         //                      lambdaU * derLambdaDerK;
469 
470         Real lambdaSATM = smilesOnExpiry_->volatility(forward);
471         Real lambdaTATM = smilesOnPayment_->volatility(forward);
472         std::vector<Real> lambdasOverPeriodU = lambdasOverPeriod(expiry, lambdaS, lambdaT);
473         //drift of Lognormal process (of Libor) "a_U()" nel paper
474         std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);
475 
476         const Real variance = std::max(startTime_, 0.)*lambdasOverPeriodU[0]*lambdasOverPeriodU[0] +
477                        std::min(expiry-startTime_, expiry)*lambdasOverPeriodU[1]*lambdasOverPeriodU[1];
478 
479         const Real forwardAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] +
480                                          std::min(expiry-startTime_, expiry)*muU[1]);
481         const Real forwardAdjusted = forward * forwardAdjustment;
482 
483         const Real d1 = (std::log(forwardAdjusted/strike)+0.5*variance)/std::sqrt(variance);
484 
485         const Real sqrtOfTimeToExpiry = (std::max(startTime_, 0.)*lambdasOverPeriodU[0] +
486                                 std::min(expiry-startTime_, expiry)*lambdasOverPeriodU[1])*
487                                 (1./std::sqrt(variance));
488 
489         CumulativeNormalDistribution phi;
490         NormalDistribution psi;
491         Real result = - forwardAdjusted*psi(d1)*sqrtOfTimeToExpiry*derLambdaDerK ;
492                        // - forwardAdjusted*phi(d1)*expiry*derDriftDerK;
493 
494         result *= deflator;
495 
496         QL_REQUIRE(std::fabs(result/deflator) <= 1.0 + std::pow(eps_,.2),
497             "RangeAccrualPricerByBgm::smileCorrection: abs(result/deflator) > 1. Ratio: "
498             << result/deflator << " result: " << result<< " deflator: " << deflator);
499 
500         return result;
501     }
502 
callSpreadPrice(Real previousForward,Real nextForward,Real previousStrike,Real nextStrike,Real deflator,Real previousVariance,Real nextVariance) const503     Real RangeAccrualPricerByBgm::callSpreadPrice(
504                                             Real previousForward,
505                                             Real nextForward,
506                                             Real previousStrike,
507                                             Real nextStrike,
508                                             Real deflator,
509                                             Real previousVariance,
510                                             Real nextVariance) const{
511          const Real nextCall =
512             blackFormula(Option::Call, nextStrike, nextForward, std::sqrt(nextVariance), deflator);
513          const Real previousCall =
514             blackFormula(Option::Call, previousStrike, previousForward, std::sqrt(previousVariance), deflator);
515 
516          QL_ENSURE(nextCall <previousCall,"RangeAccrualPricerByBgm::callSpreadPrice: nextCall > previousCall"
517             "\n nextCall: strike :" << nextStrike << "; variance: " << nextVariance <<
518             " adjusted initial value " << nextForward <<
519             "\n previousCall: strike :" << previousStrike << "; variance: " << previousVariance <<
520             " adjusted initial value " << previousForward );
521 
522          const Real result = (previousCall-nextCall)/(nextStrike-previousStrike);
523 
524          return result;
525     }
526 
527 
528 
529 
RangeAccrualLeg(const Schedule & schedule,const ext::shared_ptr<IborIndex> & index)530     RangeAccrualLeg::RangeAccrualLeg(
531                             const Schedule& schedule,
532                             const ext::shared_ptr<IborIndex>& index)
533     : schedule_(schedule), index_(index),
534       paymentAdjustment_(Following),
535       observationConvention_(ModifiedFollowing) {}
536 
withNotionals(Real notional)537     RangeAccrualLeg& RangeAccrualLeg::withNotionals(Real notional) {
538         notionals_ = std::vector<Real>(1,notional);
539         return *this;
540     }
541 
withNotionals(const std::vector<Real> & notionals)542     RangeAccrualLeg& RangeAccrualLeg::withNotionals(
543                                          const std::vector<Real>& notionals) {
544         notionals_ = notionals;
545         return *this;
546     }
547 
withPaymentDayCounter(const DayCounter & dayCounter)548     RangeAccrualLeg& RangeAccrualLeg::withPaymentDayCounter(
549                                                const DayCounter& dayCounter) {
550         paymentDayCounter_ = dayCounter;
551         return *this;
552     }
553 
withPaymentAdjustment(BusinessDayConvention convention)554     RangeAccrualLeg& RangeAccrualLeg::withPaymentAdjustment(
555                                            BusinessDayConvention convention) {
556         paymentAdjustment_ = convention;
557         return *this;
558     }
559 
withFixingDays(Natural fixingDays)560     RangeAccrualLeg& RangeAccrualLeg::withFixingDays(Natural fixingDays) {
561         fixingDays_ = std::vector<Natural>(1,fixingDays);
562         return *this;
563     }
564 
withFixingDays(const std::vector<Natural> & fixingDays)565     RangeAccrualLeg& RangeAccrualLeg::withFixingDays(
566                                      const std::vector<Natural>& fixingDays) {
567         fixingDays_ = fixingDays;
568         return *this;
569     }
570 
withGearings(Real gearing)571     RangeAccrualLeg& RangeAccrualLeg::withGearings(Real gearing) {
572         gearings_ = std::vector<Real>(1,gearing);
573         return *this;
574     }
575 
withGearings(const std::vector<Real> & gearings)576     RangeAccrualLeg& RangeAccrualLeg::withGearings(
577                                           const std::vector<Real>& gearings) {
578         gearings_ = gearings;
579         return *this;
580     }
581 
withSpreads(Spread spread)582     RangeAccrualLeg& RangeAccrualLeg::withSpreads(Spread spread) {
583         spreads_ = std::vector<Spread>(1,spread);
584         return *this;
585     }
586 
withSpreads(const std::vector<Spread> & spreads)587     RangeAccrualLeg& RangeAccrualLeg::withSpreads(
588                                          const std::vector<Spread>& spreads) {
589         spreads_ = spreads;
590         return *this;
591     }
592 
withLowerTriggers(Rate trigger)593     RangeAccrualLeg& RangeAccrualLeg::withLowerTriggers(Rate trigger) {
594         lowerTriggers_ = std::vector<Rate>(1,trigger);
595         return *this;
596     }
597 
withLowerTriggers(const std::vector<Rate> & triggers)598     RangeAccrualLeg& RangeAccrualLeg::withLowerTriggers(
599                                           const std::vector<Rate>& triggers) {
600         lowerTriggers_ = triggers;
601         return *this;
602     }
603 
withUpperTriggers(Rate trigger)604     RangeAccrualLeg& RangeAccrualLeg::withUpperTriggers(Rate trigger) {
605         upperTriggers_ = std::vector<Rate>(1,trigger);
606         return *this;
607     }
608 
withUpperTriggers(const std::vector<Rate> & triggers)609     RangeAccrualLeg& RangeAccrualLeg::withUpperTriggers(
610                                           const std::vector<Rate>& triggers) {
611         upperTriggers_ = triggers;
612         return *this;
613     }
614 
withObservationTenor(const Period & tenor)615     RangeAccrualLeg& RangeAccrualLeg::withObservationTenor(
616                                                         const Period& tenor) {
617         observationTenor_ = tenor;
618         return *this;
619     }
620 
withObservationConvention(BusinessDayConvention convention)621     RangeAccrualLeg& RangeAccrualLeg::withObservationConvention(
622                                            BusinessDayConvention convention) {
623         observationConvention_ = convention;
624         return *this;
625     }
626 
operator Leg() const627     RangeAccrualLeg::operator Leg() const {
628 
629         QL_REQUIRE(!notionals_.empty(), "no notional given");
630 
631         Size n = schedule_.size()-1;
632         QL_REQUIRE(notionals_.size() <= n,
633                    "too many nominals (" << notionals_.size() <<
634                    "), only " << n << " required");
635         QL_REQUIRE(fixingDays_.size() <= n,
636                    "too many fixingDays (" << fixingDays_.size() <<
637                    "), only " << n << " required");
638         QL_REQUIRE(gearings_.size()<=n,
639                    "too many gearings (" << gearings_.size() <<
640                    "), only " << n << " required");
641         QL_REQUIRE(spreads_.size()<=n,
642                    "too many spreads (" << spreads_.size() <<
643                    "), only " << n << " required");
644         QL_REQUIRE(lowerTriggers_.size()<=n,
645                    "too many lowerTriggers (" << lowerTriggers_.size() <<
646                    "), only " << n << " required");
647         QL_REQUIRE(upperTriggers_.size()<=n,
648                    "too many upperTriggers (" << upperTriggers_.size() <<
649                    "), only " << n << " required");
650 
651         Leg leg(n);
652 
653         // the following is not always correct
654         Calendar calendar = schedule_.calendar();
655 
656         Date refStart, start, refEnd, end;
657         Date paymentDate;
658         std::vector<ext::shared_ptr<Schedule> > observationsSchedules;
659 
660         for (Size i=0; i<n; ++i) {
661             refStart = start = schedule_.date(i);
662             refEnd   =   end = schedule_.date(i+1);
663             paymentDate = calendar.adjust(end, paymentAdjustment_);
664             if (i==0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) {
665                 BusinessDayConvention bdc = schedule_.businessDayConvention();
666                 refStart = calendar.adjust(end - schedule_.tenor(), bdc);
667             }
668             if (i==n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) {
669                 BusinessDayConvention bdc = schedule_.businessDayConvention();
670                 refEnd = calendar.adjust(start + schedule_.tenor(), bdc);
671             }
672             if (detail::get(gearings_, i, 1.0) == 0.0) { // fixed coupon
673                 leg.push_back(ext::shared_ptr<CashFlow>(new
674                     FixedRateCoupon(paymentDate,
675                                     detail::get(notionals_, i, Null<Real>()),
676                                     detail::get(spreads_, i, 0.0),
677                                     paymentDayCounter_,
678                                     start, end, refStart, refEnd)));
679             } else { // floating coupon
680                 observationsSchedules.push_back(
681                     ext::make_shared<Schedule>(start, end,
682                                  observationTenor_, calendar,
683                                  observationConvention_,
684                                  observationConvention_,
685                                  DateGeneration::Forward, false));
686 
687                     leg.push_back(ext::shared_ptr<CashFlow>(new
688                        RangeAccrualFloatersCoupon(
689                             paymentDate,
690                             detail::get(notionals_, i, Null<Real>()),
691                             index_,
692                             start, end,
693                             detail::get(fixingDays_, i, 2),
694                             paymentDayCounter_,
695                             detail::get(gearings_, i, 1.0),
696                             detail::get(spreads_, i, 0.0),
697                             refStart, refEnd,
698                             observationsSchedules.back(),
699                             detail::get(lowerTriggers_, i, Null<Rate>()),
700                             detail::get(upperTriggers_, i, Null<Rate>()))));
701             }
702         }
703         return leg;
704     }
705 
706 }
707