Annotated versions of AssetAlloc.html, AssetAlloc.java, and BasicLinePlot.java

For Homework 4 of Computational Finance by P Dybvig

This is the first homework with a simulation and the first with a plot. As before, I am concentrating on the new parts.

Quick overview

Before going into details, here is a quick look at how the program is structured.

file AssetAlloc.html (similar to homeworks 0-2, calls the applet)

file AssetAlloc.java:

public class AssetAlloc extends Applet {
  ...}

--> This creates a button in the browser you press to get a
    new Frame (window) containing the simulation and plot

class ValuePlotFrame extends Frame {
  ...}

--> This creates the new Frame.  It contains a plot (object of
    type BasicLinePlot) at the top and input cells at the bottom.

file BasicLinePlot.java:

public class BasicLinePlot extends Canvas {
  ...}

--> This relatively primitive plotting routine I have written is
just good enough to make reasonably professional line plots.

AssetAlloc.html

<HTML>
<HEAD>
<TITLE>Asset Allocation Simulation</TITLE>
</HEAD>
<BODY>
<APPLET CODE=AssetAlloc.class WIDTH=300 HEIGHT=50>
</APPLET>
</BODY>
</HTML>
--> This is very similar to the .html files used in homeworks 0-3 and does not require special comment.

AssetAlloc.java

//
// Asset Allocation Applet
//
// This applet simulates asset allocation between risky and riskless
// assets.  The horizon is fixed at 10 years and initial wealth is
// normalized to be 100.  The interest rate is constant and stock
// returns are i.i.d. over time.
//

import java.applet.*;
import java.awt.*;

public class AssetAlloc extends Applet {
  ValuePlotFrame valuePlotFrame;
  Button startASimu;
    public AssetAlloc() {
      setLayout(new GridLayout(1,1));
      add(startASimu = new Button("Start a Simulation"));

--> To start with, all this applet shows is a single Button...

      valuePlotFrame = new ValuePlotFrame();
      valuePlotFrame.setTitle("Asset Allocation Simulation");
      valuePlotFrame.pack();
      valuePlotFrame.resize(500,400);

--> but there is set up a new ValuePlotFrame that is not
    yet showing.

  }
    public boolean action(Event e, Object arg) {
      if(e.target == startASimu) {
        valuePlotFrame.reset();

--> and it is started (or restarted) and displayed if the
    button is pressed.  Note that valuePlotFrame.show()
    is called by valuePlotFrame.startSimu which is called
    by valuePlotFrame.reset.  Howver, valuePlotFrame.show()
    is not called by valuePlotFrame() or by AssetAlloc().
    This is why the new Frame shows only after the Button
    startASimu is pressed.

        return true;}
      return false;}}

class ValuePlotFrame extends Frame {
  BasicLinePlot blp;
  TextField r,mu,sigma,inrisky;
  Button newRandomDraws,resetInputs;
  double ttm = 10.0,ir;
  double[] times,values;
  int nper = 1200;
  int i;
  AssetAllocEngine vroom;

--> vroom is the computational engine for the simulation.

