20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
32#include <oret/toplevelfixture.hpp>
39#include <ql/exercise.hpp>
40#include <ql/time/daycounters/actualactual.hpp>
41#include <ql/quotes/simplequote.hpp>
42#include <ql/instruments/vanillaoption.hpp>
43#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
44#include <ql/processes/stochasticprocessarray.hpp>
45#include <ql/termstructures/yield/flatforward.hpp>
46#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
47#include <ql/time/calendars/nullcalendar.hpp>
48#include <ql/pricingengines/blackformula.hpp>
49#include <ql/termstructures/volatility/sabr.hpp>
57BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
59BOOST_AUTO_TEST_SUITE(LocalVolTest)
62void testCalibrationInstrumentRepricing(
const std::vector<Date>& expiries,
const std::vector<Real>& moneyness,
63 const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
64 const Size timeStepsPerYear,
const Size paths,
const Real tol) {
69 std::set<Date> simDates(expiries.begin(), expiries.end());
71 LocalVolModelBuilder builder(process->riskFreeRate(), process, simDates, std::set<Date>(), timeStepsPerYear,
72 LocalVolModelBuilder::Type::AndreasenHuge, moneyness,
false);
76 auto localVol = QuantLib::ext::make_shared<LocalVol>(paths,
"EUR", process->riskFreeRate(),
"EQ-DUMMY",
"EUR",
77 builder.model(), mcParams, simDates);
83 auto marketEngine = QuantLib::ext::make_shared<AnalyticEuropeanEngine>(process);
86 auto context = QuantLib::ext::make_shared<Context>();
88 context->scalars[
"Underlying"] =
IndexVec{paths,
"EQ-DUMMY"};
89 context->scalars[
"PayCcy"] =
CurrencyVec{paths,
"EUR"};
93 ScriptParser(
"Option = PAY( max( PutCall * (Underlying(Expiry)-Strike), 0), Expiry, Expiry, PayCcy );").ast(),
97 for (Size i = 0; i < expiries.size(); ++i) {
99 Real atmStrike = process->x0() / process->riskFreeRate()->discount(expiries[i]) *
100 process->dividendYield()->discount(expiries[i]);
102 for (Size j = 0; j < moneyness.size(); ++j) {
105 Real t = process->riskFreeRate()->timeFromReference(expiries[i]);
106 if (std::fabs(moneyness[j]) > 3.72)
110 Option::Type optionType = moneyness[j] > 1.0 ? Option::Call : Option::Put;
112 atmStrike * std::exp(process->blackVolatility()->blackVol(t, atmStrike) * std::sqrt(t) * moneyness[j]);
113 VanillaOption option(QuantLib::ext::make_shared<PlainVanillaPayoff>(optionType, strike),
114 QuantLib::ext::make_shared<EuropeanExercise>(expiries[i]));
115 option.setPricingEngine(marketEngine);
116 Real marketPrice = option.NPV();
119 context->scalars[
"PutCall"] =
RandomVariable(paths, optionType == Option::Call ? 1.0 : -1.0);
120 context->scalars[
"Expiry"] =
EventVec{paths, expiries[i]};
123 Real scriptPrice =
expectation(QuantLib::ext::get<RandomVariable>(context->scalars[
"Option"])).
at(0);
125 BOOST_TEST_MESSAGE(
"expiry=" << QuantLib::io::iso_date(expiries[i]) <<
" moneyness=" << moneyness[j]
126 <<
" marketVol = " << process->blackVolatility()->blackVol(t, strike,
true)
127 <<
" marketPrice=" << marketPrice <<
" mcPrice=" << scriptPrice
128 <<
" error=" << (scriptPrice - marketPrice));
129 BOOST_CHECK_SMALL(scriptPrice - marketPrice, tol);
130 maxError = std::max(maxError, scriptPrice - marketPrice);
133 BOOST_TEST_MESSAGE(
"max error = " << maxError);
138 BOOST_TEST_MESSAGE(
"Testing LocalVol with flat input vols...");
140 Date ref(7, May, 2019);
141 Settings::instance().evaluationDate() = ref;
143 std::vector<Date> expiries{ref + 1 * Months, ref + 3 * Months, ref + 6 * Months, ref + 9 * Months,
144 ref + 1 * Years, ref + 2 * Years, ref + 3 * Years, ref + 4 * Years,
145 ref + 5 * Years, ref + 7 * Years, ref + 10 * Years};
147 std::vector<Real> moneyness{-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0};
149 Handle<YieldTermStructure> r(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
150 Handle<YieldTermStructure> q(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.03, Actual365Fixed()));
151 Handle<BlackVolTermStructure> vol(QuantLib::ext::make_shared<BlackConstantVol>(0, NullCalendar(), 0.10, Actual365Fixed()));
152 Handle<Quote> spot(QuantLib::ext::make_shared<SimpleQuote>(100.0));
154 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(spot, q, r, vol);
156 testCalibrationInstrumentRepricing(expiries, moneyness, process, 20, 10000, 0.30);
160 BOOST_TEST_MESSAGE(
"Testing LocalVol with sabr input vols...");
162 Date ref(7, May, 2019);
163 Settings::instance().evaluationDate() = ref;
165 std::vector<Date> expiries{ref + 1 * Months, ref + 3 * Months, ref + 6 * Months, ref + 9 * Months,
166 ref + 1 * Years, ref + 2 * Years, ref + 3 * Years, ref + 4 * Years,
167 ref + 5 * Years, ref + 7 * Years, ref + 10 * Years};
169 std::vector<Real> moneyness{-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0};
171 Handle<YieldTermStructure> r(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
172 Handle<YieldTermStructure> q(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.03, Actual365Fixed()));
174 class SabrTestSurface :
public BlackVolatilityTermStructure {
176 SabrTestSurface(
const Handle<Quote>& spot,
const Handle<YieldTermStructure>& r,
177 const Handle<YieldTermStructure>& q)
178 : BlackVolatilityTermStructure(0, NullCalendar(), Following, ActualActual(ActualActual::ISDA)), spot_(spot), r_(r), q_(q) {}
179 Date maxDate()
const override {
return Date::maxDate(); }
180 Real minStrike()
const override {
return 0.0; }
181 Real maxStrike()
const override {
return QL_MAX_REAL; }
184 Real blackVolImpl(Time
maturity, Real strike)
const override {
185 Real forward = spot_->value() / r_->discount(
maturity) * q_->discount(
maturity);
186 Real w2 = std::min(
maturity, 10.0) / 10.0, w1 = 1.0 - w2;
187 Real alpha = 0.17 * w1 + 0.10 * w2;
189 Real nu = 0.3 * w1 + 0.05 * w2;
191 return sabrVolatility(strike, forward,
maturity, alpha, beta, nu, rho);
194 Handle<YieldTermStructure> r_, q_;
197 Handle<Quote> spot(QuantLib::ext::make_shared<SimpleQuote>(100.0));
198 Handle<BlackVolTermStructure> vol(QuantLib::ext::make_shared<SabrTestSurface>(spot, r, q));
200 auto process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(spot, q, r, vol);
202 testCalibrationInstrumentRepricing(expiries, moneyness, process, 20, 10000, 0.30);
205BOOST_AUTO_TEST_SUITE_END()
207BOOST_AUTO_TEST_SUITE_END()
black scholes model for n underlyings (fx, equity or commodity)
dummy model implementation
local vol model for n underlyings (fx, equity or commodity)
builder for an array of local vol processes
RandomVariable expectation(const RandomVariable &r)
Real at(const Size i) const
BOOST_AUTO_TEST_CASE(testFlatVols)