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;

}

}

}