1062 {
1063
1064 SavedSettings backup;
1065
1067 ObservationMode::instance().setMode(om);
1068
1069 Date today = Date(14, April, 2016);
1070 Settings::instance().evaluationDate() = today;
1071
1072 BOOST_TEST_MESSAGE("Today is " << today);
1073
1074
1075 string baseCcy = "EUR";
1076 vector<string> ccys;
1077 ccys.push_back(baseCcy);
1078 ccys.push_back("GBP");
1079 ccys.push_back("CHF");
1080 ccys.push_back("USD");
1081 ccys.push_back("JPY");
1082
1083
1084 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
1085
1086
1087 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
setupSimMarketData5();
1088
1089
1091
1092
1093 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket> simMarket =
1094 QuantLib::ext::make_shared<analytics::ScenarioSimMarket>(initMarket, simMarketData);
1095
1096
1097 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
1098 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory =
1099 QuantLib::ext::make_shared<ore::analytics::DeltaScenarioFactory>(baseScenario);
1100
1101
1102 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
1103 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
1104 scenarioFactory, false);
1105 simMarket->scenarioGenerator() = scenarioGenerator;
1106
1107
1108 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1109 engineData->model("Swap") = "DiscountedCashflows";
1110 engineData->engine("Swap") = "DiscountingSwapEngine";
1111 engineData->model("CrossCurrencySwap") = "DiscountedCashflows";
1112 engineData->engine("CrossCurrencySwap") = "DiscountingCrossCurrencySwapEngine";
1113 engineData->model("EuropeanSwaption") = "BlackBachelier";
1114 engineData->engine("EuropeanSwaption") = "BlackBachelierSwaptionEngine";
1115 engineData->model("FxForward") = "DiscountedCashflows";
1116 engineData->engine("FxForward") = "DiscountingFxForwardEngine";
1117 engineData->model("FxOption") = "GarmanKohlhagen";
1118 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1119 engineData->model("CapFloor") = "IborCapModel";
1120 engineData->engine("CapFloor") = "IborCapEngine";
1121 engineData->model("CapFlooredIborLeg") = "BlackOrBachelier";
1122 engineData->engine("CapFlooredIborLeg") = "BlackIborCouponPricer";
1123 engineData->model("YYCapFloor") = "YYCapModel";
1124 engineData->engine("YYCapFloor") = "YYCapEngine";
1125 engineData->model("Bond") = "DiscountedCashflows";
1126 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
1127 engineData->engineParameters("Bond")["TimestepPeriod"] = "6M";
1128 engineData->model("CreditDefaultSwap") = "DiscountedCashflows";
1129 engineData->engine("CreditDefaultSwap") = "MidPointCdsEngine";
1130 engineData->model("EquityOption") = "BlackScholesMerton";
1131 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
1132 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(engineData, simMarket);
1133
1134
1135 QuantLib::ext::shared_ptr<Portfolio> portfolio(
new Portfolio());
1136 portfolio->add(
buildSwap(
"1_Swap_EUR",
"EUR",
true, 10000000.0, 0, 10, 0.03, 0.00,
"1Y",
1137 "30/360", "6M", "A360", "EUR-EURIBOR-6M"));
1138 portfolio->add(
buildSwap(
"2_Swap_USD",
"USD",
true, 10000000.0, 0, 15, 0.02, 0.00,
"6M",
1139 "30/360", "3M", "A360", "USD-LIBOR-3M"));
1140 portfolio->add(
buildCap(
"9_Cap_EUR",
"EUR",
"Long", 0.05, 1000000.0, 0, 10,
"6M",
"A360",
1141 "EUR-EURIBOR-6M"));
1142 portfolio->add(
buildFloor(
"10_Floor_USD",
"USD",
"Long", 0.01, 1000000.0, 0, 10,
"3M",
"A360",
1143 "USD-LIBOR-3M"));
1144 portfolio->add(
buildZeroBond(
"11_ZeroBond_EUR",
"EUR", 1000000.0, 10));
1145 portfolio->add(
buildZeroBond(
"12_ZeroBond_USD",
"USD", 1000000.0, 10));
1146 portfolio->add(
1147 buildEquityOption(
"13_EquityOption_SP5",
"Long",
"Call", 2,
"SP5",
"USD", 2147.56, 1000));
1149 0.02, 0, 10, "1Y", "ACT/ACT", "UKRP1", "2M", 2));
1150 portfolio->build(factory);
1151 BOOST_TEST_MESSAGE("Portfolio size after build: " << portfolio->size());
1152
1153
1154
1156 parAnalysis.alignPillars();
1157 QuantLib::ext::shared_ptr<SensitivityAnalysis> zeroAnalysis = QuantLib::ext::make_shared<SensitivityAnalysis>(
1159 BOOST_TEST_MESSAGE("SensitivityAnalysis object built");
1160 zeroAnalysis->overrideTenors(true);
1161 zeroAnalysis->generateSensitivities();
1162 BOOST_TEST_MESSAGE("Raw sensitivity analsis done");
1163 BOOST_TEST_MESSAGE("Par sensitivity analsis object built");
1164
1165
1166
1167
1168
1169
1170 parAnalysis.computeParInstrumentSensitivities(zeroAnalysis->simMarket());
1171 QuantLib::ext::shared_ptr<ParSensitivityConverter> parConverter =
1172 QuantLib::ext::make_shared<ParSensitivityConverter>(parAnalysis.parSensitivities(), parAnalysis.shiftSizes());
1173 QuantLib::ext::shared_ptr<SensitivityCube> sensiCube = zeroAnalysis->sensiCube();
1175
1176 map<pair<string, string>, Real> parDelta;
1177 for (const auto& tradeId : portfolio->ids()) {
1178
1179 auto temp = parCube.parDeltas(tradeId);
1180 for (const auto& kv : temp) {
1181
1183 string des = sensiCube->factorDescription(kv.first);
1184 parDelta[make_pair(tradeId, des)] = kv.second;
1185 }
1186 }
1187 }
1188
1189 struct Results {
1190 string id;
1191 string label;
1192 Real sensi;
1193 };
1194
1195 std::vector<Results> cachedResults = {
1196 {"10_Floor_USD", "DiscountCurve/USD/0/6M", -0.00112886},
1197 {"10_Floor_USD", "DiscountCurve/USD/1/1Y", 0.00675206},
1198 {"10_Floor_USD", "DiscountCurve/USD/2/2Y", 0.00900048},
1199 {"10_Floor_USD", "DiscountCurve/USD/3/3Y", -0.0302434},
1200 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", -0.262464},
1201 {"10_Floor_USD", "DiscountCurve/USD/5/7Y", -1.07006},
1202 {"10_Floor_USD", "DiscountCurve/USD/6/10Y", -1.04325},
1203 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/0/6M", 0.00386584},
1204 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", 0.2381},
1205 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", 2.2426},
1206 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/3/3Y", 7.56822},
1207 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/4/5Y", 15.9842},
1208 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/5/7Y", 22.2464},
1209 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/6/10Y", -89.3588},
1210 {"10_Floor_USD", "OptionletVolatility/USD/0/1Y/0.01", -0.622505},
1211 {"10_Floor_USD", "OptionletVolatility/USD/10/3Y/0.01", -2.20215},
1212 {"10_Floor_USD", "OptionletVolatility/USD/15/5Y/0.01", 1.77487},
1213 {"10_Floor_USD", "OptionletVolatility/USD/20/10Y/0.01", 207.854},
1214 {"10_Floor_USD", "OptionletVolatility/USD/5/2Y/0.01", 1.78417},
1215 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/0/6M", 1.53634},
1216 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/1/1Y", 3.53444},
1217 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/2/2Y", 8.6117},
1218 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/3/3Y", 18.5064},
1219 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/4/5Y", 39.4197},
1220 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/5/7Y", 36.4505},
1221 {"11_ZeroBond_EUR", "SurvivalProbability/BondIssuer1/6/10Y", -600.06},
1222 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/0/6M", -0.657215},
1223 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/1/1Y", 2.95782},
1224 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/2/2Y", 6.06677},
1225 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/3/3Y", 14.1153},
1226 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/4/5Y", 32.8224},
1227 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/5/7Y", 58.069},
1228 {"11_ZeroBond_EUR", "YieldCurve/BondCurve1/6/10Y", -690.301},
1229 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/0/6M", 1.28029},
1230 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/1/1Y", 2.94537},
1231 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/2/2Y", 7.17642},
1232 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/3/3Y", 15.422},
1233 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/4/5Y", 32.8498},
1234 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/5/7Y", 30.3754},
1235 {"12_ZeroBond_USD", "SurvivalProbability/BondIssuer1/6/10Y", -500.05},
1236 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/0/6M", -0.547679},
1237 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/1/1Y", 2.46485},
1238 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/2/2Y", 5.05564},
1239 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/3/3Y", 11.7627},
1240 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/4/5Y", 27.352},
1241 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/5/7Y", 48.3909},
1242 {"12_ZeroBond_USD", "YieldCurve/BondCurve1/6/10Y", -575.251},
1243 {"13_EquityOption_SP5", "DiscountCurve/USD/0/6M", 0.270388},
1244 {"13_EquityOption_SP5", "DiscountCurve/USD/1/1Y", -1.35418},
1245 {"13_EquityOption_SP5", "DiscountCurve/USD/2/2Y", 158.893},
1246 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/0/6M", 0.00347664},
1247 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/1/1Y", -0.00921372},
1248 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/2/2Y", -0.0271867},
1249 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/3/3Y", -0.0973079},
1250 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/4/5Y", -0.298947},
1251 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/5/7Y", -0.69657},
1252 {"14_YoYInflationCap_UKRPI", "DiscountCurve/GBP/6/10Y", -0.950666},
1253 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/10/2Y/0.02", 0.131713},
1254 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/16/3Y/0.02", -0.155071},
1255 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/22/5Y/0.02", 0.336249},
1256 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/28/7Y/0.02", -0.585254},
1257 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/34/10Y/0.02", 9.11852},
1258 {"14_YoYInflationCap_UKRPI", "YoYInflationCapFloorVolatility/UKRP1/4/1Y/0.02", -0.0981938},
1259 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/0/1Y", -0.501498},
1260 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/1/2Y", 0.104595},
1261 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/2/3Y", -0.258415},
1262 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/3/5Y", 1.13565},
1263 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/4/7Y", -2.64434},
1264 {"14_YoYInflationCap_UKRPI", "YoYInflationCurve/UKRP1/5/10Y", 52.8805},
1265 {"1_Swap_EUR", "DiscountCurve/EUR/0/6M", 3.55166},
1266 {"1_Swap_EUR", "DiscountCurve/EUR/1/1Y", 8.07755},
1267 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", 15.787},
1268 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", 36.2307},
1269 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", 81.6737},
1270 {"1_Swap_EUR", "DiscountCurve/EUR/5/7Y", 146.97},
1271 {"1_Swap_EUR", "DiscountCurve/EUR/6/10Y", 170.249},
1272 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/0/6M", -492.385},
1273 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/1/1Y", 0.267094},
1274 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.0571774},
1275 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.00710812},
1276 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.201881},
1277 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 34.3404},
1278 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 8928.34},
1279 {"2_Swap_USD", "DiscountCurve/USD/0/6M", -1.47948},
1280 {"2_Swap_USD", "DiscountCurve/USD/1/1Y", -3.99176},
1281 {"2_Swap_USD", "DiscountCurve/USD/2/2Y", -10.9621},
1282 {"2_Swap_USD", "DiscountCurve/USD/3/3Y", -25.1411},
1283 {"2_Swap_USD", "DiscountCurve/USD/4/5Y", -57.393},
1284 {"2_Swap_USD", "DiscountCurve/USD/5/7Y", -103.903},
1285 {"2_Swap_USD", "DiscountCurve/USD/6/10Y", -250.483},
1286 {"2_Swap_USD", "DiscountCurve/USD/7/15Y", -269.282},
1287 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/0/6M", -198.455},
1288 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", 0.163363},
1289 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", -0.0310057},
1290 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/3/3Y", -0.00237856},
1291 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/4/5Y", -0.126057},
1292 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/5/7Y", 0.117712},
1293 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/6/10Y", 10.6825},
1294 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/7/15Y", 9972.55},
1295 {"9_Cap_EUR", "DiscountCurve/EUR/0/6M", 0.000267715},
1296 {"9_Cap_EUR", "DiscountCurve/EUR/1/1Y", 1.93692e-06},
1297 {"9_Cap_EUR", "DiscountCurve/EUR/2/2Y", 0.00120582},
1298 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", 0.0038175},
1299 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", 0.00870478},
1300 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", -0.0375854},
1301 {"9_Cap_EUR", "DiscountCurve/EUR/6/10Y", -0.25186},
1302 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/0/6M", 0.000685155},
1303 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/1/1Y", -0.00175651},
1304 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.0118899},
1305 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.301921},
1306 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -2.28152},
1307 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -7.16938},
1308 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 16.3599},
1309 {"9_Cap_EUR", "OptionletVolatility/EUR/14/3Y/0.05", -0.0903623},
1310 {"9_Cap_EUR", "OptionletVolatility/EUR/19/5Y/0.05", 0.0577696},
1311 {"9_Cap_EUR", "OptionletVolatility/EUR/24/10Y/0.05", 41.9784},
1312 {"9_Cap_EUR", "OptionletVolatility/EUR/4/1Y/0.05", -0.0489527},
1313 {"9_Cap_EUR", "OptionletVolatility/EUR/9/2Y/0.05", 0.0995465}};
1314
1315 std::map<pair<string, string>, Real> sensiMap;
1316 for (Size i = 0; i < cachedResults.size(); ++i) {
1317 pair<string, string> p(cachedResults[i].id, cachedResults[i].label);
1318 sensiMap[p] = cachedResults[i].sensi;
1319 }
1320
1321 Real tolerance = 0.01;
1323 for (auto data : parDelta) {
1324 pair<string, string> p =
data.first;
1325 Real delta =
data.second;
1326 if (fabs(delta) > 0.0) {
1328 BOOST_CHECK_MESSAGE(sensiMap.find(p) != sensiMap.end(),
1329 "pair (" << p.first << ", " << p.second << ") not found in sensi map");
1330 if (sensiMap.find(p) != sensiMap.end()) {
1331 BOOST_CHECK_MESSAGE(fabs(delta - sensiMap[p]) < tolerance ||
1332 fabs((delta - sensiMap[p]) / delta) < tolerance,
1333 "sensitivity regression failed for pair ("
1334 << p.first << ", " << p.second << "): " << delta << " vs " << sensiMap[p]);
1335 }
1336 }
1337 }
1338 BOOST_CHECK_MESSAGE(count == cachedResults.size(), "number of non-zero par sensitivities ("
1339 << count << ") do not match regression data ("
1340 << cachedResults.size() << ")");
1341 ObservationMode::instance().setMode(backupMode);
1342 IndexManager::instance().clearHistories();
1343}
Par Sensitivity Analysis.
static bool isParType(ore::analytics::RiskFactorKey::KeyType type)
Returns true if risk factor type is applicable for par conversion.
static const string defaultConfiguration
QuantLib::ext::shared_ptr< Trade > buildCap(string id, string ccy, string longShort, Real capRate, Real notional, int start, Size term, string floatFreq, string floatDC, string index, Calendar calendar, Natural spotDays, bool spotStartLag)
QuantLib::ext::shared_ptr< Trade > buildSwap(string id, string ccy, bool isPayer, Real notional, int start, Size term, Real rate, Real spread, string fixedFreq, string fixedDC, string floatFreq, string floatDC, string index, Calendar calendar, Natural spotDays, bool spotStartLag)
QuantLib::ext::shared_ptr< Trade > buildZeroBond(string id, string ccy, Real notional, Size term, string suffix)
QuantLib::ext::shared_ptr< Trade > buildEquityOption(string id, string longShort, string putCall, Size expiry, string equityName, string currency, Real strike, Real quantity, Real premium, string premiumCcy, string premiumDate)
QuantLib::ext::shared_ptr< Trade > buildFloor(string id, string ccy, string longShort, Real floorRate, Real notional, int start, Size term, string floatFreq, string floatDC, string index, Calendar calendar, Natural spotDays, bool spotStartLag)
QuantLib::ext::shared_ptr< Trade > buildYYInflationCapFloor(string id, string ccy, Real notional, bool isCap, bool isLong, Real capFloorRate, int start, Size term, string yyFreq, string yyDC, string yyIndex, string observationLag, Size fixDays)
QuantLib::ext::shared_ptr< SensitivityScenarioData > setupSensitivityScenarioData5(bool parConversion)
QuantLib::ext::shared_ptr< analytics::ScenarioSimMarketParameters > setupSimMarketData5()