23#include <ql/currencies/europe.hpp>
24#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
25#include <ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp>
26#include <ql/option.hpp>
27#include <ql/pricingengines/blackformula.hpp>
28#include <ql/quotes/simplequote.hpp>
29#include <ql/termstructures/credit/flathazardrate.hpp>
30#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
31#include <ql/termstructures/yield/flatforward.hpp>
32#include <ql/time/calendars/nullcalendar.hpp>
33#include <ql/time/daycounters/actual365fixed.hpp>
34#include <ql/timegrid.hpp>
36#include <boost/test/unit_test.hpp>
43BOOST_AUTO_TEST_SUITE(DefaultableEquityJumpDiffusionModelTest)
49void runMcSimulation(
const QuantLib::ext::shared_ptr<DefaultableEquityJumpDiffusionModel>& model,
const Size nPaths,
50 const Size seed,
const Size timeSteps,
const std::vector<Real>& payoffTimes,
51 const std::vector<Real>& equityCallStrikes, std::vector<Real>& resultDefaultableBonds,
52 std::vector<Real>& resultEquityOptions) {
54 TimeGrid grid(payoffTimes.begin(), payoffTimes.end(), timeSteps);
56 std::vector<Size> payoffIndices;
57 for (
auto const& t : payoffTimes) {
58 payoffIndices.push_back(grid.index(t));
61 resultDefaultableBonds.clear();
62 resultEquityOptions.clear();
63 resultDefaultableBonds.resize(payoffTimes.size(), 0.0);
64 resultEquityOptions.resize(payoffTimes.size(), 0.0);
66 auto pathGen = QuantLib::ext::make_shared<SobolBrownianGenerator>(1, grid.size() - 1, SobolBrownianGenerator::Steps, seed,
68 MersenneTwisterUniformRng mt(seed);
69 std::vector<Real> out(1);
71 for (Size p = 0; p < nPaths; ++p) {
72 Real S = model->equity()->equitySpot()->value();
78 for (Size i = 1; i < grid.size(); ++i) {
80 pathGen->nextStep(out);
83 Real t0 = grid[i - 1];
85 Real r = model->r(t0);
86 Real q = model->q(t0);
87 Real h = model->h(t0, S);
88 Real sigma = model->sigma(t0);
89 z += (r - q + model->eta() * h - 0.5 * sigma * sigma) * (t1 - t0) + sigma * std::sqrt(t1 - t0) * out[0];
90 B *= std::exp(-r * (t1 - t0));
93 jump = jump || mt.nextReal() < h * (t1 - t0);
96 S = std::exp(z) * (jump ? (1.0 - model->eta()) : 1.0);
102 if (i == payoffIndices[payoffIndex]) {
104 resultDefaultableBonds[payoffIndex] += B /
static_cast<Real
>(nPaths);
106 resultEquityOptions[payoffIndex] +=
107 std::max(S - equityCallStrikes[payoffIndex], 0.0) * B /
static_cast<Real
>(nPaths);
118 BOOST_TEST_MESSAGE(
"Test defaultable equity jump diffusion model calibration with closed form bootstrap for p=0");
121 std::vector<Real> stepTimes = {1.0, 2.0, 3.0, 4.0, 5.0};
123 Handle<YieldTermStructure> rate(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.01, Actual365Fixed()));
124 Handle<YieldTermStructure> dividend(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
125 Handle<BlackVolTermStructure> vol(QuantLib::ext::make_shared<BlackConstantVol>(0, NullCalendar(), 0.3, Actual365Fixed()));
126 Handle<DefaultProbabilityTermStructure> creditCurve(
127 QuantLib::ext::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0050, Actual365Fixed()));
129 auto equity = QuantLib::ext::make_shared<EquityIndex2>(
"myEqIndex", NullCalendar(), EURCurrency(),
130 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(S0)), rate, dividend);
133 for (
auto const& t : stepTimes) {
134 Real forward = equity->equitySpot()->value() * equity->equityDividendCurve()->discount(t) /
135 equity->equityForecastCurve()->discount(t);
146 std::vector<Real> etas = {1.0, 0.9, 0.8};
148 for (
auto const& eta : etas) {
150 std::vector<Real> resultDefaultableBonds, resultEquityOptions;
152 auto modelBuilder = QuantLib::ext::make_shared<DefaultableEquityJumpDiffusionModelBuilder>(
153 stepTimes, equity, vol, creditCurve, 0.0, eta,
false, 24, 100, 1E-4, 1.5, Null<Real>(),
154 DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Simultaneously,
false);
155 auto model = *modelBuilder->model();
157 runMcSimulation(model, 100000, 121, 5 * 24, stepTimes,
strikes, resultDefaultableBonds, resultEquityOptions);
160 BOOST_TEST_MESSAGE(std::right << std::setw(5) <<
"p" << std::setw(5) <<
"eta" << std::setw(5) <<
"t"
161 << std::setw(16) <<
"h0" << std::setw(16) <<
"sigma" << std::setw(16) <<
"bond mc"
162 << std::setw(16) <<
"bond mkt" << std::setw(16) <<
"equityCall mc"
163 << std::setw(16) <<
"equityCall mkt" << std::setw(16) <<
"bond err %"
164 << std::setw(16) <<
"eqCall err %");
165 for (Size i = 0; i < stepTimes.size(); ++i) {
166 Real bondMarket = rate->discount(stepTimes[i]) * creditCurve->survivalProbability(stepTimes[i]);
167 Real eqOptionMarket =
169 std::sqrt(vol->blackVariance(stepTimes[i],
strikes[i])), rate->discount(stepTimes[i]));
170 BOOST_TEST_MESSAGE(std::right
171 << std::setw(5) << 0.0 << std::setw(5) << eta << std::setw(5) << stepTimes[i]
172 << std::setw(16) << model->h0()[i] << std::setw(16) << model->sigma()[i] << std::setw(16)
173 << resultDefaultableBonds[i] << std::setw(16) << bondMarket << std::setw(16)
174 << resultEquityOptions[i] << std::setw(16) << eqOptionMarket << std::setw(16)
175 << 100.0 * (resultDefaultableBonds[i] - bondMarket) / bondMarket << std::setw(16)
176 << 100.0 * (resultEquityOptions[i] - eqOptionMarket) / eqOptionMarket);
177 BOOST_CHECK_CLOSE(resultDefaultableBonds[i], bondMarket, tol);
178 BOOST_CHECK_CLOSE(resultEquityOptions[i], eqOptionMarket, tol);
180 BOOST_TEST_MESSAGE(
"done.");
186 BOOST_TEST_MESSAGE(
"Test defaultable equity jump diffusion model calibration with Fokker-Planck bootstrap");
189 std::vector<Real> stepTimes = {1.0, 2.0, 3.0, 4.0, 5.0};
191 Handle<YieldTermStructure> rate(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.01, Actual365Fixed()));
192 Handle<YieldTermStructure> dividend(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.02, Actual365Fixed()));
193 Handle<BlackVolTermStructure> vol(QuantLib::ext::make_shared<BlackConstantVol>(0, NullCalendar(), 0.3, Actual365Fixed()));
194 Handle<DefaultProbabilityTermStructure> creditCurve(
195 QuantLib::ext::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0050, Actual365Fixed()));
197 auto equity = QuantLib::ext::make_shared<EquityIndex2>(
"myEqIndex", NullCalendar(), EURCurrency(),
198 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(S0)), rate, dividend);
201 for (
auto const& t : stepTimes) {
202 Real forward = equity->equitySpot()->value() * equity->equityDividendCurve()->discount(t) /
203 equity->equityForecastCurve()->discount(t);
209 std::vector<DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode> modes = {
210 DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Alternating,
211 DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Simultaneously};
212 std::vector<Real> ps = {0.0, 0.5};
213 std::vector<Real> etas = {1.0, 0.9};
215 for (
auto const& mode : modes) {
217 "Bootstrap mode = " << (mode == DefaultableEquityJumpDiffusionModelBuilder::BootstrapMode::Alternating
219 :
"Simultaneously"));
220 for (
auto const& p : ps) {
221 for (
auto const& eta : etas) {
223 std::vector<Real> resultDefaultableBonds, resultEquityOptions;
224 auto modelBuilder = QuantLib::ext::make_shared<DefaultableEquityJumpDiffusionModelBuilder>(
225 stepTimes, equity, vol, creditCurve, p, eta,
false, 24, 400, 1E-5, 1.5, Null<Real>(), mode,
true);
226 auto model = *modelBuilder->model();
228 runMcSimulation(model, 100000, 121, 5 * 24, stepTimes,
strikes, resultDefaultableBonds,
229 resultEquityOptions);
232 BOOST_TEST_MESSAGE(std::right << std::setw(5) <<
"p" << std::setw(5) <<
"eta" << std::setw(5) <<
"t"
233 << std::setw(16) <<
"h0" << std::setw(16) <<
"sigma" << std::setw(16)
234 <<
"bond mc" << std::setw(16) <<
"bond mkt" << std::setw(16)
235 <<
"equityCall mc" << std::setw(16) <<
"equityCall mkt" << std::setw(16)
236 <<
"bond err %" << std::setw(16) <<
"eqCall err %");
237 for (Size i = 0; i < stepTimes.size(); ++i) {
238 Real bondMarket = rate->discount(stepTimes[i]) * creditCurve->survivalProbability(stepTimes[i]);
239 Real eqOptionMarket = blackFormula(Option::Call,
strikes[i],
strikes[i],
240 std::sqrt(vol->blackVariance(stepTimes[i],
strikes[i])),
241 rate->discount(stepTimes[i]));
242 BOOST_TEST_MESSAGE(std::right
243 << std::setw(5) << p << std::setw(5) << eta << std::setw(5) << stepTimes[i]
244 << std::setw(16) << model->h0()[i] << std::setw(16) << model->sigma()[i]
245 << std::setw(16) << resultDefaultableBonds[i] << std::setw(16) << bondMarket
246 << std::setw(16) << resultEquityOptions[i] << std::setw(16) << eqOptionMarket
247 << std::setw(16) << 100.0 * (resultDefaultableBonds[i] - bondMarket) / bondMarket
249 << 100.0 * (resultEquityOptions[i] - eqOptionMarket) / eqOptionMarket);
250 BOOST_CHECK_CLOSE(resultDefaultableBonds[i], bondMarket, tol);
251 BOOST_CHECK_CLOSE(resultEquityOptions[i], eqOptionMarket, tol);
253 BOOST_TEST_MESSAGE(
"done.");
259BOOST_AUTO_TEST_SUITE_END()
261BOOST_AUTO_TEST_SUITE_END()
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
BOOST_AUTO_TEST_CASE(test_zero_p)
Fixture that can be used at top level.