Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
analyticoutperformanceoptionengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/currencies/exchangeratemanager.hpp>
22#include <ql/exercise.hpp>
23#include <ql/math/distributions/normaldistribution.hpp>
24#include <ql/pricingengines/blackformula.hpp>
25#include <ql/math/integrals/gaussianquadratures.hpp>
26#include <ql/math/integrals/simpsonintegral.hpp>
27#include <ql/math/integrals/kronrodintegral.hpp>
28
29namespace QuantExt {
30
31Real AnalyticOutperformanceOptionEngine::getTodaysFxConversionRate(const QuantLib::ext::shared_ptr<QuantExt::FxIndex>& fxIndex) const {
32 // The fx conversion rate as of today should always be taken from the market data, i.e. we should not use a
33 // historical fixing, even if it exists, because we should generate sensitivities to market fx spot rate changes.
34 // Furthermore, we can get the fx spot rate from the market data even if today is not a valid fixing date for the
35 // fx index, that is why we should not use Index::fixing(today, true).
36 Real tmp;
37 if (fxIndex->useQuote()) {
38 tmp = fxIndex->fxQuote()->value();
39 } else {
40 tmp = ExchangeRateManager::instance()
41 .lookup(fxIndex->sourceCurrency(), fxIndex->targetCurrency())
42 .rate();
43 }
44 return tmp;
45}
46
48 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process1,
49 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process2,
50 const Handle<CorrelationTermStructure>& correlation, Size integrationPoints)
51 : process1_(process1), process2_(process2), correlationCurve_(correlation), integrationPoints_(integrationPoints) {
52 registerWith(process1_);
53 registerWith(process2_);
54}
55
56class AnalyticOutperformanceOptionEngine::integrand_f {
58 public:
59 explicit integrand_f(const AnalyticOutperformanceOptionEngine* pricer,
60 Real phi, Real k, Real m1, Real m2, Real v1, Real v2, Real s1, Real s2, Real i1, Real i2, Real fixingTime)
61 : pricer(pricer), phi_(phi), k_(k), m1_(m1), m2_(m2), v1_(v1), v2_(v2), s1_(s1), s2_(s2), i1_(i1), i2_(i2), fixingTime_(fixingTime) {}
62 Real operator()(Real x) const {
63 return pricer->integrand(x, phi_, k_, m1_, m2_, v1_, v2_, s1_, s2_, i1_, i2_, fixingTime_);
64 }
65
66 Real phi_;
67 Real k_;
68 Real m1_;
69 Real m2_;
70 Real v1_;
71 Real v2_;
72 Real s1_;
73 Real s2_;
74 Real i1_;
75 Real i2_;
76 Real fixingTime_;
77};
78
79
80Real AnalyticOutperformanceOptionEngine::integrand(const Real x, Real phi, Real k, Real m1, Real m2, Real v1, Real v2, Real s1, Real s2, Real i1, Real i2, Real fixingTime) const {
81
82 // this is Brigo, 13.16.2 with x = v / sqrt(2)
83 Real v = M_SQRT2 * x;
84
85 //a positive real number 'a', a negative real number 'b'
86 Real a = 1 / i1;
87 Real b = -1 / i2;
88
89 CumulativeNormalDistribution cnd;
90
91 Real h =
92 k - b * s2 * std::exp((m2 - 0.5 * v2 * v2) * fixingTime +
93 v2 * std::sqrt(fixingTime) * v);
94 Real phi1, phi2;
95 phi1 = cnd(
96 phi * (std::log(a * s1 / h) +
97 (m1 + (0.5 - rho(fixingTime) * rho(fixingTime)) * v1 * v1) * fixingTime +
98 rho(fixingTime) * v1 * std::sqrt(fixingTime) * v) /
99 (v1 * std::sqrt(fixingTime * (1.0 - rho(fixingTime) * rho(fixingTime)))));
100 phi2 = cnd(
101 phi * (std::log(a * s1 / h) +
102 (m1 - 0.5 * v1 * v1) * fixingTime +
103 rho(fixingTime) * v1 * std::sqrt(fixingTime) * v) /
104 (v1 * std::sqrt(fixingTime * (1.0 - rho(fixingTime) * rho(fixingTime)))));
105 Real f = a * phi * s1 *
106 std::exp(m1 * fixingTime -
107 0.5 * rho(fixingTime) * rho(fixingTime) * v1 * v1 * fixingTime +
108 rho(fixingTime) * v1 * std::sqrt(fixingTime) * v) *
109 phi1 -
110 phi * h * phi2;
111 return std::exp(-x * x) * f;
112}
113
115
116 QL_REQUIRE(arguments_.exercise->type() == Exercise::European, "not an European option");
117
118 Option::Type optionType = arguments_.optionType;
119 Real phi = optionType == Option::Call ? 1.0 : -1.0;
120
121 Real strike = arguments_.strikeReturn;
122 QL_REQUIRE(strike >= 0, "non-negative strike expected");
123
124 Time fixingTime = process1_->time(arguments_.exercise->lastDate());
125
126 Real fx1 = arguments_.fxIndex1 ? getTodaysFxConversionRate(arguments_.fxIndex1) : 1.0;
127 Real fx2 = arguments_.fxIndex2 ? getTodaysFxConversionRate(arguments_.fxIndex2) : 1.0;
128
129 Real s1 = process1_->stateVariable()->value();
130 Real s2 = process2_->stateVariable()->value();
131 Real i1 = arguments_.initialValue1 * fx1;
132 Real i2 = arguments_.initialValue2 * fx2;
133
134 Real v1 = process1_->blackVolatility()->blackVol(
135 arguments_.exercise->lastDate(), s1);
136 Real v2 = process2_->blackVolatility()->blackVol(
137 arguments_.exercise->lastDate(), s2);
138
139 Real riskFreeRate1 = process1_->riskFreeRate()->zeroRate(
140 arguments_.exercise->lastDate(),
141 process1_->riskFreeRate()->dayCounter(),
142 Continuous, NoFrequency);
143 Real riskFreeRate2 = process2_->riskFreeRate()->zeroRate(
144 arguments_.exercise->lastDate(),
145 process2_->riskFreeRate()->dayCounter(),
146 Continuous, NoFrequency);
147 Real dividendYield1 = process1_->dividendYield()->zeroRate(
148 arguments_.exercise->lastDate(),
149 process1_->dividendYield()->dayCounter(),
150 Continuous, NoFrequency);
151 Real dividendYield2 = process2_->dividendYield()->zeroRate(
152 arguments_.exercise->lastDate(),
153 process2_->dividendYield()->dayCounter(),
154 Continuous, NoFrequency);
155
156 Real m1 = riskFreeRate1 - dividendYield1;
157 Real m2 = riskFreeRate2 - dividendYield2;
158
159 Real k = strike;
160
161 Real res = 0.0;
162
163 bool hasBarrier = (arguments_.knockInPrice != Null<Real>()) || (arguments_.knockOutPrice != Null<Real>());
164 Real integral = 0;
165 if (hasBarrier) {
166 Real my = (m2 - 0.5 * v2 * v2) * fixingTime;
167 Real vy = v2 * std::sqrt(fixingTime);
168
169 Real precision = 1.0e-6;
170
171 Real upperBound = Null<Real>();
172 Real lowerBound = Null<Real>();
173 if (arguments_.knockOutPrice != Null<Real>()) {
174 Real koPrice = fx2 * arguments_.knockOutPrice;
175 // for the integration variable y : upperBound = log( knockOutPrice / initialPrice2)
176 // in Brigo the variable change v = (y - my) / vy is made
177 // we make a further variable change x = v / sqrt(2)
178 upperBound = (std::log(koPrice / s2) - my) / ( vy * M_SQRT2);
179
180 if (arguments_.knockInPrice == Null<Real>()) {
181 // we estimate the infinite boundary
182 lowerBound = -2 * std::fabs(upperBound);
183 while(integrand(lowerBound, phi, k, m1, m2, v1, v2, s1, s2, i1, i2, fixingTime) > precision)
184 lowerBound *=2.0;
185 }
186 }
187
188 if (arguments_.knockInPrice != Null<Real>()) {
189 Real kiPrice = fx2 * arguments_.knockInPrice;
190 // for the integration variable y : lowerBound = log( knockInPrice / initialPrice2)
191 // in Brigo the variable change v = (y - my) / vy is made
192 // we make a further variable change x = v / sqrt(2)
193 lowerBound = (std::log(kiPrice / s2) - my) / ( vy * M_SQRT2);
194
195 if (arguments_.knockOutPrice == Null<Real>()) {
196 // we estimate the infinite boundary
197 upperBound = 2 * std::fabs(lowerBound);
198 while(integrand(upperBound, phi, k, m1, m2, v1, v2, s1, s2, i1, i2, fixingTime) > precision)
199 upperBound *=2.0;
200 }
201 }
202
203 QuantLib::ext::shared_ptr<Integrator> integratorBounded = QuantLib::ext::make_shared<GaussKronrodNonAdaptive>(precision, 1000000, 1.0);
204
205 QL_REQUIRE(upperBound != Null<Real>(), "AnalyticOutperformanceOptionEngine: expected valid upper bound.");
206 QL_REQUIRE(lowerBound != Null<Real>(), "AnalyticOutperformanceOptionEngine: expected valid lower bound.");
207 QL_REQUIRE(upperBound > lowerBound, "incorrect knock in levels provided");
208 integral += (*integratorBounded)(integrand_f(this, phi, k, m1, m2, v1, v2, s1, s2, i1, i2, fixingTime), lowerBound, upperBound);
209
210 } else {
211 QuantLib::ext::shared_ptr<GaussianQuadrature> integrator = QuantLib::ext::make_shared<GaussHermiteIntegration>(integrationPoints_);
212 integral +=
213 (*integrator)(integrand_f(this, phi, k, m1, m2, v1, v2, s1, s2, i1, i2, fixingTime));
214 }
215
216 res += (integral / M_SQRTPI);
217 DiscountFactor riskFreeDiscount =
218 process1_->riskFreeRate()->discount(arguments_.exercise->lastDate());
219
220 results_.value = res * riskFreeDiscount * arguments_.notional;
221
222 Real variance1 = process1_->blackVolatility()->blackVariance(
223 arguments_.exercise->lastDate(), s1);
224 Real variance2 = process2_->blackVolatility()->blackVariance(
225 arguments_.exercise->lastDate(), s2);
226 Real variance = variance1 + variance2 - 2 * rho(fixingTime) * std::sqrt(variance1)*std::sqrt(variance2);
227 Real stdDev = std::sqrt(variance);
228 results_.standardDeviation = stdDev;
229
230 results_.additionalResults["spot1"] = s1;
231 results_.additionalResults["spot2"] = s2;
232 results_.additionalResults["fx1"] = fx1;
233 results_.additionalResults["fx2"] = fx2;
234 results_.additionalResults["blackVol1"] = v1;
235 results_.additionalResults["blackVol2"] = v2;
236 results_.additionalResults["correlation"] = rho(fixingTime);
237 results_.additionalResults["strike"] = strike;
238 results_.additionalResults["residualTime"] = fixingTime;
239 results_.additionalResults["riskFreeRate1"] = riskFreeRate1;
240 results_.additionalResults["riskFreeRate2"] = riskFreeRate2;
241 results_.additionalResults["dividendYield1"] = dividendYield1;
242 results_.additionalResults["dividendYield2"] = dividendYield2;
243
244} // calculate
245} // namespace QuantExt
Analytic European engine for outperformance options.
const Instrument::results * results_
Definition: cdsoption.cpp:81
Pricing engine for European outperformance options using analytical formulae.
ext::shared_ptr< GeneralizedBlackScholesProcess > process2_
Real getTodaysFxConversionRate(const QuantLib::ext::shared_ptr< QuantExt::FxIndex > &fxIndex) const
ext::shared_ptr< GeneralizedBlackScholesProcess > process1_
AnalyticOutperformanceOptionEngine(const ext::shared_ptr< GeneralizedBlackScholesProcess > &process1, const ext::shared_ptr< GeneralizedBlackScholesProcess > &process2, const Handle< CorrelationTermStructure > &correlation, QuantLib::Size integrationPoints)
Real integrand(const Real x, Real phi, Real k, Real m1, Real m2, Real v1, Real v2, Real s1, Real s2, Real i1, Real i2, Real fixingTime) const
Real integral(const CrossAssetModel &model, const E &e, const Real a, const Real b)
RandomVariable variance(const RandomVariable &r)
JY INF index variance component.
Swap::arguments * arguments_