Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions
fxexotics.cpp File Reference
#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <ored/marketdata/marketimpl.hpp>
#include <ored/portfolio/builders/fxbarrieroption.hpp>
#include <ored/portfolio/builders/fxdigitalbarrieroption.hpp>
#include <ored/portfolio/builders/fxdigitaloption.hpp>
#include <ored/portfolio/builders/fxdoublebarrieroption.hpp>
#include <ored/portfolio/builders/fxdoubletouchoption.hpp>
#include <ored/portfolio/builders/fxforward.hpp>
#include <ored/portfolio/builders/fxoption.hpp>
#include <ored/portfolio/builders/fxtouchoption.hpp>
#include <ored/portfolio/builders/swap.hpp>
#include <ored/portfolio/enginedata.hpp>
#include <ored/portfolio/fxbarrieroption.hpp>
#include <ored/portfolio/fxdigitalbarrieroption.hpp>
#include <ored/portfolio/fxdigitaloption.hpp>
#include <ored/portfolio/fxdoublebarrieroption.hpp>
#include <ored/portfolio/fxdoubletouchoption.hpp>
#include <ored/portfolio/fxeuropeanbarrieroption.hpp>
#include <ored/portfolio/fxforward.hpp>
#include <ored/portfolio/fxkikobarrieroption.hpp>
#include <ored/portfolio/fxtouchoption.hpp>
#include <ored/portfolio/fxoption.hpp>
#include <ored/portfolio/swap.hpp>
#include <oret/toplevelfixture.hpp>
#include <ql/cashflows/simplecashflow.hpp>
#include <ql/currencies/all.hpp>
#include <ql/indexes/indexmanager.hpp>
#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <qle/indexes/fxindex.hpp>

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testFXDigitalOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXBarrierOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXBarrierOptionSymmetry)
 
 BOOST_AUTO_TEST_CASE (testFXBarrierOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXBarrierOptionTouched)
 
 BOOST_AUTO_TEST_CASE (testFXDigitalBarrierOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXDigitalBarrierOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXDigitalBarrierOptionTouched)
 
 BOOST_AUTO_TEST_CASE (testFXTouchOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXTouchOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXTouchOptionTouched)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleBarrierOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleBarrierOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleBarrierOptionTouched)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleTouchOptionPrice)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleTouchOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXDoubleTouchOptionTouched)
 
 BOOST_AUTO_TEST_CASE (testFXEuropeanBarrierOptionSymmetry)
 
 BOOST_AUTO_TEST_CASE (testFXEuropeanBarrierOptionParity)
 
 BOOST_AUTO_TEST_CASE (testFXKIKOBarrierOption)
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/20]

BOOST_AUTO_TEST_CASE ( testFXDigitalOptionPrice  )

Definition at line 136 of file fxexotics.cpp.

136 {
137 BOOST_TEST_MESSAGE("Testing FXDigitalOption Price...");
138
139 FxOptionData fxd[] = {{"Put", 100.00, 80.0, 0.06, 0.06, "20161030", 0.35, 2.6710}};
140
141 for (auto& f : fxd) {
142 // build market
143 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
144 Date today = Settings::instance().evaluationDate();
145 Settings::instance().evaluationDate() = market->asofDate();
146
147 // build FXDigitalOption - expiry in 9 months
148 // Date exDate = today + Integer(0.75*360+0.5);
149 OptionData optionData("Long", "Put", "European", true, vector<string>(1, f.t));
150 Envelope env("CP1");
151 FxDigitalOption fxOption(env, optionData, f.k, 10, "EUR", // foreign
152 "JPY"); // domestic
153
154 Real expectedNPV = f.result;
155
156 // Build and price
157 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
158 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
159 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
160
161 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
162
163 fxOption.build(engineFactory);
164
165 Real npv = fxOption.instrument()->NPV();
166
167 BOOST_TEST_MESSAGE("FX Option, NPV Currency " << fxOption.npvCurrency());
168 BOOST_TEST_MESSAGE("NPV = " << npv);
169
170 // Check NPV matches expected values.
171 QL_REQUIRE(fxOption.npvCurrency() == "JPY", "unexpected NPV currency ");
172
173 BOOST_CHECK_CLOSE(npv, expectedNPV, 0.2);
174 Settings::instance().evaluationDate() = today; // reset
175 }
176}
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Serializable FX Digital Option.
Serializable object holding option data.
Definition: optiondata.hpp:42
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [2/20]

BOOST_AUTO_TEST_CASE ( testFXBarrierOptionPrice  )

Definition at line 192 of file fxexotics.cpp.

192 {
193 BOOST_TEST_MESSAGE("Testing FXBarrierOption Price...");
194
195 // Testing option values when barrier untouched
196 // Values from "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 72
197 BarrierOptionData fxb[] = {// barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
198 {"DownAndOut", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 9.0246},
199 {"DownAndOut", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 4.8759},
200 {"DownAndOut", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
201 {"DownAndOut", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
202 {"DownAndOut", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
203 {"UpAndOut", 105.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 2.6789},
204 {"UpAndOut", 105.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 2.3580},
205 {"UpAndOut", 105.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 2.3453},
206
207 {"DownAndIn", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 7.7627},
208 {"DownAndIn", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 4.0109},
209 {"DownAndIn", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 2.0576},
210 {"DownAndIn", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 13.8333},
211 {"DownAndIn", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 7.8494},
212 {"DownAndIn", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 3.9795},
213 {"UpAndIn", 105.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 14.1112},
214 {"UpAndIn", 105.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 8.4482},
215 {"UpAndIn", 105.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 4.5910},
216
217 {"DownAndOut", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 8.8334},
218 {"DownAndOut", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 7.0285},
219 {"DownAndOut", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 5.4137},
220 {"DownAndOut", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
221 {"DownAndOut", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
222 {"DownAndOut", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
223 {"UpAndOut", 105.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 2.6341},
224 {"UpAndOut", 105.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 2.4389},
225 {"UpAndOut", 105.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 2.4315},
226
227 {"DownAndIn", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 9.0093},
228 {"DownAndIn", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 5.1370},
229 {"DownAndIn", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 2.8517},
230 {"DownAndIn", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 14.8816},
231 {"DownAndIn", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 9.2045},
232 {"DownAndIn", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 5.3043},
233 {"UpAndIn", 105.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 15.2098},
234 {"UpAndIn", 105.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 9.7278},
235 {"UpAndIn", 105.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 5.8350},
236
237 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
238 {"DownAndOut", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 2.2798},
239 {"DownAndOut", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 2.2947},
240 {"DownAndOut", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 2.6252},
241 {"DownAndOut", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
242 {"DownAndOut", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
243 {"DownAndOut", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 3.0000},
244 {"UpAndOut", 105.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 3.7760},
245 {"UpAndOut", 105.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 5.4932},
246 {"UpAndOut", 105.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 7.5187},
247
248 {"DownAndIn", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 2.9586},
249 {"DownAndIn", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 6.5677},
250 {"DownAndIn", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 11.9752},
251 {"DownAndIn", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 2.2845},
252 {"DownAndIn", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 5.9085},
253 {"DownAndIn", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 11.6465},
254 {"UpAndIn", 105.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 1.4653},
255 {"UpAndIn", 105.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 3.3721},
256 {"UpAndIn", 105.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 7.0846},
257
258 {"DownAndOut", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 2.4170},
259 {"DownAndOut", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 2.4258},
260 {"DownAndOut", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 2.6246},
261 {"DownAndOut", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
262 {"DownAndOut", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
263 {"DownAndOut", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 3.0000},
264 {"UpAndOut", 105.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 4.2293},
265 {"UpAndOut", 105.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 5.8032},
266 {"UpAndOut", 105.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 7.5649},
267
268 {"DownAndIn", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 3.8769},
269 {"DownAndIn", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 7.7989},
270 {"DownAndIn", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 13.3078},
271 {"DownAndIn", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 3.3328},
272 {"DownAndIn", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 7.2636},
273 {"DownAndIn", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 12.9713},
274 {"UpAndIn", 105.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 2.0658},
275 {"UpAndIn", 105.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 4.4226},
276 {"UpAndIn", 105.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 8.3686},
277
278 // Check 'Out' options return rebate when barrier touched
279 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
280 {"DownAndOut", 95.0, 3.0, "Call", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
281 {"DownAndOut", 95.0, 3.0, "Call", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
282 {"DownAndOut", 100.0, 3.0, "Call", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
283 {"DownAndOut", 100.0, 3.0, "Call", 100, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
284 {"DownAndOut", 100.0, 3.0, "Call", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
285 {"UpAndOut", 105.0, 3.0, "Call", 90, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
286 {"UpAndOut", 105.0, 3.0, "Call", 100, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
287 {"UpAndOut", 105.0, 3.0, "Call", 110, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
288
289 {"DownAndOut", 95.0, 3.0, "Put", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
290 {"DownAndOut", 95.0, 3.0, "Put", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
291 {"DownAndOut", 100.0, 3.0, "Put", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
292 {"DownAndOut", 100.0, 3.0, "Put", 100, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
293 {"DownAndOut", 100.0, 3.0, "Put", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 3.0},
294 {"UpAndOut", 105.0, 3.0, "Put", 90, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
295 {"UpAndOut", 105.0, 3.0, "Put", 100, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
296 {"UpAndOut", 105.0, 3.0, "Put", 110, 110.0, 0.04, 0.08, 0.50, 0.25, 3.0},
297
298 // Check 'In' options return fxOption npv when barrier touched
299 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
300 {"DownAndIn", 95.0, 3.0, "Call", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 7.06448},
301 {"DownAndIn", 95.0, 3.0, "Call", 100, 90.0, 0.04, 0.08, 0.50, 0.25, 3.29945},
302 {"DownAndIn", 95.0, 3.0, "Call", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 1.36007},
303 {"DownAndIn", 100.0, 3.0, "Call", 90, 90.0, 0.04, 0.08, 0.50, 0.25, 7.06448},
304 {"DownAndIn", 100.0, 3.0, "Call", 100, 90.0, 0.04, 0.08, 0.50, 0.25, 3.29945},
305 {"DownAndIn", 100.0, 3.0, "Call", 110, 90.0, 0.04, 0.08, 0.50, 0.25, 1.36007},
306 {"UpAndIn", 105.0, 3.0, "Call", 90, 110.0, 0.04, 0.08, 0.50, 0.25, 22.21500},
307 {"UpAndIn", 105.0, 3.0, "Call", 100, 110.0, 0.04, 0.08, 0.50, 0.25, 14.52180},
308 {"UpAndIn", 105.0, 3.0, "Call", 110, 110.0, 0.04, 0.08, 0.50, 0.25, 8.63437}};
309
310 vector<string> positions = {"Long", "Short"};
311 for (auto& f : fxb) {
312 for (auto& position : positions) {
313 // build market
314 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
315 Date today = Settings::instance().evaluationDate();
316 Settings::instance().evaluationDate() = market->asofDate();
317
318 // build FXBarrierOption - expiry in 6 months
319 OptionData optionData(position, f.optionType, "European", true, vector<string>(1, "20160801"));
320 vector<Real> barriers = {f.barrier};
321 vector<TradeBarrier> tradeBarriers;
322 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
323 BarrierData barrierData(f.barrierType, barriers, f.rebate, tradeBarriers);
324 Envelope env("CP1");
325 FxBarrierOption fxBarrierOption(env, optionData, barrierData, Date(), "", "EUR", 1, // foreign
326 "JPY", f.k); // domestic
327
328 // we'll check that the results scale as expected
329 // scaling the notional and the rebate by a million we should get npv_scaled = 1million * npv
330 Real Notional = 1000000;
331 BarrierData barrierDataScaled(f.barrierType, barriers, f.rebate * Notional, tradeBarriers);
332 FxBarrierOption fxBarrierOptionNotional(env, optionData, barrierDataScaled, Date(), "", "EUR", Notional, // foreign
333 "JPY", Notional * f.k); // domestic
334
335 Real expectedNPV = f.result;
336
337 // Build and price
338 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
339 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
340 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
341 engineData->model("FxOption") = "GarmanKohlhagen";
342 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
343
344 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
345
346 fxBarrierOption.build(engineFactory);
347 fxBarrierOptionNotional.build(engineFactory);
348
349 Real npv = (position == "Long" ? 1 : -1) * fxBarrierOption.instrument()->NPV();
350
351 BOOST_TEST_MESSAGE("NPV Currency " << fxBarrierOption.npvCurrency());
352 BOOST_TEST_MESSAGE("FX Barrier Option NPV = " << npv);
353
354 // Check NPV matches expected values.
355 QL_REQUIRE(fxBarrierOption.npvCurrency() == "JPY", "unexpected NPV currency ");
356
357 BOOST_CHECK_CLOSE(npv, expectedNPV, 0.2);
358 BOOST_CHECK_CLOSE(fxBarrierOption.instrument()->NPV() * 1000000,
359 fxBarrierOptionNotional.instrument()->NPV(), 0.2);
360 Settings::instance().evaluationDate() = today; // reset
361 }
362 }
363}
Serializable obejct holding barrier data.
Definition: barrierdata.hpp:34
Serializable FX Barrier Option.
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [3/20]

BOOST_AUTO_TEST_CASE ( testFXBarrierOptionSymmetry  )

Definition at line 365 of file fxexotics.cpp.

365 {
366 BOOST_TEST_MESSAGE("Testing FXBarrierOption Symmetry...");
367 //"Option pricing formulas, Second Edition", E.G. Haug, McGraw-Hill 2007 - page 168
368 // For single barrier options the symmetry between put and call options is:
369 // c_di(spot, strike, barrier, r, q, vol) = p_ui(strike, spot, strike*spot/barrier, q, r, vol);
370 // c_di(spot, strike, barrier, r, q, vol) = p_di(strike, spot, strike*spot/barrier, q, r, vol);
371
372 BarrierOptionData fxb[] = {// barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
373 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 9.0246},
374 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 7.7627},
375 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 4.0109},
376 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 2.0576},
377 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 13.8333},
378 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 7.8494},
379 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 3.9795},
380 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 9.0093},
381 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 5.1370},
382 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 2.8517},
383 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 14.8816},
384 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 9.2045},
385 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 5.3043}};
386
387 for (auto& f : fxb) {
388 // build market
389 QuantLib::ext::shared_ptr<Market> marketCall = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
390 QuantLib::ext::shared_ptr<Market> marketPut = QuantLib::ext::make_shared<TestMarket>(f.k, f.r, f.q, f.v);
391 Date today = Settings::instance().evaluationDate();
392 Settings::instance().evaluationDate() = marketCall->asofDate();
393
394 // build FXBarrierOptions - expiry in 6 months
395 OptionData optionCallData("Long", "Call", "European", true, vector<string>(1, "20160801"));
396 OptionData optionPutData("Long", "Put", "European", true, vector<string>(1, "20160801"));
397 vector<Real> barriersCall = {f.barrier};
398 vector<Real> barriersPut = {f.s * f.k / f.barrier};
399 vector<TradeBarrier> tradeBarriers_call, tradeBarriers_put;
400 tradeBarriers_call.push_back(TradeBarrier(f.barrier, ""));
401 tradeBarriers_put.push_back(TradeBarrier(f.s * f.k / f.barrier, ""));
402 BarrierData barrierCallData("DownAndIn", barriersCall, 0.0, tradeBarriers_call);
403 BarrierData barrierPutData("UpAndIn", barriersPut, 0.0, tradeBarriers_put);
404 Envelope env("CP1");
405
406 FxBarrierOption fxCallOption(env, optionCallData, barrierCallData, Date(), "", "EUR", 1, // foreign
407 "JPY", f.k); // domestic
408 FxBarrierOption fxPutOption(env, optionPutData, barrierPutData, Date(), "", "EUR", 1, // foreign
409 "JPY", f.s); // domestic
410
411 // Build and price
412 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
413 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
414 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
415 engineData->model("FxOption") = "GarmanKohlhagen";
416 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
417
418 QuantLib::ext::shared_ptr<EngineFactory> engineFactoryCall = QuantLib::ext::make_shared<EngineFactory>(engineData, marketCall);
419 QuantLib::ext::shared_ptr<EngineFactory> engineFactoryPut = QuantLib::ext::make_shared<EngineFactory>(engineData, marketPut);
420
421 fxCallOption.build(engineFactoryCall);
422 fxPutOption.build(engineFactoryPut);
423
424 Real npvCall = fxCallOption.instrument()->NPV();
425 Real npvPut = fxPutOption.instrument()->NPV();
426
427 BOOST_TEST_MESSAGE("NPV Currency " << fxCallOption.npvCurrency());
428 BOOST_TEST_MESSAGE("FX Barrier Option, NPV Call " << npvCall);
429 BOOST_TEST_MESSAGE("FX Barrier Option, NPV Put " << npvPut);
430 // Check NPV matches expected values.
431 BOOST_CHECK_CLOSE(npvCall, npvPut, 0.01);
432
433 Settings::instance().evaluationDate() = today; // reset
434 }
435}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [4/20]

BOOST_AUTO_TEST_CASE ( testFXBarrierOptionParity  )

Definition at line 437 of file fxexotics.cpp.

437 {
438 BOOST_TEST_MESSAGE("Testing FXBarrierOption Parity...");
439
440 // An "In" option and an "Out" option with the same strikes and expiries should have the same combined price as a
441 // vanilla Call option
442 BarrierOptionData fxb[] = {
443 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
444 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
445 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
446 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
447 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
448 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
449 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
450 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
451 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
452 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
453 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
454 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
455 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
456 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
457 };
458
459 for (auto& f : fxb) {
460 // build market
461 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
462 Date today = Settings::instance().evaluationDate();
463 Settings::instance().evaluationDate() = market->asofDate();
464
465 // build FXBarrierOption - expiry in 6 months
466 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
467
468 vector<Real> barriers = {f.barrier};
469 vector<TradeBarrier> tradeBarriers;
470 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
471 BarrierData downInData("DownAndIn", barriers, 0.0, tradeBarriers);
472 BarrierData upInData("UpAndIn", barriers, 0.0, tradeBarriers);
473 BarrierData downOutData("DownAndOut", barriers, 0.0, tradeBarriers);
474 BarrierData upOutData("UpAndOut", barriers, 0.0, tradeBarriers);
475
476 Envelope env("CP1");
477
478 FxOption fxOption(env, optionData, "EUR", 1, // foreign
479 "JPY", f.k);
480
481 FxBarrierOption downInOption(env, optionData, downInData, Date(), "", "EUR", 1, // foreign
482 "JPY", f.k); // domestic
483 FxBarrierOption upInOption(env, optionData, upInData, Date(), "", "EUR", 1, // foreign
484 "JPY", f.k); // domestic
485 FxBarrierOption downOutOption(env, optionData, downOutData, Date(), "", "EUR", 1, // foreign
486 "JPY", f.k); // domestic
487 FxBarrierOption upOutOption(env, optionData, upOutData, Date(), "", "EUR", 1, // foreign
488 "JPY", f.k); // domestic
489
490 // Build and price
491 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
492 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
493 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
494 engineData->model("FxOption") = "GarmanKohlhagen";
495 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
496
497 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
498
499 fxOption.build(engineFactory);
500 downInOption.build(engineFactory);
501 upInOption.build(engineFactory);
502 downOutOption.build(engineFactory);
503 upOutOption.build(engineFactory);
504
505 Real npv = fxOption.instrument()->NPV();
506
507 // Check NPV matches expected values.
508 BOOST_CHECK_CLOSE(npv, downInOption.instrument()->NPV() + downOutOption.instrument()->NPV(), 0.01);
509 BOOST_CHECK_CLOSE(npv, upInOption.instrument()->NPV() + upOutOption.instrument()->NPV(), 0.01);
510
511 Settings::instance().evaluationDate() = today; // reset
512 }
513}
Serializable FX Option.
Definition: fxoption.hpp:38
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [5/20]

BOOST_AUTO_TEST_CASE ( testFXBarrierOptionTouched  )

Definition at line 515 of file fxexotics.cpp.

515 {
516 BOOST_TEST_MESSAGE("Testing FXBarrierOption when barrier already touched...");
517
518 // An "In" option is equivalent to an fxOption once the barrier has been touched
519 // An "Out" option has zero value once the barrier has been touched and the rebate paid
520 BarrierOptionData fxb[] = {// barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
521 {"DownAndIn", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
522 {"DownAndIn", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
523 {"DownAndIn", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
524 {"DownAndIn", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
525 {"DownAndIn", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
526 {"DownAndIn", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
527 {"UpAndIn", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
528 {"UpAndIn", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
529 {"UpAndIn", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
530
531 {"DownAndIn", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
532 {"DownAndIn", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
533 {"DownAndIn", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
534 {"DownAndIn", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
535 {"DownAndIn", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
536 {"DownAndIn", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
537 {"UpAndIn", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
538 {"UpAndIn", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
539 {"UpAndIn", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
540
541 {"DownAndOut", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
542 {"DownAndOut", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
543 {"DownAndOut", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
544 {"DownAndOut", 100.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
545 {"DownAndOut", 100.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
546 {"DownAndOut", 100.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
547 {"UpAndOut", 95.0, 3.0, "Call", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
548 {"UpAndOut", 95.0, 3.0, "Call", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
549 {"UpAndOut", 95.0, 3.0, "Call", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
550
551 {"DownAndOut", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
552 {"DownAndOut", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
553 {"DownAndOut", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
554 {"DownAndOut", 100.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
555 {"DownAndOut", 100.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
556 {"DownAndOut", 100.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
557 {"UpAndOut", 95.0, 3.0, "Put", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
558 {"UpAndOut", 95.0, 3.0, "Put", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
559 {"UpAndOut", 95.0, 3.0, "Put", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0}};
560
561 for (auto& f : fxb) {
562 // build market
563 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
564 Date today = Settings::instance().evaluationDate();
565 Settings::instance().evaluationDate() = market->asofDate();
566
567 // build FXBarrierOption - expiry in 6 months
568 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, "20160801"));
569
570 vector<Real> barriers = {f.barrier};
571 vector<TradeBarrier> tradeBarriers;
572 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
573 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
574
575 Envelope env("CP1");
576
577 FxBarrierOption fxBarrierOption(env, optionData, barrierData, Date(1, Month::February, 2016),
578 "TARGET", "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
579 FxOption fxOption(env, optionData, "EUR", 1, // foreign
580 "JPY", f.k); // domestic
581
582 FxBarrierOption fxBarrierOptionInverted(env, optionData, barrierData, Date(1, Month::February, 2016),
583 "TARGET", "EUR", 1, // foreign
584 "JPY", f.k, "FX-Reuters-JPY-EUR"); // domestic
585
586 // Build and price
587 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
588 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
589 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
590 engineData->model("FxOption") = "GarmanKohlhagen";
591 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
592
593 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
594
595 fxOption.build(engineFactory);
596 fxBarrierOption.build(engineFactory);
597 fxBarrierOptionInverted.build(engineFactory);
598
599 // Check NPV matches expected values.
600 if (f.barrierType == "DownAndIn" || f.barrierType == "UpAndIn") {
601 BOOST_CHECK_CLOSE(fxBarrierOption.instrument()->NPV(), fxOption.instrument()->NPV(), 0.01);
602 BOOST_CHECK_CLOSE(fxBarrierOptionInverted.instrument()->NPV(), fxOption.instrument()->NPV(), 0.01);
603 } else {
604 BOOST_CHECK_CLOSE(fxBarrierOption.instrument()->NPV(), 0.0, 0.01);
605 BOOST_CHECK_CLOSE(fxBarrierOptionInverted.instrument()->NPV(), 0.0, 0.01);
606 }
607
608 Settings::instance().evaluationDate() = today; // reset
609 IndexManager::instance().clearHistories(); // reset
610 }
611}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [6/20]

BOOST_AUTO_TEST_CASE ( testFXDigitalBarrierOptionPrice  )

Definition at line 627 of file fxexotics.cpp.

627 {
628 BOOST_TEST_MESSAGE("Testing FXDigitalBarrierOption Price...");
629
630 // Testing option values when barrier untouched
631 // Values from "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 180
632 DigitalBarrierOptionData fxb[] = {
633 // barrierType, barrier, cash, type, strike, spot, q, r, t, vol, value
634 {"DownAndIn", 100.00, 15.00, "Call", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.9289},
635 {"DownAndIn", 100.00, 15.00, "Call", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 6.2150},
636 {"UpAndIn", 100.00, 15.00, "Call", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 5.8926}, // 5.3710 in Haug's book
637 {"UpAndIn", 100.00, 15.00, "Call", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 7.4519},
638 {"DownAndIn", 100.00, 15.00, "Put", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.4314},
639 {"DownAndIn", 100.00, 15.00, "Put", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 3.1454},
640 {"UpAndIn", 100.00, 15.00, "Put", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 5.3297},
641 {"UpAndIn", 100.00, 15.00, "Put", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.7704},
642 {"DownAndOut", 100.00, 15.00, "Call", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.8758},
643 {"DownAndOut", 100.00, 15.00, "Call", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.9081},
644 {"UpAndOut", 100.00, 15.00, "Call", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0000},
645 {"UpAndOut", 100.00, 15.00, "Call", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0407},
646 {"DownAndOut", 100.00, 15.00, "Put", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0323},
647 {"DownAndOut", 100.00, 15.00, "Put", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0000},
648 {"UpAndOut", 100.00, 15.00, "Put", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.0461},
649 {"UpAndOut", 100.00, 15.00, "Put", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.0054}};
650
651 for (auto& f : fxb) {
652 // build market
653 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
654 Date today = market->asofDate();
655 Settings::instance().evaluationDate() = market->asofDate();
656
657 // build FXOption - expiry in 6 months
658 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, "20160801"));
659
660 vector<Real> barriers = {f.barrier};
661 vector<TradeBarrier> tradeBarriers;
662 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
663 BarrierData barrierData(f.barrierType, barriers, 0, tradeBarriers);
664
665 Envelope env("CP1");
666 FxDigitalBarrierOption barrierOption(env, optionData, barrierData, f.k, f.cash, "EUR", // foreign
667 "JPY"); // domestic
668
669 Real expectedNPV = f.result / f.cash;
670
671 // Build and price
672 map<string, string> engineParamMap;
673 engineParamMap["Scheme"] = "Douglas";
674 engineParamMap["TimeGridPerYear"] = "800";
675 engineParamMap["XGrid"] = "400";
676 engineParamMap["DampingSteps"] = "100";
677
678 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
679 engineData->model("FxDigitalBarrierOption") = "GarmanKohlhagen";
680 engineData->engine("FxDigitalBarrierOption") = "FdBlackScholesBarrierEngine";
681 engineData->engineParameters("FxDigitalBarrierOption") = engineParamMap;
682 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
683 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
684 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
685 barrierOption.build(engineFactory);
686
687 Real npv = barrierOption.instrument()->NPV() / f.cash;
688
689 BOOST_TEST_MESSAGE("NPV Currency " << barrierOption.npvCurrency());
690
691 // Check NPV matches expected values.
692 // TODO: Implement analytical formula to improve accuracy
693 BOOST_CHECK_SMALL(npv - expectedNPV, 1e-3);
694 Settings::instance().evaluationDate() = today; // reset
695 }
696}
Serializable FX Digital Barrier Option.
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [7/20]

BOOST_AUTO_TEST_CASE ( testFXDigitalBarrierOptionParity  )

Definition at line 698 of file fxexotics.cpp.

698 {
699 BOOST_TEST_MESSAGE("Testing FXDigitalBarrierOption Price...");
700 // An "In" option and an "Out" option with the same strikes and expiries should have the same combined price as a
701 // vanilla Call option
702
703 DigitalBarrierOptionData fxb[] = {
704 // barrierType, barrier, cash, type, strike, spot, q, r, t, vol, value
705 {"", 100.00, 15.00, "Call", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
706 {"", 100.00, 15.00, "Call", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
707 {"", 100.00, 15.00, "Call", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
708 {"", 100.00, 15.00, "Call", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
709 {"", 100.00, 15.00, "Put", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
710 {"", 100.00, 15.00, "Put", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
711 {"", 100.00, 15.00, "Put", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
712 {"", 100.00, 15.00, "Put", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
713 {"", 100.00, 15.00, "Call", 102.00, 95.00, -0.14, 0.10, 0.5, 0.20, 0.0},
714 {"", 100.00, 15.00, "Call", 102.00, 95.00, 0.03, 0.10, 0.5, 0.20, 0.0},
715 {"", 100.00, 15.00, "Put", 102.00, 98.00, 0.00, 0.10, 0.5, 0.20, 0.0},
716 {"", 100.00, 15.00, "Put", 102.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0},
717 {"", 100.00, 15.00, "Call", 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0},
718 {"", 100.00, 15.00, "Call", 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0},
719 {"", 100.00, 15.00, "Put", 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0},
720 {"", 100.00, 15.00, "Put", 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0}};
721
722 vector<string> payoutCcys = {"EUR", "JPY"};
723 for (auto& f : fxb) {
724 // build market
725 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
726 Date today = market->asofDate();
727 Settings::instance().evaluationDate() = market->asofDate();
728 for (auto& payoutCcy : payoutCcys) {
729 // build FXOption - expiry in 9 months
730 // Date exDate = today + Integer(f.t*360+0.5);
731 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, "20160801"));
732
733 vector<Real> barriers = {f.barrier};
734 vector<TradeBarrier> tradeBarriers;
735 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
736
737 BarrierData downInData("DownAndIn", barriers, 0.0, tradeBarriers);
738 BarrierData upInData("UpAndIn", barriers, 0.0, tradeBarriers);
739 BarrierData downOutData("DownAndOut", barriers, 0.0, tradeBarriers);
740 BarrierData upOutData("UpAndOut", barriers, 0.0, tradeBarriers);
741 BarrierData barrierData(f.barrierType, barriers, 0, tradeBarriers);
742
743 Envelope env("CP1");
744 FxDigitalOption fxOption(env, optionData, f.k, payoutCcy, f.cash, "EUR", // foreign
745 "JPY"); // domestic
746
747 FxDigitalBarrierOption downInOption(env, optionData, downInData, f.k, f.cash, "EUR", // foreign
748 "JPY", "", "", "", payoutCcy); // domestic
749 FxDigitalBarrierOption upInOption(env, optionData, upInData, f.k, f.cash, "EUR", // foreign
750 "JPY", "", "", "", payoutCcy); // domestic
751 FxDigitalBarrierOption downOutOption(env, optionData, downOutData, f.k, f.cash, "EUR", // foreign
752 "JPY", "", "", "", payoutCcy); // domestic
753 FxDigitalBarrierOption upOutOption(env, optionData, upOutData, f.k, f.cash, "EUR", // foreign
754 "JPY", "", "", "", payoutCcy); // domestic
755
756 // Build and price
757 map<string, string> engineParamMap;
758 engineParamMap["Scheme"] = "Douglas";
759 engineParamMap["TimeGridPerYear"] = "400";
760 engineParamMap["XGrid"] = "400";
761 engineParamMap["DampingSteps"] = "100";
762
763 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
764 engineData->model("FxDigitalBarrierOption") = "GarmanKohlhagen";
765 engineData->engine("FxDigitalBarrierOption") = "FdBlackScholesBarrierEngine";
766 engineData->engineParameters("FxDigitalBarrierOption") = engineParamMap;
767 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
768 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
769 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
770
771 fxOption.build(engineFactory);
772 downInOption.build(engineFactory);
773 upInOption.build(engineFactory);
774 downOutOption.build(engineFactory);
775 upOutOption.build(engineFactory);
776
777 Real npv = fxOption.instrument()->NPV();
778
779 BOOST_TEST_MESSAGE("NPV Currency " << fxOption.npvCurrency());
780
781 // Check NPV matches expected values.
782 // Check NPV matches expected values.
783 BOOST_CHECK_CLOSE(npv, downInOption.instrument()->NPV() + downOutOption.instrument()->NPV(), 0.1);
784 BOOST_CHECK_CLOSE(npv, upInOption.instrument()->NPV() + upOutOption.instrument()->NPV(), 0.1);
785 }
786 Settings::instance().evaluationDate() = today; // reset
787 }
788}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [8/20]

BOOST_AUTO_TEST_CASE ( testFXDigitalBarrierOptionTouched  )

Definition at line 790 of file fxexotics.cpp.

790 {
791 BOOST_TEST_MESSAGE("Testing FXDigitalBarrierOption Price...");
792
793 DigitalBarrierOptionData fxb[] = {
794 // barrierType, barrier, cash, type, strike, spot, q, r, t, vol, value
795 {"", 100.00, 15.00, "Call", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
796 {"", 100.00, 15.00, "Call", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
797 {"", 100.00, 15.00, "Call", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
798 {"", 100.00, 15.00, "Call", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
799 {"", 100.00, 15.00, "Put", 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
800 {"", 100.00, 15.00, "Put", 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0},
801 {"", 100.00, 15.00, "Put", 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
802 {"", 100.00, 15.00, "Put", 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0},
803 {"", 100.00, 15.00, "Call", 102.00, 95.00, -0.14, 0.10, 0.5, 0.20, 0.0},
804 {"", 100.00, 15.00, "Call", 102.00, 95.00, 0.03, 0.10, 0.5, 0.20, 0.0},
805 {"", 100.00, 15.00, "Put", 102.00, 98.00, 0.00, 0.10, 0.5, 0.20, 0.0},
806 {"", 100.00, 15.00, "Put", 102.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0},
807 {"", 100.00, 15.00, "Call", 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0},
808 {"", 100.00, 15.00, "Call", 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0},
809 {"", 100.00, 15.00, "Put", 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0},
810 {"", 100.00, 15.00, "Put", 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0}};
811
812 vector<string> payoutCcys = {"EUR", "JPY"};
813 vector<string> fxIndices = {"FX-Reuters-EUR-JPY", "FX-Reuters-JPY-EUR"};
814 for (auto& f : fxb) {
815 // build market
816 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
817 Date today = market->asofDate();
818 Settings::instance().evaluationDate() = market->asofDate();
819 for (auto& payoutCcy : payoutCcys) {
820 for (auto& fxIndex : fxIndices) {
821 // build FXOption - expiry in 6 months
822 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, "20160801"));
823
824 vector<Real> barriers = {f.barrier};
825 vector<TradeBarrier> tradeBarriers;
826 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
827
828 BarrierData downInData("DownAndIn", barriers, 0.0, tradeBarriers);
829 BarrierData upInData("UpAndIn", barriers, 0.0, tradeBarriers);
830 BarrierData downOutData("DownAndOut", barriers, 0.0, tradeBarriers);
831 BarrierData upOutData("UpAndOut", barriers, 0.0, tradeBarriers);
832
833 Envelope env("CP1");
834 FxDigitalOption fxOption(env, optionData, f.k, payoutCcy, f.cash, "EUR", // foreign
835 "JPY"); // domestic
836 FxDigitalBarrierOption downInOption(env, optionData, downInData, f.k, f.cash, "EUR", // foreign
837 "JPY", "20160201", "TARGET", fxIndex, payoutCcy);
838 FxDigitalBarrierOption upInOption(env, optionData, upInData, f.k, f.cash, "EUR", // foreign
839 "JPY", "20160201", "TARGET", fxIndex, payoutCcy);
840 FxDigitalBarrierOption downOutOption(env, optionData, downOutData, f.k, f.cash, "EUR", // foreign
841 "JPY", "20160201", "TARGET", fxIndex, payoutCcy);
842 FxDigitalBarrierOption upOutOption(env, optionData, upOutData, f.k, f.cash, "EUR", // foreign
843 "JPY", "20160201", "TARGET", fxIndex, payoutCcy);
844
845 // Build and price
846 map<string, string> engineParamMap;
847 engineParamMap["Scheme"] = "Douglas";
848 engineParamMap["TimeGridPerYear"] = "400";
849 engineParamMap["XGrid"] = "400";
850 engineParamMap["DampingSteps"] = "100";
851
852 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
853 engineData->model("FxDigitalBarrierOption") = "GarmanKohlhagen";
854 engineData->engine("FxDigitalBarrierOption") = "FdBlackScholesBarrierEngine";
855 engineData->engineParameters("FxDigitalBarrierOption") = engineParamMap;
856 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
857 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
858 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
859
860 fxOption.build(engineFactory);
861 downInOption.build(engineFactory);
862 upInOption.build(engineFactory);
863 downOutOption.build(engineFactory);
864 upOutOption.build(engineFactory);
865
866 Real npv = fxOption.instrument()->NPV();
867
868 BOOST_TEST_MESSAGE("NPV Currency " << fxOption.npvCurrency());
869
870 // Check NPV matches expected values.
871 BOOST_CHECK_CLOSE(npv, downInOption.instrument()->NPV(), 0.01);
872 BOOST_CHECK_CLOSE(npv, upInOption.instrument()->NPV(), 0.01);
873 BOOST_CHECK_CLOSE(0.0, downOutOption.instrument()->NPV(), 0.01);
874 BOOST_CHECK_CLOSE(0.0, upOutOption.instrument()->NPV(), 0.01);
875 }
876 }
877 Settings::instance().evaluationDate() = today; // reset
878 IndexManager::instance().clearHistories(); // reset
879 }
880}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [9/20]

BOOST_AUTO_TEST_CASE ( testFXTouchOptionPrice  )

Definition at line 897 of file fxexotics.cpp.

897 {
898 BOOST_TEST_MESSAGE("Testing FXTouchOption Price...");
899
900 // The following results are from Table 4.22, pp 180 of
901 // "The Complete Guide to Option Pricing Formulas" (2nd Ed) by E. G. Haug
902 FxTouchOptionData fxd[] = {
903 // barrierType, barrier, cash, payAtExp, type, payDom, s, q, r, t, vol, value
904 {"DownAndIn", 100.0, 15.0, true, "Put", true, 105.0, 0.0, 0.1, 0.5, 0.2, 9.3604},
905 {"UpAndIn", 100.0, 15.0, true, "Call", true, 95.0, 0.0, 0.1, 0.5, 0.2, 11.2223},
906 {"DownAndOut", 100.0, 15.0, true, "Put", true, 105.0, 0.0, 0.1, 0.5, 0.2, 4.9081},
907 {"UpAndOut", 100.0, 15.0, true, "Call", true, 95.0, 0.0, 0.1, 0.5, 0.2, 3.0461},
908
909 // payoff at hit. The following are the corresponding test cases from Haug.
910 {"DownAndIn", 100.0, 15.0, false, "Put", true, 105.0, 0.0, 0.1, 0.5, 0.2, 9.3604},
911 {"UpAndIn", 100.0, 15.0, false, "Call", true, 95.0, 0.0, 0.1, 0.5, 0.2, 11.2223},
912
913 // check that if the option has already knocked in or out then the option prices appropriately.
914 {"DownAndIn", 100.0, 15.0, true, "Put", true, 95.0, 0.0, 0.1, 0.5, 0.2, 14.2684},
915 {"UpAndIn", 100.0, 15.0, true, "Call", true, 105.0, 0.0, 0.1, 0.5, 0.2, 14.2684},
916 {"DownAndOut", 100.0, 15.0, true, "Put", true, 95.0, 0.0, 0.1, 0.5, 0.2, 0.0},
917 {"UpAndOut", 100.0, 15.0, true, "Call", true, 105.0, 0.0, 0.1, 0.5, 0.2, 0.0},
918
919 // check consistent pricing in the limit of high barrier level
920 {"UpAndIn", 1000.0, 15.0, true, "Call", true, 100.0, 0.0, 0.1, 0.5, 0.2, 0.0},
921 {"UpAndOut", 1000.0, 15.0, true, "Call", true, 100.0, 0.0, 0.1, 0.5, 0.2, 14.2684},
922 };
923
924 // Set engineData
925 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
926 engineData->model("FxTouchOption") = "GarmanKohlhagen";
927 engineData->engine("FxTouchOption") = "AnalyticDigitalAmericanEngine";
928 engineData->model("Swap") = "DiscountedCashflows";
929 engineData->engine("Swap") = "DiscountingSwapEngine";
930
931 for (auto& f : fxd) {
932 // build market
933 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
934
935 Date today = Settings::instance().evaluationDate();
936 Settings::instance().evaluationDate() = market->asofDate();
937
938 // build FxTouchOption expiring in 6 months
939 vector<Real> barriers = {f.barrier};
940 vector<TradeBarrier> tradeBarriers;
941 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
942 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
943 OptionData optionData("Long", f.optionType, "American", f.payoffAtExpiry, vector<string>(1, "20160801"));
944 Envelope env("CP1");
945 FxTouchOption fxTouchOption(env, optionData, barrierData, "EUR", "JPY",
946 f.payoffCurrencyDomestic ? "JPY" : "EUR", f.cash);
947
948 Real expectedNPV = f.result;
949
950 // Build and price
951 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
952
953 fxTouchOption.build(engineFactory);
954
955 Real npv = fxTouchOption.instrument()->NPV();
956 string ccy = fxTouchOption.npvCurrency();
957
958 BOOST_TEST_MESSAGE("FX Touch Option, NPV Currency " << ccy);
959 BOOST_TEST_MESSAGE("NPV = " << npv);
960 BOOST_TEST_MESSAGE("Expected NPV = " << expectedNPV);
961
962 BOOST_CHECK_SMALL(npv - expectedNPV, 0.01);
963 Settings::instance().evaluationDate() = today; // reset
964 }
965}
Serializable FX One-Touch/No-Touch Option.
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [10/20]

BOOST_AUTO_TEST_CASE ( testFXTouchOptionParity  )

Definition at line 967 of file fxexotics.cpp.

967 {
968 BOOST_TEST_MESSAGE("Testing FXTouchOption Parity...");
969 // An "In" option and an "Out" option with the same strikes and expiries should have the same combined price as a
970 // cashflow
971
972 FxTouchOptionData fxb[] = {
973 // barrierType, barrier, cash, payAtExp, type, payDom, s, q, r, t, vol, value
974 {"", 0.0, 1e6, true, "", true, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
975 {"", 95.0, 1e6, true, "", true, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
976 {"", 100.0, 1e6, true, "", true, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
977 {"", 105.0, 1e6, true, "", true, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
978 {"", 999.0, 1e6, true, "", true, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0}};
979
980 for (auto& f : fxb) {
981 // build market
982 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
983 Date today = Settings::instance().evaluationDate();
984 Settings::instance().evaluationDate() = market->asofDate();
985
986 // build FXBarrierOption - expiry in 6 months
987 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
988
989 vector<Real> barriers = {f.barrier};
990 vector<TradeBarrier> tradeBarriers;
991 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
992
993 BarrierData downInData("DownAndIn", barriers, 0.0, tradeBarriers);
994 BarrierData upInData("UpAndIn", barriers, 0.0, tradeBarriers);
995 BarrierData downOutData("DownAndOut", barriers, 0.0, tradeBarriers);
996 BarrierData upOutData("UpAndOut", barriers, 0.0, tradeBarriers);
997
998 Envelope env("CP1");
999
1000 vector<double> amounts = {f.cash};
1001 vector<string> dates = {"2016-08-01"};
1002
1003 LegData legData(QuantLib::ext::make_shared<CashflowData>(amounts, dates), true,
1004 f.payoffCurrencyDomestic ? "JPY" : "EUR");
1005 legData.isPayer() = false;
1006 ore::data::Swap swap(env, {legData});
1007
1008 FxTouchOption downInOption(env, optionData, downInData, "EUR", "JPY", f.payoffCurrencyDomestic ? "JPY" : "EUR",
1009 f.cash);
1010 FxTouchOption upInOption(env, optionData, upInData, "EUR", "JPY", f.payoffCurrencyDomestic ? "JPY" : "EUR",
1011 f.cash);
1012 FxTouchOption downOutOption(env, optionData, downOutData, "EUR", "JPY",
1013 f.payoffCurrencyDomestic ? "JPY" : "EUR", f.cash);
1014 FxTouchOption upOutOption(env, optionData, upOutData, "EUR", "JPY", f.payoffCurrencyDomestic ? "JPY" : "EUR",
1015 f.cash);
1016
1017 // Build and price
1018 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1019 engineData->model("FxTouchOption") = "GarmanKohlhagen";
1020 engineData->engine("FxTouchOption") = "AnalyticDigitalAmericanEngine";
1021 engineData->model("Swap") = "DiscountedCashflows";
1022 engineData->engine("Swap") = "DiscountingSwapEngine";
1023
1024 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1025
1026 swap.build(engineFactory);
1027 downInOption.build(engineFactory);
1028 upInOption.build(engineFactory);
1029 downOutOption.build(engineFactory);
1030 upOutOption.build(engineFactory);
1031
1032 Real npv = swap.instrument()->NPV();
1033
1034 // Check NPV matches expected values.
1035 BOOST_CHECK_CLOSE(npv, downInOption.instrument()->NPV() + downOutOption.instrument()->NPV(), 0.01);
1036 BOOST_CHECK_CLOSE(npv, upInOption.instrument()->NPV() + upOutOption.instrument()->NPV(), 0.01);
1037
1038 Settings::instance().evaluationDate() = today; // reset
1039 }
1040}
Serializable object holding leg data.
Definition: legdata.hpp:844
Serializable Swap, Single and Cross Currency.
Definition: swap.hpp:36
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [11/20]

BOOST_AUTO_TEST_CASE ( testFXTouchOptionTouched  )

Definition at line 1042 of file fxexotics.cpp.

1042 {
1043 BOOST_TEST_MESSAGE("Testing FXTouchOption when barrier already touched...");
1044
1045 struct FxTouchOptionTouchedData {
1046 string barrierType;
1047 Real barrier;
1048 Real cash;
1049 Real s; // spot
1050 Real s_1; // spot at t-1
1051 Real s_2; // spot at t-2
1052 Rate q; // dividend
1053 Rate r; // risk-free rate
1054 Real t; // time to maturity
1055 Volatility v; // volatility
1056 Real result; // expected result
1057 };
1058
1059 // An "In" option is equivalent to a cashflow once the barrier has been touched
1060 // An "Out" option has zero value once the barrier has been touched
1061 FxTouchOptionTouchedData fxt[] = {
1062 // barrierType, barrier, cash, s, s_1, s_2, q, r, t, v, result
1063 {"DownAndIn", 80.0, 1e6, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1064 {"DownAndIn", 80.0, 1e6, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1065 {"DownAndIn", 80.0, 1e6, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1066 {"DownAndIn", 80.0, 1e6, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1067 {"DownAndIn", 80.0, 1e6, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1068 {"DownAndIn", 80.0, 1e6, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1069
1070 {"UpAndIn", 120.0, 1e6, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1071 {"UpAndIn", 120.0, 1e6, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1072 {"UpAndIn", 120.0, 1e6, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1073 {"UpAndIn", 120.0, 1e6, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1074 {"UpAndIn", 120.0, 1e6, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1075 {"UpAndIn", 120.0, 1e6, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1076
1077 {"DownAndOut", 80.0, 1e6, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1078 {"DownAndOut", 80.0, 1e6, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1079 {"DownAndOut", 80.0, 1e6, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1080 {"DownAndOut", 80.0, 1e6, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1081 {"DownAndOut", 80.0, 1e6, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1082 {"DownAndOut", 80.0, 1e6, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1083
1084 {"UpAndOut", 120.0, 1e6, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1085 {"UpAndOut", 120.0, 1e6, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1086 {"UpAndOut", 120.0, 1e6, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1087 {"UpAndOut", 120.0, 1e6, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1088 {"UpAndOut", 120.0, 1e6, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1089 {"UpAndOut", 120.0, 1e6, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0}};
1090
1091 vector<string> payoutCcys = {"EUR", "JPY"};
1092 vector<string> fxIndices = {"FX-Reuters-EUR-JPY", "FX-Reuters-JPY-EUR"};
1093 for (auto& f : fxt) {
1094 // build market
1095 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
1096 Date today = Settings::instance().evaluationDate();
1097 Settings::instance().evaluationDate() = market->asofDate();
1098 TimeSeries<Real> pastFixings;
1099 pastFixings[market->asofDate() - 1 * Days] = f.s_1;
1100 pastFixings[market->asofDate() - 2 * Days] = f.s_2;
1101 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
1102 TimeSeries<Real> pastFixingsInverted;
1103 pastFixingsInverted[market->asofDate() - 1 * Days] = 1 / pastFixings[market->asofDate() - 1 * Days];
1104 pastFixingsInverted[market->asofDate() - 2 * Days] = 1 / pastFixings[market->asofDate() - 2 * Days];
1105 IndexManager::instance().setHistory("Reuters JPY/EUR", pastFixingsInverted);
1106 for (auto& payoutCcy : payoutCcys) {
1107 for (auto& fxIndex : fxIndices) {
1108 // build FXBarrierOption - expiry in 6 months
1109 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1110
1111 vector<Real> barriers = {f.barrier};
1112 vector<TradeBarrier> tradeBarriers;
1113 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
1114 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
1115
1116 Envelope env("CP1");
1117
1118 vector<double> amounts = {f.cash};
1119 vector<string> dates = {"2016-08-01"};
1120
1121 LegData legData(QuantLib::ext::make_shared<CashflowData>(amounts, dates), true, payoutCcy);
1122 legData.isPayer() = false;
1123 ore::data::Swap swap(env, {legData});
1124
1125 FxTouchOption touchOption(env, optionData, barrierData, "EUR", "JPY", payoutCcy, f.cash, "20160201",
1126 "TARGET", fxIndex);
1127
1128 // Build and price
1129 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1130 engineData->model("FxTouchOption") = "GarmanKohlhagen";
1131 engineData->engine("FxTouchOption") = "AnalyticDigitalAmericanEngine";
1132 engineData->model("Swap") = "DiscountedCashflows";
1133 engineData->engine("Swap") = "DiscountingSwapEngine";
1134
1135 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1136
1137 touchOption.build(engineFactory);
1138 swap.build(engineFactory);
1139
1140 // Check NPV matches expected values.
1141 if (f.barrierType == "DownAndIn" || f.barrierType == "UpAndIn")
1142 BOOST_CHECK_CLOSE(touchOption.instrument()->NPV(), swap.instrument()->NPV(), 0.01);
1143 else
1144 BOOST_CHECK_CLOSE(touchOption.instrument()->NPV(), 0.0, 0.01);
1145 }
1146 }
1147 Settings::instance().evaluationDate() = today; // reset
1148 IndexManager::instance().clearHistories(); // reset
1149 }
1150}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [12/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleBarrierOptionPrice  )

Definition at line 1246 of file fxexotics.cpp.

1246 {
1247 BOOST_TEST_MESSAGE("Testing FXDoubleBarrierOption Price...");
1248 for (auto& f : fxdb) {
1249 // build market
1250 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1251 Date today = Settings::instance().evaluationDate();
1252 Settings::instance().evaluationDate() = market->asofDate();
1253
1254 // build FXBarrierOption - expiry in 6 months
1255 Date exDate = today + Integer(f.t * 360 + 0.5);
1256 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, ore::data::to_string(exDate)));
1257 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1258 vector<TradeBarrier> tradeBarriers;
1259 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1260 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1261 BarrierData barrierData(f.barrierType, barriers, f.rebate, tradeBarriers);
1262 Envelope env("CP1");
1263 FxDoubleBarrierOption fxDoubleBarrierOption(env, optionData, barrierData, Date(), "",
1264 "EUR", 1, // foreign
1265 "JPY", f.k); // domestic
1266
1267 // we'll check that the results scale as expected
1268 // scaling the notional and the rebate by a million we should get npv_scaled = 1million * npv
1269 Real Notional = 1000000;
1270 BarrierData barrierDataScaled(f.barrierType, barriers, f.rebate * Notional, tradeBarriers);
1271 FxDoubleBarrierOption fxDoubleBarrierOptionNotional(env, optionData, barrierDataScaled, Date(), "",
1272 "EUR", Notional, // foreign
1273 "JPY", Notional * f.k); // domestic
1274
1275 Real expectedNPV = f.result;
1276
1277 // Build and price
1278 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1279 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
1280 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
1281 engineData->model("FxOption") = "GarmanKohlhagen";
1282 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1283
1284 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1285
1286 fxDoubleBarrierOption.build(engineFactory);
1287
1288 fxDoubleBarrierOptionNotional.build(engineFactory);
1289
1290 Real npv = fxDoubleBarrierOption.instrument()->NPV();
1291
1292 BOOST_TEST_MESSAGE("NPV Currency " << fxDoubleBarrierOption.npvCurrency());
1293 BOOST_TEST_MESSAGE("FX Barrier Option NPV = " << npv);
1294
1295 BOOST_CHECK_CLOSE(npv, expectedNPV, 0.2);
1296 BOOST_CHECK_CLOSE(fxDoubleBarrierOption.instrument()->NPV() * 1000000,
1297 fxDoubleBarrierOptionNotional.instrument()->NPV(), 0.2);
1298 Settings::instance().evaluationDate() = today; // reset
1299 }
1300}
Serializable FX Double Barrier Option.
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [13/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleBarrierOptionParity  )

Definition at line 1302 of file fxexotics.cpp.

1302 {
1303 BOOST_TEST_MESSAGE("Testing FXDoubleBarrierOption Parity ...");
1304 for (auto& f : fxdb) {
1305 // build market
1306 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1307 Date today = Settings::instance().evaluationDate();
1308 Settings::instance().evaluationDate() = market->asofDate();
1309
1310 Date exDate = today + Integer(f.t * 360 + 0.5);
1311 OptionData optionData("Long", f.optionType, "European", true, vector<string>(1, ore::data::to_string(exDate)));
1312 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1313 vector<TradeBarrier> tradeBarriers;
1314 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1315 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1316 BarrierData barrierDataIn("KnockIn", barriers, f.rebate, tradeBarriers);
1317 BarrierData barrierDataOut("KnockOut", barriers, f.rebate, tradeBarriers);
1318 Envelope env("CP1");
1319 FxDoubleBarrierOption fxDoubleBarrierInOption(env, optionData, barrierDataIn, Date(), "",
1320 "EUR", 1, // foreign
1321 "JPY", f.k); // domestic
1322 FxDoubleBarrierOption fxDoubleBarrierOutOption(env, optionData, barrierDataOut, Date(), "",
1323 "EUR", 1, // foreign
1324 "JPY", f.k); // domestic
1325
1326 FxOption fxOption(env, optionData, "EUR", 1, // foreign
1327 "JPY", f.k); // domestic
1328
1329 // we'll check that the results scale as expected
1330 // scaling the notional and the rebate by a million we should get npv_scaled = 1million * npv
1331 Real Notional = 1000000;
1332 BarrierData barrierDataScaled(f.barrierType, barriers, f.rebate * Notional, tradeBarriers);
1333 FxDoubleBarrierOption fxDoubleBarrierOptionNotional(env, optionData, barrierDataScaled, Date(), "",
1334 "EUR", Notional, // foreign
1335 "JPY", Notional * f.k); // domestic
1336
1337 // Build and price
1338 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1339 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
1340 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
1341 engineData->model("FxOption") = "GarmanKohlhagen";
1342 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1343
1344 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1345
1346 fxDoubleBarrierInOption.build(engineFactory);
1347 fxDoubleBarrierOutOption.build(engineFactory);
1348 fxOption.build(engineFactory);
1349
1350 Real npv = fxDoubleBarrierInOption.instrument()->NPV();
1351
1352 BOOST_TEST_MESSAGE("NPV Currency " << fxDoubleBarrierInOption.npvCurrency());
1353 BOOST_TEST_MESSAGE("FX Barrier Option NPV = " << npv);
1354 BOOST_TEST_MESSAGE("FX Option NPV = " << fxOption.instrument()->NPV());
1355
1356 // Check NPV matches expected values.
1357 QL_REQUIRE(fxOption.npvCurrency() == "JPY", "unexpected NPV currency ");
1358
1359 BOOST_CHECK_CLOSE(fxDoubleBarrierInOption.instrument()->NPV() + fxDoubleBarrierOutOption.instrument()->NPV(),
1360 fxOption.instrument()->NPV(), 0.0000000002);
1361 Settings::instance().evaluationDate() = today; // reset
1362 }
1363}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [14/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleBarrierOptionTouched  )

Definition at line 1365 of file fxexotics.cpp.

1365 {
1366 BOOST_TEST_MESSAGE("Testing FXDoubleBarrierOption when barrier already touched...");
1367
1368 struct DoubleBarrierOptionTouchedData {
1369 string barrierType;
1370 Real barrierLow;
1371 Real barrierHigh;
1372 Real rebate;
1373 string type;
1374 Real k;
1375 Real s; // spot
1376 Real s_1; // spot at t-1
1377 Real s_2; // spot at t-2
1378 Rate q; // dividend
1379 Rate r; // risk-free rate
1380 Real t; // time to maturity
1381 Volatility v; // volatility
1382 Real result; // expected result
1383 };
1384
1385 // An "In" option is equivalent to a cashflow once the barrier has been touched
1386 // An "Out" option has zero value once the barrier has been touched
1387 DoubleBarrierOptionTouchedData fxdb[] = {
1388 // barrierType, barrierLow, barrierHigh, rebate, type, k, s, s_1, s_2, q, r, t, v,
1389 // result
1390 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1391 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1392 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1393 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1394 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1395 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1396
1397 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1398 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1399 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1400 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1401 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1402 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1403
1404 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1405 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1406 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1407 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1408 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1409 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1410
1411 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 80.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1412 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 80.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1413 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1414 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1415 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 70.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1416 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1417
1418 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1419 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1420 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1421 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1422 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1423 {"KnockIn", 80.0, 120.0, 3.0, "Call", 100.0, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1424
1425 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1426 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1427 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1428 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1429 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1430 {"KnockIn", 80.0, 120.0, 3.0, "Put", 100.0, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1431
1432 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1433 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1434 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1435 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1436 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1437 {"KnockOut", 80.0, 120.0, 3.0, "Call", 100.0, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1438
1439 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1440 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 120.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1441 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1442 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 100.0, 130.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1443 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 100.0, 130.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1444 {"KnockOut", 80.0, 120.0, 3.0, "Put", 100.0, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0}};
1445
1446 for (auto& f : fxdb) {
1447 // build market
1448 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
1449 Date today = Settings::instance().evaluationDate();
1450 Settings::instance().evaluationDate() = market->asofDate();
1451 TimeSeries<Real> pastFixings;
1452 pastFixings[market->asofDate() - 1 * Days] = f.s_1;
1453 pastFixings[market->asofDate() - 2 * Days] = f.s_2;
1454 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
1455 TimeSeries<Real> pastFixingsInverted;
1456 pastFixingsInverted[market->asofDate() - 1 * Days] = 1 / pastFixings[market->asofDate() - 1 * Days];
1457 pastFixingsInverted[market->asofDate() - 2 * Days] = 1 / pastFixings[market->asofDate() - 2 * Days];
1458 IndexManager::instance().setHistory("Reuters JPY/EUR", pastFixingsInverted);
1459
1460 // build FXBarrierOption - expiry in 6 months
1461 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1462
1463 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1464 vector<TradeBarrier> tradeBarriers;
1465 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1466 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1467 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
1468
1469 Envelope env("CP1");
1470
1471 FxDoubleBarrierOption doubleBarrierOption(env, optionData, barrierData, Date(1, Month::February, 2016), "TARGET",
1472 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
1473 FxDoubleBarrierOption doubleBarrierOptionInverted(env, optionData, barrierData, Date(1, Month::February, 2016), "TARGET",
1474 "EUR", 1, "JPY", f.k, "FX-Reuters-JPY-EUR");
1475 FxOption fxOption(env, optionData, "EUR", 1, // foreign
1476 "JPY", f.k); // domestic
1477
1478 // Build and price
1479 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1480 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
1481 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
1482 engineData->model("FxOption") = "GarmanKohlhagen";
1483 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1484
1485 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1486
1487 doubleBarrierOption.build(engineFactory);
1488 doubleBarrierOptionInverted.build(engineFactory);
1489 fxOption.build(engineFactory);
1490
1491 // Check NPV matches expected values.
1492 if (f.barrierType == "KnockIn") {
1493 BOOST_CHECK_CLOSE(doubleBarrierOption.instrument()->NPV(), fxOption.instrument()->NPV(), 0.01);
1494 BOOST_CHECK_CLOSE(doubleBarrierOptionInverted.instrument()->NPV(), fxOption.instrument()->NPV(), 0.01);
1495 } else {
1496 BOOST_CHECK_CLOSE(doubleBarrierOption.instrument()->NPV(), 0.0, 0.01);
1497 BOOST_CHECK_CLOSE(doubleBarrierOptionInverted.instrument()->NPV(), 0.0, 0.01);
1498 }
1499
1500 Settings::instance().evaluationDate() = today; // reset
1501 IndexManager::instance().clearHistories(); // reset
1502 }
1503}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [15/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleTouchOptionPrice  )

Definition at line 1505 of file fxexotics.cpp.

1505 {
1506 BOOST_TEST_MESSAGE("Testing FXDoubleTouchOption Price...");
1507
1508 // Set engineData
1509 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1510 engineData->model("FxDoubleTouchOption") = "GarmanKohlhagen";
1511 engineData->engine("FxDoubleTouchOption") = "AnalyticDoubleBarrierBinaryEngine";
1512 engineData->model("Swap") = "DiscountedCashflows";
1513 engineData->engine("Swap") = "DiscountingSwapEngine";
1514
1515 for (auto& f : fxdt) {
1516 // build market
1517 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1518
1519 Date today = Settings::instance().evaluationDate();
1520 Settings::instance().evaluationDate() = market->asofDate();
1521
1522 Date exDate = today + Integer(f.t * 360 + 0.5);
1523 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1524 vector<TradeBarrier> tradeBarriers;
1525 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1526 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1527 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
1528 OptionData optionData("Long", "Call", "European", true, vector<string>(1, ore::data::to_string(exDate)));
1529 Envelope env("CP1");
1530 FxDoubleTouchOption fxDoubleTouchOption(env, optionData, barrierData, "EUR", "JPY", "JPY", f.cash);
1531
1532 Real expectedNPV = f.result;
1533
1534 // Build and price
1535 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1536
1537 fxDoubleTouchOption.build(engineFactory);
1538
1539 Real npv = fxDoubleTouchOption.instrument()->NPV();
1540 string ccy = fxDoubleTouchOption.npvCurrency();
1541
1542 BOOST_TEST_MESSAGE("FX Double Touch Option, NPV Currency " << ccy);
1543 BOOST_TEST_MESSAGE("NPV = " << npv);
1544 BOOST_TEST_MESSAGE("Expected NPV = " << expectedNPV);
1545
1546 BOOST_CHECK_SMALL(npv - expectedNPV, 0.01);
1547 Settings::instance().evaluationDate() = today; // reset
1548 }
1549}
Serializable FX Double One-Touch/No-Touch Option.
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [16/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleTouchOptionParity  )

Definition at line 1551 of file fxexotics.cpp.

1551 {
1552 BOOST_TEST_MESSAGE("Testing FXDoubleTouchOption Parity...");
1553 // An "In" option and an "Out" option with the same strikes and expiries should have the same combined price as a
1554 // cashflow
1555
1556 for (auto& f : fxdt) {
1557 // build market
1558 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1559 Date today = Settings::instance().evaluationDate();
1560 Settings::instance().evaluationDate() = market->asofDate();
1561
1562 // build FXBarrierOption - expiry in 6 months
1563 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1564
1565 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1566 vector<TradeBarrier> tradeBarriers;
1567 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1568 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1569 BarrierData knonkOutData("KnockOut", barriers, 0.0, tradeBarriers);
1570 BarrierData knonkInData("KnockIn", barriers, 0.0, tradeBarriers);
1571
1572 Envelope env("CP1");
1573
1574 vector<double> amounts = {f.cash};
1575 vector<string> dates = {"2016-08-01"};
1576
1577 LegData legData(QuantLib::ext::make_shared<CashflowData>(amounts, dates), true, "JPY");
1578 legData.isPayer() = false;
1579 ore::data::Swap swap(env, {legData});
1580
1581 FxDoubleTouchOption knockOutOption(env, optionData, knonkOutData, "EUR", "JPY", "JPY", f.cash);
1582 FxDoubleTouchOption knockInOption(env, optionData, knonkInData, "EUR", "JPY", "JPY", f.cash);
1583
1584 // Build and price
1585 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1586 engineData->model("FxDoubleTouchOption") = "GarmanKohlhagen";
1587 engineData->engine("FxDoubleTouchOption") = "AnalyticDoubleBarrierBinaryEngine";
1588 engineData->model("Swap") = "DiscountedCashflows";
1589 engineData->engine("Swap") = "DiscountingSwapEngine";
1590
1591 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1592
1593 swap.build(engineFactory);
1594 knockOutOption.build(engineFactory);
1595 knockInOption.build(engineFactory);
1596
1597 Real npv = swap.instrument()->NPV();
1598
1599 // Check NPV matches expected values.
1600 BOOST_CHECK_CLOSE(npv, knockOutOption.instrument()->NPV() + knockInOption.instrument()->NPV(), 0.01);
1601
1602 Settings::instance().evaluationDate() = today; // reset
1603 }
1604}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [17/20]

BOOST_AUTO_TEST_CASE ( testFXDoubleTouchOptionTouched  )

Definition at line 1606 of file fxexotics.cpp.

1606 {
1607 BOOST_TEST_MESSAGE("Testing FXDoubleTouchOption when barrier already touched...");
1608
1609 struct DoubleTouchOptionTouchedData {
1610 string barrierType;
1611 Real barrierLow;
1612 Real barrierHigh;
1613 Real cash;
1614 Real s; // spot
1615 Real s_1; // spot at t-1
1616 Real s_2; // spot at t-2
1617 Rate q; // dividend
1618 Rate r; // risk-free rate
1619 Real t; // time to maturity
1620 Volatility v; // volatility
1621 Real result; // expected result
1622 };
1623
1624 // An "In" option is equivalent to a cashflow once the barrier has been touched
1625 // An "Out" option has zero value once the barrier has been touched
1626 DoubleTouchOptionTouchedData fxdt[] = {
1627 // barrierType, barrierLow, barrierHigh, cash, s, s_1, s_2, q, r, t, v, result
1628 {"KnockIn", 80.0, 120.0, 1e6, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1629 {"KnockIn", 80.0, 120.0, 1e6, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1630 {"KnockOut", 80.0, 120.0, 1e6, 80.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1631 {"KnockOut", 80.0, 120.0, 1e6, 70.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1632
1633 {"KnockIn", 80.0, 120.0, 1e6, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1634 {"KnockIn", 80.0, 120.0, 1e6, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1635 {"KnockOut", 80.0, 120.0, 1e6, 120.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1636 {"KnockOut", 80.0, 120.0, 1e6, 130.0, 100.0, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1637
1638 {"KnockIn", 80.0, 120.0, 1e6, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1639 {"KnockIn", 80.0, 120.0, 1e6, 100.0, 70.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1640 {"KnockIn", 80.0, 120.0, 1e6, 70.0, 70.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1641 {"KnockOut", 80.0, 120.0, 1e6, 100.0, 100.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1642 {"KnockOut", 80.0, 120.0, 1e6, 100.0, 70.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1643 {"KnockOut", 80.0, 120.0, 1e6, 70.0, 70.0, 70.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1644
1645 {"KnockIn", 80.0, 120.0, 1e6, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1646 {"KnockIn", 80.0, 120.0, 1e6, 100.0, 120.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1647 {"KnockIn", 80.0, 120.0, 1e6, 120.0, 120.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1648 {"KnockOut", 80.0, 120.0, 1e6, 100.0, 100.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1649 {"KnockOut", 80.0, 120.0, 1e6, 100.0, 120.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1650 {"KnockOut", 80.0, 120.0, 1e6, 120.0, 120.0, 120.0, 0.04, 0.08, 0.50, 0.25, 0.0}};
1651
1652 vector<string> payoutCcys = {"EUR", "JPY"};
1653 vector<string> fxIndices = {"FX-Reuters-EUR-JPY", "FX-Reuters-JPY-EUR"};
1654 for (auto& f : fxdt) {
1655 // build market
1656 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
1657 Date today = Settings::instance().evaluationDate();
1658 Settings::instance().evaluationDate() = market->asofDate();
1659 TimeSeries<Real> pastFixings;
1660 pastFixings[market->asofDate() - 1 * Days] = f.s_1;
1661 pastFixings[market->asofDate() - 2 * Days] = f.s_2;
1662 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
1663 TimeSeries<Real> pastFixingsInverted;
1664 pastFixingsInverted[market->asofDate() - 1 * Days] = 1 / pastFixings[market->asofDate() - 1 * Days];
1665 pastFixingsInverted[market->asofDate() - 2 * Days] = 1 / pastFixings[market->asofDate() - 2 * Days];
1666 IndexManager::instance().setHistory("Reuters JPY/EUR", pastFixingsInverted);
1667
1668 for (auto& payoutCcy : payoutCcys) {
1669 for (auto& fxIndex : fxIndices) {
1670 // build FXBarrierOption - expiry in 6 months
1671 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1672
1673 vector<Real> barriers = {f.barrierLow, f.barrierHigh};
1674 vector<TradeBarrier> tradeBarriers;
1675 tradeBarriers.push_back(TradeBarrier(f.barrierLow, ""));
1676 tradeBarriers.push_back(TradeBarrier(f.barrierHigh, ""));
1677 BarrierData barrierData(f.barrierType, barriers, 0.0, tradeBarriers);
1678
1679 Envelope env("CP1");
1680
1681 vector<double> amounts = {f.cash};
1682 vector<string> dates = {"2016-08-01"};
1683
1684 LegData legData(QuantLib::ext::make_shared<CashflowData>(amounts, dates), true, payoutCcy);
1685 legData.isPayer() = false;
1686 ore::data::Swap swap(env, {legData});
1687 FxDoubleTouchOption doubleTouchOption(env, optionData, barrierData, "EUR", "JPY", payoutCcy, f.cash,
1688 "20160201", "TARGET", fxIndex);
1689
1690 // Build and price
1691 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1692 engineData->model("FxDoubleTouchOption") = "GarmanKohlhagen";
1693 engineData->engine("FxDoubleTouchOption") = "AnalyticDoubleBarrierBinaryEngine";
1694 engineData->model("Swap") = "DiscountedCashflows";
1695 engineData->engine("Swap") = "DiscountingSwapEngine";
1696
1697 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1698
1699 doubleTouchOption.build(engineFactory);
1700 swap.build(engineFactory);
1701
1702 // Check NPV matches expected values.
1703 if (f.barrierType == "KnockIn")
1704 BOOST_CHECK_CLOSE(doubleTouchOption.instrument()->NPV(), swap.instrument()->NPV(), 0.01);
1705 else
1706 BOOST_CHECK_CLOSE(doubleTouchOption.instrument()->NPV(), 0.0, 0.01);
1707 }
1708 }
1709 Settings::instance().evaluationDate() = today; // reset
1710 IndexManager::instance().clearHistories(); // reset
1711 }
1712}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [18/20]

BOOST_AUTO_TEST_CASE ( testFXEuropeanBarrierOptionSymmetry  )

Definition at line 1714 of file fxexotics.cpp.

1714 {
1715 BOOST_TEST_MESSAGE("Testing FXEuropeanBarrierOption Symmetry...");
1716 // For single barrier options the symmetry between put and call options is:
1717 // c_di(spot, strike, barrier, r, q, vol) = p_ui(strike, spot, strike*spot/barrier, q, r, vol);
1718
1719 BarrierOptionData fxb[] = {
1720 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
1721 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1722 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1723 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1724 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1725 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1726 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1727 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1728 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1729 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1730 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1731 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1732 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1733 };
1734
1735 for (auto& f : fxb) {
1736 // build market
1737 QuantLib::ext::shared_ptr<Market> marketCall = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1738 QuantLib::ext::shared_ptr<Market> marketPut = QuantLib::ext::make_shared<TestMarket>(f.k, f.r, f.q, f.v);
1739 Date today = Settings::instance().evaluationDate();
1740 Settings::instance().evaluationDate() = marketCall->asofDate();
1741
1742 // build FXBarrierOptions - expiry in 6 months
1743 OptionData optionCallData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1744 OptionData optionPutData("Long", "Put", "European", true, vector<string>(1, "20160801"));
1745 vector<Real> barriersCall = {f.barrier};
1746 vector<TradeBarrier> tradeBarriers_call;
1747 tradeBarriers_call.push_back(TradeBarrier(f.barrier, ""));
1748 vector<Real> barriersPut = {f.s * f.k / f.barrier};
1749 vector<TradeBarrier> tradeBarriers_put;
1750 tradeBarriers_put.push_back(TradeBarrier(f.s * f.k / f.barrier, ""));
1751 BarrierData barrierCallData("DownAndIn", barriersCall, f.rebate, tradeBarriers_call);
1752 BarrierData barrierPutData("UpAndIn", barriersPut, f.rebate, tradeBarriers_put);
1753 Envelope env("CP1");
1754
1755 FxEuropeanBarrierOption fxCallOption(env, optionCallData, barrierCallData, "EUR", 1, // foreign
1756 "JPY", f.k); // domestic
1757 FxEuropeanBarrierOption fxPutOption(env, optionPutData, barrierPutData, "EUR", 1, // foreign
1758 "JPY", f.s); // domestic
1759
1760 // Build and price
1761 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1762 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
1763 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
1764 engineData->model("FxOption") = "GarmanKohlhagen";
1765 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1766
1767 QuantLib::ext::shared_ptr<EngineFactory> engineFactoryCall = QuantLib::ext::make_shared<EngineFactory>(engineData, marketCall);
1768 QuantLib::ext::shared_ptr<EngineFactory> engineFactoryPut = QuantLib::ext::make_shared<EngineFactory>(engineData, marketPut);
1769
1770 fxCallOption.build(engineFactoryCall);
1771 fxPutOption.build(engineFactoryPut);
1772
1773 Real npvCall = fxCallOption.instrument()->NPV();
1774 Real npvPut = fxPutOption.instrument()->NPV();
1775
1776 BOOST_TEST_MESSAGE("NPV Currency " << fxCallOption.npvCurrency());
1777 BOOST_TEST_MESSAGE("FX Barrier Option, NPV Call " << npvCall);
1778 BOOST_TEST_MESSAGE("FX Barrier Option, NPV Put " << npvPut);
1779 // Check NPV matches expected values.
1780 BOOST_TEST(npvCall >= 0);
1781 BOOST_TEST(npvPut >= 0);
1782 BOOST_CHECK_CLOSE(npvCall, npvPut, 0.01);
1783
1784 Settings::instance().evaluationDate() = today; // reset
1785 }
1786}
Serializable FX European Barrier Option.
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [19/20]

BOOST_AUTO_TEST_CASE ( testFXEuropeanBarrierOptionParity  )

Definition at line 1788 of file fxexotics.cpp.

1788 {
1789 BOOST_TEST_MESSAGE("Testing FXEuropeanBarrierOption Parity...");
1790
1791 // An "In" option and an "Out" option with the same strikes and expiries should have the same combined price as a
1792 // vanilla Call option
1793 BarrierOptionData fxb[] = {
1794 // barrierType, barrier, rebate, type, strk, s, q, r, t, v, result
1795 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1796 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1797 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1798 {"", 100.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1799 {"", 100.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1800 {"", 100.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.25, 0.0},
1801 {"", 95.0, 0.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1802 {"", 95.0, 0.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1803 {"", 95.0, 0.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1804 {"", 100.0, 3.0, "", 90, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1805 {"", 100.0, 3.0, "", 100, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1806 {"", 100.0, 3.0, "", 110, 100.0, 0.04, 0.08, 0.50, 0.30, 0.0},
1807 };
1808
1809 vector<string> optionTypes = {"Call", "Put"};
1810 for (auto& f : fxb) {
1811 for (auto& optionType : optionTypes) {
1812 // build market
1813 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v);
1814 Date today = Settings::instance().evaluationDate();
1815 Settings::instance().evaluationDate() = market->asofDate();
1816
1817 // build FXBarrierOption - expiry in 6 months
1818 OptionData optionData("Long", optionType, "European", true, vector<string>(1, "20160801"));
1819
1820 vector<Real> barriers = {f.barrier};
1821 vector<TradeBarrier> tradeBarriers;
1822 tradeBarriers.push_back(TradeBarrier(f.barrier, ""));
1823
1824 BarrierData downInData("DownAndIn", barriers, f.rebate, tradeBarriers);
1825 BarrierData upInData("UpAndIn", barriers, f.rebate, tradeBarriers);
1826 BarrierData downOutData("DownAndOut", barriers, f.rebate, tradeBarriers);
1827 BarrierData upOutData("UpAndOut", barriers, f.rebate, tradeBarriers);
1828
1829 Envelope env("CP1");
1830
1831 FxOption fxOption(env, optionData, "EUR", 1, // foreign
1832 "JPY", f.k);
1833
1834 FxEuropeanBarrierOption downInOption(env, optionData, downInData, "EUR", 1, // foreign
1835 "JPY", f.k); // domestic
1836 FxEuropeanBarrierOption upInOption(env, optionData, upInData, "EUR", 1, // foreign
1837 "JPY", f.k); // domestic
1838 FxEuropeanBarrierOption downOutOption(env, optionData, downOutData, "EUR", 1, // foreign
1839 "JPY", f.k); // domestic
1840 FxEuropeanBarrierOption upOutOption(env, optionData, upOutData, "EUR", 1, // foreign
1841 "JPY", f.k); // domestic
1842
1843 vector<double> amounts = {f.rebate};
1844 vector<string> dates = {"2016-08-01"};
1845 LegData legData(QuantLib::ext::make_shared<CashflowData>(amounts, dates), false, "JPY");
1846 ore::data::Swap swap(env, {legData});
1847
1848 // Build and price
1849 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1850 engineData->model("FxDigitalOption") = "GarmanKohlhagen";
1851 engineData->engine("FxDigitalOption") = "AnalyticEuropeanEngine";
1852 engineData->model("FxOption") = "GarmanKohlhagen";
1853 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1854 engineData->model("Swap") = "DiscountedCashflows";
1855 engineData->engine("Swap") = "DiscountingSwapEngine";
1856
1857 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1858
1859 fxOption.build(engineFactory);
1860 downInOption.build(engineFactory);
1861 upInOption.build(engineFactory);
1862 downOutOption.build(engineFactory);
1863 upOutOption.build(engineFactory);
1864 swap.build(engineFactory);
1865
1866 Real npv = fxOption.instrument()->NPV() + swap.instrument()->NPV();
1867
1868 // Check NPV matches expected values.
1869 BOOST_TEST(downInOption.instrument()->NPV() >= 0);
1870 BOOST_TEST(downOutOption.instrument()->NPV() >= 0);
1871 BOOST_TEST(upInOption.instrument()->NPV() >= 0);
1872 BOOST_TEST(upOutOption.instrument()->NPV() >= 0);
1873 BOOST_CHECK_CLOSE(npv, downInOption.instrument()->NPV() + downOutOption.instrument()->NPV(), 0.01);
1874 BOOST_CHECK_CLOSE(npv, upInOption.instrument()->NPV() + upOutOption.instrument()->NPV(), 0.01);
1875
1876 Settings::instance().evaluationDate() = today; // reset
1877 }
1878 }
1879}
vector< Option::Type > optionTypes
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [20/20]

BOOST_AUTO_TEST_CASE ( testFXKIKOBarrierOption  )

Definition at line 1881 of file fxexotics.cpp.

1881 {
1882 BOOST_TEST_MESSAGE("Testing FXDoubleBarrierOption when barrier already touched...");
1883
1884 struct KIKOBarrierOptionData {
1885 string knockInType;
1886 string knockOutType;
1887 Real barrierKnockIn;
1888 Real barrierKnockOut;
1889 Real rebate;
1890 string type;
1891 Real k;
1892 Real s; // spot
1893 Rate q; // dividend
1894 Rate r; // risk-free rate
1895 Real t; // time to maturity
1896 Volatility v; // volatility
1897 };
1898
1899 KIKOBarrierOptionData fxdb[] = {
1900 // knockInBarrierType, knockOutBarrierType, barrierKnockIn, barrierKnockOut, rebate, type, k, s, q,
1901 // r, t, v
1902 {"DownAndIn", "UpAndOut", 80.0, 120.0, 0.0, "Call", 100.0, 100.0, 0.04, 0.08, 0.50, 0.2},
1903 {"UpAndIn", "UpAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 80.0, 0.04, 0.08, 0.50, 0.2},
1904 {"UpAndIn", "DownAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 80.0, 0.04, 0.08, 0.50, 0.2},
1905 {"DownAndIn", "DownAndOut", 100.0, 80.0, 0.0, "Call", 100.0, 120.0, 0.04, 0.08, 0.50, 0.2}};
1906
1907 // we test that the trades knocks in and knocks out as expected when seasoned
1908 for (auto& f : fxdb) {
1909 // build market
1910 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
1911 Date today = Settings::instance().evaluationDate();
1912 Settings::instance().evaluationDate() = today; // reset
1913 Settings::instance().evaluationDate() = market->asofDate();
1914
1915 // build FXBarrierOption - expiry in 6 months
1916 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1917 vector<TradeBarrier> tradeBarriers_KI;
1918 tradeBarriers_KI.push_back(TradeBarrier(f.barrierKnockIn, ""));
1919 vector<TradeBarrier> tradeBarriers_KO;
1920 tradeBarriers_KO.push_back(TradeBarrier(f.barrierKnockOut, ""));
1921 BarrierData knockInBarrierData(f.knockInType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
1922 BarrierData knockOutBarrierData(f.knockOutType, {f.barrierKnockOut}, 0.0, tradeBarriers_KO);
1923
1924 vector<BarrierData> barriers = {knockInBarrierData, knockOutBarrierData};
1925 Envelope env("CP1");
1926
1927 FxKIKOBarrierOption kikoBarrierOption(env, optionData, barriers, "EUR", 1, "JPY", f.k, "20160201", "TARGET",
1928 "FX-Reuters-EUR-JPY");
1929 FxBarrierOption koBarrierOption(env, optionData, knockOutBarrierData, Date(1, Month::February, 2016), "TARGET", "EUR", 1,
1930 "JPY", f.k, "FX-Reuters-EUR-JPY");
1931
1932 FxOption fxOption(env, optionData, "EUR", 1, // foreign
1933 "JPY", f.k); // domestic
1934
1935 // Build and price
1936 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
1937 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
1938 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
1939 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
1940 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
1941 engineData->model("FxOption") = "GarmanKohlhagen";
1942 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
1943
1944 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
1945
1946 // knocked in npv = knockOut npv
1947 TimeSeries<Real> pastFixings;
1948 pastFixings[market->asofDate() - 1 * Days] = f.barrierKnockIn;
1949 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
1950
1951 kikoBarrierOption.reset();
1952 kikoBarrierOption.build(engineFactory);
1953 koBarrierOption.build(engineFactory);
1954 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), koBarrierOption.instrument()->NPV(), 0.01);
1955 // knocked out npv = 0
1956 IndexManager::instance().clearHistories(); // reset
1957 TimeSeries<Real> pastFixings2;
1958 pastFixings2[market->asofDate() - 1 * Days] = f.barrierKnockIn;
1959 pastFixings2[market->asofDate() - 2 * Days] = f.barrierKnockOut;
1960 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings2);
1961 kikoBarrierOption.reset();
1962 kikoBarrierOption.build(engineFactory);
1963
1964 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), 0.0, 0.01);
1965
1966 IndexManager::instance().clearHistories(); // reset
1967 }
1968
1969 KIKOBarrierOptionData fxdb3[] = {
1970 // knockInBarrierType, knockOutBarrierType, barrierKnockIn, barrierKnockOut, rebate, type, k, s, q,
1971 // r, t, v
1972 {"DownAndIn", "UpAndOut", 80.0, 120.0, 0.0, "Call", 100.0, 79.0, 0.04, 0.08, 0.50, 0.2},
1973 {"UpAndIn", "UpAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 101.0, 0.04, 0.08, 0.50, 0.2},
1974 {"UpAndIn", "DownAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 101.0, 0.04, 0.08, 0.50, 0.2},
1975 {"DownAndIn", "DownAndOut", 100.0, 80.0, 0.0, "Call", 100.0, 99.0, 0.04, 0.08, 0.50, 0.2}};
1976
1977 // we test trades that are knocked in but unseasoned
1978 for (auto& f : fxdb3) {
1979 // build market
1980 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
1981 Date today = Settings::instance().evaluationDate();
1982 Settings::instance().evaluationDate() = today; // reset
1983 Settings::instance().evaluationDate() = market->asofDate();
1984
1985 // build FXBarrierOption - expiry in 6 months
1986 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
1987 vector<TradeBarrier> tradeBarriers_KI;
1988 tradeBarriers_KI.push_back(TradeBarrier(f.barrierKnockIn, ""));
1989 vector<TradeBarrier> tradeBarriers_KO;
1990 tradeBarriers_KO.push_back(TradeBarrier(f.barrierKnockOut, ""));
1991 BarrierData knockInBarrierData(f.knockInType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
1992 BarrierData knockOutBarrierData(f.knockOutType, {f.barrierKnockOut}, 0.0, tradeBarriers_KO);
1993
1994 vector<BarrierData> barriers = {knockInBarrierData, knockOutBarrierData};
1995 Envelope env("CP1");
1996
1997 FxKIKOBarrierOption kikoBarrierOption(env, optionData, barriers, "EUR", 1, "JPY", f.k, "20160201", "TARGET",
1998 "FX-Reuters-EUR-JPY");
1999 FxBarrierOption koBarrierOption(env, optionData, knockOutBarrierData, Date(1, Month::February, 2016), "TARGET",
2000 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2001
2002 FxOption fxOption(env, optionData, "EUR", 1, // foreign
2003 "JPY", f.k); // domestic
2004
2005 // Build and price
2006 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
2007 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
2008 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
2009 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
2010 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
2011 engineData->model("FxOption") = "GarmanKohlhagen";
2012 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
2013
2014 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
2015
2016 kikoBarrierOption.build(engineFactory);
2017 koBarrierOption.build(engineFactory);
2018 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), koBarrierOption.instrument()->NPV(), 0.01);
2019 }
2020
2021 KIKOBarrierOptionData fxdb4[] = {
2022 // knockInBarrierType, knockOutBarrierType, barrierKnockIn, barrierKnockOut, rebate, type, k, s, q,
2023 // r, t, v
2024 {"DownAndIn", "UpAndOut", 80.0, 120.0, 0.0, "Call", 120.0, 121.0, 0.04, 0.08, 0.50, 0.2},
2025 {"UpAndIn", "UpAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 121.0, 0.04, 0.08, 0.50, 0.2},
2026 {"UpAndIn", "DownAndOut", 100.0, 120.0, 0.0, "Call", 100.0, 119.0, 0.04, 0.08, 0.50, 0.2},
2027 {"DownAndIn", "DownAndOut", 100.0, 80.0, 0.0, "Call", 100.0, 79.0, 0.04, 0.08, 0.50, 0.2}};
2028
2029 // we test trades that are knocked out but unseasoned
2030 for (auto& f : fxdb4) {
2031 // build market
2032 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
2033 Date today = Settings::instance().evaluationDate();
2034 Settings::instance().evaluationDate() = today; // reset
2035 Settings::instance().evaluationDate() = market->asofDate();
2036
2037 // build FXBarrierOption - expiry in 6 months
2038 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
2039 vector<TradeBarrier> tradeBarriers_KI;
2040 tradeBarriers_KI.push_back(TradeBarrier(f.barrierKnockIn, ""));
2041 vector<TradeBarrier> tradeBarriers_KO;
2042 tradeBarriers_KO.push_back(TradeBarrier(f.barrierKnockOut, ""));
2043 BarrierData knockInBarrierData(f.knockInType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
2044 BarrierData knockOutBarrierData(f.knockOutType, {f.barrierKnockOut}, 0.0, tradeBarriers_KO);
2045
2046 vector<BarrierData> barriers = {knockInBarrierData, knockOutBarrierData};
2047 Envelope env("CP1");
2048
2049 FxKIKOBarrierOption kikoBarrierOption(env, optionData, barriers, "EUR", 1, "JPY", f.k, "20160201", "TARGET",
2050 "FX-Reuters-EUR-JPY");
2051 FxBarrierOption koBarrierOption(env, optionData, knockOutBarrierData, Date(1, Month::February, 2016), "TARGET",
2052 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2053
2054 FxOption fxOption(env, optionData, "EUR", 1, // foreign
2055 "JPY", f.k); // domestic
2056
2057 // Build and price
2058 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
2059 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
2060 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
2061 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
2062 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
2063 engineData->model("FxOption") = "GarmanKohlhagen";
2064 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
2065
2066 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
2067
2068 // knocked out npv = 0
2069 kikoBarrierOption.build(engineFactory);
2070
2071 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), 0.0, 0.01);
2072 }
2073
2074 // we also test the cases where the knockOut barrier is an extreme value, unlikely to be triggered.
2075 // In this case we expect Kiko_npv == ki_npv
2076 KIKOBarrierOptionData fxdb2[] = {
2077 // knockInBarrierType, knockOutBarrierType, barrierKnockIn, barrierKnockOut, rebate, type, k, s, q,
2078 // r, t
2079 {"DownAndIn", "UpAndOut", 80.0, 1000000.0, 0.0, "Call", 100.0, 100.0, 0.04, 0.08, 0.50, 0.2},
2080 {"UpAndIn", "UpAndOut", 150.0, 1000000.0, 0.0, "Call", 100.0, 80.0, 0.04, 0.08, 0.50, 0.2},
2081 {"UpAndIn", "DownAndOut", 150.0, 0.000001, 0.0, "Call", 100.0, 80.0, 0.04, 0.08, 0.50, 0.2},
2082 {"DownAndIn", "DownAndOut", 100.0, 0.000001, 0.0, "Call", 100.0, 120.0, 0.04, 0.08, 0.50, 0.2}};
2083
2084 for (auto& f : fxdb2) {
2085 BOOST_TEST_MESSAGE("testing " << f.knockInType << " " << f.knockOutType);
2086 // build market
2087 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
2088 Date today = Settings::instance().evaluationDate();
2089 Settings::instance().evaluationDate() = today; // reset
2090 Settings::instance().evaluationDate() = market->asofDate();
2091
2092 // build FXBarrierOption - expiry in 6 months
2093 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
2094 vector<TradeBarrier> tradeBarriers_KI;
2095 tradeBarriers_KI.push_back(TradeBarrier(f.barrierKnockIn, ""));
2096 vector<TradeBarrier> tradeBarriers_KO;
2097 tradeBarriers_KO.push_back(TradeBarrier(f.barrierKnockOut, ""));
2098 BarrierData knockInBarrierData(f.knockInType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
2099 BarrierData knockOutBarrierData(f.knockOutType, {f.barrierKnockOut}, 0.0, tradeBarriers_KO);
2100 BarrierData knockOutBarrierData2(f.knockOutType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
2101
2102 vector<BarrierData> barriers = {knockInBarrierData, knockOutBarrierData};
2103 Envelope env("CP1");
2104
2105 FxKIKOBarrierOption kikoBarrierOption(env, optionData, barriers, "EUR", 1, "JPY", f.k, "20160201", "TARGET",
2106 "FX-Reuters-EUR-JPY");
2107 FxBarrierOption kiBarrierOption(env, optionData, knockInBarrierData, Date(1, Month::February, 2016), "TARGET",
2108 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2109 FxBarrierOption koBarrierOption(env, optionData, knockOutBarrierData, Date(1, Month::February, 2016), "TARGET",
2110 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2111 FxBarrierOption koBarrierOption2(env, optionData, knockOutBarrierData2, Date(1, Month::February, 2016),
2112 "TARGET", "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2113
2114 vector<Real> barrier = {std::min(f.barrierKnockIn, f.barrierKnockOut),
2115 std::max(f.barrierKnockIn, f.barrierKnockOut)};
2116 vector<TradeBarrier> tradeBarriers = {TradeBarrier(std::min(f.barrierKnockIn, f.barrierKnockOut), ""),
2117 TradeBarrier(std::max(f.barrierKnockIn, f.barrierKnockOut), "")};
2118 BarrierData barrierData("KnockOut", barrier, 0, tradeBarriers);
2119
2120 FxDoubleBarrierOption dkoBarrierOption(env, optionData, barrierData, Date(), "", "EUR", 1, // foreign
2121 "JPY", f.k); // domestic
2122
2123 FxOption fxOption(env, optionData, "EUR", 1, // foreign
2124 "JPY", f.k); // domestic
2125
2126 // Build and price
2127 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
2128 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
2129 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
2130 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
2131 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
2132 engineData->model("FxOption") = "GarmanKohlhagen";
2133 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
2134
2135 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
2136
2137 TimeSeries<Real> pastFixings;
2138 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
2139 // untouched kiko_npv = untouched ki_npv
2140 kikoBarrierOption.build(engineFactory);
2141 kiBarrierOption.build(engineFactory);
2142 koBarrierOption.build(engineFactory);
2143 koBarrierOption2.build(engineFactory);
2144 dkoBarrierOption.build(engineFactory);
2145 fxOption.build(engineFactory);
2146
2147 BOOST_TEST_MESSAGE("KIKO NPV: " << kikoBarrierOption.instrument()->NPV());
2148 BOOST_TEST_MESSAGE("KI NPV: " << kiBarrierOption.instrument()->NPV());
2149 BOOST_TEST_MESSAGE("KO(knockoutLevel) NPV: " << koBarrierOption.instrument()->NPV());
2150 BOOST_TEST_MESSAGE("KO(knockinLevel) NPV: " << koBarrierOption2.instrument()->NPV());
2151 BOOST_TEST_MESSAGE("DoubleKnockOut NPV: " << dkoBarrierOption.instrument()->NPV());
2152 BOOST_TEST_MESSAGE("FXOption NPV: " << fxOption.instrument()->NPV());
2153
2154 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), kiBarrierOption.instrument()->NPV(), 0.01);
2155
2156 // knocked in kiko_npv = knocked in ki_npv
2157 pastFixings[market->asofDate() - 1 * Days] = f.barrierKnockIn;
2158 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
2159
2160 kikoBarrierOption.reset();
2161 kiBarrierOption.reset();
2162 dkoBarrierOption.reset();
2163 kikoBarrierOption.build(engineFactory);
2164 kiBarrierOption.build(engineFactory);
2165 fxOption.build(engineFactory);
2166
2167 kikoBarrierOption.build(engineFactory);
2168 kiBarrierOption.build(engineFactory);
2169 koBarrierOption.build(engineFactory);
2170 dkoBarrierOption.build(engineFactory);
2171 fxOption.build(engineFactory);
2172
2173 BOOST_TEST_MESSAGE("KIKO NPV: " << kikoBarrierOption.instrument()->NPV());
2174 BOOST_TEST_MESSAGE("KI NPV: " << kiBarrierOption.instrument()->NPV());
2175 BOOST_TEST_MESSAGE("KO(knockoutLevel) NPV: " << koBarrierOption.instrument()->NPV());
2176 BOOST_TEST_MESSAGE("KO(knockinLevel) NPV: " << koBarrierOption2.instrument()->NPV());
2177 BOOST_TEST_MESSAGE("DoubleKnockOut NPV: " << dkoBarrierOption.instrument()->NPV());
2178 BOOST_TEST_MESSAGE("FXOption NPV: " << fxOption.instrument()->NPV());
2179
2180
2181 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), kiBarrierOption.instrument()->NPV(), 0.01);
2182
2183 IndexManager::instance().clearHistories(); // reset
2184 }
2185
2186
2187 // we test that when the spot value is updated the trade behaves as expected.
2188 KIKOBarrierOptionData fxdb5[] = {
2189 // knockInBarrierType, knockOutBarrierType, barrierKnockIn, barrierKnockOut, rebate, type, k, s, q,
2190 // r, t
2191 {"UpAndIn", "UpAndOut", 80.0, 150.0, 0.0, "Call", 100.0, 70.0, 0.04, 0.08, 0.50, 0.2},
2192 {"DownAndIn", "DownAndOut", 150.0, 80, 0.0, "Call", 100.0, 160.0, 0.04, 0.08, 0.50, 0.2}};
2193
2194 for (auto& f : fxdb5) {
2195 BOOST_TEST_MESSAGE("testing " << f.knockInType << " " << f.knockOutType);
2196 // build market
2197 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(f.s, f.q, f.r, f.v, true);
2198 Date today = Settings::instance().evaluationDate();
2199 Settings::instance().evaluationDate() = today; // reset
2200 Settings::instance().evaluationDate() = market->asofDate();
2201
2202 // build FXBarrierOption - expiry in 6 months
2203 OptionData optionData("Long", "Call", "European", true, vector<string>(1, "20160801"));
2204 vector<TradeBarrier> tradeBarriers_KI = {TradeBarrier(f.barrierKnockIn, "")};
2205 vector<TradeBarrier> tradeBarriers_KO = {TradeBarrier(f.barrierKnockOut, "")};
2206 BarrierData knockInBarrierData(f.knockInType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
2207 BarrierData knockOutBarrierData(f.knockOutType, {f.barrierKnockOut}, 0.0, tradeBarriers_KO);
2208 BarrierData knockOutBarrierData2(f.knockOutType, {f.barrierKnockIn}, 0.0, tradeBarriers_KI);
2209
2210 vector<BarrierData> barriers = {knockInBarrierData, knockOutBarrierData};
2211 Envelope env("CP1");
2212
2213 FxKIKOBarrierOption kikoBarrierOption(env, optionData, barriers, "EUR", 1, "JPY", f.k, "20160201", "TARGET",
2214 "FX-Reuters-EUR-JPY");
2215 FxBarrierOption kiBarrierOption(env, optionData, knockInBarrierData, Date(1, Month::February, 2016), "TARGET",
2216 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2217 FxBarrierOption koBarrierOption(env, optionData, knockOutBarrierData, Date(1, Month::February, 2016), "TARGET",
2218 "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2219 FxBarrierOption koBarrierOption2(env, optionData, knockOutBarrierData2, Date(1, Month::February, 2016),
2220 "TARGET", "EUR", 1, "JPY", f.k, "FX-Reuters-EUR-JPY");
2221
2222 vector<Real> barrier = {std::min(f.barrierKnockIn, f.barrierKnockOut),
2223 std::max(f.barrierKnockIn, f.barrierKnockOut)};
2224 vector<TradeBarrier> tradeBarriers = {TradeBarrier(std::min(f.barrierKnockIn, f.barrierKnockOut), ""),
2225 TradeBarrier(std::max(f.barrierKnockIn, f.barrierKnockOut), "")};
2226 BarrierData barrierData("KnockOut", barrier, 0, tradeBarriers);
2227
2228 FxDoubleBarrierOption dkoBarrierOption(env, optionData, barrierData, Date(), "",
2229 "EUR", 1, // foreign
2230 "JPY", f.k); // domestic
2231
2232 FxOption fxOption(env, optionData, "EUR", 1, // foreign
2233 "JPY", f.k); // domestic
2234
2235 // Build and price
2236 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
2237 engineData->model("FxDoubleBarrierOption") = "GarmanKohlhagen";
2238 engineData->engine("FxDoubleBarrierOption") = "AnalyticDoubleBarrierEngine";
2239 engineData->model("FxBarrierOption") = "GarmanKohlhagen";
2240 engineData->engine("FxBarrierOption") = "AnalyticBarrierEngine";
2241 engineData->model("FxOption") = "GarmanKohlhagen";
2242 engineData->engine("FxOption") = "AnalyticEuropeanEngine";
2243
2244 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
2245
2246 TimeSeries<Real> pastFixings;
2247 IndexManager::instance().setHistory("Reuters EUR/JPY", pastFixings);
2248
2249 kikoBarrierOption.build(engineFactory);
2250 kiBarrierOption.build(engineFactory);
2251 koBarrierOption.build(engineFactory);
2252 koBarrierOption2.build(engineFactory);
2253 dkoBarrierOption.build(engineFactory);
2254 fxOption.build(engineFactory);
2255
2256 BOOST_TEST_MESSAGE("KIKO NPV: " << kikoBarrierOption.instrument()->NPV());
2257 BOOST_TEST_MESSAGE("KI NPV: " << kiBarrierOption.instrument()->NPV());
2258 BOOST_TEST_MESSAGE("KO(knockoutLevel) NPV: " << koBarrierOption.instrument()->NPV());
2259 BOOST_TEST_MESSAGE("KO(knockinLevel) NPV: " << koBarrierOption2.instrument()->NPV());
2260 BOOST_TEST_MESSAGE("DoubleKnockOut NPV: " << dkoBarrierOption.instrument()->NPV());
2261 BOOST_TEST_MESSAGE("FXOption NPV: " << fxOption.instrument()->NPV());
2262
2263 //check trade knockedIn
2264 dynamic_pointer_cast<TestMarket>(market)->setFxSpot("EURJPY", f.barrierKnockIn);
2265 BOOST_CHECK_CLOSE(kikoBarrierOption.instrument()->NPV(), koBarrierOption.instrument()->NPV(), 0.01);
2266
2267 //check trade knockedOut
2268 dynamic_pointer_cast<TestMarket>(market)->setFxSpot("EURJPY", f.barrierKnockOut);
2269 BOOST_CHECK_SMALL(kikoBarrierOption.instrument()->NPV(), 0.0001);
2270
2271 IndexManager::instance().clearHistories(); // reset
2272 }
2273
2274}
Serializable FX KIKO Barrier Option.
+ Here is the call graph for this function: