/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 | | } |