Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions
sensitivityanalysis.cpp File Reference
#include <boost/test/unit_test.hpp>
#include <boost/timer/timer.hpp>
#include <orea/cube/inmemorycube.hpp>
#include <orea/engine/filteredsensitivitystream.hpp>
#include <orea/engine/observationmode.hpp>
#include <orea/engine/parametricvar.hpp>
#include <orea/engine/riskfilter.hpp>
#include <orea/engine/sensitivityaggregator.hpp>
#include <orea/engine/sensitivityanalysis.hpp>
#include <orea/engine/sensitivitycubestream.hpp>
#include <orea/engine/sensitivityfilestream.hpp>
#include <orea/engine/sensitivityinmemorystream.hpp>
#include <orea/engine/sensitivityrecord.hpp>
#include <orea/engine/sensitivitystream.hpp>
#include <orea/engine/stresstest.hpp>
#include <orea/engine/valuationcalculator.hpp>
#include <orea/engine/valuationengine.hpp>
#include <orea/scenario/clonescenariofactory.hpp>
#include <orea/scenario/scenariosimmarket.hpp>
#include <orea/scenario/sensitivityscenariogenerator.hpp>
#include <ored/portfolio/builders/capfloor.hpp>
#include <ored/portfolio/builders/commodityforward.hpp>
#include <ored/portfolio/builders/commodityoption.hpp>
#include <ored/portfolio/builders/equityforward.hpp>
#include <ored/portfolio/builders/equityoption.hpp>
#include <ored/portfolio/builders/fxforward.hpp>
#include <ored/portfolio/builders/fxoption.hpp>
#include <ored/portfolio/builders/swap.hpp>
#include <ored/portfolio/builders/swaption.hpp>
#include <ored/portfolio/commodityforward.hpp>
#include <ored/portfolio/commodityoption.hpp>
#include <ored/portfolio/equityforward.hpp>
#include <ored/portfolio/equityoption.hpp>
#include <ored/portfolio/fxoption.hpp>
#include <ored/portfolio/portfolio.hpp>
#include <ored/portfolio/swap.hpp>
#include <ored/portfolio/swaption.hpp>
#include <ored/utilities/log.hpp>
#include <ored/utilities/osutils.hpp>
#include <ored/utilities/to_string.hpp>
#include <oret/toplevelfixture.hpp>
#include <test/oreatoplevelfixture.hpp>
#include <test/testportfolio.hpp>
#include "testmarket.hpp"

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testPortfolioSensitivityNoneObs)
 
 BOOST_AUTO_TEST_CASE (testPortfolioSensitivityDisableObs)
 
 BOOST_AUTO_TEST_CASE (testPortfolioSensitivityDeferObs)
 
 BOOST_AUTO_TEST_CASE (testPortfolioSensitivityUnregisterObs)
 
void test1dShifts (bool granular)
 
 BOOST_AUTO_TEST_CASE (test1dShiftsSparse)
 
 BOOST_AUTO_TEST_CASE (test1dShiftsGranular)
 
 BOOST_AUTO_TEST_CASE (test2dShifts)
 
 BOOST_AUTO_TEST_CASE (testEquityOptionDeltaGamma)
 
 BOOST_AUTO_TEST_CASE (testFxOptionDeltaGamma)
 
 BOOST_AUTO_TEST_CASE (testCrossGamma)
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/10]

BOOST_AUTO_TEST_CASE ( testPortfolioSensitivityNoneObs  )

Definition at line 824 of file sensitivityanalysis.cpp.

824 {
825 BOOST_TEST_MESSAGE("Testing Portfolio sensitivity (None observation mode)");
826 testPortfolioSensitivity(ObservationMode::Mode::None);
827}

◆ BOOST_AUTO_TEST_CASE() [2/10]

BOOST_AUTO_TEST_CASE ( testPortfolioSensitivityDisableObs  )

Definition at line 829 of file sensitivityanalysis.cpp.

829 {
830 BOOST_TEST_MESSAGE("Testing Portfolio sensitivity (Disable observation mode)");
831 testPortfolioSensitivity(ObservationMode::Mode::Disable);
832}

◆ BOOST_AUTO_TEST_CASE() [3/10]

BOOST_AUTO_TEST_CASE ( testPortfolioSensitivityDeferObs  )

Definition at line 834 of file sensitivityanalysis.cpp.

834 {
835 BOOST_TEST_MESSAGE("Testing Portfolio sensitivity (Defer observation mode)");
836 testPortfolioSensitivity(ObservationMode::Mode::Defer);
837}

◆ BOOST_AUTO_TEST_CASE() [4/10]

BOOST_AUTO_TEST_CASE ( testPortfolioSensitivityUnregisterObs  )

Definition at line 839 of file sensitivityanalysis.cpp.

839 {
840 BOOST_TEST_MESSAGE("Testing Portfolio sensitivity (Unregister observation mode)");
841 testPortfolioSensitivity(ObservationMode::Mode::Unregister);
842}

◆ test1dShifts()

void test1dShifts ( bool  granular)

Definition at line 844 of file sensitivityanalysis.cpp.

844 {
845 BOOST_TEST_MESSAGE("Testing 1d shifts " << (granular ? "granular" : "sparse"));
846
847 SavedSettings backup;
848
849 ObservationMode::Mode backupMode = ObservationMode::instance().mode();
850 ObservationMode::instance().setMode(ObservationMode::Mode::None);
851
852 Date today = Date(14, April, 2016);
853 Settings::instance().evaluationDate() = today;
854
855 BOOST_TEST_MESSAGE("Today is " << today);
856
857 // build model
858 string baseCcy = "EUR";
859 vector<string> ccys;
860 ccys.push_back(baseCcy);
861 ccys.push_back("GBP");
862
863 // Init market
864 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
865
866 // build scenario sim market parameters
867 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
869
870 // sensitivity config
871 QuantLib::ext::shared_ptr<SensitivityScenarioData> sensiData;
872 if (granular)
874 else
876
877 // build sim market
878 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(initMarket, simMarketData);
879
880 // build scenario factory
881 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
882 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<CloneScenarioFactory>(baseScenario);
883
884 // build scenario generator
885 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
886 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
887 scenarioFactory, false);
888
889 // cache initial zero rates
890 vector<Period> tenors = simMarketData->yieldCurveTenors("");
891 vector<Real> initialZeros(tenors.size());
892 vector<Real> times(tenors.size());
893 string ccy = simMarketData->ccys()[0];
894 Handle<YieldTermStructure> ts = initMarket->discountCurve(ccy);
895 DayCounter dc = ts->dayCounter();
896 for (Size j = 0; j < tenors.size(); ++j) {
897 Date d = today + simMarketData->yieldCurveTenors("")[j];
898 initialZeros[j] = ts->zeroRate(d, dc, Continuous);
899 times[j] = dc.yearFraction(today, d);
900 }
901
902 // apply zero shifts for tenors on the shift curve
903 // collect shifted data at tenors of the underlying curve
904 // aggregate "observed" shifts
905 // compare to expected total shifts
906 vector<Period> shiftTenors = sensiData->discountCurveShiftData()["EUR"]->shiftTenors;
907 vector<Time> shiftTimes(shiftTenors.size());
908 for (Size i = 0; i < shiftTenors.size(); ++i)
909 shiftTimes[i] = dc.yearFraction(today, today + shiftTenors[i]);
910
911 vector<Real> shiftedZeros(tenors.size());
912 vector<Real> diffAbsolute(tenors.size(), 0.0);
913 vector<Real> diffRelative(tenors.size(), 0.0);
914 Real shiftSize = 0.01;
915 ShiftType shiftTypeAbsolute = ShiftType::Absolute;
916 ShiftType shiftTypeRelative = ShiftType::Relative;
917 for (Size i = 0; i < shiftTenors.size(); ++i) {
918 scenarioGenerator->applyShift(i, shiftSize, true, shiftTypeAbsolute, shiftTimes, initialZeros, times,
919 shiftedZeros, true);
920 for (Size j = 0; j < tenors.size(); ++j)
921 diffAbsolute[j] += shiftedZeros[j] - initialZeros[j];
922 scenarioGenerator->applyShift(i, shiftSize, true, shiftTypeRelative, shiftTimes, initialZeros, times,
923 shiftedZeros, true);
924 for (Size j = 0; j < tenors.size(); ++j)
925 diffRelative[j] += shiftedZeros[j] / initialZeros[j] - 1.0;
926 }
927
928 Real tolerance = 1.0e-10;
929 for (Size j = 0; j < tenors.size(); ++j) {
930 // BOOST_TEST_MESSAGE("1d shift: checking sensitivity to curve tenor point " << j << ": " << diff[j]);
931 BOOST_CHECK_MESSAGE(fabs(diffAbsolute[j] - shiftSize) < tolerance,
932 "inconsistency in absolute 1d shifts at curve tenor point " << j);
933 BOOST_CHECK_MESSAGE(fabs(diffRelative[j] - shiftSize) < tolerance,
934 "inconsistency in relative 1d shifts at curve tenor point " << j);
935 }
936 ObservationMode::instance().setMode(backupMode);
937 IndexManager::instance().clearHistories();
938}
static QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > setupSimMarketData2()
ScenarioSimMarketParameters instance, 2 currencies.
static QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > setupSensitivityScenarioData2()
SensitivityScenarioData instance, 2 currencies.
static QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > setupSensitivityScenarioData2b()
SensitivityScenarioData instance, 2 currencies, shifts more granular than base curve.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ BOOST_AUTO_TEST_CASE() [5/10]

BOOST_AUTO_TEST_CASE ( test1dShiftsSparse  )

Definition at line 940 of file sensitivityanalysis.cpp.

940{ test1dShifts(false); }
void test1dShifts(bool granular)
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [6/10]

BOOST_AUTO_TEST_CASE ( test1dShiftsGranular  )

Definition at line 942 of file sensitivityanalysis.cpp.

942{ test1dShifts(true); }
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [7/10]

BOOST_AUTO_TEST_CASE ( test2dShifts  )

Definition at line 944 of file sensitivityanalysis.cpp.

944 {
945 BOOST_TEST_MESSAGE("Testing 2d shifts");
946
947 SavedSettings backup;
948
949 ObservationMode::Mode backupMode = ObservationMode::instance().mode();
950 ObservationMode::instance().setMode(ObservationMode::Mode::None);
951
952 Date today = Date(14, April, 2016);
953 Settings::instance().evaluationDate() = today;
954
955 BOOST_TEST_MESSAGE("Today is " << today);
956
957 // build model
958 string baseCcy = "EUR";
959 vector<string> ccys;
960 ccys.push_back(baseCcy);
961 ccys.push_back("GBP");
962
963 // Init market
964 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
965
966 // build scenario sim market parameters
967 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
969
970 // sensitivity config
971 QuantLib::ext::shared_ptr<SensitivityScenarioData> sensiData = TestConfigurationObjects::setupSensitivityScenarioData2();
972
973 // build sim market
974 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(initMarket, simMarketData);
975
976 // build scenario factory
977 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
978 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<CloneScenarioFactory>(baseScenario);
979
980 // build scenario generator
981 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
982 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
983 scenarioFactory, false);
984
985 // cache initial zero rates
986 vector<Period> expiries = simMarketData->swapVolExpiries("");
987 vector<Period> terms = simMarketData->swapVolTerms("");
988 vector<vector<Real>> initialData(expiries.size(), vector<Real>(terms.size(), 0.0));
989 vector<Real> expiryTimes(expiries.size());
990 vector<Real> termTimes(terms.size());
991 string ccy = simMarketData->ccys()[0];
992 Handle<SwaptionVolatilityStructure> ts = initMarket->swaptionVol(ccy);
993 DayCounter dc = ts->dayCounter();
994 for (Size i = 0; i < expiries.size(); ++i)
995 expiryTimes[i] = dc.yearFraction(today, today + expiries[i]);
996 for (Size j = 0; j < terms.size(); ++j)
997 termTimes[j] = dc.yearFraction(today, today + terms[j]);
998 for (Size i = 0; i < expiries.size(); ++i) {
999 for (Size j = 0; j < terms.size(); ++j)
1000 initialData[i][j] = ts->volatility(expiries[i], terms[j], Null<Real>()); // ATM
1001 }
1002
1003 // apply shifts for tenors on the 2d shift grid
1004 // collect shifted data at tenors of the underlying 2d grid (different from the grid above)
1005 // aggregate "observed" shifts
1006 // compare to expected total shifts
1007 vector<Period> expiryShiftTenors = sensiData->swaptionVolShiftData()["EUR"].shiftExpiries;
1008 vector<Period> termShiftTenors = sensiData->swaptionVolShiftData()["EUR"].shiftTerms;
1009 vector<Real> shiftExpiryTimes(expiryShiftTenors.size());
1010 vector<Real> shiftTermTimes(termShiftTenors.size());
1011 for (Size i = 0; i < expiryShiftTenors.size(); ++i)
1012 shiftExpiryTimes[i] = dc.yearFraction(today, today + expiryShiftTenors[i]);
1013 for (Size j = 0; j < termShiftTenors.size(); ++j)
1014 shiftTermTimes[j] = dc.yearFraction(today, today + termShiftTenors[j]);
1015
1016 vector<vector<Real>> shiftedData(expiries.size(), vector<Real>(terms.size(), 0.0));
1017 vector<vector<Real>> diffAbsolute(expiries.size(), vector<Real>(terms.size(), 0.0));
1018 vector<vector<Real>> diffRelative(expiries.size(), vector<Real>(terms.size(), 0.0));
1019 Real shiftSize = 0.01; // arbitrary
1020 ShiftType shiftTypeAbsolute = ShiftType::Absolute;
1021 ShiftType shiftTypeRelative = ShiftType::Relative;
1022 for (Size i = 0; i < expiryShiftTenors.size(); ++i) {
1023 for (Size j = 0; j < termShiftTenors.size(); ++j) {
1024 scenarioGenerator->applyShift(i, j, shiftSize, true, shiftTypeAbsolute, shiftExpiryTimes, shiftTermTimes,
1025 expiryTimes, termTimes, initialData, shiftedData, true);
1026 for (Size k = 0; k < expiries.size(); ++k) {
1027 for (Size l = 0; l < terms.size(); ++l)
1028 diffAbsolute[k][l] += shiftedData[k][l] - initialData[k][l];
1029 }
1030 scenarioGenerator->applyShift(i, j, shiftSize, true, shiftTypeRelative, shiftExpiryTimes, shiftTermTimes,
1031 expiryTimes, termTimes, initialData, shiftedData, true);
1032 for (Size k = 0; k < expiries.size(); ++k) {
1033 for (Size l = 0; l < terms.size(); ++l)
1034 diffRelative[k][l] += shiftedData[k][l] / initialData[k][l] - 1.0;
1035 }
1036 }
1037 }
1038
1039 Real tolerance = 1.0e-10;
1040 for (Size k = 0; k < expiries.size(); ++k) {
1041 for (Size l = 0; l < terms.size(); ++l) {
1042 // BOOST_TEST_MESSAGE("2d shift: checking sensitivity to underlying grid point (" << k << ", " << l << "): "
1043 // << diff[k][l]);
1044 BOOST_CHECK_MESSAGE(fabs(diffAbsolute[k][l] - shiftSize) < tolerance,
1045 "inconsistency in absolute 2d shifts at grid point (" << k << ", " << l
1046 << "): " << diffAbsolute[k][l]);
1047 BOOST_CHECK_MESSAGE(fabs(diffRelative[k][l] - shiftSize) < tolerance,
1048 "inconsistency in relative 2d shifts at grid point (" << k << ", " << l
1049 << "): " << diffRelative[k][l]);
1050 }
1051 }
1052 ObservationMode::instance().setMode(backupMode);
1053 IndexManager::instance().clearHistories();
1054}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [8/10]

BOOST_AUTO_TEST_CASE ( testEquityOptionDeltaGamma  )

Definition at line 1056 of file sensitivityanalysis.cpp.

