/* * ------------------------------------------------------------------------- * $Id: Portfolio.java,v 1.9 2005/12/30 16:38:31 estewart Exp $ * ------------------------------------------------------------------------- * Copyright (c) 2004 Visual Numerics Inc. All Rights Reserved. * * This software is confidential information which is proprietary to * and a trade secret of Visual Numerics, Inc. Use, duplication or * disclosure is subject to the terms of an appropriate license * agreement. * * VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL * NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR * ITS DERIVATIVES. *-------------------------------------------------------------------------- */ package com.imsl.demo.portfolio; import com.imsl.chart.*; import com.imsl.demo.gallery.Describe; import java.awt.Dimension; /** * * @author ed * @created February 26, 2004 */ public class Portfolio extends com.imsl.chart.JFrameChart implements java.awt.event.MouseMotionListener, java.awt.event.MouseListener { private double[][] solution; private double riskFreeRate = 4.0; private Integer old1min, old2min, old3min, old4min; private Integer old1max, old2max, old3max, old4max; private int optimalIndex, oldIndex; private Chart chart; private AxisXY axis; private Pie pie, miniPie; private Data mousePoint; private ToolTip pointTip; private String[] names = {"A1","B2","C3","D4"}; private final java.awt.Color darkGreen = new java.awt.Color(47,129,47); /** Creates new form Portfolio */ public Portfolio(boolean exitOnClose) { if (!exitOnClose) { // remove the WindowListener, installed by JFrameChart, that // exits the application when the window is closed. Object l[] = getListeners(java.awt.event.WindowListener.class); for (int k = 0; k < l.length; k++) { removeWindowListener((java.awt.event.WindowListener)l[k]); } } Describe des = new Describe(this, "/com/imsl/demo/portfolio/Portfolio.html"); des.show(); Dimension ds = des.getSize(); Dimension ss = getToolkit().getScreenSize(); int h = Math.min(ss.width/2, ss.height-ds.height-32); int w = (int)(h/0.8); setSize(w, h); setLocation(ss.width-ds.width, ds.height); setTitle("Portfolio Optimization"); initComponents(); setupChart(); doAll(); } // do everything - find the efficient frontier, find the portfolio, // clear and replot the chart, and update the solution text private void doAll() { if (!checkSpinners()) return; runEfficient(); optimalIndex = findOptimalIndex(); clearChart(); plotEfficientFrontier(solution[0],solution[1]); solutionText.setText(buildHTMLText()); } // configure the axes on the chart; add a MouseMotionListener private void setupChart() { chart = getChart(); axis = new AxisXY(chart); axis.getAxisX().getAxisTitle().setTitle("Risk (Return Volatility), %"); axis.getAxisY().getAxisTitle().setTitle("Fractional Return, %"); axis.getAxisX().getAxisTitle().setFontStyle(java.awt.Font.BOLD); axis.getAxisY().getAxisTitle().setFontStyle(java.awt.Font.BOLD); axis.getAxisX().setTextFormat(new java.text.DecimalFormat("##")); axis.getAxisY().setTextFormat(new java.text.DecimalFormat("##")); axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisX().setWindow(0.0,20.0); axis.getAxisY().setWindow(0.0,30.0); getPanel().addMouseMotionListener(this); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ private void initComponents() {//GEN-BEGIN:initComponents jLabel2 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); jPanelNorth = new javax.swing.JPanel(); jPanelSpinners = new javax.swing.JPanel(); jPanelLab = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); jPanel2 = new javax.swing.JPanel(); jCheckBox1 = new javax.swing.JCheckBox(); minSpinner1 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(0, 0, 100, 1)); maxSpinner1 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(100, 0, 100, 1)); jPanel3 = new javax.swing.JPanel(); jCheckBox2 = new javax.swing.JCheckBox(); minSpinner2 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(0, 0, 100, 1)); maxSpinner2 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(100, 0, 100, 1)); jPanel4 = new javax.swing.JPanel(); jCheckBox3 = new javax.swing.JCheckBox(); minSpinner3 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(0, 0, 100, 1)); maxSpinner3 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(100, 0, 100, 1)); jPanel5 = new javax.swing.JPanel(); jCheckBox4 = new javax.swing.JCheckBox(); minSpinner4 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(0, 0, 100, 1)); maxSpinner4 = new javax.swing.JSpinner(new javax.swing.SpinnerNumberModel(100, 0, 100, 1)); jPanel8 = new javax.swing.JPanel(); jPanel6 = new javax.swing.JPanel(); jLabel4 = new javax.swing.JLabel(); rateSlider = new javax.swing.JSlider(); solutionText = new javax.swing.JEditorPane(); jButton = new javax.swing.JButton(); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { exitForm(evt); } }); jPanelNorth.setLayout(new java.awt.BorderLayout()); jPanelSpinners.setLayout(new javax.swing.BoxLayout(jPanelSpinners, javax.swing.BoxLayout.Y_AXIS)); jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); jLabel1.setText("Assets and weight constraints"); jPanelLab.add(jLabel1); jPanelSpinners.add(jPanelLab); jCheckBox1.setText(names[0]); jCheckBox1.setSelected(true); jCheckBox1.setForeground(darkGreen); jCheckBox1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jCheckBox1ActionPerformed(evt); } }); jPanel2.add(jCheckBox1); jPanel2.add(minSpinner1); jPanel2.add(maxSpinner1); jPanelSpinners.add(jPanel2); jCheckBox2.setText(names[1]); jCheckBox2.setSelected(true); jCheckBox2.setForeground(java.awt.Color.red); jCheckBox2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jCheckBox2ActionPerformed(evt); } }); jPanel3.add(jCheckBox2); jPanel3.add(minSpinner2); jPanel3.add(maxSpinner2); jPanelSpinners.add(jPanel3); jCheckBox3.setText(names[2]); jCheckBox3.setSelected(true); jCheckBox3.setForeground(java.awt.Color.blue); jCheckBox3.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jCheckBox3ActionPerformed(evt); } }); jPanel4.add(jCheckBox3); jPanel4.add(minSpinner3); jPanel4.add(maxSpinner3); jPanelSpinners.add(jPanel4); jCheckBox4.setText(names[3]); jCheckBox4.setSelected(true); jCheckBox4.setForeground(java.awt.Color.yellow); jCheckBox4.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jCheckBox4ActionPerformed(evt); } }); jPanel5.add(jCheckBox4); jPanel5.add(minSpinner4); jPanel5.add(maxSpinner4); jPanelSpinners.add(jPanel5); jPanelNorth.add(jPanelSpinners, java.awt.BorderLayout.CENTER); jPanel8.setLayout(new java.awt.BorderLayout()); jLabel4.setText("Risk Free Rate of Return: "); jPanel6.add(jLabel4); rateSlider.setPaintLabels(true); rateSlider.setPaintTicks(true); rateSlider.setMajorTickSpacing(1); rateSlider.setValue((int)riskFreeRate); rateSlider.setMinimum(1); rateSlider.setMaximum(10); rateSlider.setSnapToTicks(true); rateSlider.setPreferredSize(new java.awt.Dimension(125, 43)); rateSlider.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { rateSliderStateChanged(evt); } }); jPanel6.add(rateSlider); jPanel8.add(jPanel6, java.awt.BorderLayout.NORTH); /** Block of code is for solutionText as a JTextField * But want to enable color, so should be a JEditorPane with text/html solutionText.setEditable(false); solutionText.setColumns(40); solutionText.setFont(new java.awt.Font("Monospaced", 0, 12)); solutionText.setRows(6); solutionText.setText("Solution"); solutionText.setBackground(new java.awt.Color(204, 204, 204)); solutionText.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); * */ // This chunk of code is for solutionText as JEditorPane solutionText.setEditable(false); solutionText.setContentType("text/html"); solutionText.setFont(new java.awt.Font("Monospaced", java.awt.Font.PLAIN, 12)); solutionText.setBackground(jPanel8.getBackground()); solutionText.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); jPanel8.add(solutionText, java.awt.BorderLayout.CENTER); jButton.setText("Apply Constraints"); jButton.setToolTipText("Compute solution and plot"); jButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonActionPerformed(evt); } }); jPanel8.add(jButton, java.awt.BorderLayout.SOUTH); jPanelNorth.add(jPanel8, java.awt.BorderLayout.EAST); getContentPane().add(jPanelNorth, java.awt.BorderLayout.NORTH); pack(); }//GEN-END:initComponents private void jCheckBox4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox4ActionPerformed if (jCheckBox4.isSelected()) { minSpinner4.setEnabled(true); maxSpinner4.setEnabled(true); minSpinner4.setValue(old4min); maxSpinner4.setValue(old4max); } else { old4min = (Integer)minSpinner4.getValue(); old4max = (Integer)maxSpinner4.getValue(); minSpinner4.setValue(new Integer(0)); maxSpinner4.setValue(new Integer(0)); minSpinner4.setEnabled(false); maxSpinner4.setEnabled(false); } }//GEN-LAST:event_jCheckBox4ActionPerformed private void jCheckBox3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox3ActionPerformed if (jCheckBox3.isSelected()) { minSpinner3.setEnabled(true); maxSpinner3.setEnabled(true); minSpinner3.setValue(old3min); maxSpinner3.setValue(old3max); } else { old3min = (Integer)minSpinner3.getValue(); old3max = (Integer)maxSpinner3.getValue(); minSpinner3.setValue(new Integer(0)); maxSpinner3.setValue(new Integer(0)); minSpinner3.setEnabled(false); maxSpinner3.setEnabled(false); } }//GEN-LAST:event_jCheckBox3ActionPerformed private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox2ActionPerformed if (jCheckBox2.isSelected()) { minSpinner2.setEnabled(true); maxSpinner2.setEnabled(true); minSpinner2.setValue(old2min); maxSpinner2.setValue(old2max); } else { old2min = (Integer)minSpinner2.getValue(); old2max = (Integer)maxSpinner2.getValue(); minSpinner2.setValue(new Integer(0)); maxSpinner2.setValue(new Integer(0)); minSpinner2.setEnabled(false); maxSpinner2.setEnabled(false); } }//GEN-LAST:event_jCheckBox2ActionPerformed private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox1ActionPerformed if (jCheckBox1.isSelected()) { minSpinner1.setEnabled(true); maxSpinner1.setEnabled(true); minSpinner1.setValue(old1min); maxSpinner1.setValue(old1max); } else { old1min = (Integer)minSpinner1.getValue(); old1max = (Integer)maxSpinner1.getValue(); minSpinner1.setValue(new Integer(0)); maxSpinner1.setValue(new Integer(0)); minSpinner1.setEnabled(false); maxSpinner1.setEnabled(false); } }//GEN-LAST:event_jCheckBox1ActionPerformed private void jButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonActionPerformed doAll(); }//GEN-LAST:event_jButtonActionPerformed private void rateSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_rateSliderStateChanged riskFreeRate = (double)rateSlider.getValue(); }//GEN-LAST:event_rateSliderStateChanged // find the efficient frontier private void runEfficient() { final int num=4; EfficientFrontier ef = new EfficientFrontier(num); // hard coded var-covar matrix and expected returns double[][] cov = new double[][] {{ 0.002226421, 0.0000093276, 0.0000099678, 0.0000299572}, { 0.0000093276, 0.003398130, 0.0000107892, 0.0000169680}, { 0.0000099678, 0.0000107892, 0.001703816, 0.0000143299}, { 0.0000299572, 0.0000169680, 0.0000143299, 0.004418127}}; double[] x = new double[] {0.0009369806, 0.0016386612, 0.0003375174, 0.0005914344}; for (int i=0; i<x.length; i++) { x[i] /= 2.0; cov[i][i] *= 20.0; } ef.setCovarianceMatrix(cov); ef.setExpectedReturns(x); double[][] b = new double[num][2]; b[0][0] = ((Integer)minSpinner1.getValue()).intValue()/100.0; b[0][1] = ((Integer)maxSpinner1.getValue()).intValue()/100.0; b[1][0] = ((Integer)minSpinner2.getValue()).intValue()/100.0; b[1][1] = ((Integer)maxSpinner2.getValue()).intValue()/100.0; b[2][0] = ((Integer)minSpinner3.getValue()).intValue()/100.0; b[2][1] = ((Integer)maxSpinner3.getValue()).intValue()/100.0; b[3][0] = ((Integer)minSpinner4.getValue()).intValue()/100.0; b[3][1] = ((Integer)maxSpinner4.getValue()).intValue()/100.0; ef.setBounds(b); try { ef.compute(); } catch (com.imsl.IMSLException ie) { System.out.println(ie.getMessage()); ie.printStackTrace(); } solution = ef.getSolution(); // convert from daily fractional return to yearly percentage for (int i=0; i<solution[1].length; i++) { solution[0][i] *= 100.0; // percentages solution[1][i] *= (365.0*100.0); // daily data //solution[1][i] *= (12.0*100.0); //monthly data } } // with the efficient frontier and risk-free rate, find the index // of the optimal portfolio in the solution private int findOptimalIndex() { double slope, intercept, difference; double leastDifference = 1e6; int bestLocation = solution[0].length-1; for (int i=solution[0].length-2; i>1; i--) { slope = (solution[1][i+1]-solution[1][i-1])/(solution[0][i+1]-solution[0][i-1]); if (slope < 0.0) i=0; intercept = solution[1][i]-slope*solution[0][i]; difference = Math.abs(intercept-riskFreeRate); if (difference < leastDifference) { leastDifference = difference; bestLocation = i; } } return bestLocation; } // draw the efficient frontier, risk-free rate, tangent line, and pie chart private void plotEfficientFrontier(double[] risk, double[] ret) { // draw the efficient frontier Data efData = new Data(axis,risk,ret); efData.setTitle("Efficient Frontier"); efData.setDataType(Data.DATA_TYPE_LINE); efData.setLineColor(java.awt.Color.magenta); efData.setLineWidth(1.5); // plot the risk free rate Data rfData = new Data(axis, new double[] {0.0}, new double[] {riskFreeRate}); rfData.setTitle("Risk Free Rate"); rfData.setDataType(Data.DATA_TYPE_MARKER); rfData.setMarkerType(Data.MARKER_TYPE_FILLED_SQUARE); rfData.setMarkerColor(java.awt.Color.darkGray); // plot the line to the tangent double x = solution[0][optimalIndex]; double y = solution[1][optimalIndex]; final double slope = (y-riskFreeRate)/x; final double intercept = y-slope*x; ChartFunction tanFunc = new ChartFunction() { public double f(double x) { return slope*x + intercept; } }; Data tanData = new Data(axis, tanFunc, 0, 20);//solution[0][solution[0].length-1]); tanData.setLineColor(java.awt.Color.cyan); tanData.setLineWidth(1.5); tanData.setLineDashPattern(new double[] {2,10}); addPieChart(); repaint(); } // add the pie chart of solution weights private void addPieChart() { double y[] = {100*solution[2][optimalIndex], 100*solution[3][optimalIndex], 100*solution[4][optimalIndex], 100*solution[5][optimalIndex]}; if (pie != null) pie.remove(); pie = new Pie(chart, y); pie.setLabelType(Pie.LABEL_TYPE_TITLE); pie.setViewport(0.25, 0.55, 0.05, 0.35); PieSlice[] slice = pie.getPieSlice(); slice[0].setTitle(names[0]); slice[0].setFillColor(darkGreen); slice[1].setTitle(names[1]); slice[1].setFillColor(java.awt.Color.red); slice[2].setTitle(names[2]); slice[2].setFillColor(java.awt.Color.blue); slice[3].setTitle(names[3]); slice[3].setFillColor(java.awt.Color.yellow); } // add the pie chart of solution weights private void addMiniPieChart(int pointIndex) { double y[] = {100*solution[2][pointIndex], 100*solution[3][pointIndex], 100*solution[4][pointIndex], 100*solution[5][pointIndex]}; if (miniPie != null) miniPie.remove(); miniPie = new Pie(chart, y); miniPie.setViewport(0.7, 0.85, 0.35, 0.5); PieSlice[] slice = miniPie.getPieSlice(); slice[0].setFillColor(darkGreen); slice[1].setFillColor(java.awt.Color.red); slice[2].setFillColor(java.awt.Color.blue); slice[3].setFillColor(java.awt.Color.yellow); } // construct the summary text as plain text // no longer in use since solutionText became a JEditorPane private String buildPlainText() { java.text.DecimalFormat df = new java.text.DecimalFormat("###.##"); StringBuffer sb = new StringBuffer(); sb.append(" Parameters\t\tSolution\n"); sb.append(" ----------\t\t--------\n"); sb.append(" Risk Free Rate = "+rateSlider.getValue()+"\n"); sb.append(" "+((Integer)minSpinner1.getValue()).intValue()+" < "+names[0]+" < "+((Integer)maxSpinner1.getValue()).intValue()+"\t"); sb.append(names[0]+" = " + df.format(100*solution[2][optimalIndex]) + "%\n"); sb.append(" "+((Integer)minSpinner2.getValue()).intValue()+" < "+names[1]+" < "+((Integer)maxSpinner2.getValue()).intValue()+"\t"); sb.append(names[1]+" = " + df.format(100*solution[3][optimalIndex]) + "%\n"); sb.append(" "+((Integer)minSpinner3.getValue()).intValue()+" < "+names[2]+" < "+((Integer)maxSpinner3.getValue()).intValue()+"\t"); sb.append(names[2]+" = " + df.format(100*solution[4][optimalIndex]) + "%\n"); sb.append(" "+((Integer)minSpinner4.getValue()).intValue()+" < "+names[3]+" < "+((Integer)maxSpinner4.getValue()).intValue()+"\t"); sb.append(names[3]+" = " + df.format(100*solution[5][optimalIndex]) + "%\n"); return sb.toString(); } // construct the summary text as html private String buildHTMLText() { // note that solutionText.setFont should have worked above // but it didn't. To work around, use face="Courier New" in line java.text.DecimalFormat df = new java.text.DecimalFormat("###.##"); StringBuffer sb = new StringBuffer(); sb.append("     <font face=\"Courier New\"><U>Parameters</U>	<U>Solution</U></font><br>"); sb.append("   <font color=green face=\"Courier New\">"+((Integer)minSpinner1.getValue()).intValue()+" < "+names[0]+" < "+((Integer)maxSpinner1.getValue()).intValue()+"	"); sb.append(names[0]+" = " + df.format(100*solution[2][optimalIndex]) + "%</font><br>"); sb.append("   <font color=red face=\"Courier New\">"+((Integer)minSpinner2.getValue()).intValue()+" < "+names[1]+" < "+((Integer)maxSpinner2.getValue()).intValue()+"	"); sb.append(names[1]+" = " + df.format(100*solution[3][optimalIndex]) + "%</font><br>"); sb.append("   <font color=blue face=\"Courier New\">"+((Integer)minSpinner3.getValue()).intValue()+" < "+names[2]+" < "+((Integer)maxSpinner3.getValue()).intValue()+"	"); sb.append(names[2]+" = " + df.format(100*solution[4][optimalIndex]) + "%</font><br>"); sb.append("   <font color=yellow face=\"Courier New\">"+((Integer)minSpinner4.getValue()).intValue()+" < "+names[3]+" < "+((Integer)maxSpinner4.getValue()).intValue()+"	"); sb.append(names[3]+" = " + df.format(100*solution[5][optimalIndex]) + "%</font><br>"); sb.append("   <font face=\"Courier New\">Risk Free Rate = "+rateSlider.getValue()+"</font><br>"); return sb.toString(); } // make sure the spinners define a consistent problem private boolean checkSpinners() { if (((Integer)maxSpinner1.getValue()).intValue() < ((Integer)minSpinner1.getValue()).intValue()) { maxSpinner1.setValue((Integer)minSpinner1.getValue()); } if (((Integer)maxSpinner2.getValue()).intValue() < ((Integer)minSpinner2.getValue()).intValue()) { maxSpinner2.setValue((Integer)minSpinner2.getValue()); } if (((Integer)maxSpinner3.getValue()).intValue() < ((Integer)minSpinner3.getValue()).intValue()) { maxSpinner3.setValue((Integer)minSpinner3.getValue()); } if (((Integer)maxSpinner4.getValue()).intValue() < ((Integer)minSpinner4.getValue()).intValue()) { maxSpinner4.setValue((Integer)minSpinner4.getValue()); } int totalMin = ((Integer)minSpinner1.getValue()).intValue() + ((Integer)minSpinner2.getValue()).intValue() + ((Integer)minSpinner3.getValue()).intValue() + ((Integer)minSpinner4.getValue()).intValue(); int totalMax = ((Integer)maxSpinner1.getValue()).intValue() + ((Integer)maxSpinner2.getValue()).intValue() + ((Integer)maxSpinner3.getValue()).intValue() + ((Integer)maxSpinner4.getValue()).intValue(); if (totalMin > 100) { popWarning("This problem is infeasible because the\n"+ "minimum constraints total more than 100%"); return false; } else if (totalMin == 100) { popWarning("This problem has a trivial solution because\n"+ "the sum of the minimum constraints equals 100%"); return true; } else if (totalMax < 100) { popWarning("This problem is infeasible because the\n"+ "maximum constraints total less than 100%"); return false; } else return true; } // pop up a warning box, called by checkSpinners() private void popWarning(String msg) { javax.swing.JOptionPane.showMessageDialog(this, msg, "Warning", javax.swing.JOptionPane.WARNING_MESSAGE); } // erase the existing data from the chart private void clearChart() { if (axis == null) return; ChartNode[] children = axis.getChildren(); for (int i=0; i<children.length; i++) { if (children[i] instanceof Data) { children[i].remove(); } } } /** The mouseDragged and mouseMoved methods are to * implement MouseMotionListener */ public void mouseDragged(java.awt.event.MouseEvent e) { } // when user moves mouse over efficient frontier, display the portfolio public void mouseMoved(java.awt.event.MouseEvent e) { int currentIndex = -1; final int p = solution[0].length; final double[] eps = {1,1}; final java.text.DecimalFormat df = new java.text.DecimalFormat("##"); double[] user = new double[2]; axis.mapDeviceToUser(e.getX(), e.getY(), user); double xRange[] = axis.getAxisX().getWindow(); double yRange[] = {solution[1][0]-eps[1], solution[1][p-1]+eps[1]}; // Return if outside axis if ((user[0] < xRange[0]) || (user[0] > xRange[1]) || (user[1] < yRange[0]) || (user[1] > yRange[1])) { return; } // find the closest data point in Y double newDist; double dist = 10; int index = -1; for (int i=0; i<p; i++) { newDist = Math.abs(solution[1][i] - user[1]); if (newDist < dist) { dist = newDist; index = i; } } // now see if x is close enough if (Math.abs(solution[0][index] - user[0]) < eps[0]) { // only redraw if moved enough to be a different point if (index != oldIndex) { if (mousePoint != null) mousePoint.remove(); if (miniPie != null) miniPie.remove(); if (pointTip != null) pointTip.remove(); mousePoint = new Data(axis, new double[] {solution[0][index]}, new double[] {solution[1][index]}); mousePoint.setDataType(Data.DATA_TYPE_MARKER); mousePoint.setMarkerType(Data.MARKER_TYPE_CIRCLE_PLUS); mousePoint.setMarkerColor(java.awt.Color.magenta); mousePoint.setMarkerSize(2.0); String title = df.format(100*solution[2][index]) + ", " + df.format(100*solution[3][index]) + ", " + df.format(100*solution[4][index]) + ", " + df.format(100*solution[5][index]); mousePoint.setTitle(title); pointTip = new ToolTip(mousePoint); addMiniPieChart(index); repaint(); } } else { if (mousePoint != null) mousePoint.remove(); if (miniPie != null) miniPie.remove(); if (pointTip != null) pointTip.remove(); repaint(); } oldIndex = index; } /** mouseClicked, Enetered, Exited, Pressed, Released implement MouseListener */ public void mouseClicked(java.awt.event.MouseEvent e) { } public void mouseEntered(java.awt.event.MouseEvent e) { } public void mouseExited(java.awt.event.MouseEvent e) { // clean up when cursor leaves the chart area if (mousePoint != null) mousePoint.remove(); if (miniPie != null) miniPie.remove(); if (pointTip != null) pointTip.remove(); repaint(); } public void mousePressed(java.awt.event.MouseEvent e) { } public void mouseReleased(java.awt.event.MouseEvent e) { } /** Exit the Application */ private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm setVisible(false); dispose(); }//GEN-LAST:event_exitForm /** * @param args the command line arguments */ public static void main(String args[]) { boolean exitOnClose = true; if (args.length > 0 && args[0].equals("-noexit")) exitOnClose = false; new Portfolio(exitOnClose).show(); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton; private javax.swing.JCheckBox jCheckBox1; private javax.swing.JCheckBox jCheckBox2; private javax.swing.JCheckBox jCheckBox3; private javax.swing.JCheckBox jCheckBox4; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; private javax.swing.JPanel jPanel4; private javax.swing.JPanel jPanel5; private javax.swing.JPanel jPanel6; private javax.swing.JPanel jPanel8; private javax.swing.JPanel jPanelNorth; private javax.swing.JPanel jPanelSpinners; private javax.swing.JPanel jPanelLab; private javax.swing.JSpinner maxSpinner1; private javax.swing.JSpinner maxSpinner2; private javax.swing.JSpinner maxSpinner3; private javax.swing.JSpinner maxSpinner4; private javax.swing.JSpinner minSpinner1; private javax.swing.JSpinner minSpinner2; private javax.swing.JSpinner minSpinner3; private javax.swing.JSpinner minSpinner4; private javax.swing.JSlider rateSlider; private javax.swing.JEditorPane solutionText; // End of variables declaration//GEN-END:variables }