QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
longstaffschwartzmultipathpricer.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009 Andrea Odetti
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 <ql/experimental/mcbasket/longstaffschwartzmultipathpricer.hpp>
21#include <ql/math/generallinearleastsquares.hpp>
22#include <ql/utilities/tracing.hpp>
23#include <utility>
24
25namespace QuantLib {
26
28 : payments(numberOfTimes, 0.0),
29 exercises(numberOfTimes, 0.0),
30 states(numberOfTimes) {
31 }
32
34 return states.size();
35 }
36
37
39 const ext::shared_ptr<PathPayoff>& payoff,
40 const std::vector<Size>& timePositions,
41 std::vector<Handle<YieldTermStructure> > forwardTermStructures,
42 Array discounts,
43 Size polynomialOrder,
44 LsmBasisSystem::PolynomialType polynomialType)
45 : payoff_(payoff), coeff_(new Array[timePositions.size() - 1]),
46 lowerBounds_(new Real[timePositions.size()]), timePositions_(timePositions),
47 forwardTermStructures_(std::move(forwardTermStructures)), dF_(std::move(discounts)),
48 v_(LsmBasisSystem::multiPathBasisSystem(
49 payoff->basisSystemDimension(), polynomialOrder, polynomialType)) {
50 QL_REQUIRE( polynomialType == LsmBasisSystem::Monomial
51 || polynomialType == LsmBasisSystem::Laguerre
52 || polynomialType == LsmBasisSystem::Hermite
53 || polynomialType == LsmBasisSystem::Hyperbolic
54 || polynomialType == LsmBasisSystem::Chebyshev2nd,
55 "insufficient polynomial type");
56 }
57
58 /*
59 Extract the relevant information from the whole path
60 */
63 const {
64 const Size numberOfAssets = multiPath.assetNumber();
65 const Size numberOfTimes = timePositions_.size();
66
67 Matrix path(numberOfAssets, numberOfTimes, Null<Real>());
68
69 for (Size i = 0; i < numberOfTimes; ++i) {
70 const Size pos = timePositions_[i];
71 for (Size j = 0; j < numberOfAssets; ++j)
72 path[j][i] = multiPath[j][pos];
73 }
74
75 PathInfo info(numberOfTimes);
76
77 payoff_->value(path, forwardTermStructures_, info.payments, info.exercises, info.states);
78
79 return info;
80 }
81
83 const MultiPath& multiPath) const {
84 PathInfo path = transformPath(multiPath);
85
87 // store paths for the calibration
88 // only the relevant part
89 paths_.push_back(path);
90 // result doesn't matter
91 return 0.0;
92 }
93
94 // exercise at time t, cancels all payment AFTER t
95
96 const Size len = path.pathLength();
97 Real price = 0.0;
98
99 // this is the last event date
100 {
101 const Real payoff = path.payments[len - 1];
102 const Real exercise = path.exercises[len - 1];
103 const Array & states = path.states[len - 1];
104 const bool canExercise = !states.empty();
105
106 // at the end the continuation value is 0.0
107 if (canExercise && exercise > 0.0)
108 price += exercise;
109 price += payoff;
110 }
111
112 for (Integer i = len - 2; i >= 0; --i) {
113 price *= dF_[i + 1] / dF_[i];
114
115 const Real exercise = path.exercises[i];
116
117 /*
118 coeff_[i].size()
119 - 0 => never exercise
120 - v_.size() => use estimated continuation value
121 (if > lowerBounds_[i])
122 - v_.size() + 1 => always exercise
123
124 In any case if states is empty, no exercise is allowed.
125 */
126 const Array & states = path.states[i];
127 const bool canExercise = !states.empty();
128
129 if (canExercise) {
130 if (coeff_[i].size() == v_.size() + 1) {
131 // special value always exercise
132 price = exercise;
133 }
134 else {
135 if (!coeff_[i].empty() && exercise > lowerBounds_[i]) {
136
137 Real continuationValue = 0.0;
138 for (Size l = 0; l < v_.size(); ++l) {
139 continuationValue += coeff_[i][l] * v_[l](states);
140 }
141
142 if (continuationValue < exercise) {
143 price = exercise;
144 }
145 }
146 }
147 }
148 const Real payoff = path.payments[i];
149 price += payoff;
150 }
151
152 return price * dF_[0];
153 }
154
156 const Size n = paths_.size(); // number of paths
157 Array prices(n, 0.0), exercise(n, 0.0);
158
159 const Size basisDimension = payoff_->basisSystemDimension();
160
161 const Size len = paths_[0].pathLength();
162
163 /*
164 We try to estimate the lower bound of the continuation value,
165 so that only itm paths contribute to the regression.
166 */
167
168 for (Size j = 0; j < n; ++j) {
169 const Real payoff = paths_[j].payments[len - 1];
170 const Real exercise = paths_[j].exercises[len - 1];
171 const Array & states = paths_[j].states[len - 1];
172 const bool canExercise = !states.empty();
173
174 // at the end the continuation value is 0.0
175 if (canExercise && exercise > 0.0)
176 prices[j] += exercise;
177 prices[j] += payoff;
178 }
179
180 lowerBounds_[len - 1] = *std::min_element(prices.begin(), prices.end());
181
182 std::vector<bool> lsExercise(n);
183
184 for (Integer i = len - 2; i >= 0; --i) {
185 std::vector<Real> y;
186 std::vector<Array> x;
187
188 // prices are discounted up to time i
189 const Real discountRatio = dF_[i + 1] / dF_[i];
190 prices *= discountRatio;
191 lowerBounds_[i + 1] *= discountRatio;
192
193 //roll back step
194 for (Size j = 0; j < n; ++j) {
195 exercise[j] = paths_[j].exercises[i];
196
197 // If states is empty, no exercise in this path
198 // and the path will not partecipate to the Lesat Square regression
199
200 const Array & states = paths_[j].states[i];
201 QL_REQUIRE(states.empty() || states.size() == basisDimension,
202 "Invalid size of basis system");
203
204 // only paths that could potentially create exercise opportunities
205 // partecipate to the regression
206
207 // if exercise is lower than minimum continuation value, no point in considering it
208 if (!states.empty() && exercise[j] > lowerBounds_[i + 1]) {
209 x.push_back(states);
210 y.push_back(prices[j]);
211 }
212 }
213
214 if (v_.size() <= x.size()) {
216 }
217 else {
218 // if number of itm paths is smaller then the number of
219 // calibration functions -> never exercise
220 QL_TRACE("Not enough itm paths: default decision is NEVER");
221 coeff_[i] = Array(0);
222 }
223
224 /* attempt to avoid static arbitrage given by always or never exercising.
225
226 always is absolute: regardless of the lowerBoundContinuationValue_ (this could be changed)
227 but it still honours "canExercise"
228 */
229 Real sumOptimized = 0.0;
230 Real sumNoExercise = 0.0;
231 Real sumAlwaysExercise = 0.0; // always, if allowed
232
233 for (Size j = 0, k = 0; j < n; ++j) {
234 sumNoExercise += prices[j];
235 lsExercise[j] = false;
236
237 const bool canExercise = !paths_[j].states[i].empty();
238 if (canExercise) {
239 sumAlwaysExercise += exercise[j];
240 if (!coeff_[i].empty() && exercise[j] > lowerBounds_[i + 1]) {
241 Real continuationValue = 0.0;
242 for (Size l = 0; l < v_.size(); ++l) {
243 continuationValue += coeff_[i][l] * v_[l](x[k]);
244 }
245
246 if (continuationValue < exercise[j]) {
247 lsExercise[j] = true;
248 }
249 ++k;
250 }
251 }
252 else {
253 sumAlwaysExercise += prices[j];
254 }
255
256 sumOptimized += lsExercise[j] ? exercise[j] : prices[j];
257 }
258
259 sumOptimized /= n;
260 sumNoExercise /= n;
261 sumAlwaysExercise /= n;
262
263 QL_TRACE( "Time index: " << i
264 << ", LowerBound: " << lowerBounds_[i + 1]
265 << ", Optimum: " << sumOptimized
266 << ", Continuation: " << sumNoExercise
267 << ", Termination: " << sumAlwaysExercise);
268
269 if ( sumOptimized >= sumNoExercise
270 && sumOptimized >= sumAlwaysExercise) {
271
272 QL_TRACE("Accepted LS decision");
273 for (Size j = 0; j < n; ++j) {
274 // lsExercise already contains "canExercise"
275 prices[j] = lsExercise[j] ? exercise[j] : prices[j];
276 }
277 }
278 else if (sumAlwaysExercise > sumNoExercise) {
279 QL_TRACE("Overridden bad LS decision: ALWAYS");
280 for (Size j = 0; j < n; ++j) {
281 const bool canExercise = !paths_[j].states[i].empty();
282 prices[j] = canExercise ? exercise[j] : prices[j];
283 }
284 // special value to indicate always exercise
285 coeff_[i] = Array(v_.size() + 1);
286 }
287 else {
288 QL_TRACE("Overridden bad LS decision: NEVER");
289 // prices already contain the continuation value
290 // special value to indicate never exercise
291 coeff_[i] = Array(0);
292 }
293
294 // then we add in any case the payment at time t
295 // which is made even if cancellation happens at t
296 for (Size j = 0; j < n; ++j) {
297 const Real payoff = paths_[j].payments[i];
298 prices[j] += payoff;
299 }
300
301 lowerBounds_[i] = *std::min_element(prices.begin(), prices.end());
302 }
303
304 // remove calibration paths
305 paths_.clear();
306 // entering the calculation phase
307 calibrationPhase_ = false;
308 }
309}
1-D array used in linear algebra.
Definition: array.hpp:52
bool empty() const
whether the array is empty
Definition: array.hpp:499
Size size() const
dimension of the array
Definition: array.hpp:495
general linear least squares regression
Shared handle to an observable.
Definition: handle.hpp:41
const std::vector< Handle< YieldTermStructure > > forwardTermStructures_
const std::vector< ext::function< Real(Array)> > v_
Real operator()(const MultiPath &multiPath) const override
LongstaffSchwartzMultiPathPricer(const ext::shared_ptr< PathPayoff > &payoff, const std::vector< Size > &timePositions, std::vector< Handle< YieldTermStructure > > forwardTermStructure, Array discounts, Size polynomialOrder, LsmBasisSystem::PolynomialType polynomialType)
Matrix used in linear algebra.
Definition: matrix.hpp:41
Correlated multiple asset paths.
Definition: multipath.hpp:39
Size assetNumber() const
Definition: multipath.hpp:47
template class providing a null value for a given type.
Definition: null.hpp:76
#define QL_TRACE(message)
output tracing information
Definition: tracing.hpp:273
QL_REAL Real
real number
Definition: types.hpp:50
QL_INTEGER Integer
integer number
Definition: types.hpp:35
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
STL namespace.