Homework 5
Stochastic Volatility Option Pricing Simulation

Computational Finance

Copyright © Philip H. Dybvig 1996,1999

Scenario You are working as a consultant to an accounting firm. One of your charges is the valuation of employee stock options. The question has been raised of the effect of stochastic volatility on valuation of the options granted to officers of the company.

Action You will be given a program that uses simulations to evaluate options when the volatility of the stock is random. You will use the program to evaluate some call options, and you will perform a statistical analysis of the resultant values to form an opinion of the accuracy of the simulation.

Concept Simulation is a powerful tool for computing option values. When a one-dimensional binomial model can be used, it is much quicker, but when there are multiple sources of uncertainty, simulation is easier to program and more flexible provided there are not any American features. Simulation is the only practical technique for many hard problems in practice, for example, the valuation of mortgage-backed securities.

Instructions

  1. Put the program files (SVPrice.java, SVPriceEngine.java, and SVPrice.html) in a directory on your machine where you want to work. Rename them as necessary. Compile the programs.
  2. In addition to displaying the table giving the option prices, have the program plot this information. You can use the BasicLinePlot class defined in BasicLinePlot.java.
  3. You may have noted that the answers are not very precise unless you use many many simulations.
    1. Compute the standard error of one of the option prices. You can do this the high-tech way (by putting a column for the standard error in the table) or by hand (by entering say 20 draws in a calculator or spreadsheet). The standard error of the simulated option price is the standard deviation of the individual draws divided by the square root of the number of simulations.
    2. (thought question) Given that the typical error is proportional to the reciprocal of the square root of the number of simulations, how many simulations should be required for pricing to the nearest penny? Do you think more or fewer draws are needed to get penny significance on the difference between two prices?
  4. (optional) Reorganize the program to permit comparison of two option prices with the same underlying random draws but different parameter values. This is very useful for isolating the effect of a change in parameters. Use this analysis to say something useful about the importance of stochastic volatility. (There are several different ways of doing this. The important thing is that when the random draw for the stock price is generated, the random variable moving the stock price is used for both models. The current stocktotret() method might be modified to return two returns, one for each model. To do this, the parameters for each model would have to be saved separately in variables of the class SVPriceEngine.)
Extra for Experts
  1. Modify the program to allow the random part of the stock price change to be correlated with the random part of the change in volatility.
Challenger
  1. Estimate a GARCH or ARCH model for volatility, using actual data from a common stock or common stock index. (This is probably meaningful to you only if you have taken Advanced Econometrics.) Modify the java applet to use the model and parameters you estimate.
Exhibit A: SVPrice.html
<HTML>
<HEAD>
<TITLE>Asset Allocation Simulation</TITLE>
</HEAD>
<BODY>
<P>
<APPLET CODE=SVPrice.class WIDTH=300 HEIGHT=50>
</APPLET>
</p>
<p> WARNING:  The number of simulations equals 100 for
testing only!  It takes many more draws (perhaps 100,000)
to obtain an accurate value.</p>
</BODY>
</HTML>
Exhibit B: SVPrice.java
import java.applet.*;
import java.awt.*;

public class SVPrice extends Applet {
  SVPriceFrame svpf;
  Button startASimu;
    public SVPrice() {
      setLayout(new GridLayout(1,1));
      add(startASimu = new Button("Compute Option Prices Now"));
      svpf = new SVPriceFrame();
      svpf.setTitle("Stochastic Volatility Option Pricing Simulation");
      svpf.pack();
      svpf.resize(500,400);}
    public boolean action(Event e, Object arg) {
      if(e.target == startASimu) {
        svpf.reset();
        return true;}
      return false;}}

