-
Notifications
You must be signed in to change notification settings - Fork 0
/
version1.cs
418 lines (363 loc) · 16.1 KB
/
version1.cs
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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
/**
* This package implements a Project using MC simulation to price European Call Options
*Euler Scheme is employed for discretization
*
* @author Yulong Wang
* @version 02/09/15
*/
using System;
namespace OptionPricing
{
/// <summary>
/// This class generates Gaussian distributed pseudo-random variates
/// </summary>
public class GaussianGenerator
{
private Random rand;
public void SetSeed(int seed)
{
rand = new Random(seed);
}
/// <summary>
/// this method uses two U(0,1) distributed random variates
/// to produce a Gaussian distributed random variate
/// </summary>
public double NextGaussian()
{
double u1 = rand.NextDouble();
double u2 = rand.NextDouble();
double u = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
return u;
}
/// <summary>
/// constructor with no parameter. A random integer generated
/// by system time is employed as the seed
/// </summary>
public GaussianGenerator()
{
int seed = (new Random()).Next();
rand = new Random(seed);
}
/// <summary>
/// overloading constructor given an integer parameter
/// </summary>
/// <param name="seed"> is used to set the seed for<member>rand</member></param>
public GaussianGenerator(int seed)
{
rand = new Random(seed);
}
}
/// <summary>
/// This is an abstract class for the object of plain vanilla option
/// </summary>
public abstract class PlainVanillaOption
{
/// <summary>
/// <member>t is the time to maturity for this option</member>
/// <member>strikePrice is the strike price for this option</member>
/// <member>underlyingAssetName is the name for underlying asset</member>
/// </summary>
protected double t;
protected double strikePrice;
protected string underlyingAssetName;
public double T
{
get { return t; }
set { t = value; }
}
public double StrikePrice
{
get { return strikePrice; }
set { strikePrice = value; }
}
public string UnderlyingAssetName
{
get { return underlyingAssetName != null ? underlyingAssetName : "NA"; }
set { underlyingAssetName = value; }
}
/// <summary>
/// method which returns the value of an option at maturity
/// given its underlying asset price at maturity
/// </summary>
/// <param name="finalPrice"> assumes the underlying asset price at maturity</param>
/// <returns> A double type value which describes the future value of an option </returns>
public double ValueAtMaturity(double finalPrice)
{
return Math.Max(0.0, finalPrice - strikePrice);
}
/// <summary>
/// This method gives the estimated the option value
/// and the estimation standard error using MC simulation
/// </summary>
/// <param name="S"> describes the Brownian motion rule of the underlying asset</param>
/// <param name="D"> chooses the discretization scheme </param>
/// <param name="numberOfScenarios">decides the number of random paths to be generated</param>
/// <param name="timeSteps"> decides the number of intervals for discretization scheme </param>
/// <param name="antitheticFlag"> chooses whether to use antithetic variance reduction techniques</param>
/// <returns> a 2 elements array. The first one gives the estimated option price,
/// the second gives the estimation error</returns>
public abstract double[] PricingByMCSim(StochasticAssetPrice S, IDiscretizationScheme D,
int numberOfScenarios, int timeSteps, bool antitheticFlag);
}
/// <summary>
/// This class describes the Brownian Motion rule for the price of an asset
/// </summary>
public class StochasticAssetPrice
{
/// <summary>
/// <member> mu is the drift parameter</member>
/// <member> sigma is the volatility parameter</member>
/// <member> currentPrice is the underlying asset spot price</member>
/// </summary>
private double mu;
private double sigma;
private double currentPrice;
public double Mu
{
get { return mu; }
set { mu = value; }
}
public double Sigma
{
get { return sigma; }
set { sigma = value; }
}
public double CurrentPrice
{
get { return currentPrice; }
set { currentPrice = value; }
}
/// Constructor
public StochasticAssetPrice(double Mu, double sigma, double currentPrice)
{
this.mu = Mu;
this.sigma = sigma;
this.currentPrice = currentPrice;
}
/// Copy method
public StochasticAssetPrice(StochasticAssetPrice S)
{
this.Mu = S.Mu;
this.sigma = S.Sigma;
this.currentPrice = S.CurrentPrice;
}
}
/// <summary>
/// This interface defines the framework for discretization scheme
/// </summary>
public interface IDiscretizationScheme
{
/// <summary>
/// This method simulates a one step motion for the underlying
/// asset price based on specified discretization scheme and time interval
/// </summary>
/// <param name="S">reference to the current state of underlying asset </param>
/// <param name="dt"> decides the time length for each discretized interval</param>
/// <param name="Z"> is the random factors for Brownian motion</param>
/// <returns> the asset price at the next step </returns>
double GetNextPrice(StochasticAssetPrice S, double dt, params double[] Z);
/// <summary>
/// This method simulates a whole path for the underlying asset
/// price based on specified discretization scheme and time to maturity
/// </summary>
/// <param name="S">reference to the current state of underlying asset </param>
/// <param name="nrandom">reference to the Gaussian random variates generator</param>
/// <param name="totalTime">defines the time to maturity</param>
/// <param name="timeSteps">decides the number of discretized intervals for the whole period</param>
/// <returns>an double type array which records the asset price at each point</returns>
double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
double totalTime, int timeSteps);
/// <summary>
/// This method simulates two price path using two series of mutual negative
/// random variates named by 'antithetic' which can help reduce the variance
/// of MC simulation
/// </summary>
/// <returns>an double type 2*N array which records the asset price at each point
/// for the two paths
/// </returns>
double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
double totalTime, int timeSteps);
}
/// <summary>
/// This abstract class implements the IDiscretizationScheme interface
/// based on Black Scholes Model (mu and sigma is constant)
/// </summary>
public abstract class DiscretizationSchemeForBSModel : IDiscretizationScheme
{
public abstract double GetNextPrice(StochasticAssetPrice S, double dt, params double[] Z);
/// <summary>
/// override the <method>GeneratingRandomPricePath</method>.
/// For Black Scholes model, mu and sigma is constant, and only one random
/// variate is needed to generate the state for next point.
/// </summary>
public double[] GeneratingRandomPricePath(StochasticAssetPrice S, GaussianGenerator nrandom,
double totalTime, int timeSteps)
{
double[] pricePath = new double[timeSteps + 1];
double dt = totalTime / (double)timeSteps;
pricePath[0] = S.CurrentPrice;
for (int i = 1; i <= timeSteps; i++)
{
double z = nrandom.NextGaussian();
pricePath[i] = GetNextPrice(S, dt, z);
}
return pricePath;
}
/// <summary>
/// override the <method>DipathByAntitheticMethod</method>.
/// For Black Scholes model, mu and sigma is constant, and only one random
/// variate is needed to generate the state for next point
/// </summary>
public double[][] DipathByAntitheticMethod(StochasticAssetPrice S, GaussianGenerator nrandom,
double totalTime, int timeSteps)
{
double[][] pricePath = new double[2][];
pricePath[0] = new double[timeSteps + 1];
pricePath[1] = new double[timeSteps + 1];
StochasticAssetPrice S2 = new StochasticAssetPrice(S);
double dt = totalTime / (double)timeSteps;
pricePath[0][0] = S.CurrentPrice;
pricePath[1][0] = S.CurrentPrice;
for (int i = 1; i <= timeSteps; i++)
{
double z = nrandom.NextGaussian();
double z2 = -z;
pricePath[0][i] = GetNextPrice(S, dt, z);
pricePath[1][i] = GetNextPrice(S2, dt, z2);
}
return pricePath;
}
}
/// <summary>
/// This class inherites the DiscretizationSchemeForBSModel abstract class
/// by specify the calculation method for the next stage asset price
/// </summary>
public class EulerSchemeForBSModel : DiscretizationSchemeForBSModel
{
/// <summary>
/// Override the <method>GetNextPrice</method> based on Euler Scheme
/// The StochasticAssetPrice object is updated in this method
/// </summary>
/// <returns> a double type value for the price of next stage</returns>
public override double GetNextPrice(StochasticAssetPrice S, double dt, params double[] Z)
{
double nextPrice = S.CurrentPrice +S.Mu*S.CurrentPrice*dt
+S.Sigma*S.CurrentPrice*Z[0] * Math.Sqrt(dt);
S.CurrentPrice = nextPrice;
return nextPrice;
}
}
/// <summary>
/// This class inherites PlainVanillaOption class
/// The European call Option can only be executed at maturity, thus its value does not
/// depend on the path before maturity
/// </summary>
public class EuropeanCallOption : PlainVanillaOption
{
public EuropeanCallOption(double t, double strikePrice, string underlyingAssetName = "")
{
this.t = t;
this.strikePrice = strikePrice;
this.underlyingAssetName = underlyingAssetName;
}
/// <summary>
/// override the <method>PricingByMCSim</method>
/// In each scenario, a price path is generated and the option value under
/// this scenario is only decided by the final price. So, we take the average
/// for the 'value at maturity' of all the simulated scenarios and
/// discounted as the approximation for the option value.
/// This method also outputs the standard error for the approximation.
/// </summary>
public override double[] PricingByMCSim(StochasticAssetPrice S, IDiscretizationScheme D, int numOfScenarios,
int timeSteps, bool antitheticFlag)
{
double[] Result = new double[2]; //record the approximated price and standard error
double[] Sample = new double[numOfScenarios]; //record value at maturity for each scenario
double sumValue = 0.0; //record cumulative sum of Sample array
GaussianGenerator nrand = new GaussianGenerator();
if (antitheticFlag)
{
//when antithetic variance reduction technique is used
for (int i = 0; i < numOfScenarios; i++)
{
StochasticAssetPrice S1 = new StochasticAssetPrice(S);
double[][] PricePath;
PricePath = D.DipathByAntitheticMethod(S1, nrand, T, timeSteps);
Sample[i] = (ValueAtMaturity(PricePath[0][timeSteps])
+ ValueAtMaturity(PricePath[1][timeSteps])) / 2.0;
sumValue += Sample[i];
}
}
else
{
//when antithetic variance reduction technique is not used
for (int i = 0; i < numOfScenarios; i++)
{
StochasticAssetPrice S1 = new StochasticAssetPrice(S);
double[] PricePath;
PricePath = D.GeneratingRandomPricePath(S1, nrand, T, timeSteps);
Sample[i] = ValueAtMaturity(PricePath[timeSteps]);
sumValue += Sample[i];
}
}
double disFactor = Math.Exp(-S.Mu * T); //discount factor to convert value at maturity to current
Result[0] = sumValue / (double)numOfScenarios * disFactor;
double totalVariance = 0.0;
for (int i = 0; i < numOfScenarios; i++)
{
totalVariance += Math.Pow(Sample[i] - Result[0], 2.0);
}
if (numOfScenarios > 1)
{
double std = Math.Sqrt(totalVariance / (numOfScenarios - 1));
Result[1] = std / Math.Sqrt(numOfScenarios) * disFactor;
}
return Result;
}
}
/// <summary>
/// testing program
/// </summary>
class Program
{
static void Main(string[] args)
{
//input needed information from keyboard
Console.WriteLine("Input spot price of underlying asset:");
double spotPrice = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Input option strike price:");
double strikePrice = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Input time to maturity of this option:");
double timeToMaturity = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Input drift parameter for brownian motion:");
double Mu = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Input volatility parameter for brownian motion:");
double sigma = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Input number of scenarios generated by MC simulation:");
int numOfScenarios = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Input number of time steps for Euler discretization:");
int timeSteps = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Input whether to use antithetic variance reduction technique " +
"(true for yes,false for no): ");
bool antithetic = Convert.ToBoolean(Console.ReadLine());
EuropeanCallOption Option = new EuropeanCallOption(timeToMaturity, strikePrice);
EulerSchemeForBSModel Euler = new EulerSchemeForBSModel();
StochasticAssetPrice Asset = new StochasticAssetPrice(Mu, sigma, spotPrice);
if (antithetic)
{
double[] s1 = Option.PricingByMCSim(Asset, Euler, numOfScenarios, timeSteps,
true);
Console.WriteLine("Option price estimated by Monte Carlo Simulation and " +
"antithetic variance reduction is:\n {0:#0.00} \n Standard error is:\n {1:#0.000} ",
s1[0], s1[1]);
}
else
{
double[] s2 = Option.PricingByMCSim(Asset, Euler, numOfScenarios, timeSteps, false);
Console.WriteLine("Option price estimated by Monte Carlo Simulation is:\n {0:#0.00} \n" +
"Standard error is:\n {1:#0.000} ", s2[0], s2[1]);
}
Console.ReadLine();
}
}
}