1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | |
3 | /* |
4 | Copyright (C) 2006 Ferdinando Ametrano |
5 | Copyright (C) 2006 Katiuscia Manzoni |
6 | Copyright (C) 2015 Peter Caspers |
7 | Copyright (C) 2023 Ignacio Anguita |
8 | |
9 | This file is part of QuantLib, a free-software/open-source library |
10 | for financial quantitative analysts and developers - http://quantlib.org/ |
11 | |
12 | QuantLib is free software: you can redistribute it and/or modify it |
13 | under the terms of the QuantLib license. You should have received a |
14 | copy of the license along with this program; if not, please email |
15 | <quantlib-dev@lists.sf.net>. The license is also available online at |
16 | <http://quantlib.org/license.shtml>. |
17 | |
18 | This program is distributed in the hope that it will be useful, but WITHOUT |
19 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
20 | FOR A PARTICULAR PURPOSE. See the license for more details. |
21 | */ |
22 | |
23 | #include <ql/termstructures/volatility/swaption/interpolatedswaptionvolatilitycube.hpp> |
24 | #include <ql/termstructures/volatility/interpolatedsmilesection.hpp> |
25 | #include <ql/math/interpolations/bilinearinterpolation.hpp> |
26 | #include <ql/math/rounding.hpp> |
27 | #include <ql/indexes/swapindex.hpp> |
28 | |
29 | namespace QuantLib { |
30 | |
31 | InterpolatedSwaptionVolatilityCube::InterpolatedSwaptionVolatilityCube( |
32 | const Handle<SwaptionVolatilityStructure>& atmVolStructure, |
33 | const std::vector<Period>& optionTenors, |
34 | const std::vector<Period>& swapTenors, |
35 | const std::vector<Spread>& strikeSpreads, |
36 | const std::vector<std::vector<Handle<Quote> > >& volSpreads, |
37 | const ext::shared_ptr<SwapIndex>& swapIndexBase, |
38 | const ext::shared_ptr<SwapIndex>& shortSwapIndexBase, |
39 | bool vegaWeightedSmileFit) |
40 | : SwaptionVolatilityCube(atmVolStructure, optionTenors, swapTenors, |
41 | strikeSpreads, volSpreads, swapIndexBase, |
42 | shortSwapIndexBase, |
43 | vegaWeightedSmileFit), |
44 | volSpreadsInterpolator_(nStrikes_), |
45 | volSpreadsMatrix_(nStrikes_, Matrix(optionTenors.size(), swapTenors.size(), 0.0)) { |
46 | } |
47 | |
48 | void InterpolatedSwaptionVolatilityCube::performCalculations() const{ |
49 | |
50 | SwaptionVolatilityCube::performCalculations(); |
51 | //! set volSpreadsMatrix_ by volSpreads_ quotes |
52 | for (Size i=0; i<nStrikes_; i++) |
53 | for (Size j=0; j<nOptionTenors_; j++) |
54 | for (Size k=0; k<nSwapTenors_; k++) { |
55 | volSpreadsMatrix_[i][j][k] = |
56 | volSpreads_[j*nSwapTenors_+k][i]->value(); |
57 | } |
58 | //! create volSpreadsInterpolator_ |
59 | for (Size i=0; i<nStrikes_; i++) { |
60 | volSpreadsInterpolator_[i] = BilinearInterpolation( |
61 | swapLengths_.begin(), swapLengths_.end(), |
62 | optionTimes_.begin(), optionTimes_.end(), |
63 | volSpreadsMatrix_[i]); |
64 | volSpreadsInterpolator_[i].enableExtrapolation(); |
65 | } |
66 | } |
67 | |
68 | ext::shared_ptr<SmileSection> |
69 | InterpolatedSwaptionVolatilityCube::smileSectionImpl(Time optionTime, |
70 | Time swapLength) const { |
71 | |
72 | calculate(); |
73 | Date optionDate = optionDateFromTime(optionTime); |
74 | Rounding rounder(0); |
75 | Period swapTenor(static_cast<Integer>(rounder(swapLength*12.0)), Months); |
76 | // ensure that option date is valid fixing date |
77 | optionDate = |
78 | swapTenor > shortSwapIndexBase_->tenor() |
79 | ? swapIndexBase_->fixingCalendar().adjust(optionDate, convention: Following) |
80 | : shortSwapIndexBase_->fixingCalendar().adjust(optionDate, |
81 | convention: Following); |
82 | return smileSectionImpl(optionDate, swapTenor); |
83 | } |
84 | |
85 | ext::shared_ptr<SmileSection> |
86 | InterpolatedSwaptionVolatilityCube::smileSectionImpl(const Date& optionDate, |
87 | const Period& swapTenor) const { |
88 | calculate(); |
89 | Rate atmForward = atmStrike(optionDate, swapTenor); |
90 | Volatility atmVol = atmVol_->volatility(optionDate, |
91 | swapTenor, |
92 | strike: atmForward); |
93 | Time optionTime = timeFromReference(d: optionDate); |
94 | Real exerciseTimeSqrt = std::sqrt(x: optionTime); |
95 | std::vector<Real> strikes, stdDevs; |
96 | strikes.reserve(n: nStrikes_); |
97 | stdDevs.reserve(n: nStrikes_); |
98 | Time length = swapLength(swapTenor); |
99 | for (Size i=0; i<nStrikes_; ++i) { |
100 | strikes.push_back(x: atmForward + strikeSpreads_[i]); |
101 | stdDevs.push_back(x: exerciseTimeSqrt*( |
102 | atmVol + volSpreadsInterpolator_[i](length, optionTime))); |
103 | } |
104 | Real shift = atmVol_->shift(optionTime,swapLength: length); |
105 | return ext::shared_ptr<SmileSection>(new |
106 | InterpolatedSmileSection<Linear>(optionTime, |
107 | strikes, |
108 | stdDevs, |
109 | atmForward, |
110 | Linear(), |
111 | Actual365Fixed(), |
112 | volatilityType(), |
113 | shift)); |
114 | } |
115 | } |
116 | |