-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDiscreteHedging.java
More file actions
382 lines (310 loc) · 13.9 KB
/
Copy pathDiscreteHedging.java
File metadata and controls
382 lines (310 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/*
Copyright (C) 2008 Tito Ingargiola
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
<quantlib-dev@lists.sf.net>. The license is also available online at
<https://www.quantlib.org/license.shtml>.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
package examples;
import org.quantlib.Actual365Fixed;
import org.quantlib.BlackCalculator;
import org.quantlib.BlackConstantVol;
import org.quantlib.BlackScholesMertonProcess;
import org.quantlib.BlackVolTermStructureHandle;
import org.quantlib.Calendar;
import org.quantlib.Date;
import org.quantlib.DayCounter;
import org.quantlib.FlatForward;
import org.quantlib.GaussianPathGenerator;
import org.quantlib.GaussianRandomSequenceGenerator;
import org.quantlib.Option;
import org.quantlib.Path;
import org.quantlib.PlainVanillaPayoff;
import org.quantlib.QuoteHandle;
import org.quantlib.SamplePath;
import org.quantlib.SimpleQuote;
import org.quantlib.Statistics;
import org.quantlib.TARGET;
import org.quantlib.UniformRandomGenerator;
import org.quantlib.UniformRandomSequenceGenerator;
import org.quantlib.YieldTermStructureHandle;
/**
* DiscreteHedging Test app - java version of QuantLib/Examples/DiscreteHedging
* to illustrate use of Quantlib's MonteCarlo functionality through supplied
* SWIG interfaces.
*
* You need to run this with a correctly set library path and something like:
*
* -Djava.library.path=/usr/local/lib
*
* @author Tito Ingargiola
**/
public class DiscreteHedging {
public static void main(String[] args) throws Exception {
long begin = System.currentTimeMillis();
double maturity = 1.0/12.0; // 1 month
double strike = 100;
double underlying = 100;
double volatility = 0.20; // 20%
double riskFreeRate = 0.05; // 5%
ReplicationError rp = new ReplicationError(Option.Type.Call, maturity,
strike, underlying, volatility, riskFreeRate);
long scenarios = 50000;
long hedgesNum = 21;
rp.compute(hedgesNum, scenarios);
hedgesNum = 84;
rp.compute(hedgesNum, scenarios);
long msecs = (System.currentTimeMillis()-begin);
System.out.println("\nRun completed in "+msecs+" ms.");
}
/**
* The ReplicationError class carries out Monte Carlo simulations to
* evaluate the outcome (the replication error) of the discrete hedging
* strategy over different, randomly generated scenarios of future stock
* price evolution.
**/
public static class ReplicationError {
public ReplicationError(Option.Type type, double maturity,
double strike, double s0, double sigma, double r ) {
type_ = type;
maturity_ = maturity;
strike_ = strike;
s0_ = s0;
sigma_ = sigma;
r_ = r;
// value of the option
double rDiscount = Math.exp(-r_ * maturity_);
double qDiscount = 1.0;
double forward = s0_ * qDiscount/rDiscount;
double stdDev = Math.sqrt(sigma_*sigma_*maturity);
BlackCalculator black = new BlackCalculator
(new PlainVanillaPayoff(type,strike),forward,stdDev,rDiscount);
System.out.printf("Option value: %2.5f \n\n",black.value());
// store option's vega, since Derman and Kamal's formula needs it
vega_ = black.vega(maturity_);
String fmt ="%-8s | %-8s | %-8s | %-8s | %-12s | %-8s | %-8s \n";
System.out.printf
(fmt, " ", " ", "P&L", "P&L", "Derman&Kamal", "P&L","P&L" );
System.out.printf(fmt, " samples", "trades", "mean", "std.dev",
"formula", "skewness","kurtosis" );
for (int i = 0; i < 78; i++) System.out.print("-");
System.out.println("-");
}
void compute(long nTimeSteps, long nSamples) {
assert nTimeSteps>0 : "the number of steps must be > 0";
/* Black-Scholes framework: the underlying stock price evolves
lognormally with a fixed known volatility that stays constant
throughout time. */
Calendar calendar = new TARGET();
Date today = Date.todaysDate();
DayCounter dayCounter = new Actual365Fixed();
QuoteHandle stateVariable = new QuoteHandle(new SimpleQuote(s0_));
YieldTermStructureHandle riskFreeRate =
new YieldTermStructureHandle
(new FlatForward(today, r_, dayCounter));
YieldTermStructureHandle dividendYield =
new YieldTermStructureHandle
(new FlatForward(today, 0.0, dayCounter));
BlackVolTermStructureHandle volatility =
new BlackVolTermStructureHandle(
new BlackConstantVol(today, calendar, sigma_, dayCounter));
BlackScholesMertonProcess diffusion =
new BlackScholesMertonProcess
(stateVariable,dividendYield, riskFreeRate, volatility);
// Black Scholes equation rules the path generator:
// at each step the log of the stock
// will have drift and sigma^2 variance
boolean brownianBridge = false;
GaussianRandomSequenceGenerator rsg =
new GaussianRandomSequenceGenerator
(new UniformRandomSequenceGenerator
(nTimeSteps,new UniformRandomGenerator(0)));
GaussianPathGenerator myPathGenerator =
new GaussianPathGenerator
(diffusion,maturity_,nTimeSteps,rsg, brownianBridge);
/* Alternately you can modify the MonteCarloModel to take a
* GaussianSobolPathGenerator and uncomment these lines and
* comment those just above
*
GaussianLowDiscrepancySequenceGenerator rsg =
new GaussianLowDiscrepancySequenceGenerator
(new UniformLowDiscrepancySequenceGenerator
(nTimeSteps));
GaussianSobolPathGenerator myPathGenerator =
new GaussianSobolPathGenerator
(diffusion,maturity_,nTimeSteps,rsg, brownianBridge);*/
ReplicationPathPricer myPathPricer = new ReplicationPathPricer
(type_,strike_, r_, maturity_, sigma_);
MonteCarloModel mcSimulation = new MonteCarloModel
(myPathGenerator, myPathPricer);
mcSimulation.addSamples(nSamples);
// the sampleAccumulator method
// gives access to all the methods of statisticsAccumulator
double PLMean = mcSimulation.sampleAccumulator().mean();
double PLStDev =
mcSimulation.sampleAccumulator().standardDeviation();
double PLSkew = mcSimulation.sampleAccumulator().skewness();
double PLKurt = mcSimulation.sampleAccumulator().kurtosis();
// Derman and Kamal's formula
double theorStD = Math.sqrt(Math.PI/4/nTimeSteps)*vega_*sigma_;
String fmt =
"%-8d | %-8d | %-8.3f | %-8.2f | %-12.2f | %-8.2f | %-8.2f \n";
System.out.printf(fmt, nSamples, nTimeSteps, PLMean, PLStDev,
theorStD, PLSkew, PLKurt );
}
double maturity_;
Option.Type type_;
double strike_;
double s0_;
double sigma_;
double r_;
double vega_;
}
/**
* We pull the interface for a PathPricer into Java so we can
* support its implementation in Java while still relying upon QuantLib's
* powerful RNGs.
*/
public static interface JPathPricer {
public double price(Path path);
}
// The key for the MonteCarlo simulation is to have a PathPricer that
// implements a value(const Path& path) method.
// This method prices the portfolio for each Path of the random variable
public static class ReplicationPathPricer implements JPathPricer {
public ReplicationPathPricer(Option.Type type, double strike,
double r, double maturity, double sigma) {
assert strike > 0 : "Strike must be positive!";
assert maturity > 0 : "Risk free rate must be positive!";
assert r >= 0 : "Risk free rate must be positive or Zero!";
assert sigma >= 0 : "Volatility must be positive or Zero!";
type_ = type;
strike_ = strike;
r_ = r;
maturity_ = maturity;
sigma_ = sigma;
}
public double price(Path path) {
long n = path.length() - 1;
assert n > 0 : "The path can't be empty!";
// discrete hedging interval
double dt = maturity_ / n;
// For simplicity, we assume the stock pays no dividends.
double stockDividendYield = 0.0;
// let's start
double t = 0;
// stock value at t=0
double stock = path.front();
// money account at t=0
double money_account = 0.0;
/************************/
/*** the initial deal ***/
/************************/
// option fair price (Black-Scholes) at t=0
double rDiscount = Math.exp(-r_*maturity_);
double qDiscount = Math.exp(-stockDividendYield*maturity_);
double forward = stock*qDiscount/rDiscount;
double stdDev = Math.sqrt(sigma_*sigma_*maturity_);
PlainVanillaPayoff payoff = new PlainVanillaPayoff(type_,strike_);
BlackCalculator black = new BlackCalculator
(payoff,forward,stdDev,rDiscount);
// sell the option, cash in its premium
money_account += black.value();
// compute delta
double delta = black.delta(stock);
// delta-hedge the option buying stock
double stockAmount = delta;
money_account -= stockAmount*stock;
/**********************************/
/*** hedging during option life ***/
/**********************************/
for (long step = 0; step < n-1; step++){
// time flows
t += dt;
// accruing on the money account
money_account *= Math.exp( r_*dt );
// stock growth:
stock = path.value(step+1);
// recalculate option value at the current stock value,
// and the current time to maturity
rDiscount = Math.exp(-r_*(maturity_-t));
qDiscount = Math.exp(-stockDividendYield*(maturity_-t));
forward = stock*(qDiscount/rDiscount);
stdDev = Math.sqrt(sigma_*sigma_*(maturity_-t));
black = new BlackCalculator
(new PlainVanillaPayoff(type_,strike_),forward,stdDev,rDiscount);
// recalculate delta
delta = black.delta(stock);
// re-hedging
money_account -= (delta - stockAmount)*stock;
stockAmount = delta;
}
/*************************/
/*** option expiration ***/
/*************************/
// last accrual on my money account
money_account *= Math.exp( r_*dt );
// last stock growth
stock = path.value(n);
// the hedger delivers the option payoff to the option holder
double optionPayoff =
(new PlainVanillaPayoff(type_, strike_)).getValue(stock);
money_account -= optionPayoff;
// and unwinds the hedge selling his stock position
money_account += stockAmount*stock;
// final Profit&Loss
return money_account;
}
double maturity_;
Option.Type type_;
double strike_;
double sigma_;
double r_;
}
/**
* We pull the MonteCarloModel into Java so that we can enable the
* implementation in java of our PathPricer
*/
public static class MonteCarloModel {
/** convenience ctor **/
public MonteCarloModel
(GaussianPathGenerator gpg, JPathPricer pathpricer) {
this(gpg,pathpricer,false, null);
}
/** complete ctor **/
public MonteCarloModel(GaussianPathGenerator gpg,
JPathPricer pathpricer, boolean antitheticVariate,
Statistics stats ) {
assert gpg != null : "PathGenerator must not be null!";
assert pathpricer != null : "PathPricer must not be null!";
gpg_ = gpg;
ppricer_ = pathpricer;
stats_ = (stats==null) ? new Statistics() : stats;
av_ = antitheticVariate;
}
public Statistics sampleAccumulator () { return stats_; }
public void addSamples( long samples ) {
for(long j = 0; j < samples; j++) {
SamplePath path = gpg_.next();
double price = ppricer_.price(path.value());
if ( av_ ) {
path = gpg_.antithetic();
double price2 = ppricer_.price(path.value());
stats_.add((price+price2)/2.0, path.weight());
} else {
stats_.add(price, path.weight());
}
}
}
final boolean av_;
final GaussianPathGenerator gpg_;
final JPathPricer ppricer_;
final Statistics stats_;
}
}