1056 {
1057
1058 BOOST_TEST_MESSAGE("Testing Equity option sensitivities against QL analytic greeks");
1059
1060 ObservationMode::instance().setMode(ObservationMode::Mode::None);
1061
1062 Date today = Date(14, April, 2016);
1063 Settings::instance().evaluationDate() = today;
1064
1065 BOOST_TEST_MESSAGE("Today is " << today);
1066 // Init market
1067 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
1068
1069 // build scenario sim market parameters
1070 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
1072
1073 // sensitivity config
1074 QuantLib::ext::shared_ptr<SensitivityScenarioData> sensiData = TestConfigurationObjects::setupSensitivityScenarioData5();
1075
1076 map<string, SensitivityScenarioData::VolShiftData>& eqvs = sensiData->equityVolShiftData();
1077 for (auto& it : eqvs) {
1078 it.second.shiftSize = 0.0001; // want a smaller shift size than 1.0 to test the analytic sensitivities
1079 }
1080 map<string, SensitivityScenarioData::SpotShiftData>& eqs = sensiData->equityShiftData();
1081 for (auto& it : eqs) {
1082 it.second.shiftSize = 0.0001; // want a smaller shift size to test the analytic sensitivities
1083 }
1084
1085 // build sim market
1086 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(initMarket, simMarketData);
1087
1088 // build scenario factory
1089 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
1090 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<CloneScenarioFactory>(baseScenario);
1091
1092 // build scenario generator
1093 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
1094 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
1095 scenarioFactory, false);
1096 simMarket->scenarioGenerator() = scenarioGenerator;
1097
1098 // build portfolio
1099 QuantLib::ext::shared_ptr<EngineData> data = QuantLib::ext::make_shared<EngineData>();
1100 data->model("EquityForward") = "DiscountedCashflows";
1101 data->engine("EquityForward") = "DiscountingEquityForwardEngine";
1102 data->model("EquityOption") = "BlackScholesMerton";
1103 data->engine("EquityOption") = "AnalyticEuropeanEngine";
1104 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, simMarket);
1105
1106 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
1107 Size trnCount = 0;
1108 portfolio->add(buildEquityOption("Call_SP5", "Long", "Call", 2, "SP5", "USD", 2147.56, 1000));
1109 trnCount++;
1110 portfolio->add(buildEquityOption("Put_SP5", "Long", "Put", 2, "SP5", "USD", 2147.56, 1000));
1111 trnCount++;
1112 // portfolio->add(buildEquityForward("Fwd_SP5", "Long", 2, "SP5", "USD", 2147.56, 1000));
1113 // trnCount++;
1114 portfolio->add(buildEquityOption("Call_Luft", "Short", "Call", 2, "Lufthansa", "EUR", 12.75, 1000));
1115 trnCount++;
1116 portfolio->add(buildEquityOption("Put_Luft", "Short", "Put", 2, "Lufthansa", "EUR", 12.75, 1000));
1117 trnCount++;
1118 // portfolio->add(buildEquityForward("Fwd_Luft", "Short", 2, "Lufthansa", "EUR", 12.75, 1000));
1119 // trnCount++;
1120 portfolio->build(factory);
1121 BOOST_CHECK_EQUAL(portfolio->size(), trnCount);
1122
1123 struct AnalyticInfo {
1124 string id;
1125 string name;
1126 string npvCcy;
1127 Real spot;
1128 Real fx;
1129 Real baseNpv;
1130 Real qlNpv;
1131 Real delta;
1132 Real gamma;
1133 Real vega;
1134 Real rho;
1135 Real divRho;
1136 };
1137 map<string, AnalyticInfo> qlInfoMap;
1138 for (const auto& [tradeId, trade] : portfolio->trades()) {
1139 AnalyticInfo info;
1140 QuantLib::ext::shared_ptr<ore::data::EquityOption> eqoTrn = QuantLib::ext::dynamic_pointer_cast<ore::data::EquityOption>(trade);
1141 BOOST_CHECK(eqoTrn);
1142 info.id = tradeId;
1143 info.name = eqoTrn->equityName();
1144 info.npvCcy = trade->npvCurrency();
1145
1146 info.spot = initMarket->equitySpot(info.name)->value();
1147 string pair = info.npvCcy + simMarketData->baseCcy();
1148 info.fx = initMarket->fxRate(pair)->value();
1149 info.baseNpv = trade->instrument()->NPV() * info.fx;
1150 QuantLib::ext::shared_ptr<QuantLib::VanillaOption> qlOpt =
1151 QuantLib::ext::dynamic_pointer_cast<QuantLib::VanillaOption>(trade->instrument()->qlInstrument());
1152 BOOST_CHECK(qlOpt);
1153 Position::Type positionType = parsePositionType(eqoTrn->option().longShort());
1154 Real bsInd = (positionType == QuantLib::Position::Long ? 1.0 : -1.0);
1155 info.qlNpv = qlOpt->NPV() * eqoTrn->quantity() * bsInd;
1156 info.delta = qlOpt->delta() * eqoTrn->quantity() * bsInd;
1157 info.gamma = qlOpt->gamma() * eqoTrn->quantity() * bsInd;
1158 info.vega = qlOpt->vega() * eqoTrn->quantity() * bsInd;
1159 info.rho = qlOpt->rho() * eqoTrn->quantity() * bsInd;
1160 info.divRho = qlOpt->dividendRho() * eqoTrn->quantity() * bsInd;
1161 qlInfoMap[info.id] = info;
1162 }
1163
1164 bool recalibrateModels = true; // nothing to calibrate here
1165 QuantLib::ext::shared_ptr<SensitivityAnalysis> sa = QuantLib::ext::make_shared<SensitivityAnalysis>(
1166 portfolio, initMarket, Market::defaultConfiguration, data, simMarketData, sensiData, recalibrateModels);
1167 sa->generateSensitivities();
1168
1169 map<pair<string, string>, Real> deltaMap;
1170 map<pair<string, string>, Real> gammaMap;
1171 std::set<string> sensiTrades;
1172 for (auto [pid, p] : portfolio->trades()) {
1173 sensiTrades.insert(pid);
1174 for (const auto& f : sa->sensiCube()->factors()) {
1175 auto des = sa->sensiCube()->factorDescription(f);
1176 deltaMap[make_pair(pid, des)] = sa->sensiCube()->delta(pid, f);
1177 gammaMap[make_pair(pid, des)] = sa->sensiCube()->gamma(pid, f);
1178 }
1179 }
1180
1181 struct SensiResults {
1182 string id;
1183 Real baseNpv;
1184 Real discountDelta;
1185 Real ycDelta;
1186 Real equitySpotDelta;
1187 Real equityVolDelta;
1188 Real equitySpotGamma;
1189 };
1190
1191 Real epsilon = 1.e-15; // a small number
1192 string equitySpotStr = "EquitySpot";
1193 string equityVolStr = "EquityVolatility";
1194
1195 for (auto it : qlInfoMap) {
1196 string id = it.first;
1197 BOOST_CHECK(sensiTrades.find(id) != sensiTrades.end());
1198 AnalyticInfo qlInfo = it.second;
1199 SensiResults res = {string(""), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
1200 for (auto it2 : deltaMap) {
1201 pair<string, string> sensiKey = it2.first;
1202 string sensiTrnId = it2.first.first;
1203 if (sensiTrnId != id)
1204 continue;
1205 res.id = sensiTrnId;
1206 res.baseNpv = sa->sensiCube()->npv(sensiTrnId);
1207 string sensiId = it2.first.second;
1208 Real sensiVal = it2.second;
1209 if (std::fabs(sensiVal) < epsilon) // not interested in zero sensis
1210 continue;
1211 vector<string> tokens;
1212 boost::split(tokens, sensiId, boost::is_any_of("/-"));
1213 BOOST_CHECK(tokens.size() > 0);
1214 bool isEquitySpot = (tokens[0] == equitySpotStr);
1215 bool isEquityVol = (tokens[0] == equityVolStr);
1216 if (isEquitySpot) {
1217 BOOST_CHECK(tokens.size() > 2);
1218 bool hasGamma = (gammaMap.find(sensiKey) != gammaMap.end());
1219 BOOST_CHECK(hasGamma);
1220 Real gammaVal = 0.0;
1221 if (hasGamma) {
1222 gammaVal = gammaMap[sensiKey];
1223 }
1224 res.equitySpotDelta += sensiVal;
1225 res.equitySpotGamma += gammaVal;
1226 continue;
1227 } else if (isEquityVol) {
1228 BOOST_CHECK(tokens.size() > 2);
1229 res.equityVolDelta += sensiVal;
1230 continue;
1231 } else {
1232 continue;
1233 }
1234 }
1235
1236 Real bp = 1.e-4;
1237 Real tol = 0.5; // % relative tolerance
1238
1239 BOOST_TEST_MESSAGE("SA: id=" << res.id << ", npv=" << res.baseNpv << ", equitySpotDelta=" << res.equitySpotDelta
1240 << ", equityVolDelta=" << res.equityVolDelta
1241 << ", equitySpotGamma=" << res.equitySpotGamma);
1242 BOOST_TEST_MESSAGE("QL: id=" << qlInfo.id << ", fx=" << qlInfo.fx << ", npv=" << qlInfo.baseNpv
1243 << ", ccyNpv=" << qlInfo.qlNpv << ", delta=" << qlInfo.delta
1244 << ", gamma=" << qlInfo.gamma << ", vega=" << qlInfo.vega
1245 << ", spotDelta=" << (qlInfo.delta * qlInfo.fx * bp * qlInfo.spot));
1246
1247 Real eqVol =
1248 initMarket->equityVol(qlInfo.name)->blackVol(1.0, 1.0, true); // TO-DO more appropriate vol extraction
1249 BOOST_CHECK_CLOSE(res.equityVolDelta, qlInfo.vega * qlInfo.fx * (bp * eqVol), tol);
1250
1251 BOOST_CHECK_CLOSE(res.equitySpotDelta, qlInfo.delta * qlInfo.fx * (bp * qlInfo.spot), tol);
1252 BOOST_CHECK_CLOSE(res.equitySpotGamma, qlInfo.gamma * qlInfo.fx * (pow(bp * qlInfo.spot, 2)), tol);
1253 }
1254}
static const string defaultConfiguration
static QuantLib::ext::shared_ptr< ore::analytics::SensitivityScenarioData > setupSensitivityScenarioData5()
SensitivityScenarioData instance, 5 currencies.
static QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > setupSimMarketData5()
ScenarioSimMarketParameters instance, 5 currencies.
Position::Type parsePositionType(const string &s)
data
RandomVariable pow(RandomVariable x, const RandomVariable &y)
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)
string name
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [9/10]

BOOST_AUTO_TEST_CASE ( testFxOptionDeltaGamma  )

Definition at line 1256 of file sensitivityanalysis.cpp.

