For Homework 3 of Computational Finance by P Dybvig
There are many new features in this application as well as many differences. My comments focus on the new parts. Please refer to earlier annotated versions for the parts that are similar to before.
Before going into details, here is a quick look at how the program is structured.
file FutOpStart.java: public class FutOpStart { public static void main(String[] args) { ...} --> This is where the standalone program program starts (with the main() method in FutOpStart). class FutOpFrame extends Frame { ...} --> This is the new window (or Frame) that is opened by FutOpStart. This frame reads data about futures and futures options prices from over the net, and volatility and other parameters from user inputs in TextFields. This information is sent to a computational engine of class FutOp to compute theoretical values. All are displayed in a user-friendly format. file FutOp.java: public class FutOp { ...} --> This is the computational engine the computes futures options prices. file ValHedge.java: public class ValHedge{ ...} --> This defines the type of object (which includes an option price and the delta for hedging) returned by method Eurcall of class FutOp.
FutOpStart.java defines two classes, FutOpStart and FutOpFrame. FutOpStart is a very simple class that has the main() method that is the starting point in a standalone program. Its function is to create and activate a FutOpFrame, an object that has the user interface. This object also reads files on the internet (to get futures and futures option prices) and creates and invokes the computational engine, a FutOp, of a class defined in FutOp.java. The object passed back by FutOp, a ValHedge, is an object containing two doubles, the computed value and hedge ratio of the futures option.
// // Binomial futures option pricing program // import java.awt.*; import java.net.*; import java.io.*; --> The library java.net.* contains network interface classes, and the library java.io.* contains input-output classes. Together, the two open web files and read their contents as character streams. Another use of the same libraries could read a continuous real-time data feed. public class FutOpStart { public static void main(String[] args) { --> Standard syntax for the required main() method in a standalone program. This is analogous to the constructor in an applet. FutOpFrame f1 = new FutOpFrame(); --> This creates a frame (a new window; standalone programs do not automatically have windows) of type FutOpFrame defined below. f1.setTitle("Futures Option Pricing Program"); --> For the top line in the outside of the window. f1.pack(); --> Make everything its optimal size and fit together. f1.show();}} --> Display the new window. class FutOpFrame extends Frame { --> As mentioned above, a Frame is similar to an applet but has its own window. In a standalone program, it also has much less restricted access to files locally and on the Web than an applet running in a Web browser. int i,j,k; FutOp c1; ValHedge x; double futuresP,strikeP,optionP; String maturity, inputLine, futuresPs; URL optionsPage, futuresPage; BufferedReader futin, optin; TextField r, sigma, nper; Label[] tableBody; --> An array of Labels so we will have names for all the cells in the table without having to make up separate names. Label north; public FutOpFrame() { --> The constructor of a FutOpFrame. setLayout(new BorderLayout()); setBackground(new Color(245,255,245)); --> Something new: a color constructed with 3 integer arguments, corresponding respectively to intensities of red, green, and blue, each on a scale from 0 to 255. All 245's would be a white less bright than standard white (all 255's). With the green slightly more intense than red and blue this gives a light pastel green. add("North",north = new Label( "Japanese Yen Futures Option Pricing Program: Call", Label.CENTER)); north.setForeground(Color.blue); Panel centr = new Panel(); centr.setLayout(new GridLayout(15,6)); centr.add(new Label("maturity",Label.CENTER)); centr.add(new Label("futuresP",Label.CENTER)); centr.add(new Label("strikeP",Label.CENTER)); centr.add(new Label("call:mkt",Label.CENTER)); centr.add(new Label("call:model",Label.CENTER)); centr.add(new Label("delta",Label.CENTER)); tableBody = new Label[84]; --> There are 84 labels, one for each cell in the 6 columns x 14 remaining rows. for(i=0,k=0;i<14;i++) { for(j=0;j<6;j++,k++) { centr.add(tableBody[k]= new Label("********",Label.CENTER));}} --> Note that commas separate statements in a sequence that are treated as one in each for statement. In the outer loop, we set both i and k equal to zero at the start, and in the inner loop, we increment both j and k at the end of each time through. In this case, i is the counter for the loop as in cases we have seen before, and k tells us which Label in tableBody we are going to place next. We could eliminate reference to k and simply replace it in the centr.add statement by j + 6*i; doing the one or the other is a matter of taste. add("Center",centr); Panel south = new Panel(); south.setLayout(new GridLayout(1,6)); south.add(new Label("sigma (%) =")); south.add(sigma = new TextField("17",8)); south.add(new Label("r (%) =")); south.add(r = new TextField("5",8)); south.add(new Label("#periods =")); south.add(nper = new TextField("100",8)); add("South",south); c1 = new FutOp(); --> creating the computational engine recalc();} void recalc() { --> As in previous homeworks, the method recalc does the calculations. In this case, we have it read in the futures and futures options data each time it recalculates. With the current (static) Web pages we are reading from the course page, it might be more efficient to save that information. However, in some (future) version of this that points directly at the CME site, we would probably want to read in the data each time to get the most current prices (or, perhaps, leave this at the user's option). // // Read in futures price for near contract // try { --> The try { ... } catch(.,.) { ... } catch(.,.) { ... } is for capturing and addressing input errors. The first type of error that is caught is MalformedURLException which would be an error in the string argument to URL. The second type of error is IOException, which would be an error reading from the URL. futuresPage = new URL( "http://dybfin.wustl.edu/teaching/compufinj/homework/3/r_jy.html"); --> Sets up a URL. It is not quite ready to read at this point. /* futuresPage = new URL( "http://www.cme.com/cgi-bin/prices.cgi?prices/r_jy.html"); */ --> This commented-out URL is for the original CME web page with the prices. futin = new BufferedReader(new InputStreamReader( futuresPage.openStream())); --> This defines futin as the input buffer/reader for the URL futuresPage. Now the file can be read as a character stream coming into this buffer. while(!(futin.readLine().startsWith("STRIKE"))) {}; --> The method readLine in futin reads one line from the buffer and returns it as a String. The method startWith compares the first characters with the String argument "STRIKE", which is the start of the line giving the table header in the .html file for the Web page. The ! is a not operator; this says to keep executing the body of the while loop until we get to the header line. The body of the loop is empty, since all the work (mainly reading another line) is done in the test part. Note that we are not saving the character strings that are returned by these readLine()'s. futin.readLine(); // skip one line after table header inputLine = futin.readLine(); // read the near futures line --> We read and discard one more line (this line in the r_jy.html file contains only a formatting command), and we save the following line in the string variable inputLine. /* System.out.println(inputLine); */ --> This commented-out print command was used in the debugging of the program. Normally, I would comment this out, but I thought you might like to see what kind of diagnostics I used to make sure I was getting the right thing as I wrote this. This is like the scaffolding used when building a building. Leaving it there probably does no real damage but is aesthetically unpleasant. In that case, as in this, leaving it up may give insight to the construction process. maturity = inputLine.substring(0,5); // maturity date string --> The substring(0,5) gives the string starting with character 0 (not 1, just like arrays) and ending ->just before<- character 5. Therefore, this is a string of length 5 at the start of the line, which is the representation of the maturity (e.g. MAR99 for March, 1999). /* System.out.println(maturity); */ futuresPs = inputLine.substring(33,39); --> this is the substring representing the futures price. futuresP = Double.valueOf(futuresPs).doubleValue(); --> and the corresponding number /* System.out.println(futuresP); */ } catch(MalformedURLException e) { System.err.println("Error: bad URL"); System.exit(0); } catch(IOException e) { System.err.println( "Error: Trouble opening or reading futuresPage"); System.exit(0);} --> The error handling code, completing the earlier try {. In each case, we write a brief message to error output and exit. // // Set parameters for the option pricing engine. Hardwiring days to // maturity is a cheat! See the Challenger for more information. // c1.newPars((double) 28.0/365.25, (int) text2double(nper), (double)text2double(r)/100.0,(double)text2double(sigma)/100.0); --> Most of these parameters are provided by the user. Time to maturity is set (!) at 28 converted to an annual number. This would not be acceptable if we were to use this for the CME site on different days. The Challenger is to fix this and compute the number of days to maturity from information in the files we are reading. // // Read in corresponding futures options prices, compute theoretical // value and delta, and print // try { optionsPage = new URL( "http://dybfin.wustl.edu/teaching/compufinj/homework/3/r_oj.html"); /* optionsPage = new URL( "http://www.cme.com/cgi-bin/prices.cgi?prices/r_oj.html"); */ optin = new BufferedReader( new InputStreamReader(optionsPage.openStream())); --> similar to the code for reading the futures Web page while(!(optin.readLine().startsWith("OJ "+maturity))) {}; --> find the subheading corresponding to this maturity (OJ means option Japan, I think, and is the symbol for the futures options) i=0;k=0; while( ((inputLine = optin.readLine()) != null) && ((inputLine.length()>40) && (k<14)) ) { --> a null input line would indicate end-of-file. && indicates logical and, with the second argument executed only if the first is true (helps to avoid errors). If the inputLine is shorter than 40, then we are done with the call options at this maturity. If k is 14 or larger, we have no more lines to use in the table in the frame. Passing these tests, we are still in the table of call prices at this maturity. /* System.out.println(inputLine); System.out.println("\"" + inputLine.substring(35,39) + "\""); System.out.println("\"" + inputLine.substring(32,39) + "\""); System.out.println("\"" + inputLine.substring(39,40) + "\""); */ --> yet more debugging print statements, commented out as a group. try { optionP = Double.valueOf( inputLine.substring(32,40)).doubleValue(); strikeP = Double.valueOf( inputLine.substring(0,7)).doubleValue(); --> error-handling is doing part of our work here! if this is a maturity that does not have proper option and strike prices, the catch(NumberFormatException e) below will skip over this line in the options file. Note that its consequences are given by {} which means do nothing (as opposed to other errors above for which we print out an error message and abort the program. if(Math.abs(futuresP-strikeP)<futuresP/12.0) { --> only include strikes that are not unreasonably far away from par (since those are probably not true trading prices) (tableBody[6*k]).setText(maturity); (tableBody[6*k+1]).setText(futuresPs); (tableBody[6*k+2]).setText(inputLine.substring(0,4)); (tableBody[6*k+3]).setText(inputLine.substring(32,39)); x = c1.eurcall(futuresP,strikeP); /* System.out.println(futuresP); System.out.println(strikeP); System.out.println(x.value/100.0); System.out.println(x.delta); */ (tableBody[6*k+4]).setText(String.valueOf((float) Math.floor(1000.0 *(x.value/100.0) + 0.5)/1000.0)); --> rounding to three decimal places. arguably we should use the formatting commands that include trailing zeros as well. We divide by 100 because the file quotes futures in cents per 100 yen but options in dollars per 100 yen. (tableBody[6*k+5]).setText(String.valueOf((float) Math.floor(1000.0 *(x.delta) + 0.5)/1000.0)); (tableBody[6*k+3]).setForeground(Color.black); --> some colors are pre-defined. this doesn't change anything the first time through by may be important for recalculations. if(optionP > 1.05 *(x.value/100.0)) (tableBody[6*k+3]).setForeground(Color.red); --> label the quote with red if the market quote is more than 5 percent above the theoretical value if(optionP < .95 * (x.value/100.0)) (tableBody[6*k+3]).setForeground(Color.green); --> label the quote with green if the market quote is more than 5 percent below the theoretical value k++;} --> move the pointer to the next line in the table } catch(NumberFormatException e) {}} optin.close(); } catch(MalformedURLException e) { System.err.println("Error: bad URL"); System.exit(0); } catch(IOException e) { System.err.println("Error: Trouble opening optionsPage"); System.exit(0);}} public boolean handleEvent(Event event) { if(event.id == Event.WINDOW_DESTROY) { System.exit(0);} --> kill the application if the user kills the window return super.handleEvent(event);} double text2double(TextField tf) { return Double.valueOf(tf.getText()).doubleValue();} public boolean action(Event ev, Object arg) { if(ev.target instanceof TextField) { recalc(); return true;} return false;}}
// // Binomial futures option pricing model: computational engine // public class FutOp { int nper; double tinc,disc,up,down,prcup,prcdn; double val[]; public FutOp() {} public void newPars(double ttm,int npers,double r,double sigma) { nper = npers; tinc = ttm/(double) nper; disc = Math.exp(-r * tinc); up = 1.0 + sigma * Math.sqrt(tinc); down = 1.0 - sigma * Math.sqrt(tinc); --> This is the main difference between this computational engine and the equity option engine: since futures require no initial investment there is no interest rate in the mean return under the risk-neutral probabilities. prcup = 0.5*disc; prcdn = 0.5*disc; val = new double[npers+1];} public ValHedge eurcall(double f0,double X) { int i,j; double futprice; ValHedge x1 = new ValHedge(); // initialize terminal payoffs // i is the number of up moves over the whole life for(i=0;i<=nper;i++) { futprice = f0 * Math.pow(up,(double) i) * Math.pow(down,(double) (nper-i)); val[i] = Math.max(futprice - X,0.0);} // compute prices back through the tree // j+1 is the number of periods from the end // i is the number of up moves from the start for(j=0;j<nper-1;j++) { for(i=0;i<nper-j;i++) { val[i] = prcdn * val[i] + prcup * val[i+1];}} x1.value = prcdn * val[0] + prcup * val[1]; x1.delta = (val[1]-val[0]) / (f0*(up-down)); --> Here we compute the hedge ratio in the normal way as the ratio of value in up minus down for the hedged asset and the underlying return(x1);}}
This simple class just puts two named double numbers in an object. This just gives us the names. We could have ValHedge.Eurcall return a double[2] instead, but then the calling routine might look more obscure. It is a matter of taste whether this sort of thing is worth the effort.
public class ValHedge { public double value; public double delta; public ValHedge() {}}