QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
upperboundengine.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2006 Mark Joshi
5 Copyright (C) 2006 StatPro Italia srl
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
31#include <algorithm>
32#include <utility>
33
34namespace QuantLib {
35
36 namespace {
37
38 class DecoratedHedge : public CallSpecifiedMultiProduct {
39 public:
40 explicit DecoratedHedge(const CallSpecifiedMultiProduct& product)
41 : CallSpecifiedMultiProduct(product) {
42 savedStates_.reserve(product.evolution().numberOfSteps());
43
44 Size N = product.numberOfProducts();
46 cashFlowsGenerated_.resize(N);
47 for (Size i=0; i<N; ++i)
48 cashFlowsGenerated_[i].resize(
49 product.maxNumberOfCashFlowsPerProductPerStep());
50
51 clear();
52 }
53
54 void reset() override {
56 disableCallability();
57 for (Size i=0; i<lastSavedStep_; ++i)
59 *savedStates_[i],
60 numberCashFlowsThisStep_,
61 cashFlowsGenerated_);
62 enableCallability();
63 }
64
65 bool nextTimeStep(const CurveState& currentState,
66 std::vector<Size>& numberCashFlowsThisStep,
67 std::vector<std::vector<CashFlow> >& cashFlowsGenerated) override {
68 if (recording_)
69 savedStates_.emplace_back(currentState);
71 currentState,
72 numberCashFlowsThisStep,
73 cashFlowsGenerated);
74 }
75
76 std::unique_ptr<MarketModelMultiProduct> clone() const override {
77 return std::unique_ptr<MarketModelMultiProduct>(new DecoratedHedge(*this));
78 }
79
80 void save() {
82 }
83
84 void clear() {
86 savedStates_.clear();
87 recording_ = true;
88 }
89
90 void startRecording() {
91 recording_ = true;
92 }
93
94 void stopRecording() {
95 recording_ = false;
96 }
97 private:
98 std::vector<Clone<CurveState> > savedStates_;
101 std::vector<Size> numberCashFlowsThisStep_;
102 std::vector<std::vector<CashFlow> > cashFlowsGenerated_;
103 };
104
105 }
106
107
109 ext::shared_ptr<MarketModelEvolver> evolver,
110 std::vector<ext::shared_ptr<MarketModelEvolver> > innerEvolvers,
111 const MarketModelMultiProduct& underlying,
112 const MarketModelExerciseValue& rebate,
113 const MarketModelMultiProduct& hedge,
114 const MarketModelExerciseValue& hedgeRebate,
115 const ExerciseStrategy<CurveState>& hedgeStrategy,
116 Real initialNumeraireValue)
117 : evolver_(std::move(evolver)), innerEvolvers_(std::move(innerEvolvers)),
118 composite_(MultiProductComposite()), initialNumeraireValue_(initialNumeraireValue) {
119
120 composite_.add(underlying);
122 composite_.add(hedge);
123 composite_.add(ExerciseAdapter(hedgeRebate));
125 hedge,hedgeStrategy,ExerciseAdapter(hedgeRebate))));
127
129 underlyingSize_ = underlying.numberOfProducts();
131 rebateSize_ = 1;
136
137
139
140 const std::vector<Time>& evolutionTimes =
142 numberOfSteps_ = evolutionTimes.size();
143
144 isExerciseTime_.resize(evolutionTimes.size());
145 isExerciseTime_ = isInSubset(evolutionTimes,
146 hedgeStrategy.exerciseTimes());
147
150 for (Size i=0; i<numberOfProducts_; ++i)
151 cashFlowsGenerated_[i].resize(
153
154 const std::vector<Time>& cashFlowTimes =
156 const std::vector<Rate>& rateTimes =
158 Size n =cashFlowTimes.size();
159 discounters_.reserve(n);
160 for (Size j=0; j<n; ++j)
161 discounters_.emplace_back(cashFlowTimes[j], rateTimes);
162 }
163
164
166 Size outerPaths,
167 Size innerPaths) {
168 for (Size i=0; i<outerPaths; ++i) {
169 std::pair<Real,Real> result = singlePathValue(innerPaths);
170 stats.add(result.first, result.second);
171 }
172 }
173
174
175 std::pair<Real,Real> UpperBoundEngine::singlePathValue(Size innerPaths) {
176
177 auto& callable = dynamic_cast<DecoratedHedge&>(composite_.item(4));
178 const ExerciseStrategy<CurveState>& strategy = callable.strategy();
179
180
181 Real maximumValue = QL_MIN_REAL;
182 Real numerairesHeld = 0.0;
183 Real weight = evolver_->startNewPath();
184 callable.clear();
186 callable.disableCallability();
187 Real principalInNumerairePortfolio = 1.0;
188 Size exercise = 0;
189
190 for (Size k=0; k<numberOfSteps_; ++k) {
191 weight *= evolver_->advanceStep();
192
193 composite_.nextTimeStep(evolver_->currentState(),
196
197 // First, we accumulate cash flows from both the
198 // underlying...
199 Real underlyingCashFlows =
201 principalInNumerairePortfolio,
204
205 // ...and the hedge
206 Real hedgeCashFlows =
208 principalInNumerairePortfolio,
211
212 // we do the same for the rebates. Warning: this relies on
213 // the fact that on each exercise date an ExerciseAdapter
214 // generates a cash-flow equal to the exercise value
215 Real rebateCashFlow =
217 principalInNumerairePortfolio,
220
221 Real hedgeRebateCashFlow =
223 principalInNumerairePortfolio,
226
227
228 numerairesHeld += underlyingCashFlows - hedgeCashFlows;
229
230 // Second, we do the upper-bound thing
231 if (isExerciseTime_[k]) {
232
233 Real unexercisedHedgeValue = 0.0;
234
235 if (k != numberOfSteps_-1) {
236
237 // Here, we setup the relevant inner evolver and
238 // the decorated callable hedge such that their
239 // reset() method brings them to the current point
240 // rather than the beginning of the path.
241
242 ext::shared_ptr<MarketModelEvolver> currentEvolver =
243 innerEvolvers_[exercise++];
244 currentEvolver->setInitialState(evolver_->currentState());
245
246 callable.stopRecording();
247 callable.enableCallability();
248 callable.save();
249
250 // This allows us to write:
251 AccountingEngine engine(currentEvolver, callable,
252 1.0); // this causes the result
253 // to be in numeraire units
254 SequenceStatisticsInc innerStats(callable.numberOfProducts());
255 engine.multiplePathValues(innerStats, innerPaths);
256
257 const std::vector<Real>& values = innerStats.mean();
258 unexercisedHedgeValue =
259 std::accumulate(values.begin(), values.end(), Real(0.0))
260 / principalInNumerairePortfolio;
261
262 callable.disableCallability();
263 callable.startRecording();
264
265 }
266
267 // Now, we can calculate the total value of our hedged
268 // portfolio...
269 Real portfolioValue = numerairesHeld;
270 if (strategy.exercise(evolver_->currentState())) {
271 // get the rebates...
272 portfolioValue +=
273 rebateCashFlow - hedgeRebateCashFlow;
274 // ...and reinvest to rehedge
275 numerairesHeld +=
276 unexercisedHedgeValue - hedgeRebateCashFlow;
277 } else {
278 portfolioValue +=
279 rebateCashFlow - unexercisedHedgeValue;
280 }
281
282 // ...and use it to update the maximum value
283 maximumValue = std::max(maximumValue, portfolioValue);
284 }
285
286
287 // Lastly, we do the homework for next step (if any)
288 if (k<numberOfSteps_-1) {
289
290 // The numeraire might change between steps. This implies
291 // that we might have to convert the numeraire bonds for
292 // this step into a corresponding amount of numeraire
293 // bonds for the next step. This can be done by changing
294 // the principal of the numeraire and updating the number
295 // of bonds in the numeraire portfolio accordingly.
296
297 Size numeraire = evolver_->numeraires()[k];
298 Size nextNumeraire = evolver_->numeraires()[k+1];
299
300 principalInNumerairePortfolio *=
301 evolver_->currentState().discountRatio(numeraire,
302 nextNumeraire);
303 }
304
305 }
306
307 // finally, we update the maximum with the total accumulated
308 // cash flows (in case we never exercised)
309 maximumValue = std::max(maximumValue, numerairesHeld);
310
311
312 // all done; we just convert the result back to cash
313 maximumValue *= initialNumeraireValue_;
314
315 return std::make_pair(maximumValue, weight);
316 }
317
318
320 Real principalInNumerairePortfolio,
321 Size beginProduct,
322 Size endProduct) const {
323 Size numeraire = evolver_->numeraires()[currentStep];
324
325 Real numeraireUnits = 0.0;
326 // For each product in range...
327 for (Size i=beginProduct; i<endProduct; ++i) {
328 // ...and for each cash flow...
329 const std::vector<MarketModelMultiProduct::CashFlow>& cashflows =
331 for (Size j=0; j<numberCashFlowsThisStep_[i]; ++j) {
332 // ...convert the cash flow to numeraires. This is
333 // done by calculating the number of numeraire bonds
334 // corresponding to such cash flow...
335 const MarketModelDiscounter& discounter =
336 discounters_[cashflows[j].timeIndex];
337 // ...and adding the newly bought bonds to the total
338 numeraireUnits += cashflows[j].amount *
339 discounter.numeraireBonds(evolver_->currentState(),
340 numeraire);
341 }
342 }
343 return numeraireUnits/principalInNumerairePortfolio;
344 }
345
346}
347
Engine collecting cash flows along a market-model simulation.
void multiplePathValues(SequenceStatisticsInc &stats, Size numberOfPaths)
bool nextTimeStep(const CurveState &currentState, std::vector< Size > &numberCashFlowsThisStep, std::vector< std::vector< CashFlow > > &cashFlowsGenerated) override
return value indicates whether path is finished, TRUE means done
void reset() override
during simulation put product at start of path
const std::vector< Time > & rateTimes() const
const std::vector< Time > & evolutionTimes() const
virtual std::vector< Time > exerciseTimes() const =0
virtual bool exercise(const State &currentState) const =0
empirical-distribution risk measures
Statistics analysis of N-dimensional (sequence) data.
std::vector< Time > possibleCashFlowTimes() const override
const EvolutionDescription & evolution() const override
const MarketModelMultiProduct & item(Size i) const
void add(const Clone< MarketModelMultiProduct > &, Real multiplier=1.0)
void reset() override
during simulation put product at start of path
Real numeraireBonds(const CurveState &, Size numeraire) const
Definition: discounter.cpp:43
virtual Size numberOfProducts() const =0
Composition of one or more market-model products.
bool nextTimeStep(const CurveState &currentState, std::vector< Size > &numberCashFlowsThisStep, std::vector< std::vector< CashFlow > > &cashFlowsGenerated) override
return value indicates whether path is finished, TRUE means done
Size maxNumberOfCashFlowsPerProductPerStep() const override
ext::shared_ptr< MarketModelEvolver > evolver_
UpperBoundEngine(ext::shared_ptr< MarketModelEvolver > evolver, std::vector< ext::shared_ptr< MarketModelEvolver > > innerEvolvers, const MarketModelMultiProduct &underlying, const MarketModelExerciseValue &rebate, const MarketModelMultiProduct &hedge, const MarketModelExerciseValue &hedgeRebate, const ExerciseStrategy< CurveState > &hedgeStrategy, Real initialNumeraireValue)
std::vector< MarketModelDiscounter > discounters_
std::vector< ext::shared_ptr< MarketModelEvolver > > innerEvolvers_
MultiProductComposite composite_
std::vector< std::vector< MarketModelMultiProduct::CashFlow > > cashFlowsGenerated_
std::vector< Size > numberCashFlowsThisStep_
std::valarray< bool > isExerciseTime_
std::pair< Real, Real > singlePathValue(Size innerPaths)
Real collectCashFlows(Size currentStep, Real principalInNumerairePortfolio, Size beginProduct, Size endProduct) const
void multiplePathValues(Statistics &stats, Size outerPaths, Size innerPaths)
#define QL_MIN_REAL
Definition: qldefines.hpp:175
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
std::valarray< bool > isInSubset(const std::vector< Time > &set, const std::vector< Time > &subset)
Definition: utilities.cpp:56
STL namespace.
bool recording_
Size lastSavedStep_
std::vector< Size > numberCashFlowsThisStep_
std::vector< Clone< CurveState > > savedStates_
std::vector< std::vector< CashFlow > > cashFlowsGenerated_