/src/quantlib/ql/experimental/shortrate/generalizedhullwhite.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2010 SunTrust Bank |
5 | | Copyright (C) 2010, 2014 Cavit Hafizoglu |
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 | | <http://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 | | /*! \file generalizedhullwhite.hpp |
22 | | \brief generalized Hull-White model |
23 | | */ |
24 | | |
25 | | #ifndef quantlib_generalized_hull_white_hpp |
26 | | #define quantlib_generalized_hull_white_hpp |
27 | | |
28 | | #include <ql/experimental/shortrate/generalizedornsteinuhlenbeckprocess.hpp> |
29 | | #include <ql/math/interpolation.hpp> |
30 | | #include <ql/models/shortrate/onefactormodel.hpp> |
31 | | #include <ql/processes/ornsteinuhlenbeckprocess.hpp> |
32 | | #include <utility> |
33 | | |
34 | | namespace QuantLib { |
35 | | |
36 | | //! Parameter that holds an interpolation object |
37 | | class InterpolationParameter : public Parameter { |
38 | | private: |
39 | | class Impl final : public Parameter::Impl { |
40 | | public: |
41 | 0 | Real value(const Array&, Time t) const override { return interpolator_(t); } |
42 | 0 | void reset(const Interpolation& interp) { interpolator_ = interp; } |
43 | | private: |
44 | | Interpolation interpolator_; |
45 | | }; |
46 | | public: |
47 | | explicit InterpolationParameter( |
48 | | Size count, |
49 | | const Constraint& constraint = NoConstraint()) |
50 | 0 | : Parameter(count, |
51 | 0 | ext::shared_ptr<Parameter::Impl>( |
52 | 0 | new InterpolationParameter::Impl()), |
53 | 0 | constraint) |
54 | 0 | { } |
55 | 0 | void reset(const Interpolation &interp) { |
56 | 0 | ext::shared_ptr<InterpolationParameter::Impl> impl = |
57 | 0 | ext::dynamic_pointer_cast<InterpolationParameter::Impl>(impl_); |
58 | 0 | if (impl != nullptr) |
59 | 0 | impl->reset(interp); |
60 | 0 | } |
61 | | }; |
62 | | |
63 | | //! Generalized Hull-White model class. |
64 | | /*! This class implements the standard Black-Karasinski model defined by |
65 | | \f[ |
66 | | d f(r_t) = (\theta(t) - \alpha f(r_t))dt + \sigma dW_t, |
67 | | \f] |
68 | | where \f$ alpha \f$ and \f$ sigma \f$ are piecewise linear functions. |
69 | | |
70 | | \ingroup shortrate |
71 | | */ |
72 | | class GeneralizedHullWhite : public OneFactorAffineModel, |
73 | | public TermStructureConsistentModel { |
74 | | public: |
75 | | |
76 | | GeneralizedHullWhite( |
77 | | const Handle<YieldTermStructure>& yieldtermStructure, |
78 | | const std::vector<Date>& speedstructure, |
79 | | const std::vector<Date>& volstructure, |
80 | | const std::vector<Real>& speed, |
81 | | const std::vector<Real>& vol, |
82 | | const std::function<Real(Real)>& f = {}, |
83 | | const std::function<Real(Real)>& fInverse = {}); |
84 | | |
85 | | template <class SpeedInterpolationTraits,class VolInterpolationTraits> |
86 | | GeneralizedHullWhite( |
87 | | const Handle<YieldTermStructure>& yieldtermStructure, |
88 | | const std::vector<Date>& speedstructure, |
89 | | const std::vector<Date>& volstructure, |
90 | | const std::vector<Real>& speed, |
91 | | const std::vector<Real>& vol, |
92 | | const SpeedInterpolationTraits &speedtraits, |
93 | | const VolInterpolationTraits &voltraits, |
94 | | const std::function<Real(Real)>& f = {}, |
95 | | const std::function<Real(Real)>& fInverse = {}) : |
96 | | OneFactorAffineModel(2), TermStructureConsistentModel(yieldtermStructure), |
97 | | speedstructure_(speedstructure), volstructure_(volstructure), |
98 | | a_(arguments_[0]), sigma_(arguments_[1]), |
99 | | f_(f), fInverse_(fInverse) |
100 | | { |
101 | | initialize(yieldtermStructure,speedstructure,volstructure, |
102 | | speed,vol,speedtraits,voltraits,f,fInverse); |
103 | | } |
104 | | |
105 | 0 | ext::shared_ptr<ShortRateDynamics> dynamics() const override { |
106 | 0 | QL_FAIL("no defined process for generalized Hull-White model, " |
107 | 0 | "use HWdynamics()"); |
108 | 0 | } |
109 | | |
110 | | ext::shared_ptr<Lattice> tree(const TimeGrid& grid) const override; |
111 | | |
112 | | //Analytical calibration of HW |
113 | | |
114 | | GeneralizedHullWhite( |
115 | | const Handle<YieldTermStructure>& yieldtermStructure, |
116 | | Real a = 0.1, Real sigma = 0.01); |
117 | | |
118 | | |
119 | | ext::shared_ptr<ShortRateDynamics> HWdynamics() const; |
120 | | |
121 | | //! Only valid under Hull-White model |
122 | | Real discountBondOption(Option::Type type, |
123 | | Real strike, |
124 | | Time maturity, |
125 | | Time bondMaturity) const override; |
126 | | |
127 | | //! vector to pass to 'calibrate' to fit only volatility |
128 | | std::vector<bool> fixedReversion() const; |
129 | | |
130 | | protected: |
131 | | //Analytical calibration of HW |
132 | 0 | Real a() const { return a_(0.0); } |
133 | 0 | Real sigma() const { return sigma_(0.0); } |
134 | | void generateArguments() override; |
135 | | Real A(Time t, Time T) const override; |
136 | | Real B(Time t, Time T) const override; |
137 | | Real V(Time t, Time T) const; |
138 | | |
139 | | private: |
140 | | |
141 | | class Dynamics; |
142 | | class Helper; |
143 | | class FittingParameter;// for analytic HW fitting |
144 | | |
145 | | std::vector<Date> speedstructure_; |
146 | | std::vector<Date> volstructure_; |
147 | | std::vector<Time> speedperiods_; |
148 | | std::vector<Time> volperiods_; |
149 | | Interpolation speed_; |
150 | | Interpolation vol_; |
151 | | |
152 | | std::function<Real (Time)> speed() const; |
153 | | std::function<Real (Time)> vol() const; |
154 | | |
155 | | Parameter& a_; |
156 | | Parameter& sigma_; |
157 | | Parameter phi_; |
158 | | |
159 | | std::function<Real(Real)> f_; |
160 | | std::function<Real(Real)> fInverse_; |
161 | | |
162 | 0 | static Real identity(Real x) { |
163 | 0 | return x; |
164 | 0 | } |
165 | | |
166 | | template <class SpeedInterpolationTraits,class VolInterpolationTraits> |
167 | | void initialize(const Handle<YieldTermStructure>& yieldtermStructure, |
168 | | const std::vector<Date>& speedstructure, |
169 | | const std::vector<Date>& volstructure, |
170 | | const std::vector<Real>& speed, |
171 | | const std::vector<Real>& vol, |
172 | | const SpeedInterpolationTraits &speedtraits, |
173 | | const VolInterpolationTraits &voltraits, |
174 | | const std::function<Real(Real)>& f, |
175 | | const std::function<Real(Real)>& fInverse) |
176 | 0 | { |
177 | 0 | QL_REQUIRE(speedstructure.size()==speed.size(), |
178 | 0 | "mean reversion inputs inconsistent"); |
179 | 0 | QL_REQUIRE(volstructure.size()==vol.size(), |
180 | 0 | "volatility inputs inconsistent"); |
181 | 0 | if (!f_) |
182 | 0 | f_ = identity; |
183 | 0 | if (!fInverse_) |
184 | 0 | fInverse_ = identity; |
185 | |
|
186 | 0 | DayCounter dc = yieldtermStructure->dayCounter(); |
187 | 0 | Date ref = yieldtermStructure->referenceDate(); |
188 | 0 | for (auto i : speedstructure) |
189 | 0 | speedperiods_.push_back(dc.yearFraction(ref, i)); |
190 | 0 | for (auto i : volstructure) |
191 | 0 | volperiods_.push_back(dc.yearFraction(ref, i)); |
192 | | |
193 | | // interpolator x points to *periods_ vector, y points to |
194 | | // the internal Array in the parameter |
195 | 0 | InterpolationParameter atemp(speedperiods_.size(), NoConstraint()); |
196 | 0 | a_ = atemp; |
197 | 0 | for (Size i=0; i<speedperiods_.size(); i++) |
198 | 0 | a_.setParam(i, speed[i]); |
199 | 0 | speed_ = speedtraits.interpolate(speedperiods_.begin(), |
200 | 0 | speedperiods_.end(),a_.params().begin()); |
201 | 0 | speed_.enableExtrapolation(); |
202 | 0 | atemp.reset(speed_); |
203 | |
|
204 | 0 | InterpolationParameter sigmatemp(volperiods_.size(), PositiveConstraint()); |
205 | 0 | sigma_ = sigmatemp; |
206 | 0 | for (Size i=0; i<volperiods_.size(); i++) |
207 | 0 | sigma_.setParam(i, vol[i]); |
208 | 0 | vol_ = voltraits.interpolate(volperiods_.begin(), |
209 | 0 | volperiods_.end(),sigma_.params().begin()); |
210 | 0 | vol_.enableExtrapolation(); |
211 | 0 | sigmatemp.reset(vol_); |
212 | |
|
213 | 0 | generateArguments(); |
214 | 0 | registerWith(yieldtermStructure); |
215 | 0 | } Unexecuted instantiation: void QuantLib::GeneralizedHullWhite::initialize<QuantLib::LinearFlat, QuantLib::LinearFlat>(QuantLib::Handle<QuantLib::YieldTermStructure> const&, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::LinearFlat const&, QuantLib::LinearFlat const&, std::__1::function<double (double)> const&, std::__1::function<double (double)> const&) Unexecuted instantiation: void QuantLib::GeneralizedHullWhite::initialize<QuantLib::BackwardFlat, QuantLib::BackwardFlat>(QuantLib::Handle<QuantLib::YieldTermStructure> const&, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::BackwardFlat const&, QuantLib::BackwardFlat const&, std::__1::function<double (double)> const&, std::__1::function<double (double)> const&) |
216 | | }; |
217 | | |
218 | | //! Short-rate dynamics in the generalized Hull-White model |
219 | | /*! The short-rate is here |
220 | | |
221 | | f(r_t) = x_t + g(t) |
222 | | |
223 | | where g is the deterministic time-dependent |
224 | | parameter (which can't be determined analytically) |
225 | | used for initial term-structure fitting and x_t is the state |
226 | | variable following an Ornstein-Uhlenbeck process. |
227 | | |
228 | | In this version, the function f may also be defined as a piece-wise linear |
229 | | function and can be calibrated to the away-from-the-money instruments. |
230 | | |
231 | | */ |
232 | | class GeneralizedHullWhite::Dynamics |
233 | | : public GeneralizedHullWhite::ShortRateDynamics { |
234 | | public: |
235 | | Dynamics(Parameter fitting, |
236 | | const std::function<Real(Time)>& alpha, |
237 | | const std::function<Real(Time)>& sigma, |
238 | | std::function<Real(Real)> f, |
239 | | std::function<Real(Real)> fInverse) |
240 | 0 | : ShortRateDynamics(ext::shared_ptr<StochasticProcess1D>( |
241 | 0 | new GeneralizedOrnsteinUhlenbeckProcess(alpha, sigma))), |
242 | 0 | fitting_(std::move(fitting)), _f_(std::move(f)), _fInverse_(std::move(fInverse)) {} |
243 | | |
244 | | //classical HW dynamics |
245 | | Dynamics(Parameter fitting, Real a, Real sigma) |
246 | | : GeneralizedHullWhite::ShortRateDynamics( |
247 | | ext::shared_ptr<StochasticProcess1D>(new OrnsteinUhlenbeckProcess(a, sigma))), |
248 | 0 | fitting_(std::move(fitting)), _f_(identity()), _fInverse_(identity()) {} |
249 | | |
250 | 0 | Real variable(Time t, Rate r) const override { return _f_(r) - fitting_(t); } |
251 | | |
252 | 0 | Real shortRate(Time t, Real x) const override { return _fInverse_(x + fitting_(t)); } |
253 | | |
254 | | private: |
255 | | Parameter fitting_; |
256 | | std::function<Real(Real)> _f_; |
257 | | std::function<Real(Real)> _fInverse_; |
258 | | struct identity { |
259 | 0 | Real operator()(Real x) const {return x;}; |
260 | | }; |
261 | | }; |
262 | | |
263 | | //! Analytical term-structure fitting parameter \f$ \varphi(t) \f$. |
264 | | /*! \f$ \varphi(t) \f$ is analytically defined by |
265 | | \f[ |
266 | | \varphi(t) = f(t) + \frac{1}{2}[\frac{\sigma(1-e^{-at})}{a}]^2, |
267 | | \f] |
268 | | where \f$ f(t) \f$ is the instantaneous forward rate at \f$ t \f$. |
269 | | */ |
270 | | class GeneralizedHullWhite::FittingParameter |
271 | | : public TermStructureFittingParameter { |
272 | | private: |
273 | | class Impl final : public Parameter::Impl { |
274 | | public: |
275 | | Impl(Handle<YieldTermStructure> termStructure, Real a, Real sigma) |
276 | 0 | : termStructure_(std::move(termStructure)), a_(a), sigma_(sigma) {} |
277 | | |
278 | 0 | Real value(const Array&, Time t) const override { |
279 | 0 | Rate forwardRate = |
280 | 0 | termStructure_->forwardRate(t, t, Continuous, NoFrequency); |
281 | 0 | Real temp = a_ < std::sqrt(QL_EPSILON) ? |
282 | 0 | Real(sigma_*t) : |
283 | 0 | Real(sigma_*(1.0 - std::exp(-a_*t))/a_); |
284 | 0 | return (forwardRate + 0.5*temp*temp); |
285 | 0 | } |
286 | | |
287 | | private: |
288 | | Handle<YieldTermStructure> termStructure_; |
289 | | Real a_, sigma_; |
290 | | }; |
291 | | public: |
292 | | FittingParameter(const Handle<YieldTermStructure>& termStructure, |
293 | | Real a, Real sigma) |
294 | 0 | : TermStructureFittingParameter(ext::shared_ptr<Parameter::Impl>( |
295 | 0 | new FittingParameter::Impl(termStructure, a, sigma))) {} |
296 | | }; |
297 | | |
298 | | // Analytic fitting dynamics |
299 | | inline ext::shared_ptr<OneFactorModel::ShortRateDynamics> |
300 | 0 | GeneralizedHullWhite::HWdynamics() const { |
301 | 0 | return ext::shared_ptr<ShortRateDynamics>( |
302 | 0 | new Dynamics(phi_, a(), sigma())); |
303 | 0 | } |
304 | | |
305 | | namespace detail { |
306 | | template <class I1, class I2> |
307 | | class LinearFlatInterpolationImpl; |
308 | | } |
309 | | |
310 | | //! %Linear interpolation between discrete points with flat extapolation |
311 | | /*! \ingroup interpolations */ |
312 | | class LinearFlatInterpolation : public Interpolation { |
313 | | public: |
314 | | /*! \pre the \f$ x \f$ values must be sorted. */ |
315 | | template <class I1, class I2> |
316 | | LinearFlatInterpolation(const I1& xBegin, const I1& xEnd, |
317 | 0 | const I2& yBegin) { |
318 | 0 | impl_ = ext::shared_ptr<Interpolation::Impl>(new |
319 | 0 | detail::LinearFlatInterpolationImpl<I1,I2>(xBegin, xEnd, |
320 | 0 | yBegin)); |
321 | 0 | impl_->update(); |
322 | 0 | } |
323 | | }; |
324 | | |
325 | | //! %Linear-interpolation with flat-extrapolation factory and traits |
326 | | /*! \ingroup interpolations */ |
327 | | class LinearFlat { |
328 | | public: |
329 | | template <class I1, class I2> |
330 | | Interpolation interpolate(const I1& xBegin, const I1& xEnd, |
331 | 0 | const I2& yBegin) const { |
332 | 0 | return LinearFlatInterpolation(xBegin, xEnd, yBegin); |
333 | 0 | } |
334 | | static const bool global = false; |
335 | | static const Size requiredPoints = 1; |
336 | | }; |
337 | | |
338 | | namespace detail { |
339 | | template <class I1, class I2> |
340 | | class LinearFlatInterpolationImpl |
341 | | : public Interpolation::templateImpl<I1,I2> { |
342 | | public: |
343 | | LinearFlatInterpolationImpl(const I1& xBegin, const I1& xEnd, |
344 | | const I2& yBegin) |
345 | 0 | : Interpolation::templateImpl<I1,I2>(xBegin, xEnd, yBegin, |
346 | 0 | LinearFlat::requiredPoints), |
347 | 0 | primitiveConst_(xEnd-xBegin), s_(xEnd-xBegin) {} |
348 | 0 | void update() override { |
349 | 0 | primitiveConst_[0] = 0.0; |
350 | 0 | for (Size i=1; i<Size(this->xEnd_-this->xBegin_); ++i) { |
351 | 0 | Real dx = this->xBegin_[i]-this->xBegin_[i-1]; |
352 | 0 | s_[i-1] = (this->yBegin_[i]-this->yBegin_[i-1])/dx; |
353 | 0 | primitiveConst_[i] = primitiveConst_[i-1] |
354 | 0 | + dx*(this->yBegin_[i-1] +0.5*dx*s_[i-1]); |
355 | 0 | } |
356 | 0 | } |
357 | 0 | Real value(Real x) const override { |
358 | 0 | if (x <= this->xMin()) |
359 | 0 | return this->yBegin_[0]; |
360 | 0 | if (x >= this->xMax()) |
361 | 0 | return *(this->yBegin_+(this->xEnd_-this->xBegin_)-1); |
362 | 0 | Size i = this->locate(x); |
363 | 0 | return this->yBegin_[i] + (x-this->xBegin_[i])*s_[i]; |
364 | 0 | } |
365 | 0 | Real primitive(Real x) const override { |
366 | 0 | Size i = this->locate(x); |
367 | 0 | Real dx = x-this->xBegin_[i]; |
368 | 0 | return primitiveConst_[i] + |
369 | 0 | dx*(this->yBegin_[i] + 0.5*dx*s_[i]); |
370 | 0 | } |
371 | 0 | Real derivative(Real x) const override { |
372 | 0 | if (!this->isInRange(x)) |
373 | 0 | return 0; |
374 | 0 | Size i = this->locate(x); |
375 | 0 | return s_[i]; |
376 | 0 | } |
377 | 0 | Real secondDerivative(Real) const override { return 0.0; } |
378 | | |
379 | | private: |
380 | | std::vector<Real> primitiveConst_, s_; |
381 | | }; |
382 | | } |
383 | | |
384 | | } |
385 | | |
386 | | |
387 | | #endif |