21#ifndef quantlib_randomdefault_latent_model_hpp
22#define quantlib_randomdefault_latent_model_hpp
68 template <
class simEventOwner>
struct simEvent;
94 template<
template <
class,
class>
class derivedRandomLM,
class copulaPolicy,
117 static_cast<const derivedRandomLM<copulaPolicy, USNG>*
>(
126 const std::vector<Real>& sample =
128 static_cast<const derivedRandomLM<copulaPolicy, USNG>*
>(
129 this)->nextSample(sample);
140 const std::vector<simEvent<derivedRandomLM<copulaPolicy, USNG> > >&
146 const simEvent<derivedRandomLM<copulaPolicy, USNG> >& evt)
const
148 return static_cast<const derivedRandomLM<copulaPolicy, USNG>*
>(
226 template<
template <
class,
class>
class D,
class C,
class URNG>
233 QL_REQUIRE(
d>today,
"Date for statistic must be in the future.");
240 for(
Size iSim=0; iSim < nSims_; iSim++) {
242 const std::vector<simEvent<D<C, URNG> > >& events =
244 for(
Size iEvt=0; iEvt < events.size(); iEvt++)
246 if(val > events[iEvt].dayFromRef) simCount++;
247 if(simCount >=
n) counts++;
249 return counts/nSims_;
253 template<
template <
class,
class>
class D,
class C,
class URNG>
258 Size basketSize = basket_->size();
260 QL_REQUIRE(
n>0 &&
n<=basketSize,
"Impossible number of defaults.");
263 QL_REQUIRE(
d>today,
"Date for statistic must be in the future.");
267 std::vector<Probability> hitsByDate(basketSize, 0.);
268 for(
Size iSim=0; iSim < nSims_; iSim++) {
269 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
270 std::map<unsigned short, unsigned short> namesDefaulting;
271 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
273 if(val > events[iEvt].dayFromRef)
275 namesDefaulting.insert(std::make_pair<
unsigned short,
276 unsigned short>(events[iEvt].dayFromRef,
277 events[iEvt].nameIdx));
279 if(namesDefaulting.size() >=
n) {
280 std::map<unsigned short, unsigned short>::const_iterator
281 itdefs = namesDefaulting.begin();
283 std::advance(itdefs,
n-1);
285 hitsByDate[itdefs->second]++;
288 std::transform(hitsByDate.begin(), hitsByDate.end(),
290 [
this](
Real x){ return x/nSims_; });
296 template<
template <
class,
class>
class D,
class C,
class URNG>
304 QL_REQUIRE(
d>today,
"Date for statistic must be in the future.");
308 Real expectedDefiDefj = 0.;
311 Real expectedDefi = 0.;
312 Real expectedDefj = 0.;
313 for(
Size iSim=0; iSim < nSims_; iSim++) {
314 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
315 Real imatch = 0., jmatch = 0.;
316 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
317 if((val > events[iEvt].dayFromRef) &&
318 (events[iEvt].nameIdx == iName)) imatch = 1.;
319 if((val > events[iEvt].dayFromRef) &&
320 (events[iEvt].nameIdx == jName)) jmatch = 1.;
322 expectedDefiDefj += imatch * jmatch;
323 expectedDefi += imatch;
324 expectedDefj += jmatch;
326 expectedDefiDefj = expectedDefiDefj / (nSims_-1);
327 expectedDefi = expectedDefi / nSims_;
328 expectedDefj = expectedDefj / nSims_;
330 return (expectedDefiDefj - expectedDefi*expectedDefj) /
331 std::sqrt((expectedDefi*expectedDefj*(1.-expectedDefi)
332 *(1.-expectedDefj)));
337 template<
template <
class,
class>
class D,
class C,
class URNG>
339 const Date&
d)
const {
340 return expectedTrancheLossInterval(
d, 0.95).first;
344 template<
template <
class,
class>
class D,
class C,
class URNG>
352 Real attachAmount = basket_->attachmentAmount();
353 Real detachAmount = basket_->detachmentAmount();
357 for(
Size iSim=0; iSim < nSims_; iSim++) {
358 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
360 Real portfSimLoss=0.;
361 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
364 events[iEvt].dayFromRef)) {
365 Size iName = events[iEvt].nameIdx;
368 basket_->exposure(basket_->names()[iName],
369 Date(events[iEvt].dayFromRef +
371 (1.-getEventRecovery(events[iEvt]));
375 std::min(std::max(portfSimLoss - attachAmount, 0.),
376 detachAmount - attachAmount) );
383 template<
template <
class,
class>
class D,
class C,
class URNG>
387 std::map<Real, Probability> distrib;
391 distrib.insert(std::make_pair(0., suma));
394 distrib.insert(std::make_pair( hist.
breaks()[i-1], suma ));
400 template<
template <
class,
class>
class D,
class C,
class URNG>
402 std::vector<Real> data;
409 "Requested percentile date must lie after computation date.");
412 Real attachAmount = basket_->attachmentAmount();
413 Real detachAmount = basket_->detachmentAmount();
415 for(
Size iSim=0; iSim < nSims_; iSim++) {
416 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
418 Real portfSimLoss=0.;
419 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
421 events[iEvt].dayFromRef)) {
422 Size iName = events[iEvt].nameIdx;
426 basket_->exposure(basket_->names()[iName],
427 Date(events[iEvt].dayFromRef +
429 (1.-getEventRecovery(events[iEvt]));
432 data.push_back(std::min(std::max(portfSimLoss - attachAmount, 0.),
433 detachAmount - attachAmount));
434 keys.insert(data.back());
437 Size nPts = std::min<Size>(data.size(), 150);
438 return Histogram(data.begin(), data.end(), nPts);
442 template<
template <
class,
class>
class D,
class C,
class URNG>
444 Real percent)
const {
448 "Requested percentile date must lie after computation date.");
451 Real attachAmount = basket_->attachmentAmount();
452 Real detachAmount = basket_->detachmentAmount();
455 if(val <= 0)
return 0.;
458 std::vector<Real> losses;
459 for(
Size iSim=0; iSim < nSims_; iSim++) {
460 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
461 Real portfSimLoss=0.;
462 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
464 events[iEvt].dayFromRef)) {
465 Size iName = events[iEvt].nameIdx;
469 basket_->exposure(basket_->names()[iName],
470 Date(events[iEvt].dayFromRef +
472 (1.-getEventRecovery(events[iEvt]));
475 portfSimLoss = std::min(std::max(portfSimLoss - attachAmount, 0.),
476 detachAmount - attachAmount);
477 losses.push_back(portfSimLoss);
480 std::sort(losses.begin(), losses.end());
481 Real posit = std::ceil(percent * nSims_);
482 posit = posit >= 0. ? posit : 0.;
483 Size position =
static_cast<Size>(posit);
484 Real perctlInf = losses[position];
488 static_cast<Real>(std::distance(losses.begin() + position,
489 losses.end())) /
static_cast<Real>(nSims_);
491 return ( perctlInf * (1.-percent-probOverQ) +
492 std::accumulate(losses.begin() + position, losses.end(),
524 template<
template <
class,
class>
class D,
class C,
class URNG>
527 return ext::get<0>(percentileAndInterval(
d, perc));
537 template<
template <
class,
class>
class D,
class C,
class URNG>
539 Real percentile)
const {
541 QL_REQUIRE(percentile >= 0. && percentile <= 1.,
542 "Incorrect percentile");
545 Real attachAmount = basket_->attachmentAmount();
546 Real detachAmount = basket_->detachmentAmount();
548 std::vector<Real> rankLosses;
551 for(
Size iSim=0; iSim < nSims_; iSim++) {
552 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
553 Real portfSimLoss=0.;
554 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
556 events[iEvt].dayFromRef)) {
557 Size iName = events[iEvt].nameIdx;
560 basket_->exposure(basket_->names()[iName],
561 Date(events[iEvt].dayFromRef +
563 (1.-getEventRecovery(events[iEvt]));
566 portfSimLoss = std::min(std::max(portfSimLoss - attachAmount, 0.),
567 detachAmount - attachAmount);
569 rankLosses.push_back(portfSimLoss);
572 std::sort(rankLosses.begin(), rankLosses.end());
573 Size quantilePosition =
static_cast<Size>(floor(nSims_*percentile));
574 Real quantileValue = rankLosses[quantilePosition];
578 Real lowerPercentile, upperPercentile;
579 Size r = quantilePosition - 1;
580 Size s = quantilePosition + 1;
581 bool rLocked =
false,
585 for(
Size delta=1; delta < quantilePosition; delta++) {
588 percentile, 1.e-8, 500);
594 percentile, 1.e-8, 500)
598 percentile, 1.e-8, 500)
600 if((pMinus > confInterval) && !rLocked ) {
604 if((pPlus >= confInterval) && !sLocked) {
608 if(rLocked && sLocked)
break;
611 s = std::min(nSims_-1,
s);
613 lowerPercentile = rankLosses[
r];
614 upperPercentile = rankLosses[
s];
616 return ext::make_tuple(quantileValue, lowerPercentile, upperPercentile);
620 template<
template <
class,
class>
class D,
class C,
class URNG>
624 std::vector<Real> varLevels = splitVaRAndError(date, loss, 0.95)[0];
626 std::transform(varLevels.begin(), varLevels.end(), varLevels.begin(),
627 [=](
Real x) ->
Real { return x * loss; });
633 template<
template <
class,
class>
class D,
class C,
class URNG>
643 Real attachAmount = basket_->attachmentAmount();
644 Real detachAmount = basket_->detachmentAmount();
645 Size numLiveNames = basket_->remainingSize();
647 std::vector<Real> split(numLiveNames, 0.);
648 std::vector<GeneralStatistics> splitStats(numLiveNames,
653 for(
Size iSim=0; iSim < nSims_; iSim++) {
654 const std::vector<simEvent<D<C, URNG> > >& events = getSim(iSim);
655 Real portfSimLoss=0.;
657 std::vector<simEvent<D<C, URNG> > > splitEventsBuffer;
659 for(
Size iEvt=0; iEvt < events.size(); iEvt++) {
661 events[iEvt].dayFromRef)) {
662 Size iName = events[iEvt].nameIdx;
665 basket_->exposure(basket_->names()[iName],
666 Date(events[iEvt].dayFromRef +
668 (1.-getEventRecovery(events[iEvt]));
670 splitEventsBuffer.push_back(events[iEvt]);
673 portfSimLoss = std::min(std::max(portfSimLoss - attachAmount, 0.),
674 detachAmount - attachAmount);
678 Real ptflCumulLoss = 0.;
679 if(portfSimLoss > loss) {
680 std::sort(splitEventsBuffer.begin(), splitEventsBuffer.end());
682 split.assign(numLiveNames, 0.);
685 for(
Size i=0; i<splitEventsBuffer.size(); i++) {
686 Size iName = splitEventsBuffer[i].nameIdx;
691 basket_->exposure(basket_->names()[iName],
692 Date(splitEventsBuffer[i].dayFromRef +
694 (1.-getEventRecovery(splitEventsBuffer[i]));
696 Real tranchedLossBefore =
697 std::min(std::max(ptflCumulLoss - attachAmount, 0.),
698 detachAmount - attachAmount);
699 ptflCumulLoss += lossName;
700 Real tranchedLossAfter =
701 std::min(std::max(ptflCumulLoss - attachAmount, 0.),
702 detachAmount - attachAmount);
704 split[iName] += tranchedLossAfter - tranchedLossBefore;
706 for(
Size iName=0; iName<numLiveNames; iName++) {
707 splitStats[iName].add(split[iName] /
708 std::min(std::max(ptflCumulLoss - attachAmount, 0.),
709 detachAmount - attachAmount) );
715 std::vector<Real> means, rangeUp, rangeDown;
717 for(
Size iName=0; iName<numLiveNames; iName++) {
718 means.push_back(splitStats[iName].mean());
719 Real error = confidFactor * splitStats[iName].errorEstimate() ;
720 rangeDown.push_back(means.back() - error);
721 rangeUp.push_back(means.back() + error);
724 std::vector<std::vector<Real> >
results;
791 template<
class copulaPolicy,
class USNG>
794 : nameIdx(
n), dayFromRef(
d){}
798 return dayFromRef < evt.dayFromRef;
805 template<
class copulaPolicy,
class USNG = SobolRsg>
812 const ext::shared_ptr<DefaultLatentModel<copulaPolicy> >
model_;
819 const std::vector<Real>& recoveries = std::vector<Real>(),
821 Real accuracy = 1.e-6,
824 model->numFactors(), model->size(), model->copula(), nSims, seed),
835 Real accuracy = 1.e-6,
838 (model->numFactors(), model->size(), model->copula(),
862 void
nextSample(const std::vector<Real>& values) const;
871 const ext::shared_ptr<Pool>& pool = this->
basket_->pool();
872 for(
Size iName=0; iName < this->
basket_->size(); ++iName)
873 horizonDefaultPs_.push_back(pool->get(pool->names()[iName]).
874 defaultProbability(this->
basket_->defaultKeys()[iName])
875 ->defaultProbability(maxHorizonDate,
true));
887 return model_->latentVarValue(factorsSample, iVar);
901 "Incompatible basket and model sizes.");
903 "Incompatible basket and recovery sizes.");
919 template<
class C,
class URNG>
921 const std::vector<Real>& values)
const
923 const ext::shared_ptr<Pool>& pool = this->basket_->pool();
925 this->simsBuffer_.push_back(std::vector<defaultSimEvent> ());
927 for(
Size iName=0; iName<model_->size(); iName++) {
928 Real latentVarSample =
929 model_->latentVarValue(values, iName);
931 model_->cumulativeY(latentVarSample, iName);
933 if (horizonDefaultPs_[iName] >= simDefaultProb) {
935 pool->get(pool->names()[iName]).
936 defaultProbability(this->basket_->defaultKeys()[iName]);
basket of issuers and related notionals
Beta and beta incomplete functions.
std::int_fast32_t serial_type
serial number type
Date::serial_type serialNumber() const
Default event Latent Model.
RelinkableHandle< Basket > basket_
void add(Real value, Real weight=1.0)
adds a datum to the set, possibly with a weight
Real errorEstimate() const
Shared handle to an observable.
Real frequency(Size i) const
const std::vector< Real > & breaks() const
Inverse cumulative normal distribution function.
static Real standard_value(Real x)
Generic multifactor latent variable model.
Framework for calculation on demand and result caching.
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Real expectedRecovery(const Date &, Size iName, const DefaultProbKey &) const override
RandomDefaultLM(const ext::shared_ptr< DefaultLatentModel< copulaPolicy > > &model, const std::vector< Real > &recoveries=std::vector< Real >(), Size nSims=0, Real accuracy=1.e-6, BigNatural seed=2863311530UL)
const ext::shared_ptr< DefaultLatentModel< copulaPolicy > > model_
void nextSample(const std::vector< Real > &values) const
simEvent< RandomDefaultLM > defaultSimEvent
std::vector< Probability > horizonDefaultPs_
const std::vector< Real > recoveries_
void resetModel() override
Concrete models do now any updates/inits they need on basket reset.
RandomDefaultLM(const ext::shared_ptr< ConstantLossLatentmodel< copulaPolicy > > &model, Size nSims=0, Real accuracy=1.e-6, BigNatural seed=2863311530UL)
Real latentVarValue(const std::vector< Real > &factorsSample, Size iVar) const
Real getEventRecovery(const defaultSimEvent &evt) const
static const Size maxHorizon_
~RandomLM() override=default
void performCalculations() const override
void performSimulations() const
Real percentile(const Date &d, Real percentile) const override
Value at Risk given a default loss percentile.
Probability probAtLeastNEvents(Size n, const Date &d) const override
virtual std::pair< Real, Real > expectedTrancheLossInterval(const Date &d, Probability confidencePerc) const
std::vector< std::vector< simEvent< derivedRandomLM< copulaPolicy, USNG > > > > simsBuffer_
virtual std::vector< std::vector< Real > > splitVaRAndError(const Date &date, Real loss, Probability confInterval) const
Real defaultCorrelation(const Date &d, Size iName, Size jName) const override
Pearsons' default probability correlation.
LatentModel< copulaPolicy >::template FactorSampler< USNG > copulaRNG_type
Real expectedShortfall(const Date &d, Real percent) const override
Expected shortfall given a default loss percentile.
std::vector< Real > splitVaRLevel(const Date &date, Real loss) const override
virtual ext::tuple< Real, Real, Real > percentileAndInterval(const Date &d, Real percentile) const
std::vector< Probability > probsBeingNthEvent(Size n, const Date &d) const override
Real getEventRecovery(const simEvent< derivedRandomLM< copulaPolicy, USNG > > &evt) const
Real expectedTrancheLoss(const Date &d) const override
virtual Histogram computeHistogram(const Date &d) const
ext::shared_ptr< copulaRNG_type > copulasRng_
const std::vector< simEvent< derivedRandomLM< copulaPolicy, USNG > > > & getSim(const Size iSim) const
RandomLM(Size numFactors, Size numLMVars, copulaPolicy copula, Size nSims, BigNatural seed)
std::map< Real, Probability > lossDistribution(const Date &d) const override
Full loss distribution.
DateProxy & evaluationDate()
the date at which pricing is to be performed.
static Settings & instance()
access to the unique instance
Sobol low-discrepancy sequence generator.
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Utility for the numerical time solver.
Real operator()(Real t) const
Root(const Handle< DefaultProbabilityTermStructure > &dts, Real pd)
const Handle< DefaultProbabilityTermStructure > dts_
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
unsigned QL_INTEGER Natural
positive integer
QL_INTEGER Integer
integer number
Real Probability
probability
std::size_t Size
size of a container
statistics tool for generating histogram of given data
Generic multifactor latent variable model.
Real incompleteBetaFunction(Real a, Real b, Real x, Real accuracy, Integer maxIteration)
Incomplete Beta function.
RandomDefaultLM< GaussianCopulaPolicy > GaussianRandomDefaultLM
unsigned QL_BIG_INTEGER BigNatural
large positive integer
RandomDefaultLM< TCopulaPolicy > TRandomDefaultLM
ext::shared_ptr< YieldTermStructure > r
empirical-distribution risk measures
Sobol low-discrepancy sequence generator.
bool operator<(const simEvent &evt) const
simEvent(unsigned int n, unsigned int d)
Maps tuple to either the boost or std implementation.