Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions
fxoption.cpp File Reference
#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <ored/marketdata/marketimpl.hpp>
#include <ored/portfolio/builders/fxoption.hpp>
#include <ored/portfolio/enginedata.hpp>
#include <ored/portfolio/fxoption.hpp>
#include <ored/utilities/to_string.hpp>
#include <oret/toplevelfixture.hpp>
#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/actualactual.hpp>

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testFXOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXAmericanOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFdValues)
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/3]

BOOST_AUTO_TEST_CASE ( testFXOptionPrice  )

Definition at line 110 of file fxoption.cpp.

110 {
111
112 BOOST_TEST_MESSAGE("Testing FXOption Price...");
113
114 Date today = Settings::instance().evaluationDate();
115
116 // build market
117 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
118 Settings::instance().evaluationDate() = market->asofDate();
119
120 // build FXOption - expiry in 1 Year
121 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20170203"));
122 OptionData optionDataPremiumUSD("Long", "Call", "European", true, vector<string>(1, "20170203"), "Cash", "",
123 PremiumData(10000.0, "USD", Date(3, Feb, 2017)));
124 OptionData optionDataPremiumEUR("Long", "Call", "European", true, vector<string>(1, "20170203"), "Cash", "",
125 PremiumData(10000.0, "EUR", Date(3, Feb, 2017)));
126 Envelope env("CP1");
127 FxOption fxOption(env, optionData, "EUR", 1000000, // bought
128 "USD", 1250000); // sold
129 FxOption fxOptionPremiumUSD(env, optionDataPremiumUSD, "EUR", 1000000, "USD", 1250000);
130 FxOption fxOptionPremiumEUR(env, optionDataPremiumEUR, "EUR", 1000000, "USD", 1250000);
131
132 // NPV currency = sold currency = USD
133
134 Real expectedNPV_USD = 29148.0;
135 Real expectedNPV_EUR = 24290.0;
136 Real expectedNPV_USD_Premium_USD = 19495.6;
137 Real expectedNPV_USD_Premium_EUR = 17496.4;
138
139 // Build and price
140 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
141 engineData->model("FxOption") = "GarmanKohlhagen";
142 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
143 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
144
145 fxOption.build(engineFactory);
146 fxOptionPremiumUSD.build(engineFactory);
147 fxOptionPremiumEUR.build(engineFactory);
148
149 Real npv = fxOption.instrument()->NPV();
150 Real npv_prem_usd = fxOptionPremiumUSD.instrument()->NPV();
151 Real npv_prem_eur = fxOptionPremiumEUR.instrument()->NPV();
152
153 BOOST_TEST_MESSAGE("FX Option, NPV Currency " << fxOption.npvCurrency());
154 BOOST_TEST_MESSAGE("NPV = " << npv);
155 BOOST_TEST_MESSAGE("NPV with premium in USD = " << npv_prem_usd);
156 BOOST_TEST_MESSAGE("NPV with premium in EUR = " << npv_prem_eur);
157
158 // Check NPV matches expected values. Expected value from Wystup is rounded at each step of
159 // calculation so we get a difference of $50 here.
160 QL_REQUIRE(fxOption.npvCurrency() == "USD", "unexpected NPV currency");
161 if (fxOption.npvCurrency() == "EUR") {
162 BOOST_CHECK_CLOSE(npv, expectedNPV_EUR, 0.2);
163 } else if (fxOption.npvCurrency() == "USD") {
164 BOOST_CHECK_CLOSE(npv, expectedNPV_USD, 0.2);
165 BOOST_CHECK_CLOSE(npv_prem_usd, expectedNPV_USD_Premium_USD, 0.001);
166 BOOST_CHECK_CLOSE(npv_prem_eur, expectedNPV_USD_Premium_EUR, 0.001);
167 } else {
168 BOOST_FAIL("Unexpected FX Option npv currency " << fxOption.npvCurrency());
169 }
170
171 Settings::instance().evaluationDate() = today; // reset
172}
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Serializable FX Option.
Definition: fxoption.hpp:38
Serializable object holding option data.
Definition: optiondata.hpp:42
Serializable object holding premium data.
Definition: premiumdata.hpp:37
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [2/3]

BOOST_AUTO_TEST_CASE ( testFXAmericanOptionPrice  )

Definition at line 189 of file fxoption.cpp.

