Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bfrrvolsurface.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#include "toplevelfixture.hpp"
20
23
24#include <ql/experimental/fx/blackdeltacalculator.hpp>
25#include <ql/pricingengines/blackformula.hpp>
26#include <ql/quotes/simplequote.hpp>
27#include <ql/termstructures/yield/flatforward.hpp>
28#include <ql/time/calendars/nullcalendar.hpp>
29#include <ql/time/daycounters/actual365fixed.hpp>
30
31#include <boost/make_shared.hpp>
32#include <boost/test/unit_test.hpp>
33
34using namespace QuantLib;
35using namespace QuantExt;
36using namespace boost::unit_test_framework;
37
38struct BFRRVolFixture : public qle::test::TopLevelFixture {
39public:
40 BFRRVolFixture() { Settings::instance().evaluationDate() = refDate; }
41 Date refDate = Date(13, April, 2021);
42 std::vector<Date> dates = {refDate + 1 * Years, refDate + 3 * Years};
43 std::vector<Real> deltas = {0.10, 0.25};
44 std::vector<std::vector<Real>> bfQuotes = {{0.02, 0.01}, {0.01, 0.0050}};
45 std::vector<std::vector<Real>> rrQuotes = {{-0.015, -0.012}, {-0.011, -0.009}};
46 std::vector<Real> atmQuotes = {0.09, 0.08};
47 Actual365Fixed dc;
48 NullCalendar cal;
49 Handle<Quote> spot = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.2));
50 Size spotDays = 2;
51 Handle<YieldTermStructure> domesticTS =
52 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(refDate, 0.01, dc));
53 Handle<YieldTermStructure> foreignTS =
54 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(refDate, 0.015, dc));
55 DeltaVolQuote::DeltaType dt = DeltaVolQuote::DeltaType::PaSpot;
56 DeltaVolQuote::AtmType at = DeltaVolQuote::AtmType::AtmDeltaNeutral;
57 Period switchTenor = 2 * Years;
58 DeltaVolQuote::DeltaType ltdt = DeltaVolQuote::DeltaType::PaFwd;
59 DeltaVolQuote::AtmType ltat = DeltaVolQuote::AtmType::AtmDeltaNeutral;
60 Option::Type rrInFavorOf = Option::Call;
62 BlackVolatilitySurfaceBFRR::SmileInterpolation::Cubic;
63};
64
65BOOST_AUTO_TEST_SUITE(QuantExtTestSuite)
66
67BOOST_FIXTURE_TEST_SUITE(BFRRVolSurfaceTest, BFRRVolFixture)
68
70
71 BOOST_TEST_MESSAGE("Testing bf/rr vol surface with smile bf quotes...");
72
73 Real tol1 = 1E-5;
74
75 auto vol1 = QuantLib::ext::make_shared<BlackVolatilitySurfaceBFRR>(
76 refDate, dates, deltas, bfQuotes, rrQuotes, atmQuotes, dc, cal, spot, spotDays, cal, domesticTS, foreignTS, dt,
77 at, switchTenor, ltdt, ltat, rrInFavorOf, false, smileInterpolation);
78
79 Real t1 = vol1->timeFromReference(dates[0]);
80 Real domDisc1 = domesticTS->discount(dates[0] + spotDays) / domesticTS->discount(refDate + spotDays);
81 Real forDisc1 = foreignTS->discount(dates[0] + spotDays) / foreignTS->discount(refDate + spotDays);
82 Real k1_1_10p = getStrikeFromDelta(Option::Put, -deltas[0], dt, spot->value(), domDisc1, forDisc1, vol1, t1);
83 Real k1_1_25p = getStrikeFromDelta(Option::Put, -deltas[1], dt, spot->value(), domDisc1, forDisc1, vol1, t1);
84 Real k1_1_atm = getAtmStrike(dt, at, spot->value(), domDisc1, forDisc1, vol1, t1);
85 Real k1_1_25c = getStrikeFromDelta(Option::Call, deltas[1], dt, spot->value(), domDisc1, forDisc1, vol1, t1);
86 Real k1_1_10c = getStrikeFromDelta(Option::Call, deltas[0], dt, spot->value(), domDisc1, forDisc1, vol1, t1);
87
88 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_10p) - (atmQuotes[0] + bfQuotes[0][0] - 0.5 * rrQuotes[0][0]),
89 tol1);
90 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_25p) - (atmQuotes[0] + bfQuotes[0][1] - 0.5 * rrQuotes[0][1]),
91 tol1);
92 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_atm) - atmQuotes[0], tol1);
93 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_25c) - (atmQuotes[0] + bfQuotes[0][1] + 0.5 * rrQuotes[0][1]),
94 tol1);
95 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_10c) - (atmQuotes[0] + bfQuotes[0][0] + 0.5 * rrQuotes[0][0]),
96 tol1);
97
98 Real t2 = vol1->timeFromReference(dates[1]);
99 Real domDisc2 = domesticTS->discount(dates[1] + spotDays) / domesticTS->discount(refDate + spotDays);
100 Real forDisc2 = foreignTS->discount(dates[1] + spotDays) / foreignTS->discount(refDate + spotDays);
101 Real k1_2_10p = getStrikeFromDelta(Option::Put, -deltas[0], ltdt, spot->value(), domDisc2, forDisc2, vol1, t2);
102 Real k1_2_25p = getStrikeFromDelta(Option::Put, -deltas[1], ltdt, spot->value(), domDisc2, forDisc2, vol1, t2);
103 Real k1_2_atm = getAtmStrike(ltdt, ltat, spot->value(), domDisc2, forDisc2, vol1, t2);
104 Real k1_2_25c = getStrikeFromDelta(Option::Call, deltas[1], ltdt, spot->value(), domDisc2, forDisc2, vol1, t2);
105 Real k1_2_10c = getStrikeFromDelta(Option::Call, deltas[0], ltdt, spot->value(), domDisc2, forDisc2, vol1, t2);
106
107 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_10p) - (atmQuotes[1] + bfQuotes[1][0] - 0.5 * rrQuotes[1][0]),
108 tol1);
109 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_25p) - (atmQuotes[1] + bfQuotes[1][1] - 0.5 * rrQuotes[1][1]),
110 tol1);
111 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_atm) - atmQuotes[1], tol1);
112 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_25c) - (atmQuotes[1] + bfQuotes[1][1] + 0.5 * rrQuotes[1][1]),
113 tol1);
114 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_10c) - (atmQuotes[1] + bfQuotes[1][0] + 0.5 * rrQuotes[1][0]),
115 tol1);
116}
117
118BOOST_AUTO_TEST_CASE(testBrokerBF) {
119
120 BOOST_TEST_MESSAGE("Testing bf/rr vol surface with broker bf quotes...");
121
122 Real tol1 = 1E-5;
123 Real tol2 = 1E-5;
124
125 auto vol2 = QuantLib::ext::make_shared<BlackVolatilitySurfaceBFRR>(
126 refDate, dates, deltas, bfQuotes, rrQuotes, atmQuotes, dc, cal, spot, spotDays, cal, domesticTS, foreignTS, dt,
127 at, switchTenor, ltdt, ltat, rrInFavorOf, true, smileInterpolation);
128
129 Real t1 = vol2->timeFromReference(dates[0]);
130 Real domDisc1 = domesticTS->discount(dates[0] + spotDays) / domesticTS->discount(refDate + spotDays);
131 Real forDisc1 = foreignTS->discount(dates[0] + spotDays) / foreignTS->discount(refDate + spotDays);
132 Real t2 = vol2->timeFromReference(dates[1]);
133 Real domDisc2 = domesticTS->discount(dates[1] + spotDays) / domesticTS->discount(refDate + spotDays);
134 Real forDisc2 = foreignTS->discount(dates[1] + spotDays) / foreignTS->discount(refDate + spotDays);
135
136 // checks for expiry 1
137
138 Real bfvol2_1_10_broker = atmQuotes[0] + bfQuotes[0][0];
139 Real bfvol2_1_25_broker = atmQuotes[0] + bfQuotes[0][1];
140
141 BlackDeltaCalculator bdc_1_10_p(Option::Put, dt, spot->value(), domDisc1, forDisc1,
142 bfvol2_1_10_broker * std::sqrt(t1));
143 Real k2_1_10p_broker = bdc_1_10_p.strikeFromDelta(-deltas[0]);
144
145 BlackDeltaCalculator bdc_1_10_c(Option::Call, dt, spot->value(), domDisc1, forDisc1,
146 bfvol2_1_10_broker * std::sqrt(t1));
147 Real k2_1_10c_broker = bdc_1_10_c.strikeFromDelta(deltas[0]);
148
149 BlackDeltaCalculator bdc_1_25_p(Option::Put, dt, spot->value(), domDisc1, forDisc1,
150 bfvol2_1_25_broker * std::sqrt(t1));
151 Real k2_1_25p_broker = bdc_1_25_p.strikeFromDelta(-deltas[1]);
152
153 BlackDeltaCalculator bdc_1_25_c(Option::Call, dt, spot->value(), domDisc1, forDisc1,
154 bfvol2_1_25_broker * std::sqrt(t1));
155 Real k2_1_25c_broker = bdc_1_25_c.strikeFromDelta(deltas[1]);
156
157 Real bfPrice_1_10_broker = blackFormula(Option::Put, k2_1_10p_broker, spot->value() / domDisc1 * forDisc1,
158 bfvol2_1_10_broker * std::sqrt(t1)) +
159 blackFormula(Option::Call, k2_1_10c_broker, spot->value() / domDisc1 * forDisc1,
160 bfvol2_1_10_broker * std::sqrt(t1));
161
162 Real bfPrice_1_25_broker = blackFormula(Option::Put, k2_1_25p_broker, spot->value() / domDisc1 * forDisc1,
163 bfvol2_1_25_broker * std::sqrt(t1)) +
164 blackFormula(Option::Call, k2_1_25c_broker, spot->value() / domDisc1 * forDisc1,
165 bfvol2_1_25_broker * std::sqrt(t1));
166
167 Real k2_1_10p = getStrikeFromDelta(Option::Put, -deltas[0], dt, spot->value(), domDisc1, forDisc1, vol2, t1);
168 Real k2_1_25p = getStrikeFromDelta(Option::Put, -deltas[1], dt, spot->value(), domDisc1, forDisc1, vol2, t1);
169 Real k2_1_atm = getAtmStrike(dt, at, spot->value(), domDisc1, forDisc1, vol2, t1);
170 Real k2_1_25c = getStrikeFromDelta(Option::Call, deltas[1], dt, spot->value(), domDisc1, forDisc1, vol2, t1);
171 Real k2_1_10c = getStrikeFromDelta(Option::Call, deltas[0], dt, spot->value(), domDisc1, forDisc1, vol2, t1);
172
173 Real bfPrice_1_10_smile = blackFormula(Option::Put, k2_1_10p_broker, spot->value() / domDisc1 * forDisc1,
174 std::sqrt(vol2->blackVariance(dates[0], k2_1_10p_broker))) +
175 blackFormula(Option::Call, k2_1_10c_broker, spot->value() / domDisc1 * forDisc1,
176 std::sqrt(vol2->blackVariance(dates[0], k2_1_10c_broker)));
177
178 Real bfPrice_1_25_smile = blackFormula(Option::Put, k2_1_25p_broker, spot->value() / domDisc1 * forDisc1,
179 std::sqrt(vol2->blackVariance(dates[0], k2_1_25p_broker))) +
180 blackFormula(Option::Call, k2_1_25c_broker, spot->value() / domDisc1 * forDisc1,
181 std::sqrt(vol2->blackVariance(dates[0], k2_1_25c_broker)));
182
183 // check broker bf premium = smile bf premium
184
185 BOOST_CHECK_SMALL(bfPrice_1_10_smile - bfPrice_1_10_broker, tol2);
186 BOOST_CHECK_SMALL(bfPrice_1_25_smile - bfPrice_1_25_broker, tol2);
187
188 // check rr and atm quotes are reproduced on smile
189
190 BOOST_CHECK_SMALL(vol2->blackVol(dates[0], k2_1_10c) - vol2->blackVol(dates[0], k2_1_10p) - rrQuotes[0][0], tol1);
191 BOOST_CHECK_SMALL(vol2->blackVol(dates[0], k2_1_25c) - vol2->blackVol(dates[0], k2_1_25p) - rrQuotes[0][1], tol1);
192 BOOST_CHECK_SMALL(vol2->blackVol(dates[0], k2_1_atm) - atmQuotes[0], tol1);
193
194 // checks for expiry 2
195
196 Real bfvol2_2_10_broker = atmQuotes[1] + bfQuotes[1][0];
197 Real bfvol2_2_25_broker = atmQuotes[1] + bfQuotes[1][1];
198
199 BlackDeltaCalculator bdc_2_10_p(Option::Put, ltdt, spot->value(), domDisc2, forDisc2,
200 bfvol2_2_10_broker * std::sqrt(t2));
201 Real k2_2_10p_broker = bdc_2_10_p.strikeFromDelta(-deltas[0]);
202
203 BlackDeltaCalculator bdc_2_10_c(Option::Call, ltdt, spot->value(), domDisc2, forDisc2,
204 bfvol2_2_10_broker * std::sqrt(t2));
205 Real k2_2_10c_broker = bdc_2_10_c.strikeFromDelta(deltas[0]);
206
207 BlackDeltaCalculator bdc_2_25_p(Option::Put, ltdt, spot->value(), domDisc2, forDisc2,
208 bfvol2_2_25_broker * std::sqrt(t2));
209 Real k2_2_25p_broker = bdc_2_25_p.strikeFromDelta(-deltas[1]);
210
211 BlackDeltaCalculator bdc_2_25_c(Option::Call, ltdt, spot->value(), domDisc2, forDisc2,
212 bfvol2_2_25_broker * std::sqrt(t2));
213 Real k2_2_25c_broker = bdc_2_25_c.strikeFromDelta(deltas[1]);
214
215 Real bfPrice_2_10_broker = blackFormula(Option::Put, k2_2_10p_broker, spot->value() / domDisc2 * forDisc2,
216 bfvol2_2_10_broker * std::sqrt(t2)) +
217 blackFormula(Option::Call, k2_2_10c_broker, spot->value() / domDisc2 * forDisc2,
218 bfvol2_2_10_broker * std::sqrt(t2));
219
220 Real bfPrice_2_25_broker = blackFormula(Option::Put, k2_2_25p_broker, spot->value() / domDisc2 * forDisc2,
221 bfvol2_2_25_broker * std::sqrt(t2)) +
222 blackFormula(Option::Call, k2_2_25c_broker, spot->value() / domDisc2 * forDisc2,
223 bfvol2_2_25_broker * std::sqrt(t2));
224
225 Real k2_2_10p = getStrikeFromDelta(Option::Put, -deltas[0], ltdt, spot->value(), domDisc2, forDisc2, vol2, t2);
226 Real k2_2_25p = getStrikeFromDelta(Option::Put, -deltas[1], ltdt, spot->value(), domDisc2, forDisc2, vol2, t2);
227 Real k2_2_atm = getAtmStrike(ltdt, ltat, spot->value(), domDisc2, forDisc2, vol2, t2);
228 Real k2_2_25c = getStrikeFromDelta(Option::Call, deltas[1], ltdt, spot->value(), domDisc2, forDisc2, vol2, t2);
229 Real k2_2_10c = getStrikeFromDelta(Option::Call, deltas[0], ltdt, spot->value(), domDisc2, forDisc2, vol2, t2);
230
231 Real bfPrice_2_10_smile = blackFormula(Option::Put, k2_2_10p_broker, spot->value() / domDisc2 * forDisc2,
232 std::sqrt(vol2->blackVariance(dates[1], k2_2_10p_broker))) +
233 blackFormula(Option::Call, k2_2_10c_broker, spot->value() / domDisc2 * forDisc2,
234 std::sqrt(vol2->blackVariance(dates[1], k2_2_10c_broker)));
235
236 Real bfPrice_2_25_smile = blackFormula(Option::Put, k2_2_25p_broker, spot->value() / domDisc2 * forDisc2,
237 std::sqrt(vol2->blackVariance(dates[1], k2_2_25p_broker))) +
238 blackFormula(Option::Call, k2_2_25c_broker, spot->value() / domDisc2 * forDisc2,
239 std::sqrt(vol2->blackVariance(dates[1], k2_2_25c_broker)));
240
241 // check broker bf premium = smile bf premium
242
243 BOOST_CHECK_SMALL(bfPrice_2_10_smile - bfPrice_2_10_broker, tol2);
244 BOOST_CHECK_SMALL(bfPrice_2_25_smile - bfPrice_2_25_broker, tol2);
245
246 // check rr and atm quotes are reproduced on smile
247
248 BOOST_CHECK_SMALL(vol2->blackVol(dates[1], k2_2_10c) - vol2->blackVol(dates[1], k2_2_10p) - rrQuotes[1][0], tol1);
249 BOOST_CHECK_SMALL(vol2->blackVol(dates[1], k2_2_25c) - vol2->blackVol(dates[1], k2_2_25p) - rrQuotes[1][1], tol1);
250 BOOST_CHECK_SMALL(vol2->blackVol(dates[1], k2_2_atm) - atmQuotes[1], tol1);
251}
252
253BOOST_AUTO_TEST_SUITE_END()
254
255BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testSmileBF)
utilities to calculate strikes from deltas and atm strikes on smiles
Black volatility surface based on bf/rr quotes.
Real getStrikeFromDelta(Option::Type optionType, Real delta, DeltaVolQuote::DeltaType dt, Real spot, Real domDiscount, Real forDiscount, QuantLib::ext::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
Real getAtmStrike(DeltaVolQuote::DeltaType dt, DeltaVolQuote::AtmType at, Real spot, Real domDiscount, Real forDiscount, QuantLib::ext::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
Fixture that can be used at top level.