Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blackscholescgbase.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
23
30
31#include <ql/math/comparison.hpp>
32#include <ql/math/matrixutilities/pseudosqrt.hpp>
33#include <ql/quotes/simplequote.hpp>
34
35namespace ore {
36namespace data {
37
38using namespace QuantLib;
39using namespace QuantExt;
40
41BlackScholesCGBase::BlackScholesCGBase(const Size paths, const std::string& currency,
42 const Handle<YieldTermStructure>& curve, const std::string& index,
43 const std::string& indexCurrency, const Handle<BlackScholesModelWrapper>& model,
44 const std::set<Date>& simulationDates,
45 const IborFallbackConfig& iborFallbackConfig)
46 : BlackScholesCGBase(paths, {currency}, {curve}, {}, {}, {}, {index}, {indexCurrency}, model, {}, simulationDates,
47 iborFallbackConfig) {}
48
50 const Size paths, const std::vector<std::string>& currencies, const std::vector<Handle<YieldTermStructure>>& curves,
51 const std::vector<Handle<Quote>>& fxSpots,
52 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>& irIndices,
53 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>& infIndices,
54 const std::vector<std::string>& indices, const std::vector<std::string>& indexCurrencies,
55 const Handle<BlackScholesModelWrapper>& model,
56 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlations,
57 const std::set<Date>& simulationDates, const IborFallbackConfig& iborFallbackConfig)
58 : ModelCGImpl(curves.at(0)->dayCounter(), paths, currencies, irIndices, infIndices, indices, indexCurrencies,
59 simulationDates, iborFallbackConfig),
60 curves_(curves), fxSpots_(fxSpots), model_(model), correlations_(correlations) {
61
62 // check inputs
63
64 QL_REQUIRE(!model_.empty(), "model is empty");
65 QL_REQUIRE(!curves_.empty(), "no curves given");
66 QL_REQUIRE(currencies_.size() == curves_.size(), "number of currencies (" << currencies_.size()
67 << ") does not match number of curves ("
68 << curves_.size() << ")");
69 QL_REQUIRE(currencies_.size() == fxSpots_.size() + 1,
70 "number of currencies (" << currencies_.size() << ") does not match number of fx spots ("
71 << fxSpots_.size() << ") + 1");
72
73 QL_REQUIRE(indices_.size() == model_->processes().size(),
74 "mismatch of processes size (" << model_->processes().size() << ") and number of indices ("
75 << indices_.size() << ")");
76
77 // register with observables
78
79 for (auto const& o : fxSpots_)
80 registerWith(o);
81 for (auto const& o : correlations_)
82 registerWith(o.second);
83
84 registerWith(model_);
85
86} // BlackScholesBase ctor
87
89 calculate();
90 return referenceDate_;
91}
92
94
95 // needed for base class performCalculations()
96
97 referenceDate_ = curves_.front()->referenceDate();
98
99 // update cg version if necessary (eval date changed)
100
102
103 // if cg version has changed => update time grid related members and clear paths, so that they
104 // are populated in derived classes
105
107
108 // set up time grid
109
110 effectiveSimulationDates_ = model_->effectiveSimulationDates();
111
112 std::vector<Real> times;
113 for (auto const& d : effectiveSimulationDates_) {
114 times.push_back(curves_.front()->timeFromReference(d));
115 }
116
117 timeGrid_ = model_->discretisationTimeGrid();
118 positionInTimeGrid_.resize(times.size());
119 for (Size i = 0; i < positionInTimeGrid_.size(); ++i)
120 positionInTimeGrid_[i] = timeGrid_.index(times[i]);
121
122 underlyingPaths_.clear();
124 }
125}
126
127std::size_t BlackScholesCGBase::getIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
128 Date effFwd = fwd;
129 if (indices_[indexNo].isComm()) {
130 Date expiry = indices_[indexNo].comm(d)->expiryDate();
131 // if a future is referenced we set the forward date effectively used below to the future's expiry date
132 if (expiry != Date())
133 effFwd = expiry;
134 // if the future expiry is past the obsdate, we return the spot as of the obsdate, i.e. we
135 // freeze the future value after its expiry, but keep it available for observation
136 // TOOD should we throw an exception instead?
137 effFwd = std::max(effFwd, d);
138 }
139 QL_REQUIRE(underlyingPaths_.find(d) != underlyingPaths_.end(), "did not find path for " << d);
140 auto res = underlyingPaths_.at(d).at(indexNo);
141 // compute forwarding factor
142 if (effFwd != Null<Date>()) {
143 auto p = model_->processes().at(indexNo);
144 std::string idf = std::to_string(indexNo) + "_" + ore::data::to_string(effFwd);
145 std::string idd = std::to_string(indexNo) + "_" + ore::data::to_string(d);
146 std::size_t div_d = addModelParameter("__div_" + idd, [p, d]() { return p->dividendYield()->discount(d); });
147 std::size_t div_f = addModelParameter("__div_" + idf, [p, effFwd]() { return p->dividendYield()->discount(effFwd); });
148 std::size_t rfr_d = addModelParameter("__rfr_" + idd, [p, d]() { return p->riskFreeRate()->discount(d); });
149 std::size_t rfr_f = addModelParameter("__rfr_" + idf, [p, effFwd]() { return p->riskFreeRate()->discount(effFwd); });
150 res = cg_mult(*g_, res, cg_mult(*g_, div_f, cg_div(*g_, rfr_d, cg_mult(*g_, div_d, rfr_f))));
151 }
152 return res;
153}
154
155std::size_t BlackScholesCGBase::getIrIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
156 Date effFixingDate = d;
157 if (fwd != Null<Date>())
158 effFixingDate = fwd;
159 // ensure a valid fixing date
160 effFixingDate = irIndices_.at(indexNo).second->fixingCalendar().adjust(effFixingDate);
161 auto index = irIndices_.at(indexNo).second;
162 std::string id = "__irFix_" + index->name() + "_" + ore::data::to_string(effFixingDate);
163 return addModelParameter(id, [index, effFixingDate]() { return index->fixing(effFixingDate); });
164}
165
166std::size_t BlackScholesCGBase::getInfIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
167 Date effFixingDate = d;
168 if (fwd != Null<Date>())
169 effFixingDate = fwd;
170 auto index = infIndices_.at(indexNo).second;
171 std::string id = "__infFix_" + index->name() + "_" + ore::data::to_string(effFixingDate);
172 return addModelParameter(id, [index, effFixingDate]() { return index->fixing(effFixingDate); });
173}
174
175namespace {
176struct comp {
177 comp(const std::string& indexInput) : indexInput_(indexInput) {}
178 template <typename T> bool operator()(const std::pair<IndexInfo, QuantLib::ext::shared_ptr<T>>& p) const {
179 return p.first.name() == indexInput_;
180 }
181 const std::string indexInput_;
182};
183} // namespace
184
185std::size_t BlackScholesCGBase::fwdCompAvg(const bool isAvg, const std::string& indexInput, const Date& obsdate,
186 const Date& start, const Date& end, const Real spread, const Real gearing,
187 const Integer lookback, const Natural rateCutoff, const Natural fixingDays,
188 const bool includeSpread, const Real cap, const Real floor,
189 const bool nakedOption, const bool localCapFloor) const {
190 calculate();
191 auto index = std::find_if(irIndices_.begin(), irIndices_.end(), comp(indexInput));
192 QL_REQUIRE(index != irIndices_.end(),
193 "BlackScholesCGBase::fwdCompAvg(): did not find ir index " << indexInput << " - this is unexpected.");
194 auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index->second);
195 QL_REQUIRE(on, "BlackScholesCGBase::fwdCompAvg(): expected on index for " << indexInput);
196 // if we want to support cap / floors we need the OIS CF surface
197 QL_REQUIRE(cap > 999998.0 && floor < -999998.0,
198 "BlackScholesCGBase:fwdCompAvg(): cap (" << cap << ") / floor (" << floor << ") not supported");
199 QuantLib::ext::shared_ptr<QuantLib::FloatingRateCoupon> coupon;
200 QuantLib::ext::shared_ptr<QuantLib::FloatingRateCouponPricer> pricer;
201 if (isAvg) {
202 coupon = QuantLib::ext::make_shared<QuantExt::AverageONIndexedCoupon>(
203 end, 1.0, start, end, on, gearing, spread, rateCutoff, on->dayCounter(), lookback * Days, fixingDays);
204 pricer = QuantLib::ext::make_shared<AverageONIndexedCouponPricer>();
205 } else {
206 coupon = QuantLib::ext::make_shared<QuantExt::OvernightIndexedCoupon>(end, 1.0, start, end, on, gearing, spread, Date(),
207 Date(), on->dayCounter(), false, includeSpread,
208 lookback * Days, rateCutoff, fixingDays);
209 pricer = QuantLib::ext::make_shared<OvernightIndexedCouponPricer>();
210 }
211 coupon->setPricer(pricer);
212 std::string id = "__fwdCompAvg_" + std::to_string(g_->size());
213 return addModelParameter(id, [coupon]() { return coupon->rate(); });
214}
215
216std::size_t BlackScholesCGBase::getDiscount(const Size idx, const Date& s, const Date& t) const {
217 std::string ids = "__curve_" + std::to_string(idx) + "_" + ore::data::to_string(s);
218 std::string idt = "__curve_" + std::to_string(idx) + "_" + ore::data::to_string(t);
219 auto c = curves_.at(idx);
220 std::size_t ns = addModelParameter(ids, [c, s] { return c->discount(s); });
221 std::size_t nt = addModelParameter(idt, [c, t] { return c->discount(t); });
222 return cg_div(*g_, nt, ns);
223}
224
225std::size_t BlackScholesCGBase::getNumeraire(const Date& s) const {
226 std::string id = "__curve_0_" + ore::data::to_string(s);
227 auto c = curves_.at(0);
228 std::size_t ds = addModelParameter(id, [c, s] { return c->discount(s); });
229 return cg_div(*g_, cg_const(*g_, 1.0), ds);
230}
231
232std::size_t BlackScholesCGBase::getFxSpot(const Size idx) const {
233 std::string id = "__fxspot_" + std::to_string(idx);
234 auto c = fxSpots_.at(idx);
235 return addModelParameter(id, [c] { return c->value(); });
236}
237
238Real BlackScholesCGBase::getDirectFxSpotT0(const std::string& forCcy, const std::string& domCcy) const {
239 auto c1 = std::find(currencies_.begin(), currencies_.end(), forCcy);
240 auto c2 = std::find(currencies_.begin(), currencies_.end(), domCcy);
241 QL_REQUIRE(c1 != currencies_.end(), "currency " << forCcy << " not handled");
242 QL_REQUIRE(c2 != currencies_.end(), "currency " << domCcy << " not handled");
243 Size cidx1 = std::distance(currencies_.begin(), c1);
244 Size cidx2 = std::distance(currencies_.begin(), c2);
245 Real fx = 1.0;
246 if (cidx1 > 0)
247 fx *= fxSpots_.at(cidx1 - 1)->value();
248 if (cidx2 > 0)
249 fx /= fxSpots_.at(cidx2 - 1)->value();
250 return fx;
251}
252
253Real BlackScholesCGBase::getDirectDiscountT0(const Date& paydate, const std::string& currency) const {
254 auto c = std::find(currencies_.begin(), currencies_.end(), currency);
255 QL_REQUIRE(c != currencies_.end(), "currency " << currency << " not handled");
256 Size cidx = std::distance(currencies_.begin(), c);
257 return curves_.at(cidx)->discount(paydate);
258}
259
260std::size_t BlackScholesCGBase::npv(const std::size_t amount, const Date& obsdate, const std::size_t filter,
261 const boost::optional<long>& memSlot, const std::size_t addRegressor1,
262 const std::size_t addRegressor2) const {
263
264 calculate();
265
266 QL_REQUIRE(!memSlot, "BlackScholesCGBase::npv() with memSlot not yet supported!");
267
268 // if obsdate is today, take a plain expectation
269
270 if (obsdate == referenceDate()) {
271 return cg_conditionalExpectation(*g_, amount, {}, cg_const(*g_, 1.0));
272 }
273
274 // build the state
275
276 std::vector<std::size_t> state;
277
278 if (!underlyingPaths_.empty()) {
279 for (auto const& r : underlyingPaths_.at(obsdate))
280 state.push_back(r);
281 }
282
283 if (addRegressor1 != ComputationGraph::nan)
284 state.push_back(addRegressor1);
285 if (addRegressor2 != ComputationGraph::nan)
286 state.push_back(addRegressor2);
287
288 // if the state is empty, return the plain expectation (no conditioning)
289
290 if(state.empty()) {
291 return cg_conditionalExpectation(*g_, amount, {}, cg_const(*g_, 1.0));
292 }
293
294 // if a memSlot is given and coefficients are stored, we use them
295 // TODO ...
296
297 // otherwise compute coefficients and store them if a memSlot is given
298 // TODO ...
299
300 // compute conditional expectation and return the result
301
302 return cg_conditionalExpectation(*g_, amount, state, filter);
303}
304
305} // namespace data
306} // namespace ore
const std::string indexInput_
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > & correlations_
black scholes model base class for n underlyings (fx, equity or commodity)
static std::size_t nan
void performCalculations() const override
std::map< Date, std::vector< std::size_t > > underlyingPaths_
std::size_t npv(const std::size_t amount, const Date &obsdate, const std::size_t filter, const boost::optional< long > &memSlot, const std::size_t addRegressor1, const std::size_t addRegressor2) const override
std::size_t getNumeraire(const Date &s) const override
std::size_t getFxSpot(const Size idx) const override
Real getDirectFxSpotT0(const std::string &forCcy, const std::string &domCcy) const override
std::size_t getInfIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
const Date & referenceDate() const override
Real getDirectDiscountT0(const Date &paydate, const std::string &currency) const override
std::size_t getIrIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
BlackScholesCGBase(const Size paths, const std::vector< std::string > &currencies, const std::vector< Handle< YieldTermStructure > > &curves, const std::vector< Handle< Quote > > &fxSpots, const std::vector< std::pair< std::string, QuantLib::ext::shared_ptr< InterestRateIndex > > > &irIndices, const std::vector< std::pair< std::string, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > &infIndices, const std::vector< std::string > &indices, const std::vector< std::string > &indexCurrencies, const Handle< BlackScholesModelWrapper > &model, const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > &correlations, const std::set< Date > &simulationDates, const IborFallbackConfig &iborFallbackConfig)
const std::vector< Handle< Quote > > fxSpots_
std::size_t fwdCompAvg(const bool isAvg, const std::string &indexInput, const Date &obsdate, const Date &start, const Date &end, const Real spread, const Real gearing, const Integer lookback, const Natural rateCutoff, const Natural fixingDays, const bool includeSpread, const Real cap, const Real floor, const bool nakedOption, const bool localCapFloor) const override
const Handle< BlackScholesModelWrapper > model_
std::size_t getDiscount(const Size idx, const Date &s, const Date &t) const override
std::size_t getIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > correlations_
const std::vector< Handle< YieldTermStructure > > curves_
std::vector< Size > positionInTimeGrid_
QuantLib::ext::shared_ptr< QuantExt::ComputationGraph > g_
Definition: modelcg.hpp:149
void calculate() const override
Definition: modelcg.hpp:142
std::size_t addModelParameter(const std::string &id, std::function< double(void)> f) const
void performCalculations() const override
const std::vector< std::string > currencies_
std::size_t cgVersion() const override
std::size_t discount(const Date &obsdate, const Date &paydate, const std::string &currency) const override
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > infIndices_
std::vector< IndexInfo > indices_
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< InterestRateIndex > > > irIndices_
SafeStack< Filter > filter
const QuantLib::ext::shared_ptr< ModelCG > model_
Map text representations to QuantLib/QuantExt types.
@ data
Definition: log.hpp:77
std::size_t cg_const(ComputationGraph &g, const double value)
std::size_t cg_mult(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_conditionalExpectation(ComputationGraph &g, const std::size_t regressand, const std::vector< std::size_t > &regressor, const std::size_t filter, const std::string &label)
std::size_t cg_div(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Serializable Credit Default Swap.
Definition: namespaces.docs:23
string conversion utilities