1256 {
1257
1258 BOOST_TEST_MESSAGE("Testing FX option sensitivities against QL analytic greeks");
1259
1260 SavedSettings backup;
1261
1262 ObservationMode::Mode backupMode = ObservationMode::instance().mode();
1263 ObservationMode::instance().setMode(ObservationMode::Mode::None);
1264
1265 Date today = Date(14, April, 2016); // Settings::instance().evaluationDate();
1266 Settings::instance().evaluationDate() = today;
1267
1268 BOOST_TEST_MESSAGE("Today is " << today);
1269
1270 // Init market
1271 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
1272
1273 // build scenario sim market parameters
1274 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
1276
1277 // sensitivity config
1278 QuantLib::ext::shared_ptr<SensitivityScenarioData> sensiData = TestConfigurationObjects::setupSensitivityScenarioData5();
1279
1280 map<string, SensitivityScenarioData::VolShiftData>& fxvs = sensiData->fxVolShiftData();
1281 for (auto& it : fxvs) {
1282 it.second.shiftSize = 0.0001; // want a smaller shift size than 1.0 to test the analytic sensitivities
1283 }
1284 map<string, SensitivityScenarioData::SpotShiftData>& fxs = sensiData->fxShiftData();
1285 for (auto& it : fxs) {
1286 it.second.shiftSize = 0.0001; // want a smaller shift size to test the analytic sensitivities
1287 }
1288
1289 // build sim market
1290 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(initMarket, simMarketData);
1291
1292 // build scenario factory
1293 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
1294 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<CloneScenarioFactory>(baseScenario);
1295
1296 // build scenario generator
1297 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
1298 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
1299 scenarioFactory, false);
1300 simMarket->scenarioGenerator() = scenarioGenerator;
1301
1302 // build portfolio
1303 QuantLib::ext::shared_ptr<EngineData> data = QuantLib::ext::make_shared<EngineData>();
1304 data->model("FxOption") = "GarmanKohlhagen";
1305 data->engine("FxOption") = "AnalyticEuropeanEngine";
1306 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, simMarket);
1307
1308 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
1309 Size trnCount = 0;
1310 portfolio->add(buildFxOption("Call_1", "Long", "Call", 1, "USD", 100000000.0, "EUR", 100000000.0));
1311 trnCount++;
1312 portfolio->add(buildFxOption("Put_1", "Long", "Put", 1, "USD", 100000000.0, "EUR", 100000000.0));
1313 trnCount++;
1314 portfolio->add(buildFxOption("Call_2", "Short", "Call", 2, "GBP", 100000000.0, "CHF", 130000000.0));
1315 trnCount++;
1316 portfolio->add(buildFxOption("Put_2", "Short", "Put", 2, "GBP", 100000000.0, "CHF", 130000000.0));
1317 trnCount++;
1318 portfolio->add(buildFxOption("Call_3", "Long", "Call", 1, "EUR", 100000000.0, "USD", 100000000.0));
1319 trnCount++;
1320 portfolio->add(buildFxOption("Put_3", "Short", "Put", 1, "EUR", 100000000.0, "USD", 100000000.0));
1321 trnCount++;
1322 portfolio->add(buildFxOption("Call_4", "Long", "Call", 1, "JPY", 10000000000.0, "EUR", 100000000.0));
1323 trnCount++;
1324 portfolio->add(buildFxOption("Call_5", "Long", "Call", 1, "EUR", 100000000.0, "JPY", 10000000000.0));
1325 trnCount++;
1326 portfolio->build(factory);
1327 BOOST_CHECK_EQUAL(portfolio->size(), trnCount);
1328
1329 struct AnalyticInfo {
1330 string id;
1331 string npvCcy;
1332 string forCcy;
1333 string domCcy;
1334 Real fx;
1335 Real trnFx;
1336 Real baseNpv;
1337 Real qlNpv;
1338 Real delta;
1339 Real gamma;
1340 Real vega;
1341 Real rho;
1342 Real divRho;
1343 Real fxForBase;
1344 };
1345 map<string, AnalyticInfo> qlInfoMap;
1346 for (const auto& [tradeId, trade] : portfolio->trades()) {
1347 AnalyticInfo info;
1348 QuantLib::ext::shared_ptr<ore::data::FxOption> fxoTrn = QuantLib::ext::dynamic_pointer_cast<ore::data::FxOption>(trade);
1349 BOOST_CHECK(fxoTrn);
1350 info.id = tradeId;
1351 info.npvCcy = trade->npvCurrency();
1352 info.forCcy = fxoTrn->boughtCurrency();
1353 info.domCcy = fxoTrn->soldCurrency();
1354 BOOST_CHECK_EQUAL(info.npvCcy, info.domCcy);
1355 string pair = info.npvCcy + simMarketData->baseCcy();
1356 info.fx = initMarket->fxRate(pair)->value();
1357 string trnPair = info.forCcy + info.domCcy;
1358 info.trnFx = initMarket->fxRate(trnPair)->value();
1359 string forPair = info.forCcy + simMarketData->baseCcy();
1360 info.fxForBase = initMarket->fxRate(forPair)->value();
1361 info.baseNpv = trade->instrument()->NPV() * info.fx;
1362 QuantLib::ext::shared_ptr<QuantLib::VanillaOption> qlOpt =
1363 QuantLib::ext::dynamic_pointer_cast<QuantLib::VanillaOption>(trade->instrument()->qlInstrument());
1364 BOOST_CHECK(qlOpt);
1365 Position::Type positionType = parsePositionType(fxoTrn->option().longShort());
1366 Real bsInd = (positionType == QuantLib::Position::Long ? 1.0 : -1.0);
1367 info.qlNpv = qlOpt->NPV() * fxoTrn->boughtAmount() * bsInd;
1368 info.delta = qlOpt->delta() * fxoTrn->boughtAmount() * bsInd;
1369 info.gamma = qlOpt->gamma() * fxoTrn->boughtAmount() * bsInd;
1370 info.vega = qlOpt->vega() * fxoTrn->boughtAmount() * bsInd;
1371 info.rho = qlOpt->rho() * fxoTrn->boughtAmount() * bsInd;
1372 info.divRho = qlOpt->dividendRho() * fxoTrn->boughtAmount() * bsInd;
1373 BOOST_CHECK_CLOSE(info.fx, info.baseNpv / info.qlNpv, 0.01);
1374 qlInfoMap[info.id] = info;
1375 }
1376
1377 bool recalibrateModels = true; // nothing to calibrate here
1378 bool useOriginalFxForBaseCcyConv = true; // convert sensi to EUR using original FX rate (not the shifted rate)
1379 QuantLib::ext::shared_ptr<SensitivityAnalysis> sa = QuantLib::ext::make_shared<SensitivityAnalysis>(
1380 portfolio, initMarket, Market::defaultConfiguration, data, simMarketData, sensiData, recalibrateModels, nullptr,
1381 nullptr, useOriginalFxForBaseCcyConv);
1382 sa->generateSensitivities();
1383
1384 map<pair<string, string>, Real> deltaMap;
1385 map<pair<string, string>, Real> gammaMap;
1386 std::set<string> sensiTrades;
1387 for (const auto& [pid, _] : portfolio->trades()) {
1388 sensiTrades.insert(pid);
1389 for (const auto& f : sa->sensiCube()->factors()) {
1390 auto des = sa->sensiCube()->factorDescription(f);
1391 deltaMap[make_pair(pid, des)] = sa->sensiCube()->delta(pid, f);
1392 gammaMap[make_pair(pid, des)] = sa->sensiCube()->gamma(pid, f);
1393 }
1394 }
1395
1396 struct SensiResults {
1397 string id;
1398 Real baseNpv;
1399 Real forDiscountDelta;
1400 Real forIndexDelta;
1401 Real forYcDelta;
1402 Real domDiscountDelta;
1403 Real domIndexDelta;
1404 Real domYcDelta;
1405 Real fxSpotDeltaFor;
1406 Real fxSpotDeltaDom;
1407 Real fxVolDelta;
1408 Real fxSpotGammaFor;
1409 Real fxSpotGammaDom;
1410 Real fxRateSensiFor;
1411 Real fxRateSensiDom;
1412 bool hasFxSpotDomSensi;
1413 bool hasFxSpotForSensi;
1414 string fxSensiForCcy;
1415 string fxSensiDomCcy;
1416 };
1417
1418 Real epsilon = 1.e-15; // a small number
1419 string discountCurveStr = "DiscountCurve";
1420 string indexCurveStr = "IndexCurve";
1421 string yieldCurveStr = "YieldCurve";
1422 string fxSpotStr = "FXSpot";
1423 string fxVolStr = "FXVolatility";
1424 string swaptionStr = "SwaptionVolatility";
1425 string capStr = "OptionletVolatility";
1426 for (auto it : qlInfoMap) {
1427 string id = it.first;
1428 BOOST_CHECK(sensiTrades.find(id) != sensiTrades.end());
1429 AnalyticInfo qlInfo = it.second;
1430 SensiResults res = {string(""), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1431 0.0, 0.0, 0.0, 0.0, 0.0, false, false, string(""), string("")};
1432 for (auto it2 : deltaMap) {
1433 pair<string, string> sensiKey = it2.first;
1434 string sensiTrnId = it2.first.first;
1435 if (sensiTrnId != id)
1436 continue;
1437 res.id = sensiTrnId;
1438 res.baseNpv = sa->sensiCube()->npv(sensiTrnId);
1439 string sensiId = it2.first.second;
1440 Real sensiVal = it2.second;
1441 if (std::fabs(sensiVal) < epsilon) // not interested in zero sensis
1442 continue;
1443 vector<string> tokens;
1444 boost::split(tokens, sensiId, boost::is_any_of("/-"));
1445 BOOST_CHECK(tokens.size() > 0);
1446 bool isDiscountCurve = (tokens[0] == discountCurveStr);
1447 bool isIndexCurve = (tokens[0] == indexCurveStr);
1448 bool isYieldCurve = (tokens[0] == yieldCurveStr);
1449 bool isFxSpot = (tokens[0] == fxSpotStr);
1450 bool isFxVol = (tokens[0] == fxVolStr);
1451 bool isSwaption = (tokens[0] == swaptionStr);
1452 bool isCapFloorlet = (tokens[0] == capStr);
1453 BOOST_CHECK(!(isSwaption || isCapFloorlet)); // no relation to fx options
1454 if (isDiscountCurve || isIndexCurve || isYieldCurve) {
1455 BOOST_CHECK(tokens.size() > 2);
1456 string ccy = tokens[1];
1457 bool isFgnCcySensi = (ccy == qlInfo.forCcy);
1458 bool isDomCcySensi = (ccy == qlInfo.domCcy);
1459 BOOST_CHECK(isFgnCcySensi || isDomCcySensi);
1460 if (isDiscountCurve) {
1461 if (isFgnCcySensi)
1462 res.forDiscountDelta += sensiVal;
1463 else if (isDomCcySensi)
1464 res.domDiscountDelta += sensiVal;
1465 } else if (isIndexCurve) {
1466 if (isFgnCcySensi)
1467 res.forIndexDelta += sensiVal;
1468 else if (isDomCcySensi)
1469 res.domIndexDelta += sensiVal;
1470 } else if (isYieldCurve) {
1471 if (isFgnCcySensi)
1472 res.forYcDelta += sensiVal;
1473 if (isDomCcySensi)
1474 res.domYcDelta += sensiVal;
1475 }
1476 continue;
1477 } else if (isFxSpot) {
1478 BOOST_CHECK(tokens.size() > 2);
1479 string pair = tokens[1];
1480 BOOST_CHECK_EQUAL(pair.length(), 6);
1481 string sensiForCcy = pair.substr(0, 3);
1482 string sensiDomCcy = pair.substr(3, 3);
1483 Real fxSensi = initMarket->fxRate(pair)->value();
1484 bool isSensiForBase = (sensiForCcy == simMarketData->baseCcy());
1485 bool isSensiDomBase = (sensiDomCcy == simMarketData->baseCcy());
1486 // TO-DO this could be relaxed to handle case where market stores the currency pairs the other way
1487 // around
1488 BOOST_CHECK(isSensiForBase && !isSensiDomBase);
1489 bool hasGamma = (gammaMap.find(sensiKey) != gammaMap.end());
1490 BOOST_CHECK(hasGamma);
1491 Real gammaVal = 0.0;
1492 if (hasGamma) {
1493 gammaVal = gammaMap[sensiKey];
1494 }
1495 if (isSensiForBase) {
1496 if (sensiDomCcy == qlInfo.forCcy) {
1497 res.fxSpotDeltaFor += sensiVal;
1498 res.fxRateSensiFor = fxSensi;
1499 res.hasFxSpotForSensi = true;
1500 res.fxSpotGammaFor += gammaVal;
1501 } else if (sensiDomCcy == qlInfo.domCcy) {
1502 res.fxSpotDeltaDom += sensiVal;
1503 res.fxRateSensiDom = fxSensi;
1504 res.hasFxSpotDomSensi = true;
1505 res.fxSpotGammaDom += gammaVal;
1506 }
1507 res.fxSensiForCcy = sensiForCcy;
1508 res.fxSensiDomCcy = sensiDomCcy;
1509 } else {
1510 BOOST_ERROR("This ccy pair configuration not supported yet by this test");
1511 }
1512 continue;
1513 } else if (isFxVol) {
1514 BOOST_CHECK(tokens.size() > 2);
1515 string pair = tokens[1];
1516 BOOST_CHECK_EQUAL(pair.length(), 6);
1517 string sensiForCcy = pair.substr(0, 3);
1518 string sensiDomCcy = pair.substr(3, 3);
1519 BOOST_CHECK((sensiForCcy == qlInfo.forCcy) || (sensiForCcy == qlInfo.domCcy));
1520 BOOST_CHECK((sensiDomCcy == qlInfo.forCcy) || (sensiDomCcy == qlInfo.domCcy));
1521 res.fxVolDelta += sensiVal;
1522 continue;
1523 } else {
1524 BOOST_ERROR("Unrecognised sensitivity factor - " << sensiId);
1525 }
1526 }
1527 // HERE COME THE ACTUAL SENSI COMPARISONS
1528 // index and yc sensis not expected
1529 BOOST_CHECK_EQUAL(res.forIndexDelta, 0.0);
1530 BOOST_CHECK_EQUAL(res.domIndexDelta, 0.0);
1531 BOOST_CHECK_EQUAL(res.forYcDelta, 0.0);
1532 BOOST_CHECK_EQUAL(res.domYcDelta, 0.0);
1533 BOOST_TEST_MESSAGE(
1534 "SA: id=" << res.id << ", npv=" << res.baseNpv << ", forDiscountDelta=" << res.forDiscountDelta
1535 << ", domDiscountDelta=" << res.domDiscountDelta << ", fxSpotDeltaFor=" << res.fxSpotDeltaFor
1536 << ", fxSpotDeltaDom=" << res.fxSpotDeltaDom << ", fxVolDelta=" << res.fxVolDelta
1537 << ", fxSpotGammaFor=" << res.fxSpotGammaFor << ", fxSpotGammaDom=" << res.fxSpotGammaDom
1538 << ", hasFxDom=" << res.hasFxSpotDomSensi << ", hasFxFor=" << res.hasFxSpotForSensi);
1539 BOOST_TEST_MESSAGE("QL: id=" << qlInfo.id << ", forCcy=" << qlInfo.forCcy << ", domCcy=" << qlInfo.domCcy
1540 << ", fx=" << qlInfo.fx << ", npv=" << qlInfo.baseNpv
1541 << ", ccyNpv=" << qlInfo.qlNpv << ", delta=" << qlInfo.delta
1542 << ", gamma=" << qlInfo.gamma << ", vega=" << qlInfo.vega << ", rho=" << qlInfo.rho
1543 << ", divRho=" << qlInfo.divRho);
1544 Real bp = 1.e-4;
1545 Real tol = 1.0; // % relative tolerance
1546 // rate sensis are 1bp absolute shifts
1547 // fx vol sensis are 1bp relative shifts
1548 // fx spot sensis are 1pb relative shifts
1549 BOOST_CHECK_CLOSE(res.domDiscountDelta, qlInfo.rho * qlInfo.fx * bp, tol);
1550 BOOST_CHECK_CLOSE(res.forDiscountDelta, qlInfo.divRho * qlInfo.fx * bp, tol);
1551 Real fxVol = initMarket->fxVol(qlInfo.forCcy + qlInfo.domCcy)
1552 ->blackVol(1.0, 1.0, true); // TO-DO more appropriate vol extraction
1553 BOOST_CHECK_CLOSE(res.fxVolDelta, qlInfo.vega * qlInfo.fx * (bp * fxVol), tol);
1554 if (res.hasFxSpotDomSensi) {
1555 Real qlGamma = qlInfo.gamma;
1556 if ((res.fxSensiForCcy == qlInfo.domCcy) && (res.fxSensiDomCcy == qlInfo.forCcy)) {
1557 // the QL sensi is relative to the inverted FX quote, so we need to convert to the sensi that we want
1558 // (via chain rule)
1559 Real ql_fx = qlInfo.trnFx;
1560 qlGamma = 2 * pow(ql_fx, 3) * qlInfo.delta + pow(ql_fx, 4) * qlInfo.gamma;
1561 } else if ((res.fxSensiForCcy == qlInfo.forCcy) && (res.fxSensiDomCcy == qlInfo.domCcy)) {
1562 qlGamma = qlInfo.gamma;
1563 } else {
1564 // perform the necessary conversion for cross quotes
1565 Real otherFx = 1.0 / qlInfo.fxForBase;
1566 qlGamma = qlInfo.gamma / pow(otherFx, 2);
1567 }
1568 BOOST_CHECK_CLOSE(res.fxSpotDeltaDom, qlInfo.delta * qlInfo.fx * (bp * qlInfo.trnFx), tol);
1569 BOOST_CHECK_CLOSE(res.fxSpotGammaDom, qlGamma * qlInfo.fx * (pow(bp * res.fxRateSensiDom, 2)), tol);
1570 }
1571 if (res.hasFxSpotForSensi) {
1572 Real qlGamma = qlInfo.gamma;
1573 if ((res.fxSensiForCcy == qlInfo.domCcy) && (res.fxSensiDomCcy == qlInfo.forCcy)) {
1574 // the QL sensi is relative to the inverted FX quote, so we need to convert to the sensi that we want
1575 // (via chain rule)
1576 Real ql_fx = qlInfo.trnFx;
1577 qlGamma = 2 * pow(ql_fx, 3) * qlInfo.delta + pow(ql_fx, 4) * qlInfo.gamma;
1578 } else if ((res.fxSensiForCcy == qlInfo.forCcy) && (res.fxSensiDomCcy == qlInfo.domCcy)) {
1579 qlGamma = qlInfo.gamma;
1580 } else {
1581 // perform the necessary conversion for cross quotes
1582 Real y = 1.0 / qlInfo.fx; // BASE/TrnDom
1583 Real z = 1.0 / qlInfo.fxForBase; // BASE/TrnFor
1584 qlGamma = ((2.0 * y) / pow(z, 3)) * qlInfo.delta + (y / pow(z, 4)) * qlInfo.gamma;
1585 }
1586 BOOST_CHECK_CLOSE(res.fxSpotDeltaFor, qlInfo.delta * qlInfo.fx * (-bp * qlInfo.trnFx), tol);
1587 BOOST_CHECK_CLOSE(res.fxSpotGammaFor, qlGamma * qlInfo.fx * (pow(-bp * res.fxRateSensiFor, 2)), tol);
1588 }
1589 }
1590 ObservationMode::instance().setMode(backupMode);
1591 IndexManager::instance().clearHistories();
1592}
QuantLib::ext::shared_ptr< Trade > buildFxOption(string id, string longShort, string putCall, Size expiry, string boughtCcy, Real boughtAmount, string soldCcy, Real soldAmount, Real premium, string premiumCcy, string premiumDate)
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [10/10]

