/src/quantlib/ql/instruments/swaption.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb |
5 | | Copyright (C) 2006 Cristina Duminuco |
6 | | Copyright (C) 2006 Marco Bianchetti |
7 | | Copyright (C) 2007 StatPro Italia srl |
8 | | Copyright (C) 2014 Ferdinando Ametrano |
9 | | Copyright (C) 2016, 2018 Peter Caspers |
10 | | |
11 | | This file is part of QuantLib, a free-software/open-source library |
12 | | for financial quantitative analysts and developers - http://quantlib.org/ |
13 | | |
14 | | QuantLib is free software: you can redistribute it and/or modify it |
15 | | under the terms of the QuantLib license. You should have received a |
16 | | copy of the license along with this program; if not, please email |
17 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
18 | | <https://www.quantlib.org/license.shtml>. |
19 | | |
20 | | This program is distributed in the hope that it will be useful, but WITHOUT |
21 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
22 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
23 | | */ |
24 | | |
25 | | #include <ql/any.hpp> |
26 | | #include <ql/exercise.hpp> |
27 | | #include <ql/instruments/swaption.hpp> |
28 | | #include <ql/math/solvers1d/newtonsafe.hpp> |
29 | | #include <ql/pricingengines/swaption/blackswaptionengine.hpp> |
30 | | #include <ql/quotes/simplequote.hpp> |
31 | | #include <ql/shared_ptr.hpp> |
32 | | #include <utility> |
33 | | |
34 | | namespace QuantLib { |
35 | | |
36 | | namespace { |
37 | | |
38 | | class ImpliedSwaptionVolHelper { |
39 | | public: |
40 | | ImpliedSwaptionVolHelper(const Swaption&, |
41 | | Handle<YieldTermStructure> discountCurve, |
42 | | Real targetValue, |
43 | | Real displacement, |
44 | | VolatilityType type); |
45 | | Real operator()(Volatility x) const; |
46 | | Real derivative(Volatility x) const; |
47 | | private: |
48 | | ext::shared_ptr<PricingEngine> engine_; |
49 | | Handle<YieldTermStructure> discountCurve_; |
50 | | Real targetValue_; |
51 | | ext::shared_ptr<SimpleQuote> vol_; |
52 | | const Instrument::results* results_; |
53 | | }; |
54 | | |
55 | | ImpliedSwaptionVolHelper::ImpliedSwaptionVolHelper(const Swaption& swaption, |
56 | | Handle<YieldTermStructure> discountCurve, |
57 | | Real targetValue, |
58 | | Real displacement, |
59 | | VolatilityType type) |
60 | 0 | : discountCurve_(std::move(discountCurve)), targetValue_(targetValue), |
61 | 0 | vol_(ext::make_shared<SimpleQuote>(-1.0)) { |
62 | | |
63 | | // vol_ is set an implausible value, so that calculation is forced |
64 | | // at first ImpliedSwaptionVolHelper::operator()(Volatility x) call |
65 | |
|
66 | 0 | Handle<Quote> h(vol_); |
67 | |
|
68 | 0 | switch (type) { |
69 | 0 | case ShiftedLognormal: |
70 | 0 | engine_ = ext::make_shared<BlackSwaptionEngine>( |
71 | 0 | discountCurve_, h, Actual365Fixed(), displacement); |
72 | 0 | break; |
73 | 0 | case Normal: |
74 | 0 | engine_ = ext::make_shared<BachelierSwaptionEngine>( |
75 | 0 | discountCurve_, h, Actual365Fixed()); |
76 | 0 | break; |
77 | 0 | default: |
78 | 0 | QL_FAIL("unknown VolatilityType (" << type << ")"); |
79 | 0 | break; |
80 | 0 | } |
81 | 0 | swaption.setupArguments(engine_->getArguments()); |
82 | 0 | results_ = dynamic_cast<const Instrument::results *>( |
83 | 0 | engine_->getResults()); |
84 | 0 | } |
85 | | |
86 | 0 | Real ImpliedSwaptionVolHelper::operator()(Volatility x) const { |
87 | 0 | if (x!=vol_->value()) { |
88 | 0 | vol_->setValue(x); |
89 | 0 | engine_->calculate(); |
90 | 0 | } |
91 | 0 | return results_->value-targetValue_; |
92 | 0 | } |
93 | | |
94 | 0 | Real ImpliedSwaptionVolHelper::derivative(Volatility x) const { |
95 | 0 | if (x!=vol_->value()) { |
96 | 0 | vol_->setValue(x); |
97 | 0 | engine_->calculate(); |
98 | 0 | } |
99 | 0 | auto vega_ = results_->additionalResults.find("vega"); |
100 | 0 | QL_REQUIRE(vega_ != results_->additionalResults.end(), |
101 | 0 | "vega not provided"); |
102 | 0 | return ext::any_cast<Real>(vega_->second); |
103 | 0 | } |
104 | | } |
105 | | |
106 | | std::ostream& operator<<(std::ostream& out, |
107 | 0 | Settlement::Type t) { |
108 | 0 | switch (t) { |
109 | 0 | case Settlement::Physical: |
110 | 0 | return out << "Delivery"; |
111 | 0 | case Settlement::Cash: |
112 | 0 | return out << "Cash"; |
113 | 0 | default: |
114 | 0 | QL_FAIL("unknown Settlement::Type(" << Integer(t) << ")"); |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | 0 | std::ostream& operator<<(std::ostream& out, Settlement::Method m) { |
119 | 0 | switch (m) { |
120 | 0 | case Settlement::PhysicalOTC: |
121 | 0 | return out << "PhysicalOTC"; |
122 | 0 | case Settlement::PhysicalCleared: |
123 | 0 | return out << "PhysicalCleared"; |
124 | 0 | case Settlement::CollateralizedCashPrice: |
125 | 0 | return out << "CollateralizedCashPrice"; |
126 | 0 | case Settlement::ParYieldCurve: |
127 | 0 | return out << "ParYieldCurve"; |
128 | 0 | default: |
129 | 0 | QL_FAIL("unknown Settlement::Method(" << Integer(m) << ")"); |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | | Swaption::Swaption(ext::shared_ptr<FixedVsFloatingSwap> swap, |
134 | | const ext::shared_ptr<Exercise>& exercise, |
135 | | Settlement::Type delivery, |
136 | | Settlement::Method settlementMethod) |
137 | 0 | : Option(ext::shared_ptr<Payoff>(), exercise), swap_(std::move(swap)), |
138 | 0 | settlementType_(delivery), settlementMethod_(settlementMethod) { |
139 | 0 | registerWith(swap_); |
140 | | // When we ask for the NPV of an expired swaption, the |
141 | | // swap is not recalculated and thus wouldn't forward |
142 | | // later notifications according to the default behavior of |
143 | | // LazyObject instances. This means that even if the |
144 | | // evaluation date changes so that the swaption is no longer |
145 | | // expired, the instrument wouldn't be notified and thus it |
146 | | // wouldn't recalculate. To avoid this, we override the |
147 | | // default behavior of the underlying swap. |
148 | 0 | swap_->alwaysForwardNotifications(); |
149 | |
|
150 | 0 | vanilla_ = ext::dynamic_pointer_cast<VanillaSwap>(swap_); |
151 | 0 | } Unexecuted instantiation: QuantLib::Swaption::Swaption(boost::shared_ptr<QuantLib::FixedVsFloatingSwap>, boost::shared_ptr<QuantLib::Exercise> const&, QuantLib::Settlement::Type, QuantLib::Settlement::Method) Unexecuted instantiation: QuantLib::Swaption::Swaption(boost::shared_ptr<QuantLib::FixedVsFloatingSwap>, boost::shared_ptr<QuantLib::Exercise> const&, QuantLib::Settlement::Type, QuantLib::Settlement::Method) |
152 | | |
153 | 0 | void Swaption::deepUpdate() { |
154 | 0 | swap_->deepUpdate(); |
155 | 0 | update(); |
156 | 0 | } |
157 | | |
158 | 0 | bool Swaption::isExpired() const { |
159 | 0 | return detail::simple_event(exercise_->dates().back()).hasOccurred(); |
160 | 0 | } |
161 | | |
162 | 0 | void Swaption::setupArguments(PricingEngine::arguments* args) const { |
163 | |
|
164 | 0 | swap_->setupArguments(args); |
165 | 0 | auto* arguments = dynamic_cast<Swaption::arguments*>(args); |
166 | |
|
167 | 0 | QL_REQUIRE(arguments != nullptr, "wrong argument type"); |
168 | | |
169 | 0 | arguments->swap = swap_; |
170 | 0 | arguments->settlementType = settlementType_; |
171 | 0 | arguments->settlementMethod = settlementMethod_; |
172 | 0 | arguments->exercise = exercise_; |
173 | 0 | } |
174 | | |
175 | 0 | void Swaption::arguments::validate() const { |
176 | 0 | FixedVsFloatingSwap::arguments::validate(); |
177 | 0 | QL_REQUIRE(swap, "swap not set"); |
178 | 0 | QL_REQUIRE(exercise, "exercise not set"); |
179 | 0 | Settlement::checkTypeAndMethodConsistency(settlementType, settlementMethod); |
180 | 0 | } |
181 | | |
182 | | Volatility Swaption::impliedVolatility(Real targetValue, |
183 | | const Handle<YieldTermStructure>& discountCurve, |
184 | | Volatility guess, |
185 | | Real accuracy, |
186 | | Natural maxEvaluations, |
187 | | Volatility minVol, |
188 | | Volatility maxVol, |
189 | | VolatilityType type, |
190 | | Real displacement, |
191 | 0 | PriceType priceType) const { |
192 | |
|
193 | 0 | QL_REQUIRE(!isExpired(), "instrument expired"); |
194 | 0 | QL_REQUIRE(exercise_->type() == Exercise::European, "not a European option"); |
195 | | |
196 | 0 | if (priceType == Forward) { |
197 | | // convert to spot |
198 | 0 | targetValue *= discountCurve->discount(exercise_->date(0)); |
199 | 0 | } |
200 | |
|
201 | 0 | ImpliedSwaptionVolHelper f(*this, discountCurve, targetValue, displacement, type); |
202 | 0 | NewtonSafe solver; |
203 | 0 | solver.setMaxEvaluations(maxEvaluations); |
204 | 0 | return solver.solve(f, accuracy, guess, minVol, maxVol); |
205 | 0 | } |
206 | | |
207 | | void Settlement::checkTypeAndMethodConsistency( |
208 | | Settlement::Type settlementType, |
209 | 0 | Settlement::Method settlementMethod) { |
210 | 0 | if (settlementType == Physical) { |
211 | 0 | QL_REQUIRE(settlementMethod == PhysicalOTC || |
212 | 0 | settlementMethod == PhysicalCleared, |
213 | 0 | "invalid settlement method for physical settlement"); |
214 | 0 | } |
215 | 0 | if (settlementType == Cash) { |
216 | | QL_REQUIRE(settlementMethod == CollateralizedCashPrice || |
217 | 0 | settlementMethod == ParYieldCurve, |
218 | 0 | "invalid settlement method for cash settlement"); |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | | } |