Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bondspreadimply.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
21
29
30#include <ql/instruments/bond.hpp>
31#include <ql/pricingengines/bond/bondfunctions.hpp>
32
33#include <regex>
34
35namespace ore {
36namespace data {
37
38std::map<std::string, QuantLib::ext::shared_ptr<Security>>
39BondSpreadImply::requiredSecurities(const Date& asof, const QuantLib::ext::shared_ptr<TodaysMarketParameters>& params,
40 const QuantLib::ext::shared_ptr<CurveConfigurations>& curveConfigs, const Loader& loader,
41 const bool continueOnError, const std::string& excludeRegex) {
42 std::regex excludePattern;
43 if (!excludeRegex.empty())
44 excludePattern = std::regex(excludeRegex);
45
46 std::map<std::string, QuantLib::ext::shared_ptr<Security>> securities;
47 for (const auto& configuration : params->configurations()) {
48 LOG("identify securities that require a spread imply for configuration " << configuration.first);
49
50 /* Loop over the security curve specs, do the spread imply where we have a price, but no spread
51 and and store the calculated spread in the MarketImpl container */
52 for (const auto& it : params->curveSpecs(configuration.first)) {
53 auto securityspec = QuantLib::ext::dynamic_pointer_cast<SecuritySpec>(parseCurveSpec(it));
54 if (securityspec) {
55 std::string securityId = securityspec->securityID();
56 if (std::regex_match(securityId, excludePattern)) {
57 DLOG("skip " << securityId << " because it matches the exclude regex (" << excludeRegex << ")");
58 continue;
59 }
60 if (curveConfigs->hasSecurityConfig(securityId)) {
61 if (curveConfigs->securityConfig(securityId)->spreadQuote().empty()) {
62 DLOG("no spread quote configuraed, skip security " << securityId);
63 continue;
64 }
65 QuantLib::ext::shared_ptr<Security> security;
66 try {
67 security = QuantLib::ext::make_shared<Security>(asof, *securityspec, loader, *curveConfigs);
68 } catch (const std::exception& e) {
69 if (continueOnError) {
70 StructuredCurveErrorMessage(securityId, "Bond spread imply failed",
71 "Will continue the calculations with a zero security spread: " +
72 std::string(e.what()))
73 .log();
74 continue;
75 } else {
76 QL_FAIL("Cannot process security " << securityId << " " << e.what());
77 }
78 }
79 if (security->spread().empty()) {
80 if (!security->price().empty()) {
81 LOG("empty spread, non-empty price: will imply spread for security " << securityId);
82 securities[securityId] = security;
83 } else {
84 DLOG("empty spread, empty price: spread will be left empty for security " << securityId);
85 StructuredCurveErrorMessage("Security/" + securityId, "Missing security spread",
86 "No security spread or bond price to imply the spread is "
87 "given. Will proceed assuming a zero spread.")
88 .log();
89 }
90 } else {
91 if (!security->price().empty()) {
92 WLOG("non-empty spread, non-empty price, will not overwrite existing spread for security "
93 << securityId);
94 } else {
95 DLOG("non-empty spread, empty price, do nothing for security " << securityId);
96 }
97 }
98 } else {
99 WLOG("do not have security curve config for '" << securityId
100 << "' - skip this security in spread imply");
101 }
102 }
103 }
104 }
105 LOG("got " << securities.size() << " securities");
106 return securities;
107}
108
109QuantLib::ext::shared_ptr<Loader>
110BondSpreadImply::implyBondSpreads(const std::map<std::string, QuantLib::ext::shared_ptr<Security>>& securities,
111 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager,
112 const QuantLib::ext::shared_ptr<Market>& market,
113 const QuantLib::ext::shared_ptr<EngineData>& engineData,
114 const std::string& configuration, const IborFallbackConfig& iborFallbackConfig) {
115
116 LOG("run bond spread imply");
117
118 Settings::instance().evaluationDate() = market->asofDate();
119
120 // build engine factory against which we build the bonds
121 map<MarketContext, string> configurations;
122 configurations[MarketContext::pricing] = configuration;
123 auto spreadImplyMarket = QuantLib::ext::make_shared<BondSpreadImplyMarket>(market);
124 auto edCopy = QuantLib::ext::make_shared<EngineData>(*engineData);
125 edCopy->globalParameters()["RunType"] = "BondSpreadImply";
126 auto engineFactory = QuantLib::ext::make_shared<EngineFactory>(edCopy, spreadImplyMarket, configurations,
127 referenceDataManager, iborFallbackConfig);
128
129 // imply spreads and store the generate market data
130
131 std::map<std::string, QuantLib::ext::shared_ptr<MarketDatum>> generatedSpreads;
132 for (auto const& sec : securities) {
133 auto storedSpread = generatedSpreads.find(sec.first);
134 if (storedSpread == generatedSpreads.end()) {
135 try {
136 auto impliedSpread = QuantLib::ext::make_shared<SecuritySpreadQuote>(
137 implySpread(sec.first, sec.second->price()->value(), referenceDataManager, engineFactory,
138 spreadImplyMarket->spreadQuote(sec.first), configuration),
139 market->asofDate(), "BOND/YIELD_SPREAD/" + sec.first, sec.first);
140 generatedSpreads[sec.first] = impliedSpread;
141 LOG("spread imply succeded for security " << sec.first << ", got " << std::setprecision(10)
142 << impliedSpread->quote()->value());
143 } catch (const std::exception& e) {
145 sec.first,
146 "bond spread imply failed (target price = " + std::to_string(sec.second->price()->value()) +
147 "). Will continue the calculations with a zero security spread.",
148 e.what()).log();
149 }
150 }
151 }
152
153 // add generated market data to loader and return the loader
154
155 auto loader = QuantLib::ext::make_shared<InMemoryLoader>();
156
157 for (auto const& s : generatedSpreads) {
158 DLOG("adding market datum " << s.second->name() << " (" << s.second->quote()->value() << ") for asof "
159 << market->asofDate() << " to loader");
160 loader->add(market->asofDate(), s.second->name(), s.second->quote()->value());
161 }
162
163 LOG("bond spread imply finished.");
164 return loader;
165}
166
167Real BondSpreadImply::implySpread(const std::string& securityId, const Real cleanPrice,
168 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager,
169 const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory,
170 const QuantLib::ext::shared_ptr<SimpleQuote>& spreadQuote, const std::string& configuration) {
171
172 // checks, build bond from reference data
173
174 QL_REQUIRE(referenceDataManager, "no reference data manager given");
175
176 auto b = BondFactory::instance().build(engineFactory, referenceDataManager, securityId);
177 Real adj = b.priceQuoteMethod == QuantExt::BondIndex::PriceQuoteMethod::CurrencyPerUnit
178 ? 1.0 / b.priceQuoteBaseValue
179 : 1.0;
180
181 Real inflationFactor = b.inflationFactor();
182
183 DLOG("implySpread for securityId " << securityId << ":");
184 DLOG("settlement date = " << QuantLib::io::iso_date(b.bond->settlementDate()));
185 DLOG("market quote = " << cleanPrice);
186 DLOG("accrueds = " << b.bond->accruedAmount());
187 DLOG("inflation factor = " << inflationFactor);
188 DLOG("price quote method adj = " << adj);
189 DLOG("effective market price = " << cleanPrice * inflationFactor * adj);
190
191 auto targetFunction = [&b, spreadQuote, cleanPrice, adj, inflationFactor](const Real& s) {
192 spreadQuote->setValue(s);
193 if (b.modelBuilder != nullptr)
194 b.modelBuilder->recalibrate();
195 Real c = b.bond->cleanPrice() / 100.0;
196 TLOG("--> spread imply: trying s = " << s << " yields clean price " << c);
197 return c - cleanPrice * inflationFactor * adj;
198 };
199
200 // edge case: bond has a zero settlement value -> skip spread imply
201
202 if (QuantLib::close_enough(b.bond->cleanPrice(), 0.0)) {
203 DLOG("bond has a theoretical clean price of zero (no outstanding flows as of settlement date) -> skip spread "
204 "imply and continue with zero security spread.");
205 return 0.0;
206 }
207
208 // solve for spread and return result
209
210 Brent brent;
211 Real s = brent.solve(targetFunction, 1E-8, 0.0, 0.001);
212
213 DLOG("theoretical pricing = " << b.bond->cleanPrice() / 100.0);
214 return s;
215}
216
217} // namespace data
218} // namespace ore
Bond trade data model and serialization.
bond spread imply utility
market that can be used to imply bond spreads
bond utilities
static QuantLib::ext::shared_ptr< Loader > implyBondSpreads(const std::map< std::string, QuantLib::ext::shared_ptr< Security > > &securities, const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager, const QuantLib::ext::shared_ptr< Market > &market, const QuantLib::ext::shared_ptr< EngineData > &engineData, const std::string &configuration, const IborFallbackConfig &iborFallbackConfig)
static Real implySpread(const std::string &securityId, const Real cleanPrice, const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager, const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory, const QuantLib::ext::shared_ptr< SimpleQuote > &spreadQuote, const std::string &configuration)
helper function that computes a single implied spread for a bond
static std::map< std::string, QuantLib::ext::shared_ptr< Security > > requiredSecurities(const Date &asof, const QuantLib::ext::shared_ptr< TodaysMarketParameters > &params, const QuantLib::ext::shared_ptr< CurveConfigurations > &curveConfigs, const Loader &loader, const bool continueOnError=false, const std::string &excludeRegex=std::string())
void log() const
generate Boost log record to pass to corresponding sinks
Definition: log.cpp:491
Market data loader base class.
Definition: loader.hpp:47
Utility class for Structured Curve errors, contains the curve ID.
CurveSpec parser.
QuantLib::ext::shared_ptr< CurveSpec > parseCurveSpec(const string &s)
function to convert a string into a curve spec
@ data
Definition: log.hpp:77
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Reference data model and serialization.
A wrapper class for holding Bond Spread quotes.
Error for market data or curve.
vector< string > curveConfigs