Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/catbonds/montecarlocatbondengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2012, 2013 Grzegorz Andruszkiewicz
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/cashflows/cashflows.hpp>
21
#include <ql/experimental/catbonds/montecarlocatbondengine.hpp>
22
#include <ql/optional.hpp>
23
#include <algorithm>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    MonteCarloCatBondEngine::MonteCarloCatBondEngine(
29
        ext::shared_ptr<CatRisk> catRisk,
30
        Handle<YieldTermStructure> discountCurve,
31
        const ext::optional<bool>& includeSettlementDateFlows)
32
0
    : catRisk_(std::move(catRisk)), discountCurve_(std::move(discountCurve)),
33
0
      includeSettlementDateFlows_(includeSettlementDateFlows) {
34
0
        registerWith(discountCurve_);
35
0
    }
36
37
0
    void MonteCarloCatBondEngine::calculate() const {
38
0
        QL_REQUIRE(!discountCurve_.empty(),
39
0
                   "discounting term structure handle is empty");
40
41
0
        results_.valuationDate = (*discountCurve_)->referenceDate();
42
43
0
        bool includeRefDateFlows = includeSettlementDateFlows_ ? // NOLINT(readability-implicit-bool-conversion)
44
0
                                       *includeSettlementDateFlows_ :
45
0
                                       Settings::instance().includeReferenceDateEvents();
46
47
0
        Real lossProbability;
48
0
        Real exhaustionProbability;
49
0
        Real expectedLoss;
50
51
0
        results_.value = npv(includeRefDateFlows,
52
0
                             results_.valuationDate,
53
0
                             results_.valuationDate,
54
0
                             lossProbability,
55
0
                             exhaustionProbability,
56
0
                             expectedLoss);
57
58
0
        results_.lossProbability = lossProbability;
59
0
        results_.exhaustionProbability = exhaustionProbability;
60
0
        results_.expectedLoss = expectedLoss;
61
62
        // a bond's cashflow on settlement date is never taken into
63
        // account, so we might have to play it safe and recalculate
64
0
        if (!includeRefDateFlows
65
0
                     && results_.valuationDate == arguments_.settlementDate) {
66
            // same parameters as above, we can avoid another call
67
0
            results_.settlementValue = results_.value;
68
0
        } else {
69
            // no such luck
70
0
            results_.settlementValue =
71
0
                npv(includeRefDateFlows, arguments_.settlementDate, arguments_.settlementDate, lossProbability, exhaustionProbability, expectedLoss);
72
0
        }
73
0
    }
74
75
    Real MonteCarloCatBondEngine::npv(bool includeSettlementDateFlows, Date settlementDate, Date npvDate, Real& lossProbability, Real &exhaustionProbability, Real& expectedLoss) const
76
0
    {
77
0
        const size_t MAX_PATHS = 10000; //TODO
78
0
        lossProbability =  0.0;
79
0
        exhaustionProbability = 0.0;
80
0
        expectedLoss = 0.0;
81
0
        if (arguments_.cashflows.empty())
82
0
            return 0.0;
83
84
0
        if (settlementDate == Date())
85
0
            settlementDate = Settings::instance().evaluationDate();
86
87
0
        if (npvDate == Date())
88
0
            npvDate = settlementDate;
89
90
0
        Real totalNPV = 0.0;
91
0
        Date effectiveDate = std::max(arguments_.startDate, settlementDate);
92
0
        Date maturityDate = (*arguments_.cashflows.rbegin())->date();
93
0
        ext::shared_ptr<CatSimulation> catSimulation = catRisk_->newSimulation(effectiveDate, maturityDate);
94
0
        std::vector<std::pair<Date, Real> > eventsPath;
95
0
        NotionalPath notionalPath;
96
0
        Real riskFreeNPV = pathNpv(includeSettlementDateFlows, settlementDate, notionalPath);
97
0
        size_t pathCount=0;
98
0
        while(catSimulation->nextPath(eventsPath) && pathCount<MAX_PATHS)
99
0
        {
100
0
            arguments_.notionalRisk->updatePath(eventsPath, notionalPath);
101
0
            if(notionalPath.loss()>0) { //optimization, most paths will not include any loss
102
0
                totalNPV += pathNpv(includeSettlementDateFlows, settlementDate, notionalPath);
103
0
                lossProbability+=1;
104
0
                if (notionalPath.loss()==1) 
105
0
                    exhaustionProbability+=1;
106
0
                expectedLoss+=notionalPath.loss();
107
0
            } else {
108
0
                totalNPV += riskFreeNPV;
109
0
            }
110
0
            pathCount++;
111
0
        }
112
0
        lossProbability/=pathCount;
113
0
        exhaustionProbability/=pathCount;
114
0
        expectedLoss/=pathCount;
115
0
        return totalNPV/(pathCount*discountCurve_->discount(npvDate));
116
0
    }
117
118
    Real MonteCarloCatBondEngine::pathNpv(bool includeSettlementDateFlows, 
119
                                          Date settlementDate, 
120
0
                                          const NotionalPath& notionalPath) const {
121
0
        Real totalNPV = 0.0;
122
0
        for (auto& cashflow : arguments_.cashflows) {
123
0
            if (!cashflow->hasOccurred(settlementDate, includeSettlementDateFlows)) {
124
0
                Real amount = cashFlowRiskyValue(cashflow, notionalPath);
125
0
                totalNPV += amount * discountCurve_->discount(cashflow->date());
126
0
            }
127
0
        }
128
0
        return totalNPV;
129
0
    }
130
131
    Real MonteCarloCatBondEngine::cashFlowRiskyValue(const ext::shared_ptr<CashFlow>& cf,
132
0
                                                     const NotionalPath& notionalPath) const {
133
0
        return cf->amount()*notionalPath.notionalRate(cf->date()); //TODO: fix for more complicated cashflows
134
0
    }
135
136
}