24#include <boost/make_shared.hpp>
25#include <boost/test/unit_test.hpp>
26#include <ql/math/matrix.hpp>
27#include <ql/quotes/simplequote.hpp>
28#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
29#include <ql/termstructures/yield/discountcurve.hpp>
30#include <ql/termstructures/yield/flatforward.hpp>
31#include <ql/time/calendars/target.hpp>
32#include <ql/time/daycounters/actualactual.hpp>
33#include <ql/utilities/dataparsers.hpp>
40using namespace boost::unit_test_framework;
55 vector<Volatility> rrs;
56 vector<Volatility> bfs;
58 Handle<Quote> baseSpot;
59 Handle<YieldTermStructure> baseDomesticYield;
60 Handle<YieldTermStructure> baseForeignYield;
64 today = Date(1, Jan, 2014);
65 dc = ActualActual(ActualActual::ISDA);
67 Settings::instance().evaluationDate() = today;
69 dates.push_back(Date(1, Feb, 2014));
70 dates.push_back(Date(1, Mar, 2014));
71 dates.push_back(Date(1, Apr, 2014));
72 dates.push_back(Date(1, Jan, 2015));
92 atmVols.push_back(0.1);
93 atmVols.push_back(0.2);
94 atmVols.push_back(0.3);
95 atmVols.push_back(0.4);
97 rrs = vector<Volatility>(atmVols.size(), 0.01);
98 bfs = vector<Volatility>(atmVols.size(), 0.001);
100 baseSpot = Handle<Quote>(QuantLib::ext::shared_ptr<Quote>(
new SimpleQuote(100)));
102 baseDomesticYield = Handle<YieldTermStructure>(
103 QuantLib::ext::make_shared<FlatForward>(today, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.03)), dc));
104 baseForeignYield = Handle<YieldTermStructure>(
105 QuantLib::ext::make_shared<FlatForward>(today, Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.01)), dc));
123BOOST_AUTO_TEST_SUITE(FxVolSmileTest)
127 BOOST_TEST_MESSAGE(
"Testing fx vanna volga smile");
129 SavedSettings backup;
134 Settings::instance().evaluationDate() = Date(1, July, 2005);
135 Time t = 94 / (double)365;
137 Volatility sig_atm = 0.0905;
138 Volatility sig_rr = -0.005;
139 Volatility sig_bf = 0.0013;
141 DiscountFactor df_usd = 0.9902752;
142 DiscountFactor df_eur = 0.9945049;
145 Real rd = -
::log(df_usd) / t;
146 Real rf = -
::log(df_eur) / t;
151 Real tolerance = 0.0001;
152 if (fabs(vvss.
k_atm() - 1.2114) > tolerance)
153 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculte ATM strike, got " << vvss.
k_atm());
154 if (fabs(vvss.
k_p() - 1.1733) > tolerance)
155 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculate 25P strike, got " << vvss.
k_p());
156 if (fabs(vvss.
k_c() - 1.2487) > tolerance)
157 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculate 25C strike, got " << vvss.
k_c());
158 if (fabs(vvss.
vol_atm() - 0.0905) > tolerance)
159 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculate ATM vol, got " << vvss.
vol_atm());
160 if (fabs(vvss.
vol_p() - 0.0943) > tolerance)
161 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculate 25P vol, got " << vvss.
vol_p());
162 if (fabs(vvss.
vol_c() - 0.0893) > tolerance)
163 BOOST_FAIL(
"VannaVolgaSmileSection failed to calculate 25C vol, got " << vvss.
vol_c());
167 BOOST_FAIL(
"VannaVolgaSmileSection failed to recover ATM vol, got " << vvss.
volatility(vvss.
k_atm()));
169 BOOST_FAIL(
"VannaVolgaSmileSection failed to recover 25P vol, got " << vvss.
volatility(vvss.
k_p()));
171 BOOST_FAIL(
"VannaVolgaSmileSection failed to recover 25C vol, got " << vvss.
volatility(vvss.
k_c()));
185 BOOST_TEST_MESSAGE(
"Testing fx vanna volga surface");
187 SavedSettings backup;
193 Date asof(12, Feb, 2004);
194 Settings::instance().evaluationDate() = asof;
196 Handle<Quote> fxSpot(QuantLib::ext::shared_ptr<Quote>(
new SimpleQuote(1.2832)));
200 VolData volData[] = { {
"1W", 11.75, 0.50, 0.190, 0.0192, 0.999804, 0.999606 },
201 {
"2W", 11.60, 0.50, 0.190, 0.0384, 0.999595, 0.999208 },
202 {
"1M", 11.50, 0.60, 0.190, 0.0877, 0.999044, 0.998179 },
203 {
"2M", 11.25, 0.60, 0.210, 0.1726, 0.998083, 0.996404 },
204 {
"3M", 11.00, 0.60, 0.220, 0.2493, 0.997187, 0.994803 },
205 {
"6M", 10.87, 0.65, 0.235, 0.5014, 0.993959, 0.989548 },
206 {
"9M", 10.83, 0.69, 0.235, 0.7589, 0.990101, 0.984040 },
207 {
"1Y", 10.80, 0.70, 0.240, 1.0110, 0.985469, 0.978479 },
208 {
"2Y", 10.70, 0.65, 0.255, 2.0110, 0.960102, 0.951092 } };
211 DayCounter dc = ActualActual(ActualActual::ISDA);
212 Calendar cal = TARGET();
215 Size len =
sizeof(volData) /
sizeof(volData[0]);
216 vector<Date> dates(len);
217 vector<Volatility> atm(len);
218 vector<Volatility> rr(len);
219 vector<Volatility> bf(len);
221 vector<Date> discountDates(len + 1);
222 vector<DiscountFactor> dfDom(len + 1);
223 vector<DiscountFactor> dfFor(len + 1);
224 discountDates[0] = asof;
228 for (Size i = 0; i <
sizeof(volData) /
sizeof(volData[0]); i++) {
229 dates[i] = asof + PeriodParser::parse(volData[i].tenor);
238 atm[i] = volData[i].atm / 100;
239 rr[i] = volData[i].rr / 100;
240 bf[i] = volData[i].bf / 100;
242 discountDates[i + 1] = dates[i];
243 dfDom[i + 1] = volData[i].df_d;
244 dfFor[i + 1] = volData[i].df_f;
248 Handle<YieldTermStructure> domYTS(
249 QuantLib::ext::shared_ptr<YieldTermStructure>(
new DiscountCurve(discountDates, dfDom, dc)));
250 Handle<YieldTermStructure> forYTS(
251 QuantLib::ext::shared_ptr<YieldTermStructure>(
new DiscountCurve(discountDates, dfFor, dc)));
257 Real vol = volSurface.blackVol(1.75, 1.55);
258 Real expected = 0.121507;
259 if (fabs(vol - expected) > 0.00001)
260 BOOST_FAIL(
"Failed to get expected vol from surface " << vol);
269 for (Size i = 0; i < len; i++) {
270 Real vol = volSurface.blackVol(dates[i], Null<Real>());
271 Real vol2 = volSurface.blackVol(dates[i], 0);
272 if (fabs(vol - atm[i]) > 0.00001)
273 BOOST_FAIL(
"Failed to get expected atm vol from surface: " << vol);
274 if (fabs(vol2 - atm[i]) > 0.00001)
275 BOOST_FAIL(
"Failed to get expected atm vol from surface: " << vol);
281 BOOST_TEST_MESSAGE(
"Testing inverted vol term structure");
283 SavedSettings backup;
287 Handle<BlackVolTermStructure> surface(QuantLib::ext::shared_ptr<BlackVolTermStructure>(
288 new BlackVarianceSurface(vars.today, TARGET(), vars.dates, vars.strikes, vars.vols, vars.dc)));
292 if (surface->maxDate() != bivt.
maxDate())
293 BOOST_FAIL(
"inverted maxDate() vol surface does not match base");
296 BOOST_FAIL(
"inverted referenceDate() vol surface does not match base");
300 double testCases[][2] = { { 0.1, 104 }, { 0.5, 90 }, { 0.6, 110 }, { 0.9, 90 },
301 { 0.9, 95 }, { 0.9, 100 }, { 0.9, 105 }, { 0.9, 110 } };
303 for (Size i = 0; i <
sizeof(testCases) /
sizeof(testCases[0]); i++) {
304 Time t = testCases[i][0];
305 Real k = testCases[i][1];
307 Real vol1 = surface->blackVol(t, k);
309 Real invertedStrike = 1.0 / k;
310 Real vol2 = bivt.blackVol(t, invertedStrike);
311 if (fabs(vol1 - vol2) > 0.00001)
312 BOOST_FAIL(
"Failed to get expected vol (" << vol1 <<
") from inverted vol surface, got (" << vol2 <<
")");
316BOOST_AUTO_TEST_SUITE_END()
318BOOST_AUTO_TEST_SUITE_END()
Black volatility surface that inverts an existing surface.
Black volatility surface that inverts an existing surface.
const Date & referenceDate() const override
Date maxDate() const override
Fx Black vanna volga volatility surface.
Volatility vol_atm() const
Volatility volatility(Real strike) const override
Real k_atm() const
getters for unit test
FX Black volatility surface that incorporates an FxSmile.
FX smile section assuming a strike/volatility space using vanna volga method.
BOOST_AUTO_TEST_CASE(testVannaVolgaFxSmileSection)
CompiledFormula log(CompiledFormula x)
Fixture that can be used at top level.