189 {
190
191 BOOST_TEST_MESSAGE("Testing FXAmericanOption Price...");
192
193 AmericanOptionData fxd[] = {// type, strike, spot, q, r, t, vol, value
194 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 0.0206},
195 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8771},
196 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 10.0089},
197 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 0.3159},
198 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1280},
199 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 10.3919},
200 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 0.9495},
201 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777},
202 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 11.1679},
203 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 0.8208},
204 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842},
205 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 10.8087},
206 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 2.7437},
207 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8015},
208 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 13.0170},
209 {Option::Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 5.0063},
210 {Option::Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5106},
211 {Option::Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 15.5689},
212 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 10.0000},
213 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8770},
214 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 0.0410},
215 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 10.2533},
216 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1277},
217 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 0.4562},
218 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 10.8787},
219 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777},
220 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 1.2402},
221 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 10.5595},
222 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842},
223 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 1.0822},
224 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 12.4419},
225 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8014},
226 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 3.3226},
227 {Option::Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 14.6945},
228 {Option::Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5104},
229 {Option::Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 5.8823},
230 {Option::Put, 100.00, 100.00, 0.00, 0.00, 0.50, 0.15, 4.2294}};
231
232 for (auto& f : fxd) {
233 // build market
234 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
235 Date today = Settings::instance().evaluationDate();
236 Settings::instance().evaluationDate() = market->asofDate();
237
238 // build FXAmericanOption
239 string maturityDate = to_string(market->asofDate() + Integer(f.t * 360 + 0.5));
240 OptionData optionData("Long", f.type == Option::Call ? "Call" : "Put", "American", false,
241 vector<string>(1, maturityDate));
242 Envelope env("CP1");
243 FxOption fxOption(env, optionData, "JPY", 1, // foreign
244 "EUR", f.strike); // domestic
245
246 Real expectedNPV = f.result;
247
248 // Build and price
249 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
250 engineData->model("FxOptionAmerican") = "GarmanKohlhagen";
251 engineData->engine("FxOptionAmerican") = "BaroneAdesiWhaleyApproximationEngine";
252
253 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
254
255 fxOption.build(engineFactory);
256
257 Real npv = fxOption.instrument()->NPV();
258
259 BOOST_TEST_MESSAGE("FX American Option, NPV Currency " << fxOption.npvCurrency());
260 BOOST_TEST_MESSAGE("NPV = " << npv);
261
262 // Check NPV matches expected values.
263 QL_REQUIRE(fxOption.npvCurrency() == "EUR", "unexpected NPV currency ");
264
265 BOOST_CHECK_CLOSE(npv, expectedNPV, 0.2);
266 Settings::instance().evaluationDate() = today; // reset
267 }
268}
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [3/3]

BOOST_AUTO_TEST_CASE ( testFdValues  )

Definition at line 270 of file fxoption.cpp.

