EfficientFrontierPanel
package com.gossamer.voyant.client.data.efficientFrontier;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Shape;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JPanel;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import com.gossamer.cobra.calculator.marketdata.MarketAssumptions;
import com.gossamer.voyant.client.AppStyle;
import com.gossamer.voyant.client.swing.format.PercentConverterFormat;
import com.gossamer.voyant.model.AssetAllocation;
/**
*
*
*/
public class EfficientFrontierPanel extends JPanel {
public static final Color RED_COLOR = new Color(153, 0, 0);
public static final Color ORANGE_COLOR = Color.ORANGE;
public static final Color GREEN_COLOR = new Color(153, 204, 51);
public static final Color BLUE_COLOR = new Color(0, 102, 153);
public static final Shape ARC = new Arc2D.Double(new Rectangle2D.Double(-10, -10, 20, 20), 60, 60, Arc2D.PIE);
public static final Shape CIRCLE = new Ellipse2D.Double(-4, -4, 8, 8);
public static final Shape CIRCLE_SM = new Ellipse2D.Double(-2, -2, 4, 4);
private static Logger logger = Logger.getLogger(EfficientFrontierPanel.class);
private ChartPanel chartPanel;
private JFreeChart chart;
private XYSeriesCollection dataset;
private EfficientFrontier efficientFrontier;
public EfficientFrontierPanel(EfficientFrontier efficientFrontier) {
setLayout(new BorderLayout());
setBackground(AppStyle.getInstance().dialogBackgroundColor);
this.efficientFrontier = efficientFrontier;
// Dataset
XYSeries series = new XYSeries("Frontier");
for (EfficientFrontier.Point ef : efficientFrontier.getFrontiers()) {
series.add(ef.getStdDev(), ef.getReturnRate());
}
dataset = new XYSeriesCollection();
chart = ChartFactory.createScatterPlot("Efficient Frontier", // chart title
"Standard Deviation (%)", // x axis label
"Return Rate (%)", // y axis label
dataset, // data
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
addSeries("Efficient Allocation", CIRCLE, BLUE_COLOR);
addSeries("Your Plan", CIRCLE, RED_COLOR);
addSeries("Minimized Risk", ARC, ORANGE_COLOR);
addSeries("Maximized Return", ARC, GREEN_COLOR);
dataset.addSeries(series);
chart.getTitle().setFont(new Font("Arial", Font.BOLD, 18));
chart.getTitle().setPaint(Color.darkGray);
XYPlot xyPlot = chart.getXYPlot();
NumberAxis rangeAxis = (NumberAxis) xyPlot.getRangeAxis();
rangeAxis.setAutoRangeIncludesZero(false);
NumberAxis domainAxis = (NumberAxis) xyPlot.getDomainAxis();
rangeAxis.setLabelFont(new Font("Arial", Font.BOLD, 12));
rangeAxis.setTickLabelFont(new Font("Arial", Font.PLAIN, 10));
rangeAxis.setLabelPaint(Color.darkGray);
rangeAxis.setTickLabelPaint(Color.darkGray);
domainAxis.setLabelFont(new Font("Arial", Font.BOLD, 12));
domainAxis.setTickLabelFont(new Font("Arial", Font.PLAIN, 10));
domainAxis.setLabelPaint(Color.darkGray);
domainAxis.setTickLabelPaint(Color.darkGray);
rangeAxis.setNumberFormatOverride(PercentConverterFormat.TWO_DECIMAL_PLACES_CONVERTER);
domainAxis.setNumberFormatOverride(PercentConverterFormat.TWO_DECIMAL_PLACES_CONVERTER);
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) xyPlot.getRenderer();
renderer.setShapesVisible(true);
renderer.setSeriesShape(dataset.getSeriesCount()-1, CIRCLE_SM); // Frontier
PercentConverterFormat pcf = PercentConverterFormat.TWO_DECIMAL_PLACES_CONVERTER;
String formatString = "{0} ({1}%, {2}%)";
renderer.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator(formatString, pcf, pcf));
renderer.setSeriesToolTipGenerator(1, new StandardXYToolTipGenerator(formatString, pcf, pcf));
renderer.setSeriesToolTipGenerator(2, new StandardXYToolTipGenerator(formatString, pcf, pcf));
renderer.setSeriesToolTipGenerator(3, new StandardXYToolTipGenerator(formatString, pcf, pcf));
renderer.setSeriesToolTipGenerator(4, new StandardXYToolTipGenerator(formatString, pcf, pcf));
chartPanel = new ChartPanel(chart);
chartPanel.setDomainZoomable(false);
chartPanel.setRangeZoomable(false);
add(chartPanel);
chartPanel.setBackground(AppStyle.getInstance().dialogBackgroundColor);
chartPanel.setPopupMenu(null);
chart.setBackgroundPaint(null);
}
public EfficientFrontier getEfficientFrontier() {
return efficientFrontier;
}
/**
* @param currentDetails
* @param selectedFrontier
* @return False if currentDetails had null fields, and therefore the data was "bad", and only the "Selected" chart series can be displayed.
*/
public boolean adjustGraph(AssetAllocation.Details currentDetails, EfficientFrontier.Point selectedFrontier) {
boolean goodData = true;
// If these are missing, we're totally busted.
if (currentDetails.stdDev == null) { // If any are null, they'll all be null
logger.error("Failed to get AA details: ");
Logger aaLogger = Logger.getLogger("AA");
aaLogger.error("Unallocated: " + currentDetails.assetAllocation.validateDifference());
aaLogger.error("Valid: " + currentDetails.assetAllocation.isValid());
currentDetails.assetAllocation.listAllocations(true);// Uses Debug
goodData = false;
}
// ========================================================================
// Selected
// ========================================================================
updateSeries(0, selectedFrontier.getStdDev(), selectedFrontier.getReturnRate());
if (goodData) {
// ========================================================================
// Current
// ========================================================================
updateSeries(1, currentDetails.stdDev, currentDetails.expectedReturn);
// ========================================================================
// Optimized Risk
// ========================================================================
EfficientFrontier.Point optimalRisk = efficientFrontier.getMinimalRiskFor(currentDetails.expectedReturn);
updateSeries(2, optimalRisk.getStdDev(), optimalRisk.getReturnRate());
// ========================================================================
// Optimized Return
// ========================================================================
EfficientFrontier.Point optimalReturn = efficientFrontier.getMaximalReturnFor(currentDetails.stdDev);
updateSeries(3, optimalReturn.getStdDev(), optimalReturn.getReturnRate());
}
return goodData;
}
private void addSeries(String title, Shape shape, Color color) {
int index = dataset.getSeriesCount();
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setSeriesShape(index, shape);
renderer.setSeriesPaint(index, color);
renderer.setLinesVisible(false);
XYSeries series = new XYSeries(title);
dataset.addSeries(series);
}
private void updateSeries(int index, double x, double y) {
XYSeries series = null;
series = dataset.getSeries(index);
series.clear();
series.add(x, y);
}
public static class ChartData {
private ChartPanel chartPanel;
private JFreeChart chart;
private XYSeriesCollection dataset;
public ChartData(ChartPanel chartPanel, JFreeChart chart, XYSeriesCollection dataset) {
this.chartPanel = chartPanel;
this.chart = chart;
this.dataset = dataset;
}
public ChartPanel getChartPanel() {
return chartPanel;
}
public JFreeChart getChart() {
return chart;
}
public XYSeriesCollection getDataset() {
return dataset;
}
}
}