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>
31#include <boost/make_shared.hpp>
32#include <boost/test/unit_test.hpp>
36using namespace boost::unit_test_framework;
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};
49 Handle<Quote> spot = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.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;
65BOOST_AUTO_TEST_SUITE(QuantExtTestSuite)
67BOOST_FIXTURE_TEST_SUITE(BFRRVolSurfaceTest, BFRRVolFixture)
71 BOOST_TEST_MESSAGE(
"Testing bf/rr vol surface with smile bf quotes...");
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);
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);
88 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_10p) - (atmQuotes[0] + bfQuotes[0][0] - 0.5 * rrQuotes[0][0]),
90 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_25p) - (atmQuotes[0] + bfQuotes[0][1] - 0.5 * rrQuotes[0][1]),
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]),
95 BOOST_CHECK_SMALL(vol1->blackVol(dates[0], k1_1_10c) - (atmQuotes[0] + bfQuotes[0][0] + 0.5 * rrQuotes[0][0]),
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);
107 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_10p) - (atmQuotes[1] + bfQuotes[1][0] - 0.5 * rrQuotes[1][0]),
109 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_25p) - (atmQuotes[1] + bfQuotes[1][1] - 0.5 * rrQuotes[1][1]),
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]),
114 BOOST_CHECK_SMALL(vol1->blackVol(dates[1], k1_2_10c) - (atmQuotes[1] + bfQuotes[1][0] + 0.5 * rrQuotes[1][0]),
120 BOOST_TEST_MESSAGE(
"Testing bf/rr vol surface with broker bf quotes...");
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);
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);
138 Real bfvol2_1_10_broker = atmQuotes[0] + bfQuotes[0][0];
139 Real bfvol2_1_25_broker = atmQuotes[0] + bfQuotes[0][1];
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]);
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]);
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]);
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]);
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));
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));
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);
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)));
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)));
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);
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);
196 Real bfvol2_2_10_broker = atmQuotes[1] + bfQuotes[1][0];
197 Real bfvol2_2_25_broker = atmQuotes[1] + bfQuotes[1][1];
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]);
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]);
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]);
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]);
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));
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));
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);
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)));
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)));
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);
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);
253BOOST_AUTO_TEST_SUITE_END()
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.