270 {
271
272 BOOST_TEST_MESSAGE("Testing finite-difference engine "
273 "for American options...");
274
275 /* The data below are from
276 An Approximate Formula for Pricing American Options
277 Journal of Derivatives Winter 1999
278 Ju, N.
279 */
280 AmericanOptionData juValues[] = {// type, strike, spot, q, r, t, vol, value, tol
281 // These values are from Exhibit 3 - Short dated Put Options
282 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.0833, 0.2, 0.006},
283 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.3333, 0.2, 0.201},
284 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.5833, 0.2, 0.433},
285
286 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.0833, 0.2, 0.851},
287 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.3333, 0.2, 1.576},
288 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.5833, 0.2, 1.984},
289
290 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.0833, 0.2, 5.000},
291 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.3333, 0.2, 5.084},
292 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.5833, 0.2, 5.260},
293
294 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.0833, 0.3, 0.078},
295 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.3333, 0.3, 0.697},
296 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.5833, 0.3, 1.218},
297
298 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.0833, 0.3, 1.309},
299 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.3333, 0.3, 2.477},
300 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.5833, 0.3, 3.161},
301
302 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.0833, 0.3, 5.059},
303 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.3333, 0.3, 5.699},
304 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.5833, 0.3, 6.231},
305
306 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.0833, 0.4, 0.247},
307 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.3333, 0.4, 1.344},
308 {Option::Put, 35.00, 40.00, 0.0, 0.0488, 0.5833, 0.4, 2.150},
309
310 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.0833, 0.4, 1.767},
311 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.3333, 0.4, 3.381},
312 {Option::Put, 40.00, 40.00, 0.0, 0.0488, 0.5833, 0.4, 4.342},
313
314 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.0833, 0.4, 5.288},
315 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.3333, 0.4, 6.501},
316 {Option::Put, 45.00, 40.00, 0.0, 0.0488, 0.5833, 0.4, 7.367},
317
318 // Type in Exhibits 4 and 5 if you have some spare time ;-)
319
320 // type, strike, spot, q, r, t, vol, value, tol
321 // values from Exhibit 6 - Long dated Call Options with dividends
322 {Option::Call, 100.00, 80.00, 0.07, 0.03, 3.0, 0.2, 2.605},
323 {Option::Call, 100.00, 90.00, 0.07, 0.03, 3.0, 0.2, 5.182},
324 {Option::Call, 100.00, 100.00, 0.07, 0.03, 3.0, 0.2, 9.065},
325 {Option::Call, 100.00, 110.00, 0.07, 0.03, 3.0, 0.2, 14.430},
326 {Option::Call, 100.00, 120.00, 0.07, 0.03, 3.0, 0.2, 21.398},
327
328 {Option::Call, 100.00, 80.00, 0.07, 0.03, 3.0, 0.4, 11.336},
329 {Option::Call, 100.00, 90.00, 0.07, 0.03, 3.0, 0.4, 15.711},
330 {Option::Call, 100.00, 100.00, 0.07, 0.03, 3.0, 0.4, 20.760},
331 {Option::Call, 100.00, 110.00, 0.07, 0.03, 3.0, 0.4, 26.440},
332 {Option::Call, 100.00, 120.00, 0.07, 0.03, 3.0, 0.4, 32.709},
333
334 {Option::Call, 100.00, 80.00, 0.07, 0.00001, 3.0, 0.3, 5.552},
335 {Option::Call, 100.00, 90.00, 0.07, 0.00001, 3.0, 0.3, 8.868},
336 {Option::Call, 100.00, 100.00, 0.07, 0.00001, 3.0, 0.3, 13.158},
337 {Option::Call, 100.00, 110.00, 0.07, 0.00001, 3.0, 0.3, 18.458},
338 {Option::Call, 100.00, 120.00, 0.07, 0.00001, 3.0, 0.3, 24.786},
339
340 {Option::Call, 100.00, 80.00, 0.03, 0.07, 3.0, 0.3, 12.177},
341 {Option::Call, 100.00, 90.00, 0.03, 0.07, 3.0, 0.3, 17.411},
342 {Option::Call, 100.00, 100.00, 0.03, 0.07, 3.0, 0.3, 23.402},
343 {Option::Call, 100.00, 110.00, 0.03, 0.07, 3.0, 0.3, 30.028},
344 {Option::Call, 100.00, 120.00, 0.03, 0.07, 3.0, 0.3, 37.177}};
345
346 Real tolerance = 8.0e-2;
347
348 for (auto& f : juValues) {
349 // build market
350 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
351 Date today = Settings::instance().evaluationDate();
352 Settings::instance().evaluationDate() = market->asofDate();
353
354 // build FXAmericanOption
355 string maturityDate = to_string(market->asofDate() + Integer(f.t * 360 + 0.5));
356 OptionData optionData("Long", f.type == Option::Call ? "Call" : "Put", "American", false,
357 vector<string>(1, maturityDate));
358 Envelope env("CP1");
359 FxOption fxOption(env, optionData, "JPY", 1, // foreign
360 "EUR", f.strike); // domestic
361
362 Real expectedNPV = f.result;
363
364 // Build and price
365 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
366 engineData->model("FxOptionAmerican") = "GarmanKohlhagen";
367 engineData->engine("FxOptionAmerican") = "FdBlackScholesVanillaEngine";
368 engineData->engineParameters("FxOptionAmerican") = {
369 {"Scheme", "Douglas"}, {"TimeGridPerYear", "100"}, {"XGrid", "100"}, {"DampingSteps", "0"}};
370
371 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
372
373 fxOption.build(engineFactory);
374
375 Real npv = fxOption.instrument()->NPV();
376
377 BOOST_TEST_MESSAGE("FX American Option, NPV Currency " << fxOption.npvCurrency());
378 BOOST_TEST_MESSAGE("NPV = " << npv);
379
380 // Check NPV matches expected values.
381 QL_REQUIRE(fxOption.npvCurrency() == "EUR", "unexpected NPV currency ");
382
383 BOOST_CHECK_SMALL(npv - expectedNPV, tolerance);
384 Settings::instance().evaluationDate() = today; // reset
385 }
386}
+ Here is the call graph for this function: