File: DiscreteHedging.java

package info (click to toggle)
quantlib-swig 1.15-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 103,484 kB
  • sloc: cpp: 2,029,354; cs: 61,237; java: 45,425; perl: 27,362; python: 22,024; ruby: 989; sh: 741; makefile: 319
file content (382 lines) | stat: -rw-r--r-- 14,193 bytes parent folder | download | duplicates (5)
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
382

/*
 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
 <http://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_;
    }
}