Skip to content

Commit efcae02

Browse files
committed
Merge branch 'feature/QPR-13682' into 'master'
QPR-13682 add vol smile to additional results Closes QPR-13682 See merge request qs/oreplus!3077
2 parents 7342d56 + bad6e51 commit efcae02

3 files changed

Lines changed: 51 additions & 19 deletions

File tree

OREData/ored/portfolio/builders/varianceswap.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,20 @@ class VarSwapEngineBuilder : public ore::data::CachingPricingEngineBuilder<strin
140140
staticTodaysSpot = parseBool(modelParameter("StaticTodaysSpot", {}, false, "false"));
141141
}
142142

143+
bool generateAdditionalResults = false;
144+
auto p = globalParameters_.find("GenerateAdditionalResults");
145+
if (p != globalParameters_.end()) {
146+
generateAdditionalResults = parseBool(p->second);
147+
}
148+
143149
if (momentType == MomentType::Variance)
144150
return QuantLib::ext::make_shared<QuantExt::GeneralisedReplicatingVarianceSwapEngine>(
145151
index, gbsp, market_->discountCurve(ccy.code(), configuration(ore::data::MarketContext::pricing)),
146-
settings, staticTodaysSpot);
152+
settings, staticTodaysSpot, generateAdditionalResults);
147153
else
148154
return QuantLib::ext::make_shared<QuantExt::VolatilityFromVarianceSwapEngine>(
149155
index, gbsp, market_->discountCurve(ccy.code(), configuration(ore::data::MarketContext::pricing)),
150-
settings, staticTodaysSpot);
156+
settings, staticTodaysSpot, generateAdditionalResults);
151157
}
152158
};
153159