BOOST_AUTO_TEST_CASE ( testCrossGamma  )

Definition at line 1594 of file sensitivityanalysis.cpp.

1594 {
1595
1596 BOOST_TEST_MESSAGE("Testing cross-gamma sensitivities against cached results");
1597
1598 SavedSettings backup;
1599
1600 ObservationMode::Mode backupMode = ObservationMode::instance().mode();
1601 ObservationMode::instance().setMode(ObservationMode::Mode::None);
1602
1603 Date today = Date(14, April, 2016);
1604 Settings::instance().evaluationDate() = today;
1605
1606 BOOST_TEST_MESSAGE("Today is " << today);
1607
1608 // Init market
1609 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
1610
1611 // build scenario sim market parameters
1612 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> simMarketData =
1614
1615 // sensitivity config
1616 QuantLib::ext::shared_ptr<SensitivityScenarioData> sensiData = TestConfigurationObjects::setupSensitivityScenarioData5();
1617 vector<pair<string, string>>& cgFilter = sensiData->crossGammaFilter();
1618 BOOST_CHECK_EQUAL(cgFilter.size(), 0);
1619 cgFilter.push_back(pair<string, string>("DiscountCurve/EUR", "DiscountCurve/EUR"));
1620 cgFilter.push_back(pair<string, string>("DiscountCurve/EUR", "IndexCurve/EUR"));
1621 cgFilter.push_back(pair<string, string>("IndexCurve/EUR", "IndexCurve/EUR"));
1622 cgFilter.push_back(pair<string, string>("IndexCurve/USD", "IndexCurve/USD"));
1623 cgFilter.push_back(pair<string, string>("DiscountCurve/USD", "DiscountCurve/USD"));
1624 cgFilter.push_back(pair<string, string>("OptionletVolatility/EUR", "OptionletVolatility/EUR"));
1625 cgFilter.push_back(pair<string, string>("OptionletVolatility/EUR", "DiscountCurve/EUR"));
1626 cgFilter.push_back(pair<string, string>("OptionletVolatility/EUR", "DiscountCurve/USD"));
1627 cgFilter.push_back(pair<string, string>("OptionletVolatility/USD", "DiscountCurve/USD"));
1628 cgFilter.push_back(pair<string, string>("OptionletVolatility/USD", "OptionletVolatility/USD"));
1629 cgFilter.push_back(pair<string, string>("SwaptionVolatility/EUR", "SwaptionVolatility/EUR"));
1630 cgFilter.push_back(pair<string, string>("SwaptionVolatility/EUR", "IndexCurve/EUR"));
1631 cgFilter.push_back(pair<string, string>("SwaptionVolatility/EUR", "DiscountCurve/EUR"));
1632 cgFilter.push_back(pair<string, string>("FXVolatility/EURUSD", "DiscountCurve/EUR"));
1633 cgFilter.push_back(pair<string, string>("FXSpot/EURUSD", "DiscountCurve/EUR"));
1634 cgFilter.push_back(pair<string, string>("FXSpot/EURUSD", "IndexCurve/EUR"));
1635 cgFilter.push_back(pair<string, string>("FXSpot/EURGBP", "DiscountCurve/GBP"));
1636
1637 // build scenario sim market
1638 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket> simMarket =
1639 QuantLib::ext::make_shared<analytics::ScenarioSimMarket>(initMarket, simMarketData);
1640
1641 // build scenario factory
1642 QuantLib::ext::shared_ptr<Scenario> baseScenario = simMarket->baseScenario();
1643 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<CloneScenarioFactory>(baseScenario);
1644
1645 // build scenario generator
1646 QuantLib::ext::shared_ptr<SensitivityScenarioGenerator> scenarioGenerator =
1647 QuantLib::ext::make_shared<SensitivityScenarioGenerator>(sensiData, baseScenario, simMarketData, simMarket,
1648 scenarioFactory, false);
1649 simMarket->scenarioGenerator() = scenarioGenerator;
1650
1651 // build portfolio
1652 QuantLib::ext::shared_ptr<EngineData> data = QuantLib::ext::make_shared<EngineData>();
1653 data->model("Swap") = "DiscountedCashflows";
1654 data->engine("Swap") = "DiscountingSwapEngine";
1655 data->model("CrossCurrencySwap") = "DiscountedCashflows";
1656 data->engine("CrossCurrencySwap") = "DiscountingCrossCurrencySwapEngine";
1657 data->model("EuropeanSwaption") = "BlackBachelier";
1658 data->engine("EuropeanSwaption") = "BlackBachelierSwaptionEngine";
1659 data->model("BermudanSwaption") = "LGM";
1660 data->engine("BermudanSwaption") = "Grid";
1661 map<string, string> bermudanModelParams;
1662 bermudanModelParams["Calibration"] = "Bootstrap";
1663 bermudanModelParams["CalibrationStrategy"] = "Coterminal";
1664 bermudanModelParams["Reversion"] = "0.03";
1665 bermudanModelParams["ReversionType"] = "HullWhite";
1666 bermudanModelParams["Volatility"] = "0.01";
1667 bermudanModelParams["CalibrationType"] = "Hagan";
1668 bermudanModelParams["Tolerance"] = "0.0001";
1669 data->modelParameters("BermudanSwaption") = bermudanModelParams;
1670 map<string, string> bermudanEngineParams;
1671 bermudanEngineParams["sy"] = "3.0";
1672 bermudanEngineParams["ny"] = "10";
1673 bermudanEngineParams["sx"] = "3.0";
1674 bermudanEngineParams["nx"] = "10";
1675 data->engineParameters("BermudanSwaption") = bermudanEngineParams;
1676 data->model("FxForward") = "DiscountedCashflows";
1677 data->engine("FxForward") = "DiscountingFxForwardEngine";
1678 data->model("FxOption") = "GarmanKohlhagen";
1679 data->engine("FxOption") = "AnalyticEuropeanEngine";
1680 data->model("CapFloor") = "IborCapModel";
1681 data->engine("CapFloor") = "IborCapEngine";
1682 data->model("CapFlooredIborLeg") = "BlackOrBachelier";
1683 data->engine("CapFlooredIborLeg") = "BlackIborCouponPricer";
1684 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, simMarket);
1685
1686 // QuantLib::ext::shared_ptr<Portfolio> portfolio = buildSwapPortfolio(portfolioSize, factory);
1687 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
1688 Size trnCount = 0;
1689 portfolio->add(buildSwap("1_Swap_EUR", "EUR", true, 10000000.0, 0, 10, 0.03, 0.00, "1Y", "30/360", "6M", "A360",
1690 "EUR-EURIBOR-6M"));
1691 trnCount++;
1692 portfolio->add(buildSwap("2_Swap_USD", "USD", true, 10000000.0, 0, 15, 0.02, 0.00, "6M", "30/360", "3M", "A360",
1693 "USD-LIBOR-3M"));
1694 trnCount++;
1695 portfolio->add(buildSwap("3_Swap_GBP", "GBP", true, 10000000.0, 0, 20, 0.04, 0.00, "6M", "30/360", "3M", "A360",
1696 "GBP-LIBOR-6M"));
1697 trnCount++;
1698 portfolio->add(buildSwap("4_Swap_JPY", "JPY", true, 1000000000.0, 0, 5, 0.01, 0.00, "6M", "30/360", "3M", "A360",
1699 "JPY-LIBOR-6M"));
1700 trnCount++;
1701 portfolio->add(buildEuropeanSwaption("5_Swaption_EUR", "Long", "EUR", true, 1000000.0, 10, 10, 0.02, 0.00, "1Y",
1702 "30/360", "6M", "A360", "EUR-EURIBOR-6M", "Physical"));
1703 trnCount++;
1704 portfolio->add(buildEuropeanSwaption("6_Swaption_EUR", "Long", "EUR", true, 1000000.0, 2, 5, 0.02, 0.00, "1Y",
1705 "30/360", "6M", "A360", "EUR-EURIBOR-6M", "Physical"));
1706 trnCount++;
1707 portfolio->add(buildFxOption("7_FxOption_EUR_USD", "Long", "Call", 3, "EUR", 10000000.0, "USD", 11000000.0));
1708 trnCount++;
1709 portfolio->add(buildFxOption("8_FxOption_EUR_GBP", "Long", "Call", 7, "EUR", 10000000.0, "GBP", 11000000.0));
1710 trnCount++;
1711 portfolio->add(buildCap("9_Cap_EUR", "EUR", "Long", 0.05, 1000000.0, 0, 10, "6M", "A360", "EUR-EURIBOR-6M"));
1712 trnCount++;
1713 portfolio->add(buildFloor("10_Floor_USD", "USD", "Long", 0.01, 1000000.0, 0, 10, "3M", "A360", "USD-LIBOR-3M"));
1714 trnCount++;
1715 portfolio->build(factory);
1716 BOOST_CHECK_EQUAL(trnCount, portfolio->size());
1717
1718 bool useOriginalFxForBaseCcyConv = false;
1719 QuantLib::ext::shared_ptr<SensitivityAnalysis> sa =
1720 QuantLib::ext::make_shared<SensitivityAnalysis>(portfolio, initMarket, Market::defaultConfiguration, data,
1721 simMarketData, sensiData, useOriginalFxForBaseCcyConv);
1722 sa->generateSensitivities();
1723
1724 std::vector<ore::analytics::SensitivityScenarioGenerator::ScenarioDescription> scenDesc =
1725 sa->scenarioGenerator()->scenarioDescriptions();
1726
1727 struct GammaResult {
1728 string id;
1729 string factor1;
1730 string factor2;
1731 Real crossgamma;
1732 };
1733
1734 vector<GammaResult> cachedResults = {
1735 {"10_Floor_USD", "DiscountCurve/USD/1/1Y", "OptionletVolatility/USD/0/1Y/0.01", -1.14292006e-05},
1736 {"10_Floor_USD", "DiscountCurve/USD/1/1Y", "OptionletVolatility/USD/5/2Y/0.01", -4.75325714e-06},
1737 {"10_Floor_USD", "DiscountCurve/USD/2/2Y", "OptionletVolatility/USD/0/1Y/0.01", -5.72627955e-05},
1738 {"10_Floor_USD", "DiscountCurve/USD/2/2Y", "OptionletVolatility/USD/10/3Y/0.01", -6.0423848e-05},
1739 {"10_Floor_USD", "DiscountCurve/USD/2/2Y", "OptionletVolatility/USD/5/2Y/0.01", -0.0003282313},
1740 {"10_Floor_USD", "DiscountCurve/USD/3/3Y", "DiscountCurve/USD/4/5Y", 2.38844859e-06},
1741 {"10_Floor_USD", "DiscountCurve/USD/3/3Y", "OptionletVolatility/USD/10/3Y/0.01", -0.0032767365},
1742 {"10_Floor_USD", "DiscountCurve/USD/3/3Y", "OptionletVolatility/USD/15/5Y/0.01", -0.00124021334},
1743 {"10_Floor_USD", "DiscountCurve/USD/3/3Y", "OptionletVolatility/USD/5/2Y/0.01", -0.000490482465},
1744 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", "DiscountCurve/USD/5/7Y", 4.56303869e-05},
1745 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", "OptionletVolatility/USD/10/3Y/0.01", -0.00309734116},
1746 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", "OptionletVolatility/USD/15/5Y/0.01", -0.0154732663},
1747 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", "OptionletVolatility/USD/20/10Y/0.01", -0.0011774169},
1748 {"10_Floor_USD", "DiscountCurve/USD/4/5Y", "OptionletVolatility/USD/5/2Y/0.01", -1.15352532e-06},
1749 {"10_Floor_USD", "DiscountCurve/USD/5/7Y", "DiscountCurve/USD/6/10Y", 0.00024726356},
1750 {"10_Floor_USD", "DiscountCurve/USD/5/7Y", "OptionletVolatility/USD/10/3Y/0.01", -1.30253466e-06},
1751 {"10_Floor_USD", "DiscountCurve/USD/5/7Y", "OptionletVolatility/USD/15/5Y/0.01", -0.0325565983},
1752 {"10_Floor_USD", "DiscountCurve/USD/5/7Y", "OptionletVolatility/USD/20/10Y/0.01", -0.026175823},
1753 {"10_Floor_USD", "DiscountCurve/USD/6/10Y", "OptionletVolatility/USD/15/5Y/0.01", -0.0151532607},
1754 {"10_Floor_USD", "DiscountCurve/USD/6/10Y", "OptionletVolatility/USD/20/10Y/0.01", -0.0524224726},
1755 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/0/6M", "IndexCurve/USD-LIBOR-3M/1/1Y", -0.000206363102},
1756 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/0/6M", "IndexCurve/USD-LIBOR-3M/2/2Y", -9.83482187e-06},
1757 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", "IndexCurve/USD-LIBOR-3M/2/2Y", -0.0181056744},
1758 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", "IndexCurve/USD-LIBOR-3M/3/3Y", -0.000292001105},
1759 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", "IndexCurve/USD-LIBOR-3M/3/3Y", -0.197980608},
1760 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", "IndexCurve/USD-LIBOR-3M/4/5Y", -0.000472459871},
1761 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/3/3Y", "IndexCurve/USD-LIBOR-3M/4/5Y", -0.506924993},
1762 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/4/5Y", "IndexCurve/USD-LIBOR-3M/5/7Y", -1.31308851},
1763 {"10_Floor_USD", "IndexCurve/USD-LIBOR-3M/5/7Y", "IndexCurve/USD-LIBOR-3M/6/10Y", -1.79643202},
1764 {"10_Floor_USD", "OptionletVolatility/USD/0/1Y/0.01", "OptionletVolatility/USD/5/2Y/0.01", 0.0214845769},
1765 {"10_Floor_USD", "OptionletVolatility/USD/10/3Y/0.01", "OptionletVolatility/USD/15/5Y/0.01", 0.224709734},
1766 {"10_Floor_USD", "OptionletVolatility/USD/15/5Y/0.01", "OptionletVolatility/USD/20/10Y/0.01", 0.693920762},
1767 {"10_Floor_USD", "OptionletVolatility/USD/5/2Y/0.01", "OptionletVolatility/USD/10/3Y/0.01", 0.0649121282},
1768 {"1_Swap_EUR", "DiscountCurve/EUR/1/1Y", "DiscountCurve/EUR/2/2Y", 0.000439456664},
1769 {"1_Swap_EUR", "DiscountCurve/EUR/1/1Y", "IndexCurve/EUR-EURIBOR-6M/0/6M", 0.0488603441},
1770 {"1_Swap_EUR", "DiscountCurve/EUR/1/1Y", "IndexCurve/EUR-EURIBOR-6M/1/1Y", -0.0725961695},
1771 {"1_Swap_EUR", "DiscountCurve/EUR/1/1Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.0499326873},
1772 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", "DiscountCurve/EUR/3/3Y", 0.00136525929},
1773 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/0/6M", 0.00108389393},
1774 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/1/1Y", 0.141865394},
1775 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.191425738},
1776 {"1_Swap_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.1454702},
1777 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", "DiscountCurve/EUR/4/5Y", 0.00183080882},
1778 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/1/1Y", 0.000784549396},
1779 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", 0.425320865},
1780 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.337527203},
1781 {"1_Swap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.560276813},
1782 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", "DiscountCurve/EUR/5/7Y", -0.00376823638},
1783 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", 0.000516382745},
1784 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 0.91807051},
1785 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.606871969},
1786 {"1_Swap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -1.1789221},
1787 {"1_Swap_EUR", "DiscountCurve/EUR/5/7Y", "DiscountCurve/EUR/6/10Y", -0.0210602414},
1788 {"1_Swap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", 1.93838247},
1789 {"1_Swap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.964284878},
1790 {"1_Swap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -2.50079601},
1791 {"1_Swap_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 3.43097423},
1792 {"1_Swap_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -4.9024972},
1793 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/0/6M", "IndexCurve/EUR-EURIBOR-6M/1/1Y", -0.048865166},
1794 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/0/6M", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.00108389556},
1795 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/1/1Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.0924553103},
1796 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/1/1Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.000784546835},
1797 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.281394335},
1798 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.000516386237},
1799 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.359848329},
1800 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.779536431},
1801 {"1_Swap_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.989040876},
1802 {"2_Swap_USD", "DiscountCurve/USD/0/6M", "DiscountCurve/USD/1/1Y", 7.85577577e-05},
1803 {"2_Swap_USD", "DiscountCurve/USD/1/1Y", "DiscountCurve/USD/2/2Y", 0.00034391915},
1804 {"2_Swap_USD", "DiscountCurve/USD/2/2Y", "DiscountCurve/USD/3/3Y", 0.00101750751},
1805 {"2_Swap_USD", "DiscountCurve/USD/3/3Y", "DiscountCurve/USD/4/5Y", 0.00129107304},
1806 {"2_Swap_USD", "DiscountCurve/USD/4/5Y", "DiscountCurve/USD/5/7Y", 0.00885138742},
1807 {"2_Swap_USD", "DiscountCurve/USD/5/7Y", "DiscountCurve/USD/6/10Y", 0.0236235501},
1808 {"2_Swap_USD", "DiscountCurve/USD/6/10Y", "DiscountCurve/USD/7/15Y", 0.07325946},
1809 {"2_Swap_USD", "DiscountCurve/USD/7/15Y", "DiscountCurve/USD/8/20Y", -2.22151866e-05},
1810 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/0/6M", "IndexCurve/USD-LIBOR-3M/1/1Y", -0.0202145245},
1811 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/0/6M", "IndexCurve/USD-LIBOR-3M/2/2Y", -0.000431735534},
1812 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", "IndexCurve/USD-LIBOR-3M/2/2Y", -0.0379707172},
1813 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/1/1Y", "IndexCurve/USD-LIBOR-3M/3/3Y", -0.000316063757},
1814 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", "IndexCurve/USD-LIBOR-3M/3/3Y", -0.11422779},
1815 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/2/2Y", "IndexCurve/USD-LIBOR-3M/4/5Y", -0.000207132776},
1816 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/3/3Y", "IndexCurve/USD-LIBOR-3M/4/5Y", -0.137591099},
1817 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/4/5Y", "IndexCurve/USD-LIBOR-3M/5/7Y", -0.305644142},
1818 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/5/7Y", "IndexCurve/USD-LIBOR-3M/6/10Y", -0.37816313},
1819 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/6/10Y", "IndexCurve/USD-LIBOR-3M/7/15Y", -0.431405343},
1820 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/6/10Y", "IndexCurve/USD-LIBOR-3M/8/20Y", -0.000289136427},
1821 {"2_Swap_USD", "IndexCurve/USD-LIBOR-3M/7/15Y", "IndexCurve/USD-LIBOR-3M/8/20Y", 0.00042894634},
1822 {"3_Swap_GBP", "DiscountCurve/GBP/0/6M", "FXSpot/EURGBP/0/spot", -0.0210289143},
1823 {"3_Swap_GBP", "DiscountCurve/GBP/1/1Y", "FXSpot/EURGBP/0/spot", 0.00639700286},
1824 {"3_Swap_GBP", "DiscountCurve/GBP/2/2Y", "FXSpot/EURGBP/0/spot", 0.0173332273},
1825 {"3_Swap_GBP", "DiscountCurve/GBP/3/3Y", "FXSpot/EURGBP/0/spot", 0.0420620699},
1826 {"3_Swap_GBP", "DiscountCurve/GBP/4/5Y", "FXSpot/EURGBP/0/spot", 0.0715365904},
1827 {"3_Swap_GBP", "DiscountCurve/GBP/5/7Y", "FXSpot/EURGBP/0/spot", 0.124046364},
1828 {"3_Swap_GBP", "DiscountCurve/GBP/6/10Y", "FXSpot/EURGBP/0/spot", 0.245374591},
1829 {"3_Swap_GBP", "DiscountCurve/GBP/7/15Y", "FXSpot/EURGBP/0/spot", 0.388570486},
1830 {"3_Swap_GBP", "DiscountCurve/GBP/8/20Y", "FXSpot/EURGBP/0/spot", -0.308991311},
1831 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "DiscountCurve/EUR/7/15Y", 0.00500290218},
1832 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "DiscountCurve/EUR/8/20Y", -0.000119650445},
1833 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 0.193956982},
1834 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/7/15Y", -0.274626882},
1835 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/8/20Y", -0.0230959074},
1836 {"5_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", -0.0783525323},
1837 {"5_Swaption_EUR", "DiscountCurve/EUR/7/15Y", "DiscountCurve/EUR/8/20Y", 0.00909222141},
1838 {"5_Swaption_EUR", "DiscountCurve/EUR/7/15Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 0.318897412},
1839 {"5_Swaption_EUR", "DiscountCurve/EUR/7/15Y", "IndexCurve/EUR-EURIBOR-6M/7/15Y", -0.113123194},
1840 {"5_Swaption_EUR", "DiscountCurve/EUR/7/15Y", "IndexCurve/EUR-EURIBOR-6M/8/20Y", -0.492342945},
1841 {"5_Swaption_EUR", "DiscountCurve/EUR/7/15Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", -0.277872723},
1842 {"5_Swaption_EUR", "DiscountCurve/EUR/8/20Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.0231524316},
1843 {"5_Swaption_EUR", "DiscountCurve/EUR/8/20Y", "IndexCurve/EUR-EURIBOR-6M/7/15Y", 0.586686233},
1844 {"5_Swaption_EUR", "DiscountCurve/EUR/8/20Y", "IndexCurve/EUR-EURIBOR-6M/8/20Y", -0.741062084},
1845 {"5_Swaption_EUR", "DiscountCurve/EUR/8/20Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", -0.207022576},
1846 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", "IndexCurve/EUR-EURIBOR-6M/7/15Y", -0.438748346},
1847 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", "IndexCurve/EUR-EURIBOR-6M/8/20Y", -4.80598188},
1848 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", 0.0374673201},
1849 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/7/15Y", "IndexCurve/EUR-EURIBOR-6M/8/20Y", 0.578274874},
1850 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/7/15Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", -0.00750543873},
1851 {"5_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/8/20Y", "SwaptionVolatility/EUR/5/10Y/10Y/ATM", -0.134678679},
1852 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "DiscountCurve/EUR/3/3Y", 7.34225287e-05},
1853 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "DiscountCurve/EUR/4/5Y", -1.39672557e-06},
1854 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "DiscountCurve/EUR/5/7Y", -4.54013752e-05},
1855 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", 0.00762697723},
1856 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.00743193871},
1857 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.000253325068},
1858 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.0091003291},
1859 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -3.51917679e-05},
1860 {"6_Swaption_EUR", "DiscountCurve/EUR/2/2Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", 0.000131859928},
1861 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "DiscountCurve/EUR/4/5Y", 9.36521301e-05},
1862 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "DiscountCurve/EUR/5/7Y", 1.17673517e-06},
1863 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", 0.0217662195},
1864 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.0173020895},
1865 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.0288530865},
1866 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 0.000542137221},
1867 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.0105191516},
1868 {"6_Swaption_EUR", "DiscountCurve/EUR/3/3Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -1.92268253e-05},
1869 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "DiscountCurve/EUR/5/7Y", 0.000380955356},
1870 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.000175687061},
1871 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 0.0470703001},
1872 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.0309543681},
1873 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.0603712949},
1874 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 3.56844794e-06},
1875 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.0194332275},
1876 {"6_Swaption_EUR", "DiscountCurve/EUR/4/5Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -3.55200336e-05},
1877 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "DiscountCurve/EUR/6/10Y", -3.53218638e-06},
1878 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -0.00907584063},
1879 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 0.000465011277},
1880 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", 0.100206381},
1881 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.110760564},
1882 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.000747127526},
1883 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.0212825534},
1884 {"6_Swaption_EUR", "DiscountCurve/EUR/5/7Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -3.89078705e-05},
1885 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -7.04072845e-05},
1886 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 2.90610478e-06},
1887 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", 0.00015803353},
1888 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 1.87784499e-05},
1889 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -2.49396362e-06},
1890 {"6_Swaption_EUR", "DiscountCurve/EUR/6/10Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -5.68973592e-05},
1891 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.0374868064},
1892 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.0510088999},
1893 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -1.83061212},
1894 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.00707882478},
1895 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", 0.0237742927},
1896 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", 4.3887334e-05},
1897 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.0162251326},
1898 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 0.0753026757},
1899 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 0.000291552333},
1900 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.00109766971},
1901 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -2.02629781e-06},
1902 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 0.126414823},
1903 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 0.000493449001},
1904 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.00244118512},
1905 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -4.50652442e-06},
1906 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", 0.0233867156},
1907 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.116493942},
1908 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", -0.000215046299},
1909 {"6_Swaption_EUR", "IndexCurve/EUR-EURIBOR-6M/6/10Y", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", -0.000336383262},
1910 {"6_Swaption_EUR", "SwaptionVolatility/EUR/0/2Y/5Y/ATM", "SwaptionVolatility/EUR/2/5Y/5Y/ATM", 2.31331433e-06},
1911 {"7_FxOption_EUR_USD", "DiscountCurve/EUR/3/3Y", "DiscountCurve/EUR/4/5Y", 0.0027612336},
1912 {"7_FxOption_EUR_USD", "DiscountCurve/EUR/3/3Y", "FXSpot/EURUSD/0/spot", -42.4452352},
1913 {"7_FxOption_EUR_USD", "DiscountCurve/EUR/3/3Y", "FXVolatility/EURUSD/0/5Y/ATM", 168.577072},
1914 {"7_FxOption_EUR_USD", "DiscountCurve/EUR/4/5Y", "FXSpot/EURUSD/0/spot", -0.0776202832},
1915 {"7_FxOption_EUR_USD", "DiscountCurve/EUR/4/5Y", "FXVolatility/EURUSD/0/5Y/ATM", 0.308961544},
1916 {"7_FxOption_EUR_USD", "DiscountCurve/USD/3/3Y", "DiscountCurve/USD/4/5Y", 0.00206353236},
1917 {"8_FxOption_EUR_GBP", "DiscountCurve/GBP/5/7Y", "FXSpot/EURGBP/0/spot", 40.247185},
1918 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", 1.89362237e-06},
1919 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 1.60204674e-05},
1920 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -3.54807444e-05},
1921 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "OptionletVolatility/EUR/14/3Y/0.05", -7.41440071e-05},
1922 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "OptionletVolatility/EUR/19/5Y/0.05", -2.8717396e-05},
1923 {"9_Cap_EUR", "DiscountCurve/EUR/3/3Y", "OptionletVolatility/EUR/9/2Y/0.05", -3.95373826e-06},
1924 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "DiscountCurve/EUR/5/7Y", 1.87918619e-06},
1925 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", 0.000141954676},
1926 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", 0.000136532169},
1927 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.000558091084},
1928 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "OptionletVolatility/EUR/14/3Y/0.05", -0.000195855626},
1929 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "OptionletVolatility/EUR/19/5Y/0.05", -0.0013501175},
1930 {"9_Cap_EUR", "DiscountCurve/EUR/4/5Y", "OptionletVolatility/EUR/24/10Y/0.05", -9.03819837e-05},
1931 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "DiscountCurve/EUR/6/10Y", 2.44087892e-05},
1932 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", 0.00131097735},
1933 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 0.000537751659},
1934 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.00376190752},
1935 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "OptionletVolatility/EUR/19/5Y/0.05", -0.00650057233},
1936 {"9_Cap_EUR", "DiscountCurve/EUR/5/7Y", "OptionletVolatility/EUR/24/10Y/0.05", -0.00529335126},
1937 {"9_Cap_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", 0.00677440175},
1938 {"9_Cap_EUR", "DiscountCurve/EUR/6/10Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.0101355366},
1939 {"9_Cap_EUR", "DiscountCurve/EUR/6/10Y", "OptionletVolatility/EUR/19/5Y/0.05", -0.00512368197},
1940 {"9_Cap_EUR", "DiscountCurve/EUR/6/10Y", "OptionletVolatility/EUR/24/10Y/0.05", -0.0166702108},
1941 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/1/1Y", "IndexCurve/EUR-EURIBOR-6M/2/2Y", -3.22099407e-06},
1942 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/3/3Y", -0.00114858136},
1943 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/2/2Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -3.32910605e-06},
1944 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/3/3Y", "IndexCurve/EUR-EURIBOR-6M/4/5Y", -0.0325351415},
1945 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/4/5Y", "IndexCurve/EUR-EURIBOR-6M/5/7Y", -0.22049032},
1946 {"9_Cap_EUR", "IndexCurve/EUR-EURIBOR-6M/5/7Y", "IndexCurve/EUR-EURIBOR-6M/6/10Y", -0.599739496},
1947 {"9_Cap_EUR", "OptionletVolatility/EUR/14/3Y/0.05", "OptionletVolatility/EUR/19/5Y/0.05", 0.0480747768},
1948 {"9_Cap_EUR", "OptionletVolatility/EUR/19/5Y/0.05", "OptionletVolatility/EUR/24/10Y/0.05", 0.670249341},
1949 {"9_Cap_EUR", "OptionletVolatility/EUR/4/1Y/0.05", "OptionletVolatility/EUR/9/2Y/0.05", 2.49049523e-05},
1950 {"9_Cap_EUR", "OptionletVolatility/EUR/9/2Y/0.05", "OptionletVolatility/EUR/14/3Y/0.05", 0.00180372518}};
1951
1952 map<tuple<string, string, string>, Real> cachedMap;
1953 for (Size i = 0; i < cachedResults.size(); ++i) {
1954 tuple<string, string, string> p(cachedResults[i].id, cachedResults[i].factor1, cachedResults[i].factor2);
1955 cachedMap[p] = cachedResults[i].crossgamma;
1956 }
1957
1958 vector<tuple<string, string, string>> ids;
1959 Real rel_tol = 0.005;
1960 Real threshold = 1.e-6;
1961 Size count = 0;
1962 for (const auto& [tradeId, _] : portfolio->trades()) {
1963 for (auto const& s : scenDesc) {
1964 if (s.type() == ShiftScenarioGenerator::ScenarioDescription::Type::Cross) {
1965 string factor1 = s.factor1();
1966 string factor2 = s.factor2();
1967 ostringstream os;
1968 os << tradeId << "_" << factor1 << "_" << factor2;
1969 string keyStr = os.str();
1970 tuple<string, string, string> key = make_tuple(tradeId, factor1, factor2);
1971 Real crossgamma = sa->sensiCube()->crossGamma(tradeId, make_pair(s.key1(), s.key2()));
1972 if (fabs(crossgamma) >= threshold) {
1973 ids.push_back(make_tuple(tradeId, factor1, factor2));
1974 // BOOST_TEST_MESSAGE("{ \"" << id << std::setprecision(9) << "\", \"" << factor1 << "\", \"" <<
1975 // factor2 <<
1976 // "\", " << crossgamma << " },");
1977 auto cached_it = cachedMap.find(key);
1978 BOOST_CHECK_MESSAGE(cached_it != cachedMap.end(), keyStr << " not found in cached results");
1979 if (cached_it != cachedMap.end()) {
1980 Real cached_cg = cached_it->second;
1981 BOOST_CHECK_CLOSE(crossgamma, cached_cg, rel_tol);
1982 count++;
1983 }
1984 }
1985 }
1986 }
1987 }
1988 BOOST_TEST_MESSAGE("number of cross-gammas checked = " << count);
1989 BOOST_CHECK_MESSAGE(count == cachedResults.size(), "number of non-zero sensitivities ("
1990 << count << ") do not match regression data ("
1991 << cachedResults.size() << ")");
1992 ObservationMode::instance().setMode(backupMode);
1993 IndexManager::instance().clearHistories();
1994}
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 > 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 > buildEuropeanSwaption(string id, string longShort, string ccy, bool isPayer, Real notional, int start, Size term, Real rate, Real spread, string fixedFreq, string fixedDC, string floatFreq, string floatDC, string index, string cashPhysical, Real premium, string premiumCcy, string premiumDate)
std::size_t count
+ Here is the call graph for this function: