19#include <boost/make_shared.hpp>
21#include <boost/test/unit_test.hpp>
22#include <boost/test/data/test_case.hpp>
37#include <oret/datapaths.hpp>
38#include <oret/toplevelfixture.hpp>
39#include <ql/termstructures/credit/flathazardrate.hpp>
40#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
41#include <ql/termstructures/yield/flatforward.hpp>
42#include <ql/time/calendars/target.hpp>
43#include <ql/time/daycounters/actualactual.hpp>
44#include <ql/time/daycounters/simpledaycounter.hpp>
47using namespace boost::unit_test_framework;
50using ore::test::TopLevelFixture;
58 TestMarket(Real hazardRate, Real recoveryRate, Real liborRate) :
MarketImpl(false) {
59 asof_ = Date(3, Feb, 2016);
62 flatRateYts(liborRate);
65 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(recoveryRate));
73 Handle<YieldTermStructure> flatRateYts(Real forward) {
74 QuantLib::ext::shared_ptr<YieldTermStructure> yts(
new FlatForward(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
75 yts->enableExtrapolation();
76 return Handle<YieldTermStructure>(yts);
78 Handle<QuantExt::CreditCurve> flatRateDcs(Volatility forward) {
79 QuantLib::ext::shared_ptr<DefaultProbabilityTermStructure> dcs(
80 new FlatHazardRate(asof_, forward, ActualActual(ActualActual::ISDA)));
81 return Handle<QuantExt::CreditCurve>(
82 QuantLib::ext::make_shared<QuantExt::CreditCurve>(Handle<DefaultProbabilityTermStructure>(dcs)));
104 vector<Real> notionals;
108 QuantLib::ext::shared_ptr<ore::data::CreditDefaultSwap> makeCDS(
string end, Real rate) {
112 QuantLib::ext::shared_ptr<FixedLegData> fixedLegRateData = QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, rate));
113 LegData fixedLegData(fixedLegRateData, isPayer, ccy, fixedSchedule, fixDC, notionals);
116 QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault);
124 : ccy(
"EUR"), creditCurveId(
"CreditCurve_A"), issuerId(
"CPTY_A"), isPayer(false), start(
"20160203"),
125 issue(
"20160203"), fixtenor(
"1Y") {
134 notionals.push_back(1);
135 spread.push_back(0.0);
146ostream&
operator<<(ostream& os,
const MarketInputs& m) {
147 return os <<
"[" << m.hazardRate <<
"," << m.recoveryRate <<
"," << m.liborRate <<
"]";
156ostream&
operator<<(ostream& os,
const TradeInputs& t) {
return os <<
"[" << t.endDate <<
"," << t.fixedRate <<
"]"; }
158Real cdsNpv(
const MarketInputs& m,
const TradeInputs& t) {
161 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(m.hazardRate, m.recoveryRate, m.liborRate);
162 Settings::instance().evaluationDate() = market->asofDate();
165 QuantLib::ext::shared_ptr<ore::data::CreditDefaultSwap> cds = vars.makeCDS(t.endDate, t.fixedRate);
168 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
169 engineData->model(
"CreditDefaultSwap") =
"DiscountedCashflows";
170 engineData->engine(
"CreditDefaultSwap") =
"MidPointCdsEngine";
172 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
174 cds->build(engineFactory);
176 return cds->instrument()->NPV();
186MarketInputs marketInputs[] = {{0, 1, 0}, {1, 1, 0}, {0.02, 1, 0.05}, {0.02, 0.4, 0.05}};
189TradeInputs tradeInputs[] = {{
"20170203", 0}, {
"20170203", 0}, {
"20210203", 0.0124248849209095}, {
"20210203", 0.0}};
192Real expNpvs[] = {0, 0, 0.050659, -0.05062};
195vector<string> trades = {
"cds_minimal_with_rules",
"cds_minimal_with_dates"};
197struct TodaysMarketFiles {
198 string todaysMarket =
"todaysmarket.xml";
199 string conventions =
"conventions.xml";
200 string curveConfig =
"curveconfig.xml";
201 string market =
"market.txt";
202 string fixings =
"fixings.txt";
206QuantLib::ext::shared_ptr<TodaysMarket> createTodaysMarket(
const Date& asof,
const string& inputDir,
207 const TodaysMarketFiles& tmf) {
209 auto conventions = QuantLib::ext::make_shared<Conventions>();
210 conventions->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/" + tmf.conventions)));
211 InstrumentConventions::instance().setConventions(conventions);
213 auto curveConfigs = QuantLib::ext::make_shared<CurveConfigurations>();
214 curveConfigs->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/" + tmf.curveConfig)));
216 auto todaysMarketParameters = QuantLib::ext::make_shared<TodaysMarketParameters>();
217 todaysMarketParameters->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/" + tmf.todaysMarket)));
219 auto loader = QuantLib::ext::make_shared<CSVLoader>(TEST_INPUT_FILE(
string(inputDir +
"/" + tmf.market)),
220 TEST_INPUT_FILE(
string(inputDir +
"/" + tmf.fixings)),
false);
222 return QuantLib::ext::make_shared<TodaysMarket>(asof, todaysMarketParameters, loader,
curveConfigs);
227BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
229BOOST_AUTO_TEST_SUITE(CreditDefaultSwapTests)
234 BOOST_CHECK_CLOSE(cdsNpv(mkt, trd),
exp, 0.01);
239 BOOST_TEST_MESSAGE(
"Test the building of various CDS trades from XML");
241 Settings::instance().evaluationDate() = Date(3, Feb, 2016);
245 string portfolioFile =
"trades/" + trade +
".xml";
246 p.
fromFile(TEST_INPUT_FILE(portfolioFile));
247 BOOST_REQUIRE_MESSAGE(p.
size() == 1,
"Expected portfolio to contain a single trade");
250 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(0.02, 0.4, 0.05);
253 QuantLib::ext::shared_ptr<EngineData> ed = QuantLib::ext::make_shared<EngineData>();
254 ed->model(
"CreditDefaultSwap") =
"DiscountedCashflows";
255 ed->engine(
"CreditDefaultSwap") =
"MidPointCdsEngine";
258 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(ed, market);
259 BOOST_CHECK_NO_THROW(p.
build(engineFactory));
261 BOOST_CHECK_NO_THROW(npv = p.
trades().begin()->second->instrument()->NPV());
262 BOOST_TEST_MESSAGE(
"CDS NPV is: " << npv);
275UpfrontFiles
upfrontFiles[] = {{
"market.txt",
"curveconfig.xml"}, {
"market_rs.txt",
"curveconfig_rs.xml"}};
282 BOOST_TEST_MESSAGE(
"Testing upfront default curve consistency ...");
284 Date asof(6, Nov, 2020);
285 Settings::instance().evaluationDate() = asof;
286 string dir(
"upfront");
289 TodaysMarketFiles tmf;
290 tmf.market = files.market;
291 tmf.curveConfig = files.curveConfig;
293 QuantLib::ext::shared_ptr<TodaysMarket> tm;
294 BOOST_REQUIRE_NO_THROW(tm = createTodaysMarket(asof, dir, tmf));
296 QuantLib::ext::shared_ptr<EngineData>
data = QuantLib::ext::make_shared<EngineData>();
297 data->fromFile(TEST_INPUT_FILE(
string(dir +
"/pricingengine.xml")));
298 QuantLib::ext::shared_ptr<EngineFactory> ef = QuantLib::ext::make_shared<EngineFactory>(
data, tm);
301 portfolio.
fromFile(TEST_INPUT_FILE(
string(dir +
"/portfolio.xml")));
304 for (
const auto& [tradeId, trade] : portfolio.
trades()) {
305 auto npv = trade->instrument()->NPV();
306 BOOST_TEST_CONTEXT(
"NPV of trade " << tradeId <<
" is " << fixed << setprecision(12) << npv) {
307 BOOST_CHECK_SMALL(trade->instrument()->NPV(), tol);
314 BOOST_TEST_MESSAGE(
"Testing upfront failure when no running spread ...");
316 TodaysMarketFiles tmf;
317 tmf.curveConfig =
"curveconfig_no_rs.xml";
319 Date asof(6, Nov, 2020);
320 Settings::instance().evaluationDate() = Date(6, Nov, 2020);
321 BOOST_CHECK_THROW(createTodaysMarket(asof,
"upfront", tmf), QuantLib::Error);
326 BOOST_TEST_MESSAGE(
"Testing different CDS quote types can be used together ...");
328 TodaysMarketFiles tmf;
329 tmf.todaysMarket =
"todaysmarket_all_cds_quote_types.xml";
331 Date asof(6, Nov, 2020);
332 Settings::instance().evaluationDate() = Date(6, Nov, 2020);
335 QuantLib::ext::shared_ptr<TodaysMarket> tm;
336 BOOST_CHECK_NO_THROW(tm = createTodaysMarket(asof,
"upfront", tmf));
339 vector<string> curveNames = {
340 "RED:8B69AP|SNRFOR|USD|CR-UPFRONT",
341 "RED:8B69AP|SNRFOR|USD|CR-PAR_SPREAD",
342 "RED:8B69AP|SNRFOR|USD|CR-CONV_SPREAD",
345 for (
const string& curveName : curveNames) {
346 BOOST_TEST_CONTEXT(
"Checking default curve " << curveName) {
347 Handle<DefaultProbabilityTermStructure> dpts;
348 BOOST_CHECK_NO_THROW(dpts = tm->defaultCurve(curveName)->curve());
349 BOOST_CHECK_NO_THROW(dpts->survivalProbability(1.0));
354BOOST_AUTO_TEST_SUITE_END()
356BOOST_AUTO_TEST_SUITE_END()
Builder that returns an engine to price a credit default swap.
BOOST_AUTO_TEST_CASE(testUpfrontCurveBuildFailsIfNoRunningSpread)
ostream & operator<<(ostream &os, const UpfrontFiles &upfrontFiles)
UpfrontFiles upfrontFiles[]
BOOST_DATA_TEST_CASE_F(TopLevelFixture, testCreditDefaultSwapBuilding, bdata::make(trades), trade)
BOOST_DATA_TEST_CASE(testCreditDefaultSwap, bdata::make(marketInputs) ^ bdata::make(tradeInputs) ^ bdata::make(expNpvs), mkt, trd, exp)
Serializable object holding generic trade data, reporting dimensions.
Serializable object holding leg data.
static const string defaultConfiguration
Default configuration label.
const std::map< std::string, QuantLib::ext::shared_ptr< Trade > > & trades() const
Return the map tradeId -> trade.
QuantLib::Size size() const
Portfolio size.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &, const std::string &context="unspecified", const bool emitStructuredError=true)
Call build on all trades in the portfolio, the context is included in error messages.
Serializable schedule data.
Serializable object holding schedule Rules data.
void fromFile(const std::string &filename)
Ibor cap, floor or collar trade data model and serialization.
Market Datum Loader Implementation.
Curve configuration repository.
A class to hold pricing engine parameters.
trade envelope data model and serialization
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Map text representations to QuantLib/QuantExt types.
leg data model and serialization
Market Datum Loader Interface.
An implementation of the Market class that stores the required objects in maps.
RandomVariable exp(RandomVariable x)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
trade schedule data model and serialization
vector< string > curveConfigs
An concrete implementation of the Market class that loads todays market and builds the required curve...