  double[] xgrid,ygrid;
  public ValuePlotFrame() {
    setLayout(new BorderLayout());
    blp = new BasicLinePlot();

--> blp is the plot at the top of the Applet.

    add("Center",blp);
    Panel inputs = new Panel();
    inputs.setLayout(new GridLayout(6,2));
      inputs.add(new Label("interest rate (%/yr)"));
      inputs.add(r = new TextField("5",10));
      inputs.add(new Label("mean stock return (%/yr)"));
      inputs.add(mu = new TextField("15",10));
      inputs.add(new Label("std dev of stock return (%/yr)"));
      inputs.add(sigma = new TextField("30",10));
      inputs.add(new Label("allocation to risky asset (%)"));
      inputs.add(inrisky = new TextField("70",10));
      inputs.add(newRandomDraws = new Button("Simulate again"));
      inputs.add(new Label(""));
      inputs.add(resetInputs = new Button(
        "Reset and simulate again"));
    add("South",inputs);

--> The various input cells and buttons should be familiar now.
    If in doubt, compare this with the Applet itself.

    vroom = new AssetAllocEngine(nper);

--> nper is the number of subperiods in the simulation.
    Currently nper is hardwired above to be 1200.

    times = new double[nper + 1];
    for(i=0;i<=nper;i++) times[i] = ((double) i) * ttm /
      ((double) nper);

--> This makes times an evenly spaced array of nper+1 times from
    0 to ttm.  Currently ttm is hardwired to be 10 years.

    xgrid = new double[6];
    for(i=0;i<6;i++) xgrid[i] = ((double) i) * 2.0;

--> This gives equally spaced x (time) values for the plot.
    A higher level plot program would choose these for you.

    ygrid = new double[6];
    for(i=0;i<6;i++) ygrid[i] = ((double) i) * 100.0;}

--> This gives equally spaced y (portfolio value) values for
    the plot.  A higher level plot program would choose these
    for you.

  public void startSimu() {

--> This method starts a new simulation.  It is called either
    by reset() or by action(...).

    vroom.newPars(text2double(r)/100.0,
        text2double(mu)/100.0,text2double(sigma)/100.0,ttm);

--> reading the new parameters and sending them to vroom

    ir = text2double(inrisky)/100.0;

--> reading the proportion in the risky asset to determine
    this proportion and initial positions to send to vroom.

    values = vroom.fixProps(ir,ir * 100.0,(1.0-ir)*100.0);

--> Have vroom compute the vector of portfolio values.  The
    first argument is the proportion in the risky stock, the
    second is the initial holding of risky stock, and the
    third is the initial holding of the riskless asset.

    blp.newXY(times,values,xgrid,ygrid,
      "Simulated Wealth, Fixed Proportions Startegy: 0-10 years out"
      );

--> Tell blp the new values to be plotted.  The arguments
    are the array of times, the array of portfolio values,
    the values for axis labels on the horizontal axis, the
    values for axis labels on the vertical axis, and the
    title to go across the top of the graph.

    show();}

--> Display this window and update everything as needed.

  public void reset() {
    r.setText("5");
    mu.setText("15");
    sigma.setText("30");
    inrisky.setText("70");
    startSimu();}

--> This function sets all the TextFields to their defaults
    and starts a new simulation.  This is called by
    AssetAlloc.action() whenever we press the button in
    the original Applet or the reset button in the new
    window.

  public boolean action(Event e, Object arg) {
    if(e.target == newRandomDraws) {
      startSimu();
      return true;}

--> Call startSimu if the user asks for new random draws...

    if(e.target == resetInputs) {
      reset();
      return true;}

--> ...or reset if the user asks to resetInputs.

    return false;}
  public boolean handleEvent(Event event) {
    if(event.id == Event.WINDOW_DESTROY) {
      dispose();}
    return super.handleEvent(event);}

--> This makes it so the window is killed if that is what
    you ask (by clicking in the corner).

  double text2double(TextField tf) {
    return Double.valueOf(tf.getText()).doubleValue();}}

class AssetAllocEngine {
  int nper;
  double tinc,r1per,mean1per,std1persqrt12;
  double[] values;
  public AssetAllocEngine(int nper) {
    values = new double[nper+1];
    this.nper = nper;}

--> The constructor creates an array for storing values and
    saves the value of nper that is passed.

  public void newPars(double r,double mu,double sigma,
    double ttm) {
    tinc = ttm/((double) nper);
    r1per = 1.0 + r * tinc;
    mean1per = 1.0 + mu * tinc;
    std1persqrt12 = sigma * Math.sqrt(12.0 * tinc);}

--> newPars takes annual numbers for the interest rate r,
    mean return mu, standard deviation, and time-to-maturity
    ttm, and converts them all per-period values.

