/*
* -------------------------------------------------------------------------
* $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
}