QuantExt/qle/pricingengines/varianceswapgeneralreplicationengine.cpp

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ using namespace QuantLib;
3939
namespace QuantExt {
4040

4141
GeneralisedReplicatingVarianceSwapEngine::GeneralisedReplicatingVarianceSwapEngine(
42-
const QuantLib::ext::shared_ptr<Index>& index, const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
43-
const Handle<YieldTermStructure>& discountingTS, const VarSwapSettings settings, const bool staticTodaysSpot)
42+
const QuantLib::ext::shared_ptr<Index>& index,
43+
const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
44+
const Handle<YieldTermStructure>& discountingTS, const VarSwapSettings settings, const bool staticTodaysSpot,
45+
const bool generateAdditionalResults)
4446
: index_(index), process_(process), discountingTS_(discountingTS), settings_(settings),
45-
staticTodaysSpot_(staticTodaysSpot) {
47+
staticTodaysSpot_(staticTodaysSpot), generateAdditionalResults_(generateAdditionalResults) {
4648

4749
QL_REQUIRE(process_, "Black-Scholes process not present.");
4850

@@ -75,21 +77,26 @@ void GeneralisedReplicatingVarianceSwapEngine::calculate() const {
7577
variance = (calculateFutureVariance(arguments_.maturityDate) * teTime -
7678
calculateFutureVariance(arguments_.startDate) * tsTime) /
7779
fwdTime;
78-
results_.additionalResults["accruedVariance"] = 0;
79-
results_.additionalResults["futureVariance"] = variance;
80+
if (generateAdditionalResults_) {
81+
results_.additionalResults["accruedVariance"] = 0;
82+
results_.additionalResults["futureVariance"] = variance;
83+
}
8084
} else if (arguments_.startDate == today) {
8185
// The only time the QL price works
8286
variance = calculateFutureVariance(arguments_.maturityDate);
83-
results_.additionalResults["accruedVariance"] = 0;
84-
results_.additionalResults["futureVariance"] = variance;
87+
if (generateAdditionalResults_) {
88+
results_.additionalResults["accruedVariance"] = 0;
89+
results_.additionalResults["futureVariance"] = variance;
90+
}
8591
} else {
8692
// Get weighted average of Future and Realised variancies.
8793
Real accVar = calculateAccruedVariance(jointCal);
8894
Real futVar = calculateFutureVariance(arguments_.maturityDate);
89-
results_.additionalResults["accruedVariance"] = accVar;
90-
results_.additionalResults["futureVariance"] = futVar;
91-
Real totalTime =
92-
jointCal.businessDaysBetween(arguments_.startDate, arguments_.maturityDate, true, true);
95+
if (generateAdditionalResults_) {
96+
results_.additionalResults["accruedVariance"] = accVar;
97+
results_.additionalResults["futureVariance"] = futVar;
98+
}
99+
Real totalTime = jointCal.businessDaysBetween(arguments_.startDate, arguments_.maturityDate, true, true);
93100
Real accTime = jointCal.businessDaysBetween(arguments_.startDate, today, true, true);
94101
Real futTime = jointCal.businessDaysBetween(today, arguments_.maturityDate, false, true);
95102
variance = (accVar * accTime / totalTime) + (futVar * futTime / totalTime);
@@ -98,18 +105,20 @@ void GeneralisedReplicatingVarianceSwapEngine::calculate() const {
98105
results_.additionalResults["totalVariance"] = variance;
99106

100107
DiscountFactor df = discountingTS_->discount(arguments_.maturityDate);
101-
results_.additionalResults["MaturityDiscountFactor"] = df;
102108
Real multiplier = arguments_.position == Position::Long ? 1.0 : -1.0;
103109

104110
results_.variance = variance;
105111
results_.value = multiplier * df * arguments_.notional * 10000.0 *
106112
(variance - arguments_.strike); // factor of 10000 to convert vols to market quotes
107113

108114
Real volStrike = std::sqrt(arguments_.strike);
109-
results_.additionalResults["VarianceNotional"] = arguments_.notional;
110-
results_.additionalResults["VarianceStrike"] = arguments_.strike;
111-
results_.additionalResults["VolatilityStrike"] = volStrike;
112-
results_.additionalResults["VegaNotional"] = arguments_.notional * 2 * 100 * volStrike;
115+
if (generateAdditionalResults_) {
116+
results_.additionalResults["MaturityDiscountFactor"] = df;
117+
results_.additionalResults["VarianceNotional"] = arguments_.notional;
118+
results_.additionalResults["VarianceStrike"] = arguments_.strike;
119+
results_.additionalResults["VolatilityStrike"] = volStrike;
120+
results_.additionalResults["VegaNotional"] = arguments_.notional * 2 * 100 * volStrike;
121+
}
113122
}
114123

115124
Real GeneralisedReplicatingVarianceSwapEngine::calculateAccruedVariance(const Calendar& jointCal) const {
@@ -221,6 +230,21 @@ Real GeneralisedReplicatingVarianceSwapEngine::calculateFutureVariance(const Dat
221230
QL_FAIL("GeneralisedReplicationVarianceSwapEngine: internal error, unknown bounds");
222231
}
223232

233+
// additional result: vol smile at maturity
234+
235+
if (generateAdditionalResults_) {
236+
std::vector<Real> volSmileStrikes;
237+
std::vector<Real> volSmileVolatilities;
238+
constexpr Size N = 50;
239+
for (Size i = 0; i < N; ++i) {
240+
Real K = lower + (upper - lower) / static_cast<Real>(N) * static_cast<Real>(i);
241+
volSmileStrikes.push_back(K);
242+
volSmileVolatilities.push_back(process_->blackVolatility()->blackVol(T, K, true));
243+
}
244+
results_.additionalResults["VolatilitySmile.Strikes"] = volSmileStrikes;
245+
results_.additionalResults["VolatilitySmile.Volatilities"] = volSmileVolatilities;
246+
}
247+
224248
// calculate the integration integral
225249

226250
try {

QuantExt/qle/pricingengines/varianceswapgeneralreplicationengine.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ class GeneralisedReplicatingVarianceSwapEngine : public QuantExt::VarianceSwap2:
8080
const QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
8181
const Handle<YieldTermStructure>& discountingTS,
8282
const VarSwapSettings settings = VarSwapSettings(),
83-
const bool staticTodaysSpot = true);
83+
const bool staticTodaysSpot = true,
84+
const bool generateAdditionalResults = true);
8485

8586
void calculate() const override;
8687

@@ -93,6 +94,7 @@ class GeneralisedReplicatingVarianceSwapEngine : public QuantExt::VarianceSwap2:
9394
Handle<YieldTermStructure> discountingTS_;
9495
VarSwapSettings settings_;
9596
bool staticTodaysSpot_;
97+
bool generateAdditionalResults_;
9698

9799
mutable Real cachedTodaysSpot_ = Null<Real>();
98100
};

0 commit comments

Comments
 (0)