class SVPriceFrame extends Frame {
  double[] initSigmas;
  Label[] opvals; 
  TextField r,sigmabar,kappa,sigmasigma,ttm,s0,strike,nsimu,nper;
  Button newRandomDraws,resetInputs;
  SVPriceEngine vroom;
  int i;
  public SVPriceFrame() {
    setLayout(new BorderLayout());
    Panel outputs = new Panel();
      outputs.setLayout(new GridLayout(6,2));
      outputs.add(new Label("Initial Sigma"));
      outputs.add(new Label("Call Price"));
      opvals = new Label[5];
      initSigmas = new double[5];
      for(i=0;i<5;i++) {
        initSigmas[i] = .3 + .05*((double) i);
        outputs.add(new Label(Double.toString(initSigmas[i])));
        outputs.add(opvals[i] = new Label("**********"));
        opvals[i].setForeground(Color.yellow);}
    add("North",outputs);
    Panel inputs = new Panel();
      inputs.setLayout(new GridLayout(8,3));
      inputs.add(new Label("stock price")); 
      inputs.add(new Label("strike price")); 
      inputs.add(new Label("number of simus"));   
      inputs.add(s0=new TextField("50",10));
      inputs.add(strike=new TextField("60",10));  
      inputs.add(nsimu=new TextField("100",10));
      inputs.add(new Label("interest (%/yr)"));
      inputs.add(new Label("avg sigma (%/yr)"));
      inputs.add(new Label("kappa (%/yr)"));
      inputs.add(r=new TextField("5",10));
      inputs.add(sigmabar=new TextField("40",10));
      inputs.add(kappa=new TextField("3.0",10));
      inputs.add(new Label("vol of vol (%/yr)"));
      inputs.add(new Label("time to mat (yr)"));
      inputs.add(new Label("# subperiods"));
      inputs.add(sigmasigma=new TextField("10",10));
      inputs.add(ttm=new TextField("5",10));
      inputs.add(nper=new TextField("25",10));
      inputs.add(newRandomDraws = new Button("Compute Prices"));
      inputs.add(new Label(""));
      inputs.add(new Label(""));
      inputs.add(resetInputs = new Button("Reset Parameters"));
    add("South",inputs);
    vroom = new SVPriceEngine();}
  public void startSimu() {
    newRandomDraws.setLabel("-- Computing --");
    newRandomDraws.setForeground(Color.red);
    for(i=0;i<5;i++) {
      opvals[i].setForeground(Color.yellow);}
    for(i=0;i<5;i++) {
      vroom.newPars(text2double(ttm),(int) text2double(nper),
        text2double(r)/100.0,initSigmas[i],text2double(sigmabar)/100.0,
        text2double(kappa)/100.0,text2double(sigmasigma)/100.0);
      opvals[i].setText(String.valueOf((float)
        vroom.eurCall(text2double(s0),text2double(strike),
          (long) text2double(nsimu))));
      opvals[i].setForeground(Color.black);}
    newRandomDraws.setLabel("Compute Prices");
    newRandomDraws.setForeground(Color.black);}
  public void reset() {
    r.setText("5");
    sigmabar.setText("40");
    kappa.setText("3.0");
    sigmasigma.setText("10");
    ttm.setText("5");
    nper.setText("25");
    s0.setText("50");
    strike.setText("60");
    nsimu.setText("100");
    show();}
  public boolean action(Event e, Object arg) {
    if(e.target == newRandomDraws) {
      startSimu();
      return true;}
    if(e.target == resetInputs) {
      reset();
      return true;}
    return false;}
  public boolean handleEvent(Event event) {
    if(event.id == Event.WINDOW_DESTROY) {
      dispose();}
    return super.handleEvent(event);}

  double text2double(TextField tf) {
    return Double.valueOf(tf.getText()).doubleValue();}}
Exhibit B: SVPrice.java
 
public class SVPriceEngine {
  double npers, tinc, r1per, sigma0, meansigma, sigmasigma, kappa,
    c0, c1, c2, c3, c4;
  double stockP, sigma;

  public SVPriceEngine(){}

  public void newPars(double ttm,int nper,double r,double initstd,
    double meanstd, double k, double sigstd) {
    npers = nper;
    tinc = ttm/(double) nper;
    r1per = 1.0 + r*tinc;
    sigma0 = initstd;
    meansigma = meanstd;
    sigmasigma = sigstd;
    kappa = k;
    c0 = kappa * tinc * meansigma;
    c1 = 1.0 - kappa * tinc;
    c2 = 1.0 - sigmasigma * Math.sqrt(tinc)*0.5*Math.sqrt((double) 12);
    c3 = sigmasigma * Math.sqrt(tinc) * Math.sqrt((double) 12);
    c4 = Math.sqrt(tinc)*Math.sqrt((double) 12);}

public double eurCall(double stock,double strike,long nsimu) {
  long i,n;
  double x;
  x=0.0;
  for(n=0;n<nsimu;n++) {
    stockP = stock;
    sigma = sigma0;
    for(i=0;i<npers;i++) {
      stockP *= stocktotret();
      }
    x += Math.max(stockP-strike,0);}
  return(x/((double) nsimu * Math.pow(r1per,(double) npers)));}

double stocktotret() {
  double stockret;
//
//  The following straightforward computations are algebraically
//  the same as the ones used below but are much slower because
//  many more calculations are performed in each pass through the
//  nested for loops in eurCall.
//
//  stockret = r1per + sigma * Math.sqrt(tinc) *
//    (Math.random()-0.5) * Math.sqrt((double) 12);
//  sigma = (kappa*tinc * meansigma + (1.0 - kappa * tinc) * sigma) *
//    (1 + sigmasigma * Math.sqrt(tinc) * 
//    (Math.random()-0.5) * Math.sqrt((double) 12));
//  return(stockret);}
//
  stockret = r1per + sigma * c4 * (Math.random() - 0.5);
  sigma = (c0 + c1 * sigma) * (c2 + c3 * Math.random());
  return(stockret);}}