  public double[] fixProps(double inrisky,double initstock,
    double initcash) {

--> fixProps does the basic simulation, calling on stockTotRet
    for each random draw.

    double stock,cash,wealth,stockret;
    int i;
    stock = initstock;
    cash = initcash;

--> Initial stock and cash positions are passed by the
    calling program.

    for(i=0,values[0]=stock + cash;i<=nper;i++) {

--> The int i indexes the period, initially 0.  The value
    to start is the initial stock position plus the initial
    cash position.  We go through the loop for i running
    from 1 through nper.

      wealth = stock + cash;
      stock = wealth * inrisky;
      cash = wealth - stock;

--> At the start of each period we rebalance the stock
    position keeping wealth fixed.  (Note: This is the
    rebalancing that would be done infrequently.  With
    transaction costs, wealth will be reduced by the
    amount of the transaction cost.)

      stockret = stockTotRet();
      stock *= stockret;
      cash *= r1per;

--> The stock grows by the random factor stockret while
    cash grows by the nonrandom factor r1per.

      values[i] = cash + stock;}

--> Before continuing, we store the portfolio value.

    return values;}

--> Once the loop is done, return the array of values
    to the calling program.

  double stockTotRet() {
    return mean1per + std1persqrt12 * (Math.random()-0.5);}}

--> The random stock return is made by matching the right
    mean and standard deviation using a linear function of
    Math.random().  Math.random() has a mean of 0.5 and a
    standard deviation of sqrt(1/12):  we subtract 0.5 to
    get the mean right and we multiply by std1per times
    sqrt(12) to get the standard deviation right.

BasicLinePlot.java

Unless you solve an Extra-for-experts or Challenger question, you will not to need anything in the plotting routine, and you may want to skip learning what is in here. Annotation is provided for the curious and ambitious.

In the plot, we work with two sorts of coordinants. There are user coordinants, which are the actual time and portfolio value numbers. Then there are also pixel ("picture element") coordinants within the Canvas on which things are plotted. These pixel coordinants are ints, since a fractional pixel does not make sense. Pixel coordinants start at zero in the upper left corner of the plot. To make it easier for me to read the code, I make coordinants in the user system start with x (horizontal position) or y (vertical position), while I put i in front for the ints that are pixel coordinants. The functions x2ix and y2iy convert from user coordinants to pixel coordinants. (This conversion is messy but not really difficult.)

import java.awt.*;
      
public class BasicLinePlot extends Canvas {

--> The Canvas class is used for painting graphics on.

  double[] x,y,xgrid,ygrid;
  int i,nper;
  int ixleft,ixright,iytop,iybottom,ih,iw,ihalftic,iRise,
    fmHeight;
  double xleft,xright,ytop,ybottom;
  double lmarg = 0.10,rmarg = 0.05,tmarg = 0.10,bmarg = 0.10,
    halftic = 0.0035;
  double ixintercept,ixslope,iyintercept,iyslope;
  String thingy;
  public BasicLinePlot() {}

--> The constructor has no work to do!

  String mainTitle;
  public void newXY(double[] x,double[] y,double[] xgrid,
    double[] ygrid,String mainTitle) {

--> The method newXY is called to plot new lines
    and erase any already there.

    if(x.length != y.length) {
      System.err.println(
        "Error: x and y must have the same length");
      System.exit(-1);}

--> Checking for compatible x and y arrays...  We could
    also check for length > 1 if we wanted...

    this.x = new double[x.length];
    this.y = new double[x.length];
    for(i=0;i<x.length;i++) {
      this.x[i] = x[i];
      this.y[i] = y[i];}

--> creating new arrays and storing the values that were
    passed to us

    this.xgrid = new double[xgrid.length];
    this.ygrid = new double[ygrid.length];
    for(i=0;i<xgrid.length;i++)
      this.xgrid[i] = xgrid[i];
    for(i=0;i<ygrid.length;i++)
      this.ygrid[i] = ygrid[i];

--> storing too the numbers for plot labels and...

    this.mainTitle = mainTitle;

--> ... the main title

    repaint();}

--> finally call repaint() which does all the work

