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
20
21#include <oret/toplevelfixture.hpp>
22
25
26#include <ql/cashflows/couponpricer.hpp>
27#include <ql/cashflows/iborcoupon.hpp>
28#include <ql/cashflows/simplecashflow.hpp>
29#include <ql/currencies/america.hpp>
30#include <ql/currencies/europe.hpp>
31#include <ql/indexes/iborindex.hpp>
32#include <ql/quotes/simplequote.hpp>
33#include <ql/termstructures/yield/flatforward.hpp>
34#include <ql/time/calendars/target.hpp>
35#include <ql/time/calendars/unitedstates.hpp>
36#include <ql/time/daycounters/actual360.hpp>
37#include <ql/time/daycounters/actual365fixed.hpp>
38
39#include <boost/test/unit_test.hpp>
40
41using namespace QuantLib;
42using namespace QuantExt;
43using namespace boost::unit_test_framework;
44
45struct RepresentativeFxOptionFixture : public ore::test::TopLevelFixture {
46public:
47 RepresentativeFxOptionFixture() { Settings::instance().evaluationDate() = today; }
48 Date today = Date(20, Apr, 2021);
49 Handle<Quote> eurUsdSpot = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.2));
50 Actual365Fixed dc;
51 Handle<YieldTermStructure> eurCurve = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(today, 0.01, dc));
52 Handle<YieldTermStructure> usdCurve = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(today, 0.03, dc));
53};
54
55BOOST_AUTO_TEST_SUITE(OREDataTestSuite)
56
57BOOST_FIXTURE_TEST_SUITE(RepresentativeFxOptionTest, RepresentativeFxOptionFixture)
58
59BOOST_AUTO_TEST_CASE(testSimpleCashflows) {
60
61 BOOST_TEST_MESSAGE("test reproducing simple cashflows");
62
63 Real tol = 1E-12, tol2 = 1E-10;
64
65 Date refDate = today + 5 * Years;
66
67 Real eurAmount = 35000.0;
68 Real usdAmount = 14222.0;
69 Leg eurLeg = {QuantLib::ext::make_shared<SimpleCashFlow>(eurAmount, refDate)};
70 Leg usdLeg = {QuantLib::ext::make_shared<SimpleCashFlow>(usdAmount, refDate)};
71
72 RepresentativeFxOptionMatcher matcher1({eurLeg, usdLeg}, {true, false}, {"EUR", "USD"}, refDate, "EUR", "USD",
73 eurCurve, usdCurve, eurUsdSpot, true);
74
75 BOOST_CHECK_EQUAL(matcher1.currency1(), "EUR");
76 BOOST_CHECK_EQUAL(matcher1.currency2(), "USD");
77 BOOST_CHECK_CLOSE(matcher1.amount1(), -eurAmount, tol);
78 BOOST_CHECK_CLOSE(matcher1.amount2(), usdAmount, tol);
79
80 RepresentativeFxOptionMatcher matcher2({eurLeg, usdLeg}, {false, true}, {"EUR", "USD"}, refDate, "EUR", "USD",
81 eurCurve, usdCurve, eurUsdSpot, true);
82
83 BOOST_CHECK_EQUAL(matcher2.currency1(), "EUR");
84 BOOST_CHECK_EQUAL(matcher2.currency2(), "USD");
85 BOOST_CHECK_CLOSE(matcher2.amount1(), eurAmount, tol);
86 BOOST_CHECK_CLOSE(matcher2.amount2(), -usdAmount, tol);
87
88 RepresentativeFxOptionMatcher matcher3({eurLeg, usdLeg}, {false, false}, {"EUR", "USD"}, refDate, "EUR", "USD",
89 eurCurve, usdCurve, eurUsdSpot, true);
90
91 BOOST_CHECK_EQUAL(matcher3.currency1(), "EUR");
92 BOOST_CHECK_EQUAL(matcher3.currency2(), "USD");
93 BOOST_CHECK_CLOSE(matcher3.amount1(), eurAmount, tol);
94 BOOST_CHECK_CLOSE(matcher3.amount2(), usdAmount, tol);
95
96 RepresentativeFxOptionMatcher matcher4({eurLeg, usdLeg}, {true, true}, {"EUR", "USD"}, refDate, "EUR", "USD",
97 eurCurve, usdCurve, eurUsdSpot, true);
98
99 BOOST_CHECK_EQUAL(matcher4.currency1(), "EUR");
100 BOOST_CHECK_EQUAL(matcher4.currency2(), "USD");
101 BOOST_CHECK_CLOSE(matcher4.amount1(), -eurAmount, tol);
102 BOOST_CHECK_CLOSE(matcher4.amount2(), -usdAmount, tol);
103
104 RepresentativeFxOptionMatcher matcher5({eurLeg}, {true}, {"EUR"}, refDate, "EUR", "USD", eurCurve, usdCurve,
105 eurUsdSpot, true);
106
107 BOOST_CHECK_EQUAL(matcher5.currency1(), "EUR");
108 BOOST_CHECK_EQUAL(matcher5.currency2(), "USD");
109 BOOST_CHECK_CLOSE(matcher5.amount1(), -eurAmount, tol);
110 BOOST_CHECK_SMALL(matcher5.amount2(), tol2);
111
112 RepresentativeFxOptionMatcher matcher6({}, {}, {}, refDate, "EUR", "USD", eurCurve, usdCurve, eurUsdSpot, true);
113
114 BOOST_CHECK_EQUAL(matcher6.currency1(), "EUR");
115 BOOST_CHECK_EQUAL(matcher6.currency2(), "USD");
116 BOOST_CHECK_SMALL(matcher6.amount1(), tol2);
117 BOOST_CHECK_SMALL(matcher6.amount2(), tol2);
118}
119
120BOOST_AUTO_TEST_CASE(testFxLinkedCashflow) {
121
122 BOOST_TEST_MESSAGE("test fx linked cashflow");
123
124 Real tol = 1E-12;
125
126 Date refDate = today + 5 * Years;
127 Date fixDate = UnitedStates(UnitedStates::Settlement).advance(refDate, -2 * Days);
128
129 auto fxSpotScen = QuantLib::ext::make_shared<SimpleQuote>(eurUsdSpot->value());
130 auto fxIndex = QuantLib::ext::make_shared<FxIndex>("dummy", 2, EURCurrency(), USDCurrency(), UnitedStates(UnitedStates::Settlement),
131 Handle<Quote>(fxSpotScen), eurCurve, usdCurve);
132 Real forAmount = 100000.0;
133
134 Leg leg = {QuantLib::ext::make_shared<FXLinkedCashFlow>(refDate, fixDate, forAmount, fxIndex)};
135
136 RepresentativeFxOptionMatcher matcher({leg}, {false}, {"USD"}, today, "EUR", "USD", eurCurve, usdCurve, eurUsdSpot);
137
138 BOOST_CHECK_EQUAL(matcher.currency1(), "EUR");
139 BOOST_CHECK_EQUAL(matcher.currency2(), "USD");
140
141 Real eurAmount = matcher.amount1();
142 Real usdAmount = matcher.amount2();
143
144 // check npv matches
145
146 BOOST_CHECK_CLOSE(eurAmount * eurUsdSpot->value() + usdAmount, leg.front()->amount() * usdCurve->discount(refDate),
147 tol);
148
149 // check fx delta matches
150
151 fxSpotScen->setValue(eurUsdSpot->value() * 1.01);
152 Real upNpv = leg.front()->amount() * usdCurve->discount(refDate);
153
154 fxSpotScen->setValue(eurUsdSpot->value() * 0.99);
155 Real downNpv = leg.front()->amount() * usdCurve->discount(refDate);
156
157 BOOST_CHECK_CLOSE(eurAmount, (upNpv - downNpv) / (eurUsdSpot->value() * 0.02), tol);
158}
159
160BOOST_AUTO_TEST_CASE(testFxLinkedFloatingRateCoupon) {
161
162 BOOST_TEST_MESSAGE("test fx linked floating rate coupon");
163
164 Real tol = 1E-12;
165
166 Date refDate = today + 5 * Years;
167 Date fixDate = UnitedStates(UnitedStates::Settlement).advance(refDate, -2 * Days);
168
169 auto fxSpotScen = QuantLib::ext::make_shared<SimpleQuote>(eurUsdSpot->value());
170 auto fxIndex = QuantLib::ext::make_shared<FxIndex>("dummy", 2, EURCurrency(), USDCurrency(), UnitedStates(UnitedStates::Settlement),
171 Handle<Quote>(fxSpotScen), eurCurve, usdCurve);
172 Real forAmount = 100000.0;
173
174 auto iborIndex = QuantLib::ext::make_shared<IborIndex>("dummyIbor", 6 * Months, 2, EURCurrency(), TARGET(), Following,
175 false, Actual360(), eurCurve);
176 Date start(refDate - 6 * Months), end(refDate);
177 auto iborCoupon = QuantLib::ext::make_shared<IborCoupon>(refDate, 1.0, start, end, 2, iborIndex, 1.0, 0.0);
178 iborCoupon->setPricer(QuantLib::ext::make_shared<BlackIborCouponPricer>());
179
180 Leg leg = {QuantLib::ext::make_shared<FloatingRateFXLinkedNotionalCoupon>(fixDate, forAmount, fxIndex, iborCoupon)};
181
182 RepresentativeFxOptionMatcher matcher({leg}, {false}, {"USD"}, today, "EUR", "USD", eurCurve, usdCurve, eurUsdSpot);
183
184 BOOST_CHECK_EQUAL(matcher.currency1(), "EUR");
185 BOOST_CHECK_EQUAL(matcher.currency2(), "USD");
186
187 Real eurAmount = matcher.amount1();
188 Real usdAmount = matcher.amount2();
189
190 // check npv matches
191
192 BOOST_CHECK_CLOSE(eurAmount * eurUsdSpot->value() + usdAmount, leg.front()->amount() * usdCurve->discount(refDate),
193 tol);
194
195 // check fx delta matches
196
197 fxSpotScen->setValue(eurUsdSpot->value() * 1.01);
198 Real upNpv = leg.front()->amount() * usdCurve->discount(refDate);
199
200 fxSpotScen->setValue(eurUsdSpot->value() * 0.99);
201 Real downNpv = leg.front()->amount() * usdCurve->discount(refDate);
202
203 BOOST_CHECK_CLOSE(eurAmount, (upNpv - downNpv) / (eurUsdSpot->value() * 0.02), tol);
204}
205
206BOOST_AUTO_TEST_SUITE_END()
207
208BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testSimpleCashflows)