Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
pairwisevarianceswapengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 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/pairwisevarianceswapengine.cpp
20 \brief pairwise 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/calendars/jointcalendar.hpp>
33#include <ql/time/daycounters/actualactual.hpp>
34
35#include <iostream>
36
37using namespace QuantLib;
38
39namespace QuantExt {
40
42 const QuantLib::ext::shared_ptr<Index>& index1, const QuantLib::ext::shared_ptr<Index>& index2,
43 const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process1,
44 const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process2, const Handle<YieldTermStructure>& discountingTS,
45 Handle<Quote> correlation)
46 : index1_(index1), index2_(index2), process1_(process1), process2_(process2), discountingTS_(discountingTS),
47 correlation_(correlation) {
48
49 QL_REQUIRE(process1_ && process2_, "Black-Scholes process not present.");
50
51 registerWith(process1_);
52 registerWith(process2_);
53 registerWith(discountingTS_);
54}
55
57
58 QL_REQUIRE(!discountingTS_.empty(), "Empty discounting term structure handle");
59
60 results_.value = 0.0;
61
62 Date today = QuantLib::Settings::instance().evaluationDate();
63 const Date& maturityDate = arguments_.settlementDate;
64
65 if (today > maturityDate)
66 return;
67
68 // Variance is defined here as the annualised volatility squared.
69 Variances variances = calculateVariances(arguments_.valuationSchedule, arguments_.laggedValuationSchedule, today);
70 results_.additionalResults["accruedVariance1"] = variances.accruedVariance1;
71 results_.additionalResults["accruedVariance2"] = variances.accruedVariance2;
72 results_.additionalResults["accruedBasketVariance"] = variances.accruedBasketVariance;
73 results_.additionalResults["futureVariance1"] = variances.futureVariance1;
74 results_.additionalResults["futureVariance2"] = variances.futureVariance2;
75 results_.additionalResults["futureBasketVariance"] = variances.futureBasketVariance;
76 results_.additionalResults["totalVariance1"] = variances.totalVariance1;
77 results_.additionalResults["totalVariance2"] = variances.totalVariance2;
78 results_.additionalResults["totalBasketVariance"] = variances.totalBasketVariance;
79
80 Real variance1 = variances.totalVariance1;
81 Real variance2 = variances.totalVariance2;
82 Real basketVariance = variances.totalBasketVariance;
83 results_.variance1 = variance1;
84 results_.variance2 = variance2;
85 results_.basketVariance = basketVariance;
86
87 Real strike1 = arguments_.strike1 * arguments_.strike1;
88 Real strike2 = arguments_.strike2 * arguments_.strike2;
89 Real basketStrike = arguments_.basketStrike * arguments_.basketStrike;
90
91 if (!QuantLib::close_enough(arguments_.floor, 0.0)) {
92 Real floor = arguments_.floor * arguments_.floor;
93 variance1 = std::max(floor * strike1, variance1);
94 variance2 = std::max(floor * strike2, variance2);
95 basketVariance = std::max(floor * basketStrike, basketVariance);
96 }
97
98 if (!QuantLib::close_enough(arguments_.cap, 0.0)) {
99 Real cap = arguments_.cap * arguments_.cap;
100 variance1 = std::min(cap * strike1, variance1);
101 variance2 = std::min(cap * strike2, variance2);
102 basketVariance = std::min(cap * basketStrike, basketVariance);
103 }
104
105 results_.finalVariance1 = variance1;
106 results_.finalVariance2 = variance2;
107 results_.finalBasketVariance = basketVariance;
108 results_.additionalResults["finalVariance1"] = variance1;
109 results_.additionalResults["finalVariance2"] = variance2;
110 results_.additionalResults["finalBasketVariance"] = basketVariance;
111
112 Real currentNotional1 = 10000.0 * arguments_.notional1 / (2.0 * 100.0 * arguments_.strike1);
113 Real currentNotional2 = 10000.0 * arguments_.notional2 / (2.0 * 100.0 * arguments_.strike2);
114 Real currentBasketNotional = 10000.0 * arguments_.basketNotional / (2.0 * 100.0 * arguments_.basketStrike);
115
116 results_.additionalResults["varianceAmount1"] = currentNotional1;
117 results_.additionalResults["varianceAmount2"] = currentNotional2;
118 results_.additionalResults["basketVarianceAmount"] = currentBasketNotional;
119
120 Real equityAmount1 = currentNotional1 * (variance1 - strike1);
121 Real equityAmount2 = currentNotional2 * (variance2 - strike2);
122 Real equityAmountBasket = currentBasketNotional * (basketVariance - basketStrike);
123
124 results_.equityAmount1 = equityAmount1;
125 results_.equityAmount2 = equityAmount2;
126 results_.equityAmountBasket = equityAmountBasket;
127 results_.additionalResults["equityAmount1"] = equityAmount1;
128 results_.additionalResults["equityAmount2"] = equityAmount2;
129 results_.additionalResults["equityAmountBasket"] = equityAmountBasket;
130
131 Real pairwiseEquityAmount = equityAmount1 + equityAmount2 + equityAmountBasket;
132
133 results_.pairwiseEquityAmount = pairwiseEquityAmount;
134
135 if (!QuantLib::close_enough(arguments_.payoffLimit, 0.0)) {
136 Real equityAmountCap =
137 arguments_.payoffLimit * (std::abs(arguments_.notional1) + std::abs(arguments_.notional2));
138 Real equityAmountFloor = -1.0 * equityAmountCap;
139 pairwiseEquityAmount = std::max(equityAmountFloor, pairwiseEquityAmount);
140 pairwiseEquityAmount = std::min(equityAmountCap, pairwiseEquityAmount);
141 }
142
143 results_.finalEquityAmount = pairwiseEquityAmount;
144 results_.additionalResults["finalEquityAmount"] = pairwiseEquityAmount;
145
146 Real multiplier = arguments_.position == Position::Long ? 1.0 : -1.0;
147 DiscountFactor df = discountingTS_->discount(arguments_.settlementDate);
148
149 results_.value = multiplier * df * pairwiseEquityAmount;
150}
151
153 const Schedule& laggedValuationSchedule,
154 const Date& evalDate) const {
155
156 const Date& accrStartDate = valuationSchedule.dates().back();
157 const Date& accrEndDate = laggedValuationSchedule.dates().back();
158
159 Variances res;
160
161 for (Size i = 0; i < valuationSchedule.size(); i++) {
162 const Date& vd = valuationSchedule.dates()[i];
163 const Date& lvd = laggedValuationSchedule.dates()[i];
164
165 // Calculate accrued (squared) variations, i.e. both val date and lagged val date are known
166 if (lvd <= evalDate) {
167 Real price1 = index1_->fixing(vd);
168 Real laggedPrice1 = index1_->fixing(lvd);
169 Real variation1 = std::log(laggedPrice1 / price1);
170 res.accruedVariance1 += variation1 * variation1;
171
172 Real price2 = index2_->fixing(vd);
173 Real laggedPrice2 = index2_->fixing(lvd);
174 Real variation2 = std::log(laggedPrice2 / price2);
175 res.accruedVariance2 += variation2 * variation2;
176
177 Real basketPrice = price1 + price2;
178 Real laggedBasketPrice = laggedPrice1 + laggedPrice2;
179 Real basketVariation = std::log(laggedBasketPrice / basketPrice);
180 res.accruedBasketVariance += basketVariation * basketVariation;
181 } else {
182 // At this point, all variation contributions have been accrued, so now calculate the accrued variance
183 Real expectedN = valuationSchedule.dates().size() - 1.0;
184 Real factor = 252 / (expectedN * arguments_.accrualLag);
185 res.accruedVariance1 *= factor;
186 res.accruedVariance2 *= factor;
187 res.accruedBasketVariance *= factor;
188
189 // Calculate future variance
190 Real t = ActualActual(ActualActual::ISDA).yearFraction(evalDate, accrEndDate);
191 Real F1 =
192 process1_->x0() / process1_->riskFreeRate()->discount(t) * process1_->dividendYield()->discount(t);
193 Real F2 =
194 process2_->x0() / process2_->riskFreeRate()->discount(t) * process2_->dividendYield()->discount(t);
195
196 Real variance1 = process1_->blackVolatility()->blackVariance(t, F1);
197 Real variance2 = process2_->blackVolatility()->blackVariance(t, F2);
198 Real basketVariance =
199 variance1 + variance2 + 2.0 * std::sqrt(variance1) * std::sqrt(variance2) * correlation_->value();
200
201 res.futureVariance1 = variance1;
202 res.futureVariance2 = variance2;
203 res.futureBasketVariance = basketVariance;
204 break;
205 }
206 }
207
208 Calendar jointCal =
209 JointCalendar(std::vector<Calendar>{valuationSchedule.calendar(), laggedValuationSchedule.calendar(),
210 index1_->fixingCalendar(), index2_->fixingCalendar()});
211 Real accrTime = std::abs(jointCal.businessDaysBetween(accrStartDate, evalDate, true, true));
212 Real futTime = std::abs(jointCal.businessDaysBetween(evalDate, accrEndDate, true, false));
213 Real totalTime = accrTime + futTime;
214 Real accrFactor = accrTime / totalTime;
215 Real futFactor = futTime / totalTime;
216
217 res.totalVariance1 = (res.accruedVariance1 * accrFactor) + (res.futureVariance1 * futFactor);
218 res.totalVariance2 = (res.accruedVariance2 * accrFactor) + (res.futureVariance2 * futFactor);
219 res.totalBasketVariance = (res.accruedBasketVariance * accrFactor) + (res.futureBasketVariance * futFactor);
220
221 return res;
222}
223
224} // namespace QuantExt
const Instrument::results * results_
Definition: cdsoption.cpp:81
QuantLib::ext::shared_ptr< Index > index2_
PairwiseVarianceSwapEngine(const QuantLib::ext::shared_ptr< QuantLib::Index > &index1, const QuantLib::ext::shared_ptr< QuantLib::Index > &index2, const QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > &process1, const QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > &process2, const Handle< YieldTermStructure > &discountingTS, Handle< Quote > correlation)
Variances calculateVariances(const Schedule &valuationSchedule, const Schedule &laggedValuationSchedule, const Date &evalDate) const
QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > process2_
QuantLib::ext::shared_ptr< GeneralizedBlackScholesProcess > process1_
QuantLib::ext::shared_ptr< Index > index1_
equity index class for holding equity fixing histories and forwarding.
pairwise variance swap engine
Swap::arguments * arguments_