/src/quantlib/ql/pricingengines/vanilla/analyticbsmhullwhiteengine.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2007 Klaus Spanderen |
5 | | |
6 | | This file is part of QuantLib, a free-software/open-source library |
7 | | for financial quantitative analysts and developers - http://quantlib.org/ |
8 | | |
9 | | QuantLib is free software: you can redistribute it and/or modify it |
10 | | under the terms of the QuantLib license. You should have received a |
11 | | copy of the license along with this program; if not, please email |
12 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
13 | | <https://www.quantlib.org/license.shtml>. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
18 | | */ |
19 | | |
20 | | /*! \file analyticbsmhullwhiteengine.hpp |
21 | | \brief analytic Black-Scholes engines including stochastic interest rates |
22 | | */ |
23 | | |
24 | | #include <ql/pricingengines/vanilla/analyticbsmhullwhiteengine.hpp> |
25 | | #include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp> |
26 | | #include <ql/termstructures/volatility/equityfx/blackvoltermstructure.hpp> |
27 | | #include <utility> |
28 | | |
29 | | namespace QuantLib { |
30 | | |
31 | | namespace { |
32 | | |
33 | | class ShiftedBlackVolTermStructure : public BlackVolTermStructure { |
34 | | public: |
35 | | ShiftedBlackVolTermStructure( |
36 | | Real varianceOffset, |
37 | | const Handle<BlackVolTermStructure> & volTS) |
38 | 0 | : BlackVolTermStructure(volTS->referenceDate(), |
39 | 0 | volTS->calendar(), |
40 | 0 | Following, |
41 | 0 | volTS->dayCounter()), |
42 | 0 | varianceOffset_(varianceOffset), |
43 | 0 | volTS_(volTS) { } |
44 | | |
45 | 0 | Real minStrike() const override { return volTS_->minStrike(); } |
46 | 0 | Real maxStrike() const override { return volTS_->maxStrike(); } |
47 | 0 | Date maxDate() const override { return volTS_->maxDate(); } |
48 | | |
49 | | protected: |
50 | 0 | Real blackVarianceImpl(Time t, Real strike) const override { |
51 | 0 | return volTS_->blackVariance(t, strike, true)+varianceOffset_; |
52 | 0 | } |
53 | 0 | Volatility blackVolImpl(Time t, Real strike) const override { |
54 | 0 | Time nonZeroMaturity = (t==0.0 ? 0.00001 : t); |
55 | 0 | Real var = blackVarianceImpl(nonZeroMaturity, strike); |
56 | 0 | return std::sqrt(var/nonZeroMaturity); |
57 | 0 | } |
58 | | |
59 | | private: |
60 | | const Real varianceOffset_; |
61 | | const Handle<BlackVolTermStructure> volTS_; |
62 | | }; |
63 | | } |
64 | | |
65 | | AnalyticBSMHullWhiteEngine::AnalyticBSMHullWhiteEngine( |
66 | | Real equityShortRateCorrelation, |
67 | | ext::shared_ptr<GeneralizedBlackScholesProcess> process, |
68 | | const ext::shared_ptr<HullWhite>& model) |
69 | 0 | : GenericModelEngine<HullWhite, VanillaOption::arguments, VanillaOption::results>(model), |
70 | 0 | rho_(equityShortRateCorrelation), process_(std::move(process)) { |
71 | 0 | QL_REQUIRE(process_, "no Black-Scholes process specified"); |
72 | 0 | QL_REQUIRE(!model_.empty(), "no Hull-White model specified"); |
73 | 0 | registerWith(process_); |
74 | 0 | } |
75 | | |
76 | 0 | void AnalyticBSMHullWhiteEngine::calculate() const { |
77 | |
|
78 | 0 | QL_REQUIRE(process_->x0() > 0.0, "negative or null underlying given"); |
79 | | |
80 | 0 | const ext::shared_ptr<StrikedTypePayoff> payoff = |
81 | 0 | ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff); |
82 | 0 | QL_REQUIRE(payoff, "non-striked payoff given"); |
83 | | |
84 | 0 | const ext::shared_ptr<Exercise> exercise = arguments_.exercise; |
85 | |
|
86 | 0 | Time t = process_->riskFreeRate()->dayCounter().yearFraction( |
87 | 0 | process_->riskFreeRate()->referenceDate(), |
88 | 0 | exercise->lastDate()); |
89 | |
|
90 | 0 | const Real a = model_->params()[0]; |
91 | 0 | const Real sigma = model_->params()[1]; |
92 | 0 | const Real eta = |
93 | 0 | process_->blackVolatility()->blackVol(exercise->lastDate(), |
94 | 0 | payoff->strike()); |
95 | |
|
96 | 0 | Real varianceOffset; |
97 | 0 | if (a*t > std::pow(QL_EPSILON, 0.25)) { |
98 | 0 | const Real v = sigma*sigma/(a*a) |
99 | 0 | *(t + 2/a*std::exp(-a*t) - 1/(2*a)*std::exp(-2*a*t) - 3/(2*a)); |
100 | 0 | const Real mu = 2*rho_*sigma*eta/a*(t-1/a*(1-std::exp(-a*t))); |
101 | |
|
102 | 0 | varianceOffset = v + mu; |
103 | 0 | } |
104 | 0 | else { |
105 | | // low-a algebraic limit |
106 | 0 | const Real v = sigma*sigma*t*t*t*(1/3.0-0.25*a*t+7/60.0*a*a*t*t); |
107 | 0 | const Real mu = rho_*sigma*eta*t*t*(1-a*t/3.0+a*a*t*t/12.0); |
108 | |
|
109 | 0 | varianceOffset = v + mu; |
110 | 0 | } |
111 | |
|
112 | 0 | Handle<BlackVolTermStructure> volTS( |
113 | 0 | ext::make_shared<ShiftedBlackVolTermStructure>(varianceOffset, |
114 | 0 | process_->blackVolatility())); |
115 | |
|
116 | 0 | auto adjProcess = ext::make_shared<GeneralizedBlackScholesProcess>(process_->stateVariable(), |
117 | 0 | process_->dividendYield(), |
118 | 0 | process_->riskFreeRate(), |
119 | 0 | volTS); |
120 | |
|
121 | 0 | auto bsmEngine = ext::make_shared<AnalyticEuropeanEngine>(adjProcess); |
122 | |
|
123 | 0 | VanillaOption(payoff, exercise).setupArguments( |
124 | 0 | bsmEngine->getArguments()); |
125 | 0 | bsmEngine->calculate(); |
126 | |
|
127 | 0 | results_ = *dynamic_cast<const OneAssetOption::results*>( |
128 | 0 | bsmEngine->getResults()); |
129 | 0 | } |
130 | | } |