Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
varianceswapgeneralreplicationengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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
19/*! \file qle/pricingengines/varianceswapgeneralreplicationengine.cpp
20 \brief equity variance swap engine
21 \ingroup engines
22*/
23
25
27
28#include <ql/indexes/indexmanager.hpp>
29#include <ql/math/integrals/gausslobattointegral.hpp>
30#include <ql/math/integrals/segmentintegral.hpp>
31#include <ql/pricingengines/blackformula.hpp>
32#include <ql/time/daycounters/actualactual.hpp>
33#include <ql/time/calendars/jointcalendar.hpp>
34
35#include <iostream>
36
37using namespace QuantLib;
38
39namespace QuantExt {
40
42 const QuantLib::ext::shared_ptr<Index>& index, const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
43 const Handle<YieldTermStructure>& discountingTS, const VarSwapSettings settings, const bool staticTodaysSpot)
44 : index_(index), process_(process), discountingTS_(discountingTS), settings_(settings),
45 staticTodaysSpot_(staticTodaysSpot) {
46
47 QL_REQUIRE(process_, "Black-Scholes process not present.");
48
49 registerWith(process_);
50 registerWith(discountingTS_);
51}
52
54
55 QL_REQUIRE(!discountingTS_.empty(), "Empty discounting term structure handle");
56
57 results_.value = 0.0;
58
59 Date today = QuantLib::Settings::instance().evaluationDate();
60
61 if (today >= arguments_.maturityDate)
62 return;
63
64 // set up calendar combining holidays form index and instrument
65 Calendar jointCal = JointCalendar(arguments_.calendar, index_->fixingCalendar());
66
67 // Variance is defined here as the annualised volatility squared.
68 Real variance = 0;
69 if (arguments_.startDate > today) {
70 // This calculation uses the (time-weighted) additivity of variance to calculate the result.
71 Real tsTime = jointCal.businessDaysBetween(today, arguments_.startDate, true, true);
72 Real teTime = jointCal.businessDaysBetween(today, arguments_.maturityDate, false, true);
73 Real fwdTime =
74 jointCal.businessDaysBetween(arguments_.startDate, arguments_.maturityDate, true, true);
75 variance = (calculateFutureVariance(arguments_.maturityDate) * teTime -
76 calculateFutureVariance(arguments_.startDate) * tsTime) /
77 fwdTime;
78 results_.additionalResults["accruedVariance"] = 0;
79 results_.additionalResults["futureVariance"] = variance;
80 } else if (arguments_.startDate == today) {
81 // The only time the QL price works
83 results_.additionalResults["accruedVariance"] = 0;
84 results_.additionalResults["futureVariance"] = variance;
85 } else {
86 // Get weighted average of Future and Realised variancies.
87 Real accVar = calculateAccruedVariance(jointCal);
88 Real futVar = calculateFutureVariance(arguments_.maturityDate);
89 results_.additionalResults["accruedVariance"] = accVar;
90 results_.additionalResults["futureVariance"] = futVar;
91 Real totalTime =
92 jointCal.businessDaysBetween(arguments_.startDate, arguments_.maturityDate, true, true);
93 Real accTime = jointCal.businessDaysBetween(arguments_.startDate, today, true, true);
94 Real futTime = jointCal.businessDaysBetween(today, arguments_.maturityDate, false, true);
95 variance = (accVar * accTime / totalTime) + (futVar * futTime / totalTime);
96 }
97
98 results_.additionalResults["totalVariance"] = variance;
99
100 DiscountFactor df = discountingTS_->discount(arguments_.maturityDate);
101 results_.additionalResults["MaturityDiscountFactor"] = df;
102 Real multiplier = arguments_.position == Position::Long ? 1.0 : -1.0;
103
104 results_.variance = variance;
105 results_.value = multiplier * df * arguments_.notional * 10000.0 *
106 (variance - arguments_.strike); // factor of 10000 to convert vols to market quotes
107
108 Real volStrike = std::sqrt(arguments_.strike);
109 results_.additionalResults["VarianceNotional"] = arguments_.notional;
110 results_.additionalResults["VarianceStrike"] = arguments_.strike;
111 results_.additionalResults["VolatilityStrike"] = volStrike;
112 results_.additionalResults["VegaNotional"] = arguments_.notional * 2 * 100 * volStrike;
113}
114
116 // return annualised accrued variance
117 // Calculate historical variance from arguments_.startDate to today
118 Date today = QuantLib::Settings::instance().evaluationDate();
119
120 std::map<Date, Real> dividends;
121 if (arguments_.addPastDividends) {
122 if (auto eqIndex = QuantLib::ext::dynamic_pointer_cast<EquityIndex2>(index_)) {
123 auto divs = eqIndex->dividendFixings();
124 for (const auto& d : divs)
125 dividends[d.exDate] = d.rate;
126 }
127 }
128
129 Real variance = 0.0;
130 Size counter = 0;
131 Date firstDate = jointCal.adjust(arguments_.startDate);
132 Real last = index_->fixing(firstDate);
133 QL_REQUIRE(last != Null<Real>(),
134 "No fixing for " << index_->name() << " on date " << firstDate
135 << ". This is required for fixing the return on the first day of the variance swap.");
136
137 for (Date d = jointCal.advance(firstDate, 1, Days); d < today;
138 d = jointCal.advance(d, 1, Days)) {
139 Real price = index_->fixing(d);
140 QL_REQUIRE(price != Null<Real>(), "No fixing for " << index_->name() << " on date " << d);
141 QL_REQUIRE(price > 0.0, "Fixing for " << index_->name() << " on date " << d << " must be greater than zero.");
142 // Add historical dividend payment back to price
143 Real dividend = dividends[d] != Null<Real>() ? dividends[d] : 0;
144 Real move = log((price + dividend) / last);
145 variance += move * move;
146 counter++;
147 last = price;
148 }
149
150 // Now do final move. Yesterday is a fixing, todays price is in equityPrice_
151 Real dividend = dividends[today] != Null<Real>() ? dividends[today] : 0;
152 Real x0 = staticTodaysSpot_ && cachedTodaysSpot_ != Null<Real>() ? cachedTodaysSpot_ : process_->x0();
153 Real lastMove = log((x0 + dividend) / last);
154 variance += lastMove * lastMove;
155 counter++;
156
157 // cache todays spot if this switch is active
160
161 // Annualize
162 Size days = 252; // FIXME: maybe get days from calendar?
163 return days * variance / counter;
164}
165
167
168 // calculate maturity time
169
170 Date today = QuantLib::Settings::instance().evaluationDate();
171 Real T = ActualActual(ActualActual::ISDA).yearFraction(today, maturity);
172
173 // calculate forward
174
175 Real F = process_->x0() / process_->riskFreeRate()->discount(T) * process_->dividendYield()->discount(T);
176
177 // set up integrator
178
179 QuantLib::ext::shared_ptr<Integrator> integrator;
181 integrator = QuantLib::ext::make_shared<GaussLobattoIntegral>(settings_.maxIterations, QL_MAX_REAL, settings_.accuracy);
183 integrator = QuantLib::ext::make_shared<SegmentIntegral>(settings_.steps);
184 } else {
185 QL_FAIL("GeneralisedReplicationVarianceSwapEngine: internal error, unknown scheme");
186 }
187
188 // set up replication integrand
189
190 auto replication = [F, T, this](Real K) {
191 if (K < 1E-10)
192 return 0.0;
193 return blackFormula(K < F ? Option::Put : Option::Call, K, F,
194 std::sqrt(std::max(0.0, process_->blackVolatility()->blackVariance(T, K, true)))) /
195 (K * K);
196 };
197
198 // determine lower and upper integration bounds
199
200 Real lower = F, upper = F;
201
203 Real tmp = std::max(0.01, T);
204 Real stdDev = std::max(0.01, process_->blackVolatility()->blackVol(tmp, F, true)) * std::sqrt(tmp);
205 lower = F * std::exp(settings_.fixedMinStdDevs * stdDev);
206 upper = F * std::exp(settings_.fixedMaxStdDevs * stdDev);
208 Size i = 0, j = 0;
209 for (; i < settings_.maxPriceThresholdSteps && replication(lower) > settings_.priceThreshold; ++i)
210 lower *= (1.0 - settings_.priceThresholdStep);
211 for (; j < settings_.maxPriceThresholdSteps && replication(upper) > settings_.priceThreshold; ++j)
212 upper *= (1.0 + settings_.priceThresholdStep);
214 "GeneralisedReplicatingVarianceSwapEngine(): far otm call / put prices do not go to zero, put("
215 << lower << ")=" << replication(lower)
216 << " (vol=" << process_->blackVolatility()->blackVol(T, lower, true) << "), call(" << upper
217 << ")=" << replication(upper)
218 << ", vol=" << process_->blackVolatility()->blackVol(T, upper, true) << ", threshold is "
219 << settings_.priceThreshold << ", check validity of volatility surface (are vols exploding?)");
220 } else {
221 QL_FAIL("GeneralisedReplicationVarianceSwapEngine: internal error, unknown bounds");
222 }
223
224 // calculate the integration integral
225
226 try {
227 Real res = 0.0;
228 if (!close_enough(lower, F))
229 res += integrator->operator()(replication, lower, F);
230 if (!close_enough(upper, F))
231 res += integrator->operator()(replication, F, upper);
232 return 2.0 / T * res;
233 } catch (const std::exception& e) {
234 QL_FAIL("GeneralisedReplicatingVarianceSwapEngine(): error during calculation, check volatility input and "
235 "resulting replication integrand: "
236 << e.what());
237 }
238}
239
240} // namespace QuantExt
const Instrument::results * results_
Definition: cdsoption.cpp:81
GeneralisedReplicatingVarianceSwapEngine(const QuantLib::ext::shared_ptr< QuantLib::Index > &index, const QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > &process, const Handle< YieldTermStructure > &discountingTS, const VarSwapSettings settings=VarSwapSettings(), const bool staticTodaysSpot=true)
QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > process_
equity index class for holding equity fixing histories and forwarding.
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable variance(const RandomVariable &r)
CompiledFormula log(CompiledFormula x)
Swap::arguments * arguments_