  public void paint(Graphics g) {
      if(xgrid != null) {

--> note if xgrid has not been assigned, there is nothing
    yet to plot!

      xleft = xgrid[0];
      xright = xgrid[xgrid.length - 1];
      ybottom = ygrid[0];
      ytop = ygrid[ygrid.length - 1];

--> finding extreme points (xleft, xright, ybottom, ytop)
    in user coordinants of the body of the plot

  
      Rectangle r = bounds();
      ih = r.height;
      iw = r.width;

--> finding the height and width in pixels of the whole Canvas.

      iytop = (int) (tmarg * ((double) ih));
      iybottom = (int) ((1.0-bmarg) * ((double) ih));
      ixleft = (int) (lmarg * ((double) iw));
      ixright = (int) ((1.0-rmarg) * ((double) iw));

--> converting limits of body of the graph to pixel units. the
    relative margin sides are hardwired above.

      ihalftic = (int) (halftic * ((double) iw));

--> size of a tic mark is computed from the width.  the fraction
    is hardwired above.

  
      ixslope = ((double) (ixright - ixleft))/(xright - xleft);
      ixintercept = 0.5 + (((double) ixright) * xleft - 
        ((double) ixleft) * xright)/(xleft - xright);
      iyslope = ((double) (iybottom - iytop))/(ybottom - ytop);
      iyintercept = 0.5 + (((double) iybottom) * ytop - 
        ((double) iytop) * ybottom)/(ytop - ybottom);

--> slope and intercept for conversion of user coordinants
    pixel coordinants (to be performed by x2ix and y2iy below)

//
//  white background
//
      g.setColor(Color.white);
      g.fillRect(0,0,iw,ih);
  
//
//  draw the axes
//
      g.setColor(Color.black);
      g.drawLine(ixleft,iytop,ixleft,iybottom);
      g.drawLine(ixleft,iybottom,ixright,iybottom);
  
//
//  add tick marks
//
      g.setColor(Color.black);
      for(i=0;i<ygrid.length;i++)
        g.drawLine(ixleft-ihalftic,y2iy(ygrid[i]),
          ixleft+ihalftic,y2iy(ygrid[i]));
      for(i=0;i<xgrid.length;i++)
        g.drawLine(x2ix(xgrid[i]),iybottom-ihalftic,
          x2ix(xgrid[i]),iybottom+ihalftic);
  
//
//  add axis numbers
//
      g.setColor(Color.black);
      FontMetrics fm = g.getFontMetrics();
      iRise = fm.getAscent()/2;
      fmHeight = fm.getHeight();

--> The FontMetrics object has information about height
    of the font, normal spacing from one line to the
    next, etc., which help us to place the axis numbers

      for(i=0;i<ygrid.length;i++) {
        thingy = Double.toString(ygrid[i]);

--> thingy = one axis label, as a String

        g.drawString(thingy,ixleft - ihalftic
          -fm.stringWidth(thingy),
          y2iy(ygrid[i]) + iRise);}

--> fm.stringWidth(thingy) puts the string far enough to the
    left of the axis.

      for(i=0;i<xgrid.length;i++) {
        thingy = Double.toString(xgrid[i]);
        g.drawString(thingy,x2ix(xgrid[i])
          - fm.stringWidth(thingy)/2,
          iybottom + ihalftic + fmHeight);}

--> here fm.stringWidth(thingy)/2 centers the string below
    the corresponding tic mark.

  
//
//  plot lines
//
      g.setColor(Color.blue);
      for(i=1;i<x.length;i++)
        g.drawLine(x2ix(x[i-1]),y2iy(y[i-1]),
          x2ix(x[i]),y2iy(y[i]));

--> ironically, this is just about the easiest part :-)

  
//
//  add the mainTitle
//
      g.drawString(mainTitle,(ixright + ixleft)/2 - 
        fm.stringWidth(mainTitle)/2,fmHeight);}}
  int x2ix(double x) {return (int) Math.floor(
      ixintercept + ixslope * x);}
  int y2iy(double y) {return (int) Math.floor(
      iyintercept + iyslope * y);}}

--> This is the conversion to pixel coordinants.