55 {
57 return;
58
59 LOG(
"ParConversionAnalytic::runAnalytic called");
60
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()) {
71
72 auto parAnalysis = QuantLib::ext::make_shared<ParSensitivityAnalysis>(
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
84
85 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(
86 analytic()->market(), configs.simMarketParams,
inputs_->marketConfig(
"pricing"),
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;
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 }
127 auto it = factorToIndex.find(rf);
128 if (it == factorToIndex.end()) {
130
131 StructuredAnalyticsErrorMessage("Par conversion", "",
133 " not found in factorToIndex map")
134 .log();
135 } else {
136 SensitivityRecord sr;
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)) {
166 SensitivityRecord sr;
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>();
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");
201}
Analytic * analytic() const
QuantLib::ext::shared_ptr< InputParameters > inputs_
analytic_reports & reports()
Result reports.
Configurations & configurations()
virtual void buildMarket(const QuantLib::ext::shared_ptr< ore::data::InMemoryLoader > &loader, const bool marketRequired=true)
static bool isParType(ore::analytics::RiskFactorKey::KeyType type)
Returns true if risk factor type is applicable for par conversion.
static const string defaultConfiguration
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)