Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
parconversionanalytic.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2023 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
28
29using namespace ore::data;
30using namespace boost::filesystem;
31
32namespace ore {
33namespace analytics {
34
35std::map<RiskFactorKey, std::string> getScenarioDescriptions(QuantLib::ext::shared_ptr<ScenarioGenerator> scenGen) {
36 std::map<RiskFactorKey, std::string> descriptions;
37 auto sensiScenGen = QuantLib::ext::dynamic_pointer_cast<SensitivityScenarioGenerator>(scenGen);
38 if (sensiScenGen) {
39 for (const auto& desc : sensiScenGen->scenarioDescriptions()) {
40 descriptions[desc.key1()] = desc.indexDesc1();
41 }
42 }
43 return descriptions;
44}
45
46
48 analytic()->configurations().todaysMarketParams = inputs_->todaysMarketParams();
49 analytic()->configurations().simMarketParams = inputs_->parConversionSimMarketParams();
50 analytic()->configurations().sensiScenarioData = inputs_->parConversionScenarioData();
51 analytic()->configurations().engineData = inputs_->parConversionPricingEngine();
52}
53
54void ParConversionAnalyticImpl::runAnalytic(const QuantLib::ext::shared_ptr<ore::data::InMemoryLoader>& loader,
55 const std::set<std::string>& runTypes) {
56 if (!analytic()->match(runTypes))
57 return;
58
59 LOG("ParConversionAnalytic::runAnalytic called");
60
61 analytic()->buildMarket(loader, false);
62
63 auto parConversionAnalytic = static_cast<ParConversionAnalytic*>(analytic());
64
65 QL_REQUIRE(parConversionAnalytic, "ParConversionAnalyticImpl internal error, can not convert analytic() to ParConversionAnalytic");
66
67 auto zeroSensis = parConversionAnalytic->loadZeroSensitivities();
68
69 if (!zeroSensis.empty()) {
70 set<RiskFactorKey::KeyType> typesDisabled{RiskFactorKey::KeyType::OptionletVolatility};
71
72 auto parAnalysis = QuantLib::ext::make_shared<ParSensitivityAnalysis>(
73 inputs_->asof(), analytic()->configurations().simMarketParams,
74 *analytic()->configurations().sensiScenarioData, Market::defaultConfiguration, true, typesDisabled);
75
76 if (inputs_->parConversionAlignPillars()) {
77 LOG("Sensi analysis - align pillars (for the par conversion or because alignPillars is enabled)");
78 parAnalysis->alignPillars();
79 } else {
80 LOG("Sensi analysis - skip aligning pillars");
81 }
82
83 auto& configs = analytic()->configurations();
84
85 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(
86 analytic()->market(), configs.simMarketParams, inputs_->marketConfig("pricing"),
87 configs.curveConfig ? *configs.curveConfig : ore::data::CurveConfigurations(),
88 configs.todaysMarketParams ? *configs.todaysMarketParams : ore::data::TodaysMarketParameters(), true,
89 configs.sensiScenarioData->useSpreadedTermStructures(), false, false, *inputs_->iborFallbackConfig());
90
91 auto scenarioGenerator = QuantLib::ext::make_shared<SensitivityScenarioGenerator>(
92 configs.sensiScenarioData, simMarket->baseScenario(), configs.simMarketParams, simMarket,
93 QuantLib::ext::make_shared<DeltaScenarioFactory>(simMarket->baseScenario()), true, std::string(), true,
94 simMarket->baseScenarioAbsolute());
95
96 simMarket->scenarioGenerator() = scenarioGenerator;
97
98 parAnalysis->computeParInstrumentSensitivities(simMarket);
99
100 QuantLib::ext::shared_ptr<ParSensitivityConverter> parConverter =
101 QuantLib::ext::make_shared<ParSensitivityConverter>(parAnalysis->parSensitivities(), parAnalysis->shiftSizes());
102
103 map<RiskFactorKey, Size> factorToIndex;
104
105 auto shiftSizes = parAnalysis->shiftSizes();
106
107 Size counter = 0;
108 for (auto const& k : parConverter->rawKeys()) {
109 factorToIndex[k] = counter++;
110 }
111
112 std::vector<SensitivityRecord> results;
113 std::map<RiskFactorKey, std::string> descriptions = getScenarioDescriptions(simMarket->scenarioGenerator());
114
115 for (const auto& [id, sensis] : zeroSensis) {
116 boost::numeric::ublas::vector<Real> zeroDeltas(parConverter->rawKeys().size(), 0.0);
117 std::vector<SensitivityRecord> excludedDeltas;
118 bool valid = true;
119 for (const auto& zero : sensis) {
120 if (zero.currency != configs.simMarketParams->baseCcy()) {
121 valid = false;
122 ALOG("Currency in the sensitivity input and config aren't consistent. Skip trade " << id);
123 break;
124 }
125 auto [rf, desc] = deconstructFactor(zero.riskFactor);
126 if (rf.keytype != RiskFactorKey::KeyType::None) {
127 auto it = factorToIndex.find(rf);
128 if (it == factorToIndex.end()) {
129 if (ParSensitivityAnalysis::isParType(rf.keytype) && typesDisabled.count(rf.keytype) != 1) {
130
131 StructuredAnalyticsErrorMessage("Par conversion", "",
132 "Par factor " + ore::data::to_string(rf) +
133 " not found in factorToIndex map")
134 .log();
135 } else {
137 sr.tradeId = id;
138 sr.isPar = true;
139 sr.key_1 = rf;
140 sr.desc_1 = desc;
141 sr.delta = zero.delta;
142 sr.baseNpv = zero.baseNpv;
143 sr.currency = zero.currency;
144 sr.shift_1 = zero.shiftSize;
145 sr.gamma = QuantLib::Null<QuantLib::Real>();
146 excludedDeltas.push_back(sr);
147 }
148 } else {
149 auto shiftSize = shiftSizes.find(rf);
150 if (shiftSize == shiftSizes.end() || !close_enough(shiftSize->second.first, zero.shiftSize)) {
151 valid = false;
152 ALOG("Shift sizes in the sensitivity input and config aren't consistent. Skip trade "
153 << id);
154 break;
155 }
156
157 zeroDeltas[it->second] = zero.delta;
158 }
159 }
160 }
161 if (!sensis.empty() && valid) {
162 boost::numeric::ublas::vector<Real> parDeltas = parConverter->convertSensitivity(zeroDeltas);
163 Size counter = 0;
164 for (const auto& key : parConverter->parKeys()) {
165 if (!close(parDeltas[counter], 0.0)) {
167 sr.tradeId = id;
168 sr.isPar = true;
169 sr.key_1 = key;
170 sr.desc_1 = descriptions[key];
171 sr.delta = parDeltas[counter];
172 sr.baseNpv = sensis.begin()->baseNpv;
173 sr.currency = sensis.begin()->currency;
174 sr.shift_1 = shiftSizes[key].second;
175 sr.gamma = QuantLib::Null<QuantLib::Real>();
176 results.push_back(sr);
177 }
178 counter++;
179 }
180 results.insert(results.end(), excludedDeltas.begin(), excludedDeltas.end());
181 }
182 }
183
184 auto ss = QuantLib::ext::make_shared<SensitivityInMemoryStream>(results.begin(), results.end());
185 QuantLib::ext::shared_ptr<InMemoryReport> report = QuantLib::ext::make_shared<InMemoryReport>();
186 ReportWriter(inputs_->reportNaString()).writeSensitivityReport(*report, ss, inputs_->parConversionThreshold());
187 analytic()->reports()["PARCONVERSION"]["parConversionSensitivity"] = report;
188
189 if (inputs_->parConversionOutputJacobi()) {
190 QuantLib::ext::shared_ptr<InMemoryReport> jacobiReport = QuantLib::ext::make_shared<InMemoryReport>();
191 writeParConversionMatrix(parAnalysis->parSensitivities(), *jacobiReport);
192 analytic()->reports()["PARCONVERSION"]["parConversionJacobi"] = jacobiReport;
193
194 QuantLib::ext::shared_ptr<InMemoryReport> jacobiInverseReport = QuantLib::ext::make_shared<InMemoryReport>();
195 parConverter->writeConversionMatrix(*jacobiInverseReport);
196 analytic()->reports()["PARCONVERSION"]["parConversionJacobi_inverse"] = jacobiInverseReport;
197 }
198 }
199 LOG("Sensi Analysis - Completed");
200 CONSOLE("OK");
201}
202
203} // namespace analytics
204} // namespace ore
Analytic * analytic() const
Definition: analytic.hpp:193
QuantLib::ext::shared_ptr< InputParameters > inputs_
Definition: analytic.hpp:216
analytic_reports & reports()
Result reports.
Definition: analytic.hpp:131
Configurations & configurations()
Definition: analytic.hpp:128
virtual void buildMarket(const QuantLib::ext::shared_ptr< ore::data::InMemoryLoader > &loader, const bool marketRequired=true)
Definition: analytic.cpp:178
void runAnalytic(const QuantLib::ext::shared_ptr< ore::data::InMemoryLoader > &loader, const std::set< std::string > &runTypes={}) override
static bool isParType(ore::analytics::RiskFactorKey::KeyType type)
Returns true if risk factor type is applicable for par conversion.
Write ORE outputs to reports.
virtual void writeSensitivityReport(ore::data::Report &report, const QuantLib::ext::shared_ptr< SensitivityStream > &ss, QuantLib::Real outputThreshold=0.0, QuantLib::Size outputPrecision=2)
static const string defaultConfiguration
factory class for cloning a cached scenario
#define LOG(text)
#define ALOG(text)
#define CONSOLE(text)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
bool close(const Real &t_1, const Real &t_2)
void writeParConversionMatrix(const ParSensitivityAnalysis::ParContainer &parSensitivities, Report &report)
Write par instrument sensitivity report.
std::map< RiskFactorKey, std::string > getScenarioDescriptions(QuantLib::ext::shared_ptr< ScenarioGenerator > scenGen)
pair< RiskFactorKey, string > deconstructFactor(const string &factor)
std::string to_string(const LocationInfo &l)
Perfrom sensitivity analysis for a given portfolio.
A Class to write ORE outputs to reports.
Scenario class.
Class for streaming SensitivityRecords from in-memory container.
Shift scenario generation.
QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > sensiScenarioData
Definition: analytic.hpp:70
QuantLib::ext::shared_ptr< ore::data::EngineData > engineData
Definition: analytic.hpp:74
QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > todaysMarketParams
Definition: analytic.hpp:68
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > simMarketParams
Definition: analytic.hpp:69
Structured analytics error.