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