Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
representativefxoption.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
19/*! \file qle/models/representativefxoption.hpp
20 \brief representative fx option matcher
21 \ingroup models
22*/
23
25
27
30
31#include <ql/cashflows/cashflows.hpp>
32#include <ql/cashflows/simplecashflow.hpp>
33#include <ql/math/functional.hpp>
34#include <ql/quotes/derivedquote.hpp>
35#include <ql/quotes/simplequote.hpp>
36
37namespace QuantExt {
38
40 const std::vector<Leg>& underlying, const std::vector<bool>& isPayer, const std::vector<std::string>& currencies,
41 const Date& referenceDate, const std::string& forCcy, const std::string& domCcy,
42 const Handle<YieldTermStructure>& forCurve, const Handle<YieldTermStructure>& domCurve, const Handle<Quote>& fxSpot,
43 const bool includeRefDateFlows) {
44
45 Date today = Settings::instance().evaluationDate();
46
47 // 1 check inputs
48
49 QL_REQUIRE(referenceDate >= today, "RepresentativeFxOptionMatcher: referenceDate ("
50 << referenceDate << ") must be < today (" << today << ")");
51 QL_REQUIRE(isPayer.size() == underlying.size(), "RepresentativeFxOptionMatcher: isPayer ("
52 << isPayer.size() << ") does not match underlying ("
53 << underlying.size() << ")");
54 QL_REQUIRE(currencies.size() == underlying.size(), "RepresentativeFxOptionMatcher: currencies ("
55 << currencies.size() << ") does not match underlying ("
56 << underlying.size() << ")");
57
58 // 2a make a copy of the input fx spot that we can shift later
59
60 auto fxScenarioValue = QuantLib::ext::make_shared<SimpleQuote>(fxSpot->value());
61 Handle<Quote> fxSpotScen(fxScenarioValue);
62
63 // 2b create the inverse spot, this is convenient when we clone fx linked cashflows below
64
65 auto m = [](Real x) { return 1.0 / x; };
66 Handle<Quote> fxSpotScenInv(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotScen, m));
67
68 // 3 collect cashflows by their pay currencies and link them to the fx spot copy (if applicable)
69
70 Leg forCfs, domCfs;
71
72 for (Size i = 0; i < underlying.size(); ++i) {
73 // 3a check the pay ccy is one of the two currencies to be matched
74
75 QL_REQUIRE(currencies[i] == forCcy || currencies[i] == domCcy,
76 "RepresentativeFxOptionMatcher: currency at index "
77 << i << " (" << currencies[i] << ") does not match forCcy (" << forCcy << ") or domCcy ("
78 << domCcy << ")");
79
80 for (auto const& c : underlying[i]) {
81
82 // 3b skip cashflows with pay date on or
83
84 if (c->date() < referenceDate || (c->date() == referenceDate && !includeRefDateFlows))
85 continue;
86
87 // 3b check for non-supported coupon types that are linked to fx
88
89 QL_REQUIRE(!QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(c),
90 "RepresentativeFxOptionMatcher: Indexed Coupons are not supported");
91
92 QuantLib::ext::shared_ptr<CashFlow> res;
93
94 if (auto fxlinked = QuantLib::ext::dynamic_pointer_cast<FXLinked>(c)) {
95
96 // 3c clone fx linked coupons and link them to our scenario fx spot quote
97
98 std::set<std::string> ccys = {fxlinked->fxIndex()->sourceCurrency().code(),
99 fxlinked->fxIndex()->targetCurrency().code()};
100 QL_REQUIRE((ccys == std::set<std::string>{forCcy, domCcy}),
101 "RepresentativeFxOptionMatcher: FXLinked coupon ccys "
102 << fxlinked->fxIndex()->sourceCurrency().code() << ", "
103 << fxlinked->fxIndex()->targetCurrency().code()
104 << " do noth match currencies to be matched (" << forCcy << ", " << domCcy << ")");
105
106 res = QuantLib::ext::dynamic_pointer_cast<CashFlow>(fxlinked->clone(fxlinked->fxIndex()->clone(
107 fxlinked->fxIndex()->sourceCurrency().code() == forCcy ? fxSpotScen : fxSpotScenInv,
108 fxlinked->fxIndex()->sourceCurve(), fxlinked->fxIndex()->targetCurve())));
109 QL_REQUIRE(res, "RepresentativeFxOptionMatcher: internal error, cloned fx linked cashflow could not be "
110 "cast to CashFlow");
111
112 } else {
113
114 // 3d for all other coupons, just push a fixed cashflow with the amount
115
116 res = QuantLib::ext::make_shared<SimpleCashFlow>(c->amount(), c->date());
117 }
118
119 // 3e push the cashflow with the correct sign to the vector holding the flows in the its pay ccy
120
121 res = QuantLib::ext::make_shared<ScaledCashFlow>(isPayer[i] ? -1.0 : 1.0, res);
122
123 if (currencies[i] == forCcy)
124 forCfs.push_back(res);
125 else
126 domCfs.push_back(res);
127 }
128 }
129
130 // 4a determine the NPV of the collected cashflows in dom ccy and as seen from the global evaluation date
131
132 Real npv =
133 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
134
135 // 4b determine the FX Delta of the collected cashflows as seen from the global evaluation date
136
137 constexpr Real relShift = 0.01;
138
139 Real baseFx = fxScenarioValue->value();
140
141 fxScenarioValue->setValue(baseFx * (1.0 + relShift));
142 Real npv_up =
143 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
144
145 fxScenarioValue->setValue(baseFx * (1.0 - relShift));
146 Real npv_down =
147 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
148
149 Real fxDelta = (npv_up - npv_down) / (2.0 * baseFx * relShift);
150
151 // 4c determine the amounts in for and dom ccy matching the fx delta and the npv, as seen from the global evaluation
152 // date, then compound the resulting amounts to the reference date
153
154 amount1_ = fxDelta / forCurve->discount(referenceDate);
155 amount2_ = (npv - fxDelta * baseFx) / domCurve->discount(referenceDate);
156 ccy1_ = forCcy;
157 ccy2_ = domCcy;
158}
159
160} // namespace QuantExt
RepresentativeFxOptionMatcher(const std::vector< Leg > &underlying, const std::vector< bool > &isPayer, const std::vector< std::string > &currencies, const Date &referenceDate, const std::string &forCcy, const std::string &domCcy, const Handle< YieldTermStructure > &forCurve, const Handle< YieldTermStructure > &domCurve, const Handle< Quote > &fxSpot, const bool includeRefDateFlows=false)
An FX linked cashflow.
coupon with an indexed notional
representative fx option matcher
Coupon / Cashflow paying scaled amounts.