1062 {
1063
1065
1066
1067
1068 bool atmOnly;
1069 map<string, SensitivityScenarioData::GenericYieldVolShiftData> shiftData;
1070 function<Size(string)> get_n_term;
1071 function<Size(string)> get_n_expiry;
1072 function<vector<Real>(string)> getVolStrikes;
1073 function<vector<Period>(string)> getVolExpiries;
1074 function<vector<Period>(string)> getVolTerms;
1075 function<string(string)> getDayCounter;
1076 function<ScenarioDescription(string, Size, Size, Size, bool, SensitivityScenarioData::ShiftData&)>
1077 getScenarioDescription;
1078
1079 if (rfType == RFType::SwaptionVolatility) {
1082 get_n_term = [
this](
const string& k) {
return simMarketData_->swapVolTerms(k).size(); };
1083 get_n_expiry = [
this](
const string& k) {
return simMarketData_->swapVolExpiries(k).size(); };
1084 getVolStrikes = [
this](
const string& k) {
return simMarketData_->swapVolStrikeSpreads(k); };
1085 getVolExpiries = [
this](
const string& k) {
return simMarketData_->swapVolExpiries(k); };
1086 getVolTerms = [
this](
const string& k) {
return simMarketData_->swapVolTerms(k); };
1087 getDayCounter = [this](const string& k) {
1088 try {
1090 return to_string(s->swaptionVol(k)->dayCounter());
1091 } else {
1092 QL_FAIL("Internal error: could not lock simMarket. Contact dev.");
1093 }
1094 } catch (const std::exception&) {
1095 WLOG(
"Day counter lookup in simulation market failed for swaption vol '" << k
1096 << "', using default A365");
1097 return std::string("A365F");
1098 }
1099 };
1100 getScenarioDescription = [this](string q, Size n, Size m, Size k, bool up,
1101 SensitivityScenarioData::ShiftData&
data) {
1103 };
1104 } else if (rfType == RFType::YieldVolatility) {
1105 atmOnly = true;
1107 get_n_term = [
this](
const string& k) {
return simMarketData_->yieldVolTerms().size(); };
1108 get_n_expiry = [
this](
const string& k) {
return simMarketData_->yieldVolExpiries().size(); };
1109 getVolStrikes = [](const string& k) { return vector<Real>({0.0}); };
1110 getVolExpiries = [
this](
const string& k) {
return simMarketData_->yieldVolExpiries(); };
1111 getVolTerms = [
this](
const string& k) {
return simMarketData_->yieldVolTerms(); };
1112 getDayCounter = [this](const string& k) {
1113 try {
1115 return to_string(s->yieldVol(k)->dayCounter());
1116 } else {
1117 QL_FAIL("Internal error: could not lock simMarket. Contact dev.");
1118 }
1119 } catch (const std::exception&) {
1120 WLOG(
"Day counter lookup in simulation market failed for swaption vol '" << k
1121 << "', using default A365");
1122 return std::string("A365F");
1123 }
1124 };
1125 getScenarioDescription = [this](string q, Size n, Size m, Size k, bool up,
1126 SensitivityScenarioData::ShiftData&
data) {
1128 };
1129 } else {
1130 QL_FAIL("SensitivityScenarioGenerator::generateGenericYieldVolScenarios: risk factor type " << rfType
1131 << " not handled.");
1132 }
1133
1134
1135 for (auto s : shiftData) {
1136 std::string qualifier = s.first;
1137
1138 Size n_term;
1139 try {
1140 n_term = get_n_term(qualifier);
1141 } catch (const std::exception& e) {
1142 ALOG(
"skip scenario generation for general yield vol " << qualifier <<
": " << e.what());
1143 continue;
1144 }
1145 Size n_expiry = get_n_expiry(qualifier);
1146
1147 vector<Real> volExpiryTimes(n_expiry, 0.0);
1148 vector<Real> volTermTimes(n_term, 0.0);
1149 Size n_strike = getVolStrikes(qualifier).size();
1150
1151 vector<vector<vector<Real>>> volData(n_strike, vector<vector<Real>>(n_expiry, vector<Real>(n_term, 0.0)));
1152 vector<vector<vector<Real>>> shiftedVolData = volData;
1153
1154 SensitivityScenarioData::GenericYieldVolShiftData
data = s.second;
1156 continue;
1159
1160 vector<Real> shiftExpiryTimes(
data.shiftExpiries.size(), 0.0);
1161 vector<Real> shiftTermTimes(
data.shiftTerms.size(), 0.0);
1162
1163 vector<Real> shiftStrikes;
1164 if (!atmOnly) {
1165 shiftStrikes =
data.shiftStrikes;
1166 QL_REQUIRE(
data.shiftStrikes.size() == n_strike,
1167 "number of simulated strikes must equal number of sensitivity strikes");
1168 } else {
1169 shiftStrikes = {0.0};
1170 }
1171
1173
1174
1175 for (Size j = 0; j < n_expiry; ++j) {
1176 Date expiry =
asof + getVolExpiries(qualifier)[j];
1177 volExpiryTimes[j] = dc.yearFraction(
asof, expiry);
1178 }
1179 for (Size j = 0; j < n_term; ++j) {
1180 Date term =
asof + getVolTerms(qualifier)[j];
1181 volTermTimes[j] = dc.yearFraction(
asof, term);
1182 }
1183
1184 bool valid = true;
1185 for (Size j = 0; j < n_expiry; ++j) {
1186 for (Size k = 0; k < n_term; ++k) {
1187 for (Size l = 0; l < n_strike; ++l) {
1188 Size idx = j * n_term * n_strike + k * n_strike + l;
1189 RiskFactorKey key(rfType, qualifier, idx);
1190 valid = valid &&
1192 }
1193 }
1194 }
1195 if (!valid)
1196 continue;
1197
1198
1199 for (Size j = 0; j < shiftExpiryTimes.size(); ++j)
1200 shiftExpiryTimes[j] = dc.yearFraction(
asof,
asof +
data.shiftExpiries[j]);
1201 for (Size j = 0; j < shiftTermTimes.size(); ++j)
1202 shiftTermTimes[j] = dc.yearFraction(
asof,
asof +
data.shiftTerms[j]);
1203
1204
1205 bool validShiftSize =
vectorEqual(volExpiryTimes, shiftExpiryTimes);
1206 validShiftSize = validShiftSize &&
vectorEqual(volTermTimes, shiftTermTimes);
1207 validShiftSize = validShiftSize &&
vectorEqual(getVolStrikes(qualifier), shiftStrikes);
1208
1209
1210 for (Size j = 0; j < shiftExpiryTimes.size(); ++j) {
1211 for (Size k = 0; k < shiftTermTimes.size(); ++k) {
1212 for (Size l = 0; l < shiftStrikes.size(); ++l) {
1213 Size strikeBucket = l;
1214 QuantLib::ext::shared_ptr<Scenario> scenario =
1216
1217
1218 Size loopStart = atmOnly ? 0 : l;
1219 Size loopEnd = atmOnly ? n_strike : loopStart + 1;
1220
1221 for (Size ll = loopStart; ll < loopEnd; ++ll) {
1222 applyShift(j, k, shiftSize, up, shiftType, shiftExpiryTimes, shiftTermTimes, volExpiryTimes,
1223 volTermTimes, volData[ll], shiftedVolData[ll], true);
1224 }
1225
1226 for (Size jj = 0; jj < n_expiry; ++jj) {
1227 for (Size kk = 0; kk < n_term; ++kk) {
1228 for (Size ll = 0; ll < n_strike; ++ll) {
1229
1230 Size idx = jj * n_term * n_strike + kk * n_strike + ll;
1231 RiskFactorKey key(rfType, qualifier, idx);
1232
1233 if (ll >= loopStart && ll < loopEnd) {
1235 scenario->add(key, shiftedVolData[ll][jj][kk] - volData[ll][jj][kk]);
1236 } else {
1237 scenario->add(key, shiftedVolData[ll][jj][kk]);
1238 }
1239 }
1240
1241
1242 if (validShiftSize && j == jj && k == kk && l == ll) {
1243 storeShiftData(key, volData[ll][jj][kk], shiftedVolData[ll][jj][kk]);
1244 }
1245 }
1246 }
1247 }
1248
1249
1253 DLOG(
"Sensitivity scenario # " <<
scenarios_.size() <<
", label " << scenario->label()
1254 << " created for generic yield vol " << qualifier);
1255 }
1256 }
1257 }
1258 }
1259}
ScenarioDescription yieldVolScenarioDescription(string securityId, Size expiryBucket, Size termBucket, bool up, ShiftScheme shiftScheme)
ScenarioDescription swaptionVolScenarioDescription(string ccy, Size expiryBucket, Size termBucket, Size strikeBucket, bool up, ShiftScheme shiftScheme)
DayCounter parseDayCounter(const string &s)