Coverage Report

Created: 2026-06-23 06:40

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