1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2005 StatPro Italia srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include "pathgenerator.hpp"
21#include "utilities.hpp"
22#include <ql/methods/montecarlo/mctraits.hpp>
23#include <ql/processes/blackscholesprocess.hpp>
24#include <ql/processes/geometricbrownianprocess.hpp>
25#include <ql/processes/ornsteinuhlenbeckprocess.hpp>
26#include <ql/processes/squarerootprocess.hpp>
27#include <ql/processes/stochasticprocessarray.hpp>
28#include <ql/time/daycounters/actual360.hpp>
29#include <ql/quotes/simplequote.hpp>
30#include <ql/utilities/dataformatters.hpp>
31
32using namespace QuantLib;
33using namespace boost::unit_test_framework;
34
35namespace {
36
37 void testSingle(const ext::shared_ptr<StochasticProcess1D>& process,
38 const std::string& tag, bool brownianBridge,
39 Real expected, Real antithetic) {
40 typedef PseudoRandom::rsg_type rsg_type;
41 typedef PathGenerator<rsg_type>::sample_type sample_type;
42
43 BigNatural seed = 42;
44 Time length = 10;
45 Size timeSteps = 12;
46 rsg_type rsg = PseudoRandom::make_sequence_generator(dimension: timeSteps, seed);
47 PathGenerator<rsg_type> generator(process, length, timeSteps,
48 rsg, brownianBridge);
49 Size i;
50 for (i=0; i<100; i++)
51 generator.next();
52
53 sample_type sample = generator.next();
54 Real calculated = sample.value.back();
55 Real error = std::fabs(x: calculated-expected);
56 Real tolerance = 2.0e-8;
57 if (error > tolerance) {
58 BOOST_ERROR("using " << tag << " process "
59 << (brownianBridge ? "with " : "without ")
60 << "brownian bridge:\n"
61 << std::setprecision(13)
62 << " calculated: " << calculated << "\n"
63 << " expected: " << expected << "\n"
64 << " error: " << error << "\n"
65 << " tolerance: " << tolerance);
66 }
67
68 sample = generator.antithetic();
69 calculated = sample.value.back();
70 error = std::fabs(x: calculated-antithetic);
71 tolerance = 2.0e-7;
72 if (error > tolerance) {
73 BOOST_ERROR("using " << tag << " process "
74 << (brownianBridge ? "with " : "without ")
75 << "brownian bridge:\n"
76 << "antithetic sample:\n"
77 << std::setprecision(13)
78 << " calculated: " << calculated << "\n"
79 << " expected: " << antithetic << "\n"
80 << " error: " << error << "\n"
81 << " tolerance: " << tolerance);
82 }
83
84 }
85
86 void testMultiple(const ext::shared_ptr<StochasticProcess>& process,
87 const std::string& tag,
88 Real expected[], Real antithetic[]) {
89 typedef PseudoRandom::rsg_type rsg_type;
90 typedef MultiPathGenerator<rsg_type>::sample_type sample_type;
91
92 BigNatural seed = 42;
93 Time length = 10;
94 Size timeSteps = 12;
95 Size assets = process->size();
96 rsg_type rsg = PseudoRandom::make_sequence_generator(dimension: timeSteps*assets,
97 seed);
98 MultiPathGenerator<rsg_type> generator(process,
99 TimeGrid(length, timeSteps),
100 rsg, false);
101 Size i, j;
102 for (i=0; i<100; i++)
103 generator.next();
104
105 sample_type sample = generator.next();
106 Array calculated(assets);
107 Real error, tolerance = 2.0e-7;
108 for (j=0; j<assets; j++)
109 calculated[j] = sample.value[j].back();
110 for (j=0; j<assets; j++) {
111 error = std::fabs(x: calculated[j]-expected[j]);
112 if (error > tolerance) {
113 BOOST_ERROR("using " << tag << " process "
114 << "(" << io::ordinal(j+1) << " asset:)\n"
115 << std::setprecision(13)
116 << " calculated: " << calculated[j] << "\n"
117 << " expected: " << expected[j] << "\n"
118 << " error: " << error << "\n"
119 << " tolerance: " << tolerance);
120 }
121 }
122
123 sample = generator.antithetic();
124 for (j=0; j<assets; j++)
125 calculated[j] = sample.value[j].back();
126 for (j=0; j<assets; j++) {
127 error = std::fabs(x: calculated[j]-antithetic[j]);
128 if (error > tolerance) {
129 BOOST_ERROR("using " << tag << " process "
130 << "(" << io::ordinal(j+1) << " asset:)\n"
131 << "antithetic sample:\n"
132 << std::setprecision(13)
133 << " calculated: " << calculated[j] << "\n"
134 << " expected: " << antithetic[j] << "\n"
135 << " error: " << error << "\n"
136 << " tolerance: " << tolerance);
137 }
138 }
139 }
140
141}
142
143
144void PathGeneratorTest::testPathGenerator() {
145
146 BOOST_TEST_MESSAGE("Testing 1-D path generation against cached values...");
147
148 Settings::instance().evaluationDate() = Date(26,April,2005);
149
150 Handle<Quote> x0(ext::shared_ptr<Quote>(new SimpleQuote(100.0)));
151 Handle<YieldTermStructure> r(flatRate(forward: 0.05, dc: Actual360()));
152 Handle<YieldTermStructure> q(flatRate(forward: 0.02, dc: Actual360()));
153 Handle<BlackVolTermStructure> sigma(flatVol(volatility: 0.20, dc: Actual360()));
154 // commented values must be used when Halley's correction is enabled
155 testSingle(process: ext::shared_ptr<StochasticProcess1D>(
156 new BlackScholesMertonProcess(x0,q,r,sigma)),
157 tag: "Black-Scholes", brownianBridge: false, expected: 26.13784357783, antithetic: 467.2928561411);
158 // 26.13784357783, 467.2928562519);
159 testSingle(process: ext::shared_ptr<StochasticProcess1D>(
160 new BlackScholesMertonProcess(x0,q,r,sigma)),
161 tag: "Black-Scholes", brownianBridge: true, expected: 60.28215549393, antithetic: 202.6143139999);
162 // 60.28215551021, 202.6143139437);
163
164 testSingle(process: ext::shared_ptr<StochasticProcess1D>(
165 new GeometricBrownianMotionProcess(100.0, 0.03, 0.20)),
166 tag: "geometric Brownian", brownianBridge: false, expected: 27.62223714065, antithetic: 483.6026514084);
167 // 27.62223714065, 483.602651493);
168
169 testSingle(process: ext::shared_ptr<StochasticProcess1D>(
170 new OrnsteinUhlenbeckProcess(0.1, 0.20)),
171 tag: "Ornstein-Uhlenbeck", brownianBridge: false, expected: -0.8372003433557, antithetic: 0.8372003433557);
172
173 testSingle(process: ext::shared_ptr<StochasticProcess1D>(
174 new SquareRootProcess(0.1, 0.1, 0.20, 10.0)),
175 tag: "square-root", brownianBridge: false, expected: 1.70608664108, antithetic: 6.024200546031);
176}
177
178
179void PathGeneratorTest::testMultiPathGenerator() {
180
181 BOOST_TEST_MESSAGE("Testing n-D path generation against cached values...");
182
183 Settings::instance().evaluationDate() = Date(26,April,2005);
184
185 Handle<Quote> x0(ext::shared_ptr<Quote>(new SimpleQuote(100.0)));
186 Handle<YieldTermStructure> r(flatRate(forward: 0.05, dc: Actual360()));
187 Handle<YieldTermStructure> q(flatRate(forward: 0.02, dc: Actual360()));
188 Handle<BlackVolTermStructure> sigma(flatVol(volatility: 0.20, dc: Actual360()));
189
190 Matrix correlation(3,3);
191 correlation[0][0] = 1.0; correlation[0][1] = 0.9; correlation[0][2] = 0.7;
192 correlation[1][0] = 0.9; correlation[1][1] = 1.0; correlation[1][2] = 0.4;
193 correlation[2][0] = 0.7; correlation[2][1] = 0.4; correlation[2][2] = 1.0;
194
195 std::vector<ext::shared_ptr<StochasticProcess1D> > processes(3);
196 ext::shared_ptr<StochasticProcess> process;
197
198 processes[0] = ext::shared_ptr<StochasticProcess1D>(
199 new BlackScholesMertonProcess(x0,q,r,sigma));
200 processes[1] = ext::shared_ptr<StochasticProcess1D>(
201 new BlackScholesMertonProcess(x0,q,r,sigma));
202 processes[2] = ext::shared_ptr<StochasticProcess1D>(
203 new BlackScholesMertonProcess(x0,q,r,sigma));
204 process = ext::shared_ptr<StochasticProcess>(
205 new StochasticProcessArray(processes,correlation));
206 // commented values must be used when Halley's correction is enabled
207 Real result1[] = {
208 188.2235868185,
209 270.6713069569,
210 113.0431145652 };
211 // Real result1[] = {
212 // 188.2235869273,
213 // 270.6713071508,
214 // 113.0431145652 };
215 Real result1a[] = {
216 64.89105742957,
217 45.12494404804,
218 108.0475146914 };
219 // Real result1a[] = {
220 // 64.89105739157,
221 // 45.12494401537,
222 // 108.0475146914 };
223 testMultiple(process, tag: "Black-Scholes", expected: result1, antithetic: result1a);
224
225 processes[0] = ext::shared_ptr<StochasticProcess1D>(
226 new GeometricBrownianMotionProcess(100.0, 0.03, 0.20));
227 processes[1] = ext::shared_ptr<StochasticProcess1D>(
228 new GeometricBrownianMotionProcess(100.0, 0.03, 0.20));
229 processes[2] = ext::shared_ptr<StochasticProcess1D>(
230 new GeometricBrownianMotionProcess(100.0, 0.03, 0.20));
231 process = ext::shared_ptr<StochasticProcess>(
232 new StochasticProcessArray(processes,correlation));
233 Real result2[] = {
234 174.8266131680,
235 237.2692443633,
236 119.1168555440 };
237 // Real result2[] = {
238 // 174.8266132344,
239 // 237.2692444869,
240 // 119.1168555605 };
241 Real result2a[] = {
242 57.69082393020,
243 38.50016862915,
244 116.4056510107 };
245 // Real result2a[] = {
246 // 57.69082387657,
247 // 38.50016858691,
248 // 116.4056510107 };
249 testMultiple(process, tag: "geometric Brownian", expected: result2, antithetic: result2a);
250
251 processes[0] = ext::shared_ptr<StochasticProcess1D>(
252 new OrnsteinUhlenbeckProcess(0.1, 0.20));
253 processes[1] = ext::shared_ptr<StochasticProcess1D>(
254 new OrnsteinUhlenbeckProcess(0.1, 0.20));
255 processes[2] = ext::shared_ptr<StochasticProcess1D>(
256 new OrnsteinUhlenbeckProcess(0.1, 0.20));
257 process = ext::shared_ptr<StochasticProcess>(
258 new StochasticProcessArray(processes,correlation));
259 Real result3[] = {
260 0.2942058437284,
261 0.5525006418386,
262 0.02650931054575 };
263 Real result3a[] = {
264 -0.2942058437284,
265 -0.5525006418386,
266 -0.02650931054575 };
267 testMultiple(process, tag: "Ornstein-Uhlenbeck", expected: result3, antithetic: result3a);
268
269 processes[0] = ext::shared_ptr<StochasticProcess1D>(
270 new SquareRootProcess(0.1, 0.1, 0.20, 10.0));
271 processes[1] = ext::shared_ptr<StochasticProcess1D>(
272 new SquareRootProcess(0.1, 0.1, 0.20, 10.0));
273 processes[2] = ext::shared_ptr<StochasticProcess1D>(
274 new SquareRootProcess(0.1, 0.1, 0.20, 10.0));
275 process = ext::shared_ptr<StochasticProcess>(
276 new StochasticProcessArray(processes,correlation));
277 Real result4[] = {
278 4.279510844897,
279 4.943783503533,
280 3.590930385958 };
281 Real result4a[] = {
282 2.763967737724,
283 2.226487196647,
284 3.503859264341 };
285 testMultiple(process, tag: "square-root", expected: result4, antithetic: result4a);
286}
287
288
289test_suite* PathGeneratorTest::suite() {
290 auto* suite = BOOST_TEST_SUITE("Path generation tests");
291 suite->add(QUANTLIB_TEST_CASE(&PathGeneratorTest::testPathGenerator));
292 suite->add(QUANTLIB_TEST_CASE(&PathGeneratorTest::testMultiPathGenerator));
293 return suite;
294}
295
296

source code of quantlib/test-suite/pathgenerator.cpp

Morty Proxy This is a proxified and sanitized view of the page, visit original site.