Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
modelimpl.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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
21
23
26
27#include <ql/math/comparison.hpp>
28#include <ql/quotes/simplequote.hpp>
29
30namespace ore {
31namespace data {
32
33using namespace QuantLib;
34
35ModelImpl::ModelImpl(const DayCounter& dayCounter, const Size size, const std::vector<std::string>& currencies,
36 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>& irIndices,
37 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>& infIndices,
38 const std::vector<std::string>& indices, const std::vector<std::string>& indexCurrencies,
39 const std::set<Date>& simulationDates, const IborFallbackConfig& iborFallbackConfig)
40 : Model(size), dayCounter_(dayCounter), currencies_(currencies), indexCurrencies_(indexCurrencies),
41 simulationDates_(simulationDates), iborFallbackConfig_(iborFallbackConfig) {
42
43 // populate index vectors
44
45 for (auto const& str : indices) {
46 indices_.push_back(IndexInfo(str));
47 }
48
49 for (auto const& p : irIndices) {
50 irIndices_.push_back(std::make_pair(IndexInfo(p.first), p.second));
51 }
52
53 for (auto const& p : infIndices) {
54 infIndices_.push_back(std::make_pair(IndexInfo(p.first), p.second));
55 }
56
57 // check consistency of inputs
58
59 QL_REQUIRE(indexCurrencies_.size() == indices_.size(), "mismatch of indexCurrencies (" << indexCurrencies_.size()
60 << ") and indices ("
61 << indices_.size() << ")");
62
63 for (auto const& c : currencies_)
64 QL_REQUIRE(!c.empty(), "empty currency string");
65
66 // look for fx indices, check consistency with currencies and index currencies vectors
67
68 for (Size i = 0; i < indices_.size(); ++i) {
69 if (indices_[i].isFx()) {
70 QL_REQUIRE(indices_[i].fx()->targetCurrency().code() == currencies_.front(),
71 "fx index domestic currency (" << indices_[i].fx()->targetCurrency().code()
72 << ") does not match base currency (" << currencies_.front()
73 << ")");
74 QL_REQUIRE(indices_[i].fx()->sourceCurrency().code() == indexCurrencies_[i],
75 "fx index foreign currency (" << indices_[i].fx()->sourceCurrency().code()
76 << ") does not match index currency (" << indexCurrencies_[i]);
77 auto ccy = std::find(currencies_.begin(), currencies_.end(), indexCurrencies_[i]);
78 QL_REQUIRE(ccy != currencies_.end(),
79 "fx index foreign currency (" << indexCurrencies_[i] << ") not found in model currencies");
80 }
81 }
82
83 // register with observables
84
85 for (auto const& i : irIndices_)
86 registerWith(i.second);
87
88 for (auto const& i : infIndices_)
89 registerWith(i.second);
90
91 for (auto const& i : indices_) {
92 if (i.isComm()) {
93 for (auto const& d : simulationDates_)
94 registerWith(i.index(d));
95 } else
96 registerWith(i.index());
97 }
98
99} // ModelImpl ctor
100
101RandomVariable ModelImpl::pay(const RandomVariable& amount, const Date& obsdate, const Date& paydate,
102 const std::string& currency) const {
103 calculate();
104
105 // result is as of max(obsdate, refDate) by definition of pay()
106
107 Date effectiveDate = std::max(obsdate, referenceDate());
108 auto c = std::find(currencies_.begin(), currencies_.end(), currency);
109 QL_REQUIRE(c != currencies_.end(), "currency " << currency << " not handled");
110 Size cidx = std::distance(currencies_.begin(), c);
111
112 // do we have a dynamic fx underlying to convert to base at the effective date?
113
114 RandomVariable fxSpot;
115 for (Size i = 0; i < indexCurrencies_.size(); ++i) {
116 if (indices_.at(i).isFx() && currency == indexCurrencies_[i]) {
117 fxSpot = getIndexValue(i, effectiveDate);
118 break;
119 }
120 }
121
122 // if no we use the zero vol fx spot at the effective date
123
124 if (!fxSpot.initialised()) {
125 if (cidx > 0)
126 fxSpot = RandomVariable(size(), getFxSpot(cidx - 1)) * getDiscount(cidx, referenceDate(), effectiveDate) /
127 getDiscount(0, referenceDate(), effectiveDate);
128 else
129 fxSpot = RandomVariable(size(), 1.0);
130 }
131
132 // discount from pay to obs date on ccy curve, convert to base ccy and divide by the numeraire
133
134 return amount * getDiscount(cidx, effectiveDate, paydate) / getNumeraire(effectiveDate) * fxSpot;
135}
136
137RandomVariable ModelImpl::discount(const Date& obsdate, const Date& paydate, const std::string& currency) const {
138 calculate();
139 auto c = std::find(currencies_.begin(), currencies_.end(), currency);
140 QL_REQUIRE(c != currencies_.end(), "currency " << currency << " not handled");
141 Size cidx = std::distance(currencies_.begin(), c);
142 return getDiscount(cidx, obsdate, paydate);
143}
144
145namespace {
146struct comp {
147 comp(const std::string& indexInput) : indexInput_(indexInput) {}
148 template <typename T> bool operator()(const std::pair<IndexInfo, QuantLib::ext::shared_ptr<T>>& p) const {
149 return p.first.name() == indexInput_;
150 }
151 const std::string indexInput_;
152};
153} // namespace
154
155RandomVariable ModelImpl::getInflationIndexFixing(const bool returnMissingFixingAsNull, const std::string& indexInput,
156 const QuantLib::ext::shared_ptr<ZeroInflationIndex>& infIndex,
157 const Size indexNo, const Date& limDate, const Date& obsdate,
158 const Date& fwddate, const Date& baseDate) const {
159 RandomVariable res(size());
160 Real f = infIndex->timeSeries()[limDate];
161 // we exclude historical fixings
162 // - which are "impossible" to know (limDate > refDate)
163 // - which have to be projected because a fwd date is given and they are later than the obsdate
164 if (f != Null<Real>() && !(limDate > referenceDate()) && (fwddate == Null<Date>() || limDate <= obsdate)) {
165 res = RandomVariable(size(), f);
166 } else {
167 Date effectiveObsDate = std::min(obsdate, limDate);
168 if (effectiveObsDate >= baseDate) {
169 res = getInfIndexValue(indexNo, effectiveObsDate, limDate);
170 } else if (returnMissingFixingAsNull) {
171 return RandomVariable();
172 } else {
173 QL_FAIL("missing " << indexInput << " fixing for " << QuantLib::io::iso_date(limDate) << " (obsdate="
174 << QuantLib::io::iso_date(obsdate) << ", fwddate=" << QuantLib::io::iso_date(fwddate)
175 << ", basedate=" << QuantLib::io::iso_date(baseDate) << ")");
176 }
177 }
178 return res;
179}
180
181RandomVariable ModelImpl::eval(const std::string& indexInput, const Date& obsdate, const Date& fwddate,
182 const bool returnMissingFixingAsNull, const bool ignoreTodaysFixing) const {
183 calculate();
184
185 std::string index = indexInput;
186 IndexInfo indexInfo(index);
187
188 // 1 handle inflation indices
189 QL_DEPRECATED_DISABLE_WARNING
190 // Remove in later version, scripts should take care of interpolation
191 if (indexInfo.isInf()) {
192 auto inf = std::find_if(infIndices_.begin(), infIndices_.end(), comp(indexInput));
193 QL_REQUIRE(inf != infIndices_.end(),
194 "ModelImpl::eval(): did not find inflation index '" << indexInput << "' in model index list.");
195 Date baseDate = inf->second->zeroInflationTermStructure()->baseDate();
196 Date effectiveFixingDate = fwddate != Null<Date>() ? fwddate : obsdate;
197 std::pair<Date, Date> lim = inflationPeriod(effectiveFixingDate, inf->second->frequency());
198 RandomVariable indexStart =
199 getInflationIndexFixing(returnMissingFixingAsNull, indexInput, inf->second,
200 std::distance(infIndices_.begin(), inf), lim.first, obsdate, fwddate, baseDate);
201 // if the index is not interpolated we are done
202 if (!indexInfo.inf()->interpolated()) {
203 return indexStart;
204 }
205 ALOG("Interpolated Inflation Indices are deprecated, adjust your script to handle interpolation there");
206 // otherwise we need to get a second value and interpolate as in ZeroInflationIndex
207 RandomVariable indexEnd = getInflationIndexFixing(returnMissingFixingAsNull, indexInput, inf->second,
208 std::distance(infIndices_.begin(), inf), lim.second + 1,
209 obsdate, fwddate, baseDate);
210 // this is not entirely correct, since we should use the days in the lagged period, but we don't know the lag
211 return indexStart +
212 (indexEnd - indexStart) * RandomVariable(size(), static_cast<Real>(effectiveFixingDate - lim.first) /
213 static_cast<Real>(lim.second + 1 - lim.first));
214 }
215 QL_DEPRECATED_ENABLE_WARNING
216 // 2 handle non-inflation indices
217
218 // 2a handle historical fixings and today's fixings (if given as a historical fixing)
219 // for fx indices try to get the fixing both from the straight and the inverse index
220
221 if (fwddate == Null<Date>()) {
222 if (obsdate < referenceDate() || (obsdate == referenceDate() && !ignoreTodaysFixing)) {
224 // handle ibor fallback indices, they don't fit into the below treatment...
225 auto ir = std::find_if(irIndices_.begin(), irIndices_.end(), comp(indexInput));
226 if (ir != irIndices_.end()) {
227 return RandomVariable(size(),
228 ir->second->fixing(ir->second->fixingCalendar().adjust(obsdate, Preceding)));
229 } else {
230 QL_FAIL("ir (fallback ibor) index '" << indexInput
231 << "' not found in ir indices list, internal error.");
232 }
233 } else {
234 // handle all other cases than ibor fallback indices
235 QuantLib::ext::shared_ptr<Index> idx = indexInfo.index(obsdate);
236 Real fixing = Null<Real>();
237 try {
238 fixing = idx->fixing(idx->fixingCalendar().adjust(obsdate, Preceding));
239 } catch (...) {
240 }
241 if (fixing != Null<Real>())
242 return RandomVariable(size(), fixing);
243 else {
244 // for dates < refDate we are stuck now
245 if (obsdate != referenceDate()) {
246 if (returnMissingFixingAsNull)
247 return RandomVariable();
248 else {
249 QL_FAIL("missing "
250 << idx->name() << " fixing for " << QuantLib::io::iso_date(obsdate)
251 << " (adjusted fixing date = "
252 << QuantLib::io::iso_date(idx->fixingCalendar().adjust(obsdate, Preceding)) << ")");
253 }
254 }
255 }
256 }
257 }
258 } else {
259 // if fwd date is given, ensure we can project
260 QL_REQUIRE(obsdate >= referenceDate(), "if fwd date is given ("
261 << QuantLib::io::iso_date(fwddate) << "), obsdate ("
262 << QuantLib::io::iso_date(obsdate) << ") must be >= refdate ("
263 << QuantLib::io::iso_date(referenceDate()) << ")");
264 }
265
266 // 2b handle fixings >= today (and fwd fixings, in which case we know fwddate > obsdate >= refdate)
267
268 // 2b1 handle IR indices
269
270 if (indexInfo.isIr()) {
271 auto ir = std::find_if(irIndices_.begin(), irIndices_.end(), comp(indexInput));
272 if (ir != irIndices_.end()) {
273 RandomVariable res = getIrIndexValue(std::distance(irIndices_.begin(), ir), obsdate, fwddate);
274 QL_REQUIRE(res.initialised(), "internal error: could not project "
275 << ir->second->name() << " fixing for (obsdate/fwddate) = ("
276 << QuantLib::io::iso_date(obsdate) << ","
277 << QuantLib::io::iso_date(fwddate) << ")");
278 return res;
279 }
280 }
281
282 // 2b2 handle FX, EQ, COMM indices
283
284 // if we have an FX index, we "normalise" the tag by GENERIC (since it does not matter for projections)
285
286 if (indexInfo.isFx())
287 indexInfo = IndexInfo("FX-GENERIC-" + indexInfo.fx()->sourceCurrency().code() + "-" +
288 indexInfo.fx()->targetCurrency().code());
289
290 RandomVariable res;
291 auto i = std::find(indices_.begin(), indices_.end(), indexInfo);
292 if (i != indices_.end()) {
293 // we have the index directly as an underlying
294 res = getIndexValue(std::distance(indices_.begin(), i), obsdate, fwddate);
295 } else {
296 // if not, we can only try something else for FX indices
297 QL_REQUIRE(indexInfo.isFx(), "ModelImpl::eval(): index " << index << " not handled");
298 // is it a trivial fx index (CCY-CCY) or can we triangulate it?
299 RandomVariable fx1(size(), 1.0), fx2(size(), 1.0);
300 if (indexInfo.fx()->sourceCurrency() == indexInfo.fx()->targetCurrency()) {
301 res = fx1;
302 // no fwd correction required, if ccy1=ccy2 we have spot = fwd = 1
303 } else {
304 Size ind1 = Null<Size>(), ind2 = Null<Size>();
305 for (Size i = 0; i < indexCurrencies_.size(); ++i) {
306 if (indices_[i].isFx()) {
307 if (indexInfo.fx()->sourceCurrency().code() == indexCurrencies_[i])
308 ind1 = i;
309 if (indexInfo.fx()->targetCurrency().code() == indexCurrencies_[i])
310 ind2 = i;
311 }
312 }
313 if (ind1 != Null<Size>()) {
314 fx1 = getIndexValue(ind1, obsdate);
315 }
316 if (ind2 != Null<Size>()) {
317 fx2 = getIndexValue(ind2, obsdate);
318 }
319 res = fx1 / fx2;
320 if (fwddate != Null<Date>()) {
321 auto ind1 = std::find(currencies_.begin(), currencies_.end(), indexInfo.fx()->sourceCurrency().code());
322 auto ind2 = std::find(currencies_.begin(), currencies_.end(), indexInfo.fx()->targetCurrency().code());
323 QL_REQUIRE(ind1 != currencies_.end(), "currency " << indexInfo.fx()->sourceCurrency().code()
324 << " in index " << index << " not handled");
325 QL_REQUIRE(ind2 != currencies_.end(), "currency " << indexInfo.fx()->targetCurrency().code()
326 << " in index " << index << " not handled");
327 res *= getDiscount(std::distance(currencies_.begin(), ind1), obsdate, fwddate) /
328 getDiscount(std::distance(currencies_.begin(), ind2), obsdate, fwddate);
329 }
330 }
331 }
332
333 return res;
334}
335
336Real ModelImpl::fxSpotT0(const std::string& forCcy, const std::string& domCcy) const {
337 auto c1 = std::find(currencies_.begin(), currencies_.end(), forCcy);
338 auto c2 = std::find(currencies_.begin(), currencies_.end(), domCcy);
339 QL_REQUIRE(c1 != currencies_.end(), "currency " << forCcy << " not handled");
340 QL_REQUIRE(c2 != currencies_.end(), "currency " << domCcy << " not handled");
341 Size cidx1 = std::distance(currencies_.begin(), c1);
342 Size cidx2 = std::distance(currencies_.begin(), c2);
343 Real fx = 1.0;
344 if (cidx1 > 0)
345 fx *= getFxSpot(cidx1 - 1);
346 if (cidx2 > 0)
347 fx /= getFxSpot(cidx2 - 1);
348 return fx;
349}
350
351RandomVariable ModelImpl::barrierProbability(const std::string& index, const Date& obsdate1, const Date& obsdate2,
352 const RandomVariable& barrier, const bool above) const {
353
354 calculate();
355
356 // determine the fixing calendar (assume that for commodity this is the same for different futures)
357
358 IndexInfo ind(index);
359 auto qlIndex = ind.index(obsdate1);
360
361 // handle all dates < reference date here
362
363 Filter barrierHit(barrier.size(), false);
364 Date d = obsdate1;
365 while (d < std::min(referenceDate(), obsdate2)) {
366 if (qlIndex->fixingCalendar().isBusinessDay(d)) {
367 RandomVariable f = eval(index, d, Null<Date>(), true);
368 if (f.initialised()) {
369 if (above) {
370 barrierHit = barrierHit || f >= barrier;
371 } else {
372 barrierHit = barrierHit || f <= barrier;
373 }
374 } else {
375 // lax check of historical fixings, since e.g. for equity underlyings
376 // we can't expect to get the actual fixing calendar from index info
377 // TODO can we improve this by using the correct calendars?
378 TLOG("ignore missing fixing for " << qlIndex->name() << " on " << QuantLib::io::iso_date(d)
379 << " in ModelImpl::barrierProbability()");
380 }
381 }
382 ++d;
383 }
384
385 RandomVariable barrierHitAsNumber = RandomVariable(barrierHit, 1.0, 0.0);
386 if (obsdate2 < referenceDate()) {
387 return barrierHitAsNumber;
388 }
389
390 // handle future part (call into derived classes, this is model dependent)
391
392 RandomVariable res =
393 getFutureBarrierProb(index, std::max<Date>(obsdate1, referenceDate()), obsdate2, barrier, above);
394
395 // combine historical and future part and return result
396
397 return applyInverseFilter(res, barrierHit) + barrierHitAsNumber;
398}
399
401
402} // namespace data
403} // namespace ore
const std::string indexInput_
const std::vector< std::string > & indexCurrencies_
QuantLib::ext::shared_ptr< Index > index(const Date &obsDate=Date()) const
Definition: utilities.cpp:449
QuantLib::ext::shared_ptr< ZeroInflationIndex > inf() const
Definition: utilities.hpp:123
bool isIr() const
Definition: utilities.hpp:103
QuantLib::ext::shared_ptr< FallbackIborIndex > irIborFallback(const IborFallbackConfig &iborFallbackConfig, const Date &asof=QuantLib::Date::maxDate()) const
Definition: utilities.cpp:501
bool isFx() const
Definition: utilities.hpp:100
QuantLib::ext::shared_ptr< FxIndex > fx() const
Definition: utilities.hpp:109
bool isInf() const
Definition: utilities.hpp:106
virtual const Date & referenceDate() const =0
virtual Size size() const
Definition: model.hpp:71
virtual RandomVariable getInfIndexValue(const Size indexNo, const Date &d, const Date &fwd) const =0
const std::vector< std::string > currencies_
Definition: modelimpl.hpp:107
Real extractT0Result(const RandomVariable &value) const override
Definition: modelimpl.cpp:400
Real fxSpotT0(const std::string &forCcy, const std::string &domCcy) const override
Definition: modelimpl.cpp:336
RandomVariable barrierProbability(const std::string &index, const Date &obsdate1, const Date &obsdate2, const RandomVariable &barrier, const bool above) const override
Definition: modelimpl.cpp:351
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > infIndices_
Definition: modelimpl.hpp:113
ModelImpl(const DayCounter &dayCounter, const Size size, const std::vector< std::string > &currencies, 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 std::set< Date > &simulationDates, const IborFallbackConfig &iborFallbackConfig)
Definition: modelimpl.cpp:35
RandomVariable discount(const Date &obsdate, const Date &paydate, const std::string &currency) const override
Definition: modelimpl.cpp:137
virtual RandomVariable getFutureBarrierProb(const std::string &index, const Date &obsdate1, const Date &obsdate2, const RandomVariable &barrier, const bool above) const =0
std::vector< IndexInfo > indices_
Definition: modelimpl.hpp:114
RandomVariable getInflationIndexFixing(const bool returnMissingFixingAsNull, const std::string &indexInput, const QuantLib::ext::shared_ptr< ZeroInflationIndex > &infIndex, const Size indexNo, const Date &limDate, const Date &obsdate, const Date &fwddate, const Date &baseDate) const
Definition: modelimpl.cpp:155
virtual Real getFxSpot(const Size idx) const =0
virtual RandomVariable getDiscount(const Size idx, const Date &s, const Date &t) const =0
RandomVariable eval(const std::string &index, const Date &obsdate, const Date &fwddate, const bool returnMissingMissingAsNull=false, const bool ignoreTodaysFixing=false) const override
Definition: modelimpl.cpp:181
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< InterestRateIndex > > > irIndices_
Definition: modelimpl.hpp:112
virtual RandomVariable getIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const =0
RandomVariable pay(const RandomVariable &amount, const Date &obsdate, const Date &paydate, const std::string &currency) const override
Definition: modelimpl.cpp:101
virtual RandomVariable getNumeraire(const Date &s) const =0
const std::vector< std::string > indexCurrencies_
Definition: modelimpl.hpp:108
const IborFallbackConfig iborFallbackConfig_
Definition: modelimpl.hpp:110
const std::set< Date > simulationDates_
Definition: modelimpl.hpp:109
virtual RandomVariable getIrIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const =0
SafeStack< ValueType > value
Map text representations to QuantLib/QuantExt types.
@ data
Definition: log.hpp:77
#define ALOG(text)
Logging Macro (Level = Alert)
Definition: log.hpp:544
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
basis implementation for a script engine model
RandomVariable expectation(const RandomVariable &r)
RandomVariable applyInverseFilter(RandomVariable x, const Filter &f)
Size size(const ValueType &v)
Definition: value.cpp:145
Serializable Credit Default Swap.
Definition: namespaces.docs:23
some utility functions
Real at(const Size i) const