Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
numericlgmriskparticipationagreementengine_tlock.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
24#include <ql/cashflows/coupon.hpp>
25#include <ql/pricingengines/bond/bondfunctions.hpp>
26#include <ql/quotes/simplequote.hpp>
27#include <ql/timegrid.hpp>
28
29namespace ore {
30namespace data {
31
32using namespace QuantLib;
33using namespace QuantExt;
34
36 const std::string& baseCcy, const std::map<std::string, Handle<YieldTermStructure>>& discountCurves,
37 const std::map<std::string, Handle<Quote>>& fxSpots, const QuantLib::ext::shared_ptr<QuantExt::LinearGaussMarkovModel>& model,
38 const Real sy, const Size ny, const Real sx, const Size nx, const Handle<YieldTermStructure>& treasuryCurve,
39 const Handle<DefaultProbabilityTermStructure>& defaultCurve, const Handle<Quote>& recoveryRate,
40 const Size timeStepsPerYear)
41 : LgmConvolutionSolver2(model, sy, ny, sx, nx), baseCcy_(baseCcy), discountCurves_(discountCurves),
42 fxSpots_(fxSpots), treasuryCurve_(treasuryCurve), defaultCurve_(defaultCurve), recoveryRate_(recoveryRate),
43 timeStepsPerYear_(timeStepsPerYear) {
45 for (auto const& d : discountCurves_)
46 registerWith(d.second);
47 for (auto const& s : fxSpots_)
48 registerWith(s.second);
49 fxSpots_[baseCcy] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.0));
50 registerWith(treasuryCurve_);
51 registerWith(defaultCurve_);
52 registerWith(recoveryRate_);
53}
54
56
57 QL_REQUIRE(!discountCurves_[baseCcy_].empty(),
58 "RiskParticipationAgreementEngineTLock::calculate(): empty discount curve for base ccy " << baseCcy_);
59 QL_REQUIRE(!defaultCurve_.empty(), "RiskParticipationAgreementEngineTLock::calculate(): empty default curve");
60 QL_REQUIRE(arguments_.fixedRecoveryRate != Null<Real>() || !recoveryRate_.empty(),
61 "RiskParticipationAgreementEngineTLock::calculate(): empty recovery and trade does not specify "
62 "fixed recovery");
63
64 // asof date for valuation
65
66 referenceDate_ = discountCurves_[baseCcy_]->referenceDate();
67
68 // effective recovery rate to use
69
70 Real effectiveRecoveryRate =
71 arguments_.fixedRecoveryRate == Null<Real>() ? recoveryRate_->value() : arguments_.fixedRecoveryRate;
72
73 // Compute the fee leg NPV
74
75 Real fee = 0.0;
76 Size idx = 0;
77 for (auto const& l : arguments_.protectionFee) {
78 for (auto const& c : l) {
79 if (c->date() <= referenceDate_)
80 continue;
81 QL_REQUIRE(!discountCurves_[arguments_.protectionFeeCcys[idx]].empty(),
82 "RiskParticipationAgreementEngineTLock::calculate(): empty discount curve for ccy "
83 << arguments_.protectionFeeCcys[idx]);
84 QL_REQUIRE(!fxSpots_[arguments_.protectionFeeCcys[idx]].empty(),
85 "RiskParticipationAgreementEngineTLock::calculate(): empty fx spot for ccy pair "
86 << arguments_.protectionFeeCcys[idx] + baseCcy_);
87 // the fee is only paid if the reference entity is still alive at the payment date
88 fee += c->amount() * discountCurves_[arguments_.protectionFeeCcys[idx]]->discount(c->date()) *
89 fxSpots_[arguments_.protectionFeeCcys[idx]]->value() * defaultCurve_->survivalProbability(c->date());
90 // accrual settlement using the mid of the coupon periods
91 auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c);
92 if (cpn && arguments_.settlesAccrual) {
93 Date start = std::max(cpn->accrualStartDate(), referenceDate_);
94 Date end = cpn->accrualEndDate();
95 if (start < end) {
96 Date mid = start + (end - start) / 2;
97 fee += cpn->accruedAmount(mid) * discountCurves_[arguments_.protectionFeeCcys[idx]]->discount(mid) *
98 fxSpots_[arguments_.protectionFeeCcys[idx]]->value() *
99 defaultCurve_->defaultProbability(start, end);
100 }
101 }
102 }
103 ++idx;
104 }
105
106 // We have a zero protection npv, if the protection end is <= ref date. We also set the protection value to zero,
107 // if the termination date < ref date. This is not entirely correct in case the payment date is still in the future
108 // and there is a positive payoff, but we can not compute the bond yield on past dates.
109
110 Real protection = 0.0;
111 if (arguments_.protectionEnd > referenceDate_ && arguments_.terminationDate >= referenceDate_) {
112
113 if (arguments_.terminationDate == referenceDate_) {
114
115 // Handle the case termination date = ref date
116
117 Real optionPv = std::max(0.0, computePayoff().at(0));
118 Date riskHorizon = std::min(arguments_.protectionEnd, arguments_.paymentDate);
119 Real t = discountCurves_[baseCcy_]->timeFromReference(riskHorizon);
120 if (riskHorizon > referenceDate_) {
121 protection = optionPv * defaultCurve_->defaultProbability(0.0, t) * (1.0 - effectiveRecoveryRate) *
122 discountCurves_[baseCcy_]->discount(t / 2.0);
123 }
124
125 } else {
126
127 // Case termination date > ref date: We build a grid on which we compute the positive part of the NPV of the
128 // underlying TLock. The last grid point is the termination date
129
130 Real t_term = discountCurves_[baseCcy_]->timeFromReference(arguments_.terminationDate);
131
132 Size effTimeSteps =
133 std::max<Size>(1, std::lround(static_cast<Real>(std::max<Size>(timeStepsPerYear_, 1)) * t_term + 0.5));
134
135 TimeGrid grid(t_term, effTimeSteps);
136
137 QuantExt::RandomVariable underlyingPv(gridSize(), 0.0);
138 std::vector<Real> optionPv(grid.size(), 0.0);
139
140 // roll back the payoff and positive pv of the TLock from the termination date, and calculate the option pvs
141
142 underlyingPv = computePayoff();
143 optionPv.back() = expectation(max(underlyingPv, QuantExt::RandomVariable(gridSize(), 0.0))).at(0);
144
145 for (Size i = grid.size() - 1; i > 0; --i) {
146 Real t_from = grid[i];
147 Real t_to = grid[i - 1];
148
149 underlyingPv = rollback(underlyingPv, t_from, t_to);
150 optionPv[i - 1] = expectation(max(underlyingPv, QuantExt::RandomVariable(gridSize(), 0.0))).at(0);
151 }
152
153 // compute the protection npv
154
155 for (Size i = 0; i < grid.size(); ++i) {
156 Real t0 = 0.0;
157 if (i > 0) {
158 t0 = 0.5 * (grid[i - 1] + grid[i]);
159 }
160 Real t1 = discountCurves_[baseCcy_]->timeFromReference(arguments_.paymentDate);
161 if (i < grid.size() - 1) {
162 t1 = 0.5 * (grid[i] + grid[i + 1]);
163 }
164 t1 = std::min(t1, discountCurves_[baseCcy_]->timeFromReference(arguments_.protectionEnd));
165 if (t1 > t0 && !QuantLib::close_enough(t0, t1)) {
166 protection +=
167 optionPv[i] * defaultCurve_->defaultProbability(t0, t1) * (1.0 - effectiveRecoveryRate);
168 }
169 }
170 }
171 }
172
173 // multiply the protection npv by the participation rate
174
175 protection *= arguments_.participationRate;
176
177 // compute the total NPV, we buy the protection if we pay the fee
178
179 results_.value = (arguments_.protectionFeePayer ? 1.0 : -1.0) * (protection - fee);
180}
181
183
184 Date settlement = arguments_.bond->settlementDate(arguments_.terminationDate);
185 Real multiplier = (arguments_.payer ? -1.0 : 1.0) * arguments_.bondNotional * arguments_.bond->notional(settlement);
186
187 if (arguments_.terminationDate == referenceDate_) {
188 Real price = BondFunctions::cleanPrice(*arguments_.bond, **treasuryCurve_, settlement);
189 Real yield =
190 BondFunctions::yield(*arguments_.bond, price, arguments_.dayCounter, Compounded, Semiannual, settlement);
191 Real dv01 = price / 100.0 *
192 BondFunctions::duration(*arguments_.bond, yield, arguments_.dayCounter, Compounded, Semiannual,
193 Duration::Modified, settlement);
194 return QuantExt::RandomVariable(gridSize(), multiplier * (arguments_.referenceRate - yield) * dv01 *
195 discountCurves_[baseCcy_]->discount(arguments_.paymentDate));
196 }
197
198 QuantExt::RandomVariable result(gridSize(), 0.0);
199
200 LgmImpliedYtsFwdFwdCorrected modelCurve(model(), treasuryCurve_, treasuryCurve_->dayCounter(), false, true);
201 modelCurve.move(arguments_.terminationDate, 0.0);
202
203 Real t = discountCurves_[baseCcy_]->timeFromReference(arguments_.terminationDate);
204 Real t_pay = discountCurves_[baseCcy_]->timeFromReference(arguments_.paymentDate);
205 auto states = stateGrid(t);
206
207 for (Size i = 0; i < states.size(); ++i) {
208
209 modelCurve.state(states[i]);
210
211 // use hardcoded conventions for Treasury bonds
212 Real price = BondFunctions::cleanPrice(*arguments_.bond, modelCurve, settlement);
213 Real yield =
214 BondFunctions::yield(*arguments_.bond, price, arguments_.dayCounter, Compounded, Semiannual, settlement);
215 Real dv01 = price / 100.0 *
216 BondFunctions::duration(*arguments_.bond, yield, arguments_.dayCounter, Compounded, Semiannual,
217 Duration::Modified, settlement);
218
219 result.set(i, multiplier * (arguments_.referenceRate - yield) * dv01 *
220 model()->discountBond(t, t_pay, states[i], discountCurves_[baseCcy_]) /
221 model()->numeraire(t, states[i], discountCurves_[baseCcy_]));
222 }
223
224 return result;
225}
226
227} // namespace data
228} // namespace ore
const Instrument::results * results_
const boost::shared_ptr< LinearGaussMarkovModel > & model() const
RandomVariable rollback(const RandomVariable &v, const Real t1, const Real t0) const
RandomVariable stateGrid(const Real t) const
void move(const Date &d, const Real s)
NumericLgmRiskParticipationAgreementEngineTLock(const std::string &baseCcy, const std::map< std::string, Handle< YieldTermStructure > > &discountCurves, const std::map< std::string, Handle< Quote > > &fxSpots, const QuantLib::ext::shared_ptr< QuantExt::LinearGaussMarkovModel > &model, const Real sy, const Size ny, const Real sx, const Size nx, const Handle< YieldTermStructure > &treasuryCurve, const Handle< DefaultProbabilityTermStructure > &defaultCurve, const Handle< Quote > &recoveryRate, const Size timeStepsPerYear)
@ data
Definition: log.hpp:77
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable expectation(const RandomVariable &r)
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Real at(const Size i) const
void set(const Size i, const Real v)
Swap::arguments * arguments_