Skip to content

Commit a67ae0b

Browse files
pcaspersjenkins
authored andcommitted
Merge remote-tracking branch 'origin/master' into 1.0.75_into_master
1 parent be2cf11 commit a67ae0b

6 files changed

Lines changed: 61 additions & 17 deletions

File tree

OREAnalytics/orea/engine/decomposedsensitivitystream.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,39 @@ DecomposedSensitivityStream::fxRiskShiftSizes(const std::map<std::string, std::v
177177
return results;
178178
}
179179

180-
double DecomposedSensitivityStream::assetSpotShiftSize(const std::string name) const {
180+
double DecomposedSensitivityStream::equitySpotShiftSize(const std::string name) const {
181181
auto eqShiftSizeIt = ssd_->equityShiftData().find(name);
182-
QL_REQUIRE(eqShiftSizeIt != ssd_->equityShiftData().end(), "Couldn't find a shift size for " << name);
182+
QL_REQUIRE(eqShiftSizeIt != ssd_->equityShiftData().end(), "Couldn't find a equity shift size for " << name);
183183
QL_REQUIRE(eqShiftSizeIt->second.shiftType == ore::analytics::ShiftType::Relative,
184184
"Requires a relative eqSpot shift for index decomposition");
185185
return eqShiftSizeIt->second.shiftSize;
186186
}
187187

188+
double DecomposedSensitivityStream::assetSpotShiftSize(const std::string indexName,
189+
const ore::data::CurveSpec::CurveType curveType) const {
190+
if (curveType == ore::data::CurveSpec::CurveType::Equity) {
191+
return equitySpotShiftSize(indexName);
192+
} else if (curveType == ore::data::CurveSpec::CurveType::Commodity) {
193+
return commoditySpotShiftSize(indexName);
194+
} else {
195+
QL_FAIL("unsupported curveType, got "
196+
<< curveType << ". Only Equity and Commodity curves are supported for decomposition.");
197+
}
198+
}
199+
200+
double DecomposedSensitivityStream::commoditySpotShiftSize(const std::string name) const {
201+
auto commShiftSizeIt = ssd_->commodityCurveShiftData().find(name);
202+
if (commShiftSizeIt != ssd_->commodityCurveShiftData().end()) {
203+
QL_REQUIRE(commShiftSizeIt->second->shiftType == ore::analytics::ShiftType::Relative,
204+
"Requires a relative eqSpot shift for index decomposition");
205+
return commShiftSizeIt->second->shiftSize;
206+
} else {
207+
LOG("Could not find a commodity shift size for commodity index "
208+
<< name << ". Try to find a equity spot shift size as fallback")
209+
return equitySpotShiftSize(name);
210+
}
211+
}
212+
188213
std::map<std::string, std::vector<std::string>>
189214
DecomposedSensitivityStream::getConstituentCurrencies(const std::map<std::string, double>& constituents,
190215
const std::string& indexCurrency,
@@ -225,7 +250,7 @@ DecomposedSensitivityStream::indexDecomposition(double delta, const std::string&
225250
auto spotRisk = constituentSpotRiskFromDecomposition(delta, indexWeights);
226251
auto currencies = getConstituentCurrencies(spotRisk, indexCurrency, curveType);
227252
auto fxShifts = fxRiskShiftSizes(currencies);
228-
auto spotShift = assetSpotShiftSize(indexName);
253+
double spotShift = assetSpotShiftSize(indexName, curveType);
229254
auto fxRisk = fxRiskFromDecomposition(spotRisk, currencies, fxShifts, spotShift);
230255
result.spotRisk = spotRisk;
231256
result.fxRisk = fxRisk;
@@ -260,7 +285,7 @@ DecomposedSensitivityStream::decomposeCurrencyHedgedIndexRisk(const SensitivityR
260285
QL_REQUIRE(quantity != QuantLib::Null<double>(),
261286
"CurrencyHedgedIndexDecomposition failed, index quantity cannot be NULL.");
262287

263-
double assetSensiShift = assetSpotShiftSize(indexName);
288+
double assetSensiShift = assetSpotShiftSize(indexName, ore::data::CurveSpec::CurveType::Equity);
264289

265290
double hedgedExposure = sr.delta / assetSensiShift;
266291

OREAnalytics/orea/engine/decomposedsensitivitystream.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ class DecomposedSensitivityStream : public SensitivityStream {
7878
std::map<std::string, double> fxRiskShiftSizes(const std::map<std::string, std::vector<std::string>>& constituentCurrencies) const;
7979

8080
//! Return the asset spot shift size
81-
double assetSpotShiftSize(const std::string name) const;
81+
double assetSpotShiftSize(const std::string name, const ore::data::CurveSpec::CurveType curveType) const;
82+
double equitySpotShiftSize(const std::string name) const;
83+
double commoditySpotShiftSize(const std::string name) const;
8284

8385
std::map<std::string, std::vector<std::string>> getConstituentCurrencies(const std::map<std::string, double>& constituents,
8486
const std::string& indexCurrency,

OREData/ored/model/lgmbuilder.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ createSwaptionHelper(const E& expiry, const T& term, const Handle<SwaptionVolati
8383
const Handle<YieldTermStructure>& yts, BlackCalibrationHelper::CalibrationErrorType errorType,
8484
Real strike, Real shift, const Size settlementDays, const RateAveraging::Type averagingMethod) {
8585

86+
DLOG("LgmBuilder::createSwaptionHelper(" << expiry << ", " << term << ")");
87+
8688
// hardcoded parameters to ensure a robust cailbration:
8789

8890
// 1 If the helper's strike is too far away from the ATM level in terms of the relevant std dev, we move the
@@ -188,6 +190,9 @@ LgmBuilder::LgmBuilder(const boost::shared_ptr<ore::data::Market>& market, const
188190
(data_->calibrateA() || data_->calibrateH()) && data_->calibrationType() != CalibrationType::None;
189191

190192
try {
193+
svts_ = market_->swaptionVol(data_->qualifier(), configuration_);
194+
shortSwapIndex_ =
195+
market_->swapIndex(market_->shortSwapIndexBase(data_->qualifier(), configuration_), configuration_);
191196
swapIndex_ = market_->swapIndex(market_->swapIndexBase(data_->qualifier(), configuration_), configuration_);
192197
// see the comment for dinscountCurve() in the interface
193198
modelDiscountCurve_ = RelinkableHandle<YieldTermStructure>(*swapIndex_->discountingTermStructure());
@@ -201,9 +206,6 @@ LgmBuilder::LgmBuilder(const boost::shared_ptr<ore::data::Market>& market, const
201206
}
202207

203208
if (requiresCalibration_) {
204-
svts_ = market_->swaptionVol(data_->qualifier(), configuration_);
205-
shortSwapIndex_ =
206-
market_->swapIndex(market_->shortSwapIndexBase(data_->qualifier(), configuration_), configuration_);
207209
registerWith(svts_);
208210
marketObserver_->addObservable(swapIndex_->forwardingTermStructure());
209211
marketObserver_->addObservable(shortSwapIndex_->forwardingTermStructure());
@@ -487,6 +489,7 @@ void LgmBuilder::getExpiryAndTerm(const Size j, Period& expiryPb, Period& termPb
487489
}
488490

489491
Real LgmBuilder::getStrike(const Size j) const {
492+
DLOG("LgmBuilder::getStrike(" << j << "): '" << data_->optionStrikes()[j] << "'");
490493
Strike strike = parseStrike(data_->optionStrikes()[j]);
491494
Real strikeValue;
492495
// TODO: Extend strike type coverage

OREData/ored/portfolio/builders/swaption.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,14 @@ boost::shared_ptr<QuantExt::LGM> LGMBermudanAmericanSwaptionEngineBuilder::model
152152
std::copy_if(grid.dates().begin(), grid.dates().end(), std::back_inserter(effExpiries),
153153
[&expiries](const Date& d) { return d >= expiries[0] && d < expiries[1]; });
154154
// simple linear interpolation of calibration strikes between endpoints, this can be refined obviously
155-
effStrikes.resize(effExpiries.size());
156-
Real t0 = Actual365Fixed().yearFraction(today, expiries[0]);
157-
Real t1 = Actual365Fixed().yearFraction(today, expiries[1]);
158-
for(Size i=0;i<effExpiries.size();++i) {
159-
Real t = Actual365Fixed().yearFraction(today, effExpiries[i]);
160-
effStrikes[i] = strikes[0] + (strikes[1] - strikes[0]) / (t1 - t0) * (t - t0);
155+
effStrikes.resize(effExpiries.size(), Null<Real>());
156+
if (strikes[0] != Null<Real>() && strikes[1] != Null<Real>()) {
157+
Real t0 = Actual365Fixed().yearFraction(today, expiries[0]);
158+
Real t1 = Actual365Fixed().yearFraction(today, expiries[1]);
159+
for (Size i = 0; i < effExpiries.size(); ++i) {
160+
Real t = Actual365Fixed().yearFraction(today, effExpiries[i]);
161+
effStrikes[i] = strikes[0] + (strikes[1] - strikes[0]) / (t1 - t0) * (t - t0);
162+
}
161163
}
162164
}
163165

OREData/ored/portfolio/equityoptionposition.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ void EquityOptionPosition::build(const boost::shared_ptr<ore::data::EngineFactor
7474
currencies_.clear();
7575
fxConversion_.clear();
7676

77+
setSensitivityTemplate(std::string()); // default, will usually be overwritten below
78+
7779
for (auto const& u : data_.underlyings()) {
7880

7981
// get equity, populate weight, currency

OREData/ored/portfolio/swaption.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,19 @@ void Swaption::buildBermudanOrAmerican(const boost::shared_ptr<EngineFactory>& e
371371
std::vector<Real> strikes(exerciseBuilder_->noticeDates().size(), Null<Real>());
372372
boost::shared_ptr<InterestRateIndex> index;
373373
for (Size i = 0; i < exerciseBuilder_->noticeDates().size(); ++i) {
374-
Real firstFixedRate = Null<Real>();
375-
Real firstFloatSpread = Null<Real>();
374+
Real firstFixedRate = Null<Real>(), lastFixedRate = Null<Real>();
375+
Real firstFloatSpread = Null<Real>(), lastFloatSpread = Null<Real>();
376376
for (auto const& l : underlying_->legs()) {
377377
for (auto const& c : l) {
378378
if (auto cpn = boost::dynamic_pointer_cast<FixedRateCoupon>(c)) {
379379
if (cpn->accrualStartDate() >= exerciseBuilder_->noticeDates()[i] && firstFixedRate == Null<Real>())
380380
firstFixedRate = cpn->rate();
381+
lastFixedRate = cpn->rate();
381382
} else if (auto cpn = boost::dynamic_pointer_cast<FloatingRateCoupon>(c)) {
382-
firstFloatSpread = cpn->spread();
383+
if (cpn->accrualStartDate() >= exerciseBuilder_->noticeDates()[i] &&
384+
firstFloatSpread == Null<Real>())
385+
firstFloatSpread = cpn->spread();
386+
lastFloatSpread = cpn->spread();
383387
if (index == nullptr) {
384388
if (auto tmp = boost::dynamic_pointer_cast<IborIndex>(cpn->index())) {
385389
DLOG("found ibor / ois index '" << tmp->name() << "'");
@@ -396,6 +400,12 @@ void Swaption::buildBermudanOrAmerican(const boost::shared_ptr<EngineFactory>& e
396400
}
397401
}
398402
}
403+
// if no first fixed rate (float spread) was found, fall back on the last values
404+
if(firstFixedRate == Null<Real>())
405+
firstFixedRate = lastFixedRate;
406+
if(firstFloatSpread == Null<Real>())
407+
firstFloatSpread = lastFloatSpread;
408+
// construct calibration strike
399409
if (firstFixedRate != Null<Real>()) {
400410
strikes[i] = firstFixedRate;
401411
if (firstFloatSpread != Null<Real>()) {

0 commit comments

Comments
 (0)