www.pudn.com > JSystemTrader.zip > StrategyPerformanceChart.java
package com.jsystemtrader.chart;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import com.jsystemtrader.indicator.*;
import com.jsystemtrader.performance.*;
import com.jsystemtrader.platform.*;
import com.jsystemtrader.util.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.labels.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.data.time.*;
import org.jfree.data.xy.*;
import org.jfree.ui.*;
/**
* Multi-indicator chart where indicators can be grouped together and displayed
* on subplots, one group of indicators per plot.
*/
public class StrategyPerformanceChart {
private static final int PRICE_PLOT_WEIGHT = 5;
private static final int ANNOTATION_RADIUS = 6;
private static final int CANDLE_WIDTH = 4;
private static final Font ANNOTATION_FONT = new Font("SansSerif", Font.BOLD, 12);
private JFreeChart chart;
private CombinedDomainXYPlot combinedPlot;
private ChartPanel chartPanel;
private NumberAxis valueAxis;
private DateAxis dateAxis;
private final Strategy strategy;
private final Map tsCollections;
private FastXYPlot pricePlot, profitAndLossPlot;
private CandlestickRenderer candleRenderer;
private MultiColoredBarRenderer mcbRenderer;
private JComboBox chartTypeCombo, timeLineCombo, profitAndLossCombo, tradesCombo;
private ArrayList annotations = new ArrayList ();
public StrategyPerformanceChart(Strategy strategy) throws JSystemTraderException {
this.strategy = strategy;
tsCollections = new HashMap ();
chart = createChart();
}
private void setRenderer() {
int chartType = chartTypeCombo.getSelectedIndex();
switch (chartType) {
case 0:
pricePlot.setRenderer(mcbRenderer);
break;
case 1:
pricePlot.setRenderer(candleRenderer);
break;
}
}
private void setTimeline() {
int timeLineType = timeLineCombo.getSelectedIndex();
int barSizeInMinutes = strategy.getBarSizeInSecs() / 60;
QuoteHistory qh = strategy.getQuoteHistory();
MarketTimeLine mtl = new MarketTimeLine(barSizeInMinutes, qh.getFirstPriceBar().getDate(),
qh.getLastPriceBar().getDate());
SegmentedTimeline segmentedTimeline = null;
switch (timeLineType) {
case 0:
segmentedTimeline = mtl.getAllHoursTimeline();
break;
case 1:
segmentedTimeline = mtl.getNormalHoursTimeline();
break;
}
dateAxis.setTimeline(segmentedTimeline);
}
public JFrame getChartFrame(JFrame parent) {
final JFrame chartFrame = new JFrame("Strategy Performance Chart - " + strategy);
chartFrame.setIconImage(parent.getIconImage());
chartPanel = new ChartPanel(chart, true);
chartPanel.setRangeZoomable(false);
chartPanel.setPreferredSize(new Dimension(640, 480));
XYPlot plot = (XYPlot) combinedPlot.getSubplots().get(0);
Container contentPane = chartFrame.getContentPane();
JPanel chartOptionsPanel = new JPanel(new SpringLayout());
Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
TitledBorder border = BorderFactory.createTitledBorder(etchedBorder);
border.setTitle("Chart Options");
chartOptionsPanel.setBorder(border);
Dimension dimension = new Dimension(110, 20);
JLabel chartTypeLabel = new JLabel("Chart Type:", JLabel.TRAILING);
chartTypeCombo = new JComboBox(new String[] {"OHLC Bar", "Candlestick"});
chartTypeCombo.setPreferredSize(dimension);
chartTypeCombo.setMaximumSize(dimension);
chartTypeLabel.setLabelFor(chartTypeCombo);
JLabel timeLineLabel = new JLabel("Timeline:", JLabel.TRAILING);
// "Regular hours" option is disabled for now until zoom problems are fixed.
timeLineCombo = new JComboBox(new String[] {"All Hours"});
timeLineCombo.setPreferredSize(dimension);
timeLineCombo.setMaximumSize(dimension);
timeLineLabel.setLabelFor(timeLineCombo);
JLabel profitAndLossLabel = new JLabel("P&L:", JLabel.TRAILING);
profitAndLossCombo = new JComboBox(new String[] {"Show", "Hide"});
profitAndLossCombo.setPreferredSize(dimension);
profitAndLossCombo.setMaximumSize(dimension);
profitAndLossLabel.setLabelFor(profitAndLossCombo);
JLabel tradesLabel = new JLabel("Trades:", JLabel.TRAILING);
tradesCombo = new JComboBox(new String[] {"Show", "Hide"});
tradesCombo.setPreferredSize(dimension);
tradesCombo.setMaximumSize(dimension);
tradesLabel.setLabelFor(tradesCombo);
setRenderer();
chartTypeCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setRenderer();
}
});
timeLineCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setTimeline();
}
});
profitAndLossCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
boolean show = (profitAndLossCombo.getSelectedIndex() == 0);
if (show) {
combinedPlot.add(profitAndLossPlot, 1);
} else {
combinedPlot.remove(profitAndLossPlot);
}
}
});
tradesCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
boolean show = (tradesCombo.getSelectedIndex() == 0);
for (CircledTextAnnotation annotation : annotations) {
if (show) {
pricePlot.addAnnotation(annotation);
} else {
pricePlot.removeAnnotation(annotation);
}
}
}
});
chartOptionsPanel.add(chartTypeLabel);
chartOptionsPanel.add(chartTypeCombo);
chartOptionsPanel.add(timeLineLabel);
chartOptionsPanel.add(timeLineCombo);
chartOptionsPanel.add(profitAndLossLabel);
chartOptionsPanel.add(profitAndLossCombo);
chartOptionsPanel.add(tradesLabel);
chartOptionsPanel.add(tradesCombo);
SpringUtilities.makeCompactGrid(chartOptionsPanel, 2, 4, 10, 5, 15, 5); //rows, cols, initX, initY, xPad, yPad
contentPane.add(chartOptionsPanel, BorderLayout.NORTH);
JPanel scrollBarPanel = new JPanel(new BorderLayout());
DateScrollBar dateScrollBar = new DateScrollBar(plot);
scrollBarPanel.add(dateScrollBar, BorderLayout.SOUTH);
contentPane.add(chartPanel, BorderLayout.CENTER);
contentPane.add(scrollBarPanel, BorderLayout.PAGE_END);
chartFrame.setContentPane(contentPane);
chartFrame.pack();
RefineryUtilities.centerFrameOnScreen(chartFrame);
chartFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
chartFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
});
return chartFrame;
}
private TimeSeries createIndicatorSeries(IndicatorHistory indHistory) {
TimeSeries ts = new TimeSeries(indHistory.getName(), Minute.class);
ts.setRangeDescription(indHistory.getName());
synchronized (indHistory) {
for (Indicator indicator : indHistory.getHistory()) {
try {
ts.add(new Minute(indicator.getDate()), indicator.getValue(), false);
} catch (Exception e) {
Account.getLogger().write(e);
}
}
}
ts.fireSeriesChanged();
return ts;
}
private TimeSeries createProfitAndLossSeries(ProfitAndLossHistory profitAndLossHistory) {
TimeSeries ts = new TimeSeries("P&L", Minute.class);
synchronized (profitAndLossHistory) {
for (ProfitAndLoss profitAndLoss : profitAndLossHistory.getHistory()) {
Date date = new Date(profitAndLoss.getDate());
Minute minute = new Minute(date);
ts.setRangeDescription("P&L");
try {
ts.add(minute, profitAndLoss.getValue());
} catch (Exception e) {
Account.getLogger().write(e);
}
}
}
return ts;
}
private OHLCDataset createHighLowDataset() {
QuoteHistory qh = strategy.getQuoteHistory();
int qhSize = qh.size();
Date[] dates = new Date[qhSize];
double[] highs = new double[qhSize];
double[] lows = new double[qhSize];
double[] opens = new double[qhSize];
double[] closes = new double[qhSize];
double[] volumes = new double[qhSize];
for (int bar = 0; bar < qhSize; bar++) {
Calendar cal = Calendar.getInstance();
PriceBar priceBar = qh.getPriceBar(bar);
cal.setTimeInMillis(priceBar.getDate());
dates[bar] = cal.getTime();
highs[bar] = priceBar.getHigh();
lows[bar] = priceBar.getLow();
opens[bar] = priceBar.getOpen();
closes[bar] = priceBar.getClose();
volumes[bar] = priceBar.getVolume();
}
OHLCDataset dataset = new DefaultHighLowDataset(strategy.getTicker(), dates, highs, lows, opens, closes,
volumes);
return dataset;
}
private JFreeChart createChart() {
int barSizeInSeconds = strategy.getBarSizeInSecs();
// create OHLC bar renderer
mcbRenderer = new MultiColoredBarRenderer();
//mcbRenderer.setSeriesPaint(0, Color.GREEN);
mcbRenderer.setSeriesPaint(0, Color.WHITE);
mcbRenderer.setStroke(new BasicStroke(4));
// create candlestick renderer
candleRenderer = new CandlestickRenderer(CANDLE_WIDTH, false, new HighLowItemLabelGenerator());
candleRenderer.setDrawVolume(false);
candleRenderer.setAutoWidthMethod(candleRenderer.WIDTHMETHOD_AVERAGE);
candleRenderer.setAutoWidthFactor(8.0);
candleRenderer.setAutoWidthGap(1.0);
candleRenderer.setUpPaint(Color.GREEN);
candleRenderer.setDownPaint(Color.RED);
candleRenderer.setSeriesPaint(0, Color.WHITE);
candleRenderer.setStroke(new BasicStroke(1.0f));
candleRenderer.setSeriesPaint(1, Color.GRAY);
dateAxis = new DateAxis();
int barSizeInMinutes = strategy.getBarSizeInSecs() / 60;
QuoteHistory qh = strategy.getQuoteHistory();
MarketTimeLine mtl = new MarketTimeLine(barSizeInMinutes, qh.getFirstPriceBar().getDate(),
qh.getLastPriceBar().getDate());
SegmentedTimeline segmentedTimeline = mtl.getAllHoursTimeline();
dateAxis.setTimeline(segmentedTimeline);
// create price plot
OHLCDataset highLowDataset = createHighLowDataset();
valueAxis = new NumberAxis("Price");
valueAxis.setAutoRangeIncludesZero(false);
pricePlot = new FastXYPlot(highLowDataset, dateAxis, valueAxis, null);
pricePlot.setBackgroundPaint(Color.BLACK);
pricePlot.setDomainCrosshairVisible(true);
pricePlot.setDomainCrosshairLockedOnData(false);
pricePlot.setRangeCrosshairVisible(true);
pricePlot.setRangeCrosshairLockedOnData(false);
pricePlot.setRangeCrosshairPaint(Color.WHITE);
pricePlot.setDomainCrosshairPaint(Color.WHITE);
// parent plot
combinedPlot = new CombinedDomainXYPlot(dateAxis);
combinedPlot.setGap(10.0);
combinedPlot.setOrientation(PlotOrientation.VERTICAL);
combinedPlot.add(pricePlot, PRICE_PLOT_WEIGHT);
// Put all indicators into groups, so that each group is
// displayed on its own subplot
for (IndicatorHistory indHist : strategy.getIndicators()) {
TimeSeries ts = createIndicatorSeries(indHist);
int subChart = indHist.getSubChartNumber();
TimeSeriesCollection tsCollection = tsCollections.get(subChart);
if (tsCollection == null) {
tsCollection = new TimeSeriesCollection();
tsCollections.put(subChart, tsCollection);
}
tsCollection.addSeries(ts);
}
// Plot executions
for (OrderStatus execution : strategy.getExecutions()) {
Date date = new Date(execution.getDate());
double aveFill = execution.getAvgFillPrice();
int decision = execution.getDecision();
String annotationText = "?";
Color bkColor = null;
switch (decision) {
case Strategy.DECISION_LONG:
annotationText = "L";
bkColor = Color.GREEN;
break;
case Strategy.DECISION_SHORT:
annotationText = "S";
bkColor = Color.RED;
break;
case Strategy.DECISION_FLAT:
annotationText = "F";
bkColor = Color.YELLOW;
break;
}
CircledTextAnnotation circledText = new CircledTextAnnotation(annotationText, date.getTime(), aveFill,
ANNOTATION_RADIUS);
circledText.setFont(ANNOTATION_FONT);
circledText.setBkColor(bkColor);
circledText.setPaint(Color.BLACK);
circledText.setTextAnchor(TextAnchor.CENTER);
pricePlot.addAnnotation(circledText);
annotations.add(circledText);
}
// Now that the indicators are grouped, create subplots
for (Map.Entry mapEntry : tsCollections.entrySet()) {
int subChart = (Integer) mapEntry.getKey();
TimeSeriesCollection tsCollection = (TimeSeriesCollection) mapEntry.getValue();
StandardXYItemRenderer renderer = new StandardXYItemRenderer();
renderer.setStroke(new BasicStroke(2));
if (subChart == 0) {
pricePlot.setDataset(1, tsCollection);
pricePlot.setRenderer(1, renderer);
} else {
NumberAxis valueAxis = new NumberAxis();
valueAxis.setAutoRangeIncludesZero(false);
FastXYPlot plot = new FastXYPlot(tsCollection, dateAxis, valueAxis, renderer);
plot.setBackgroundPaint(Color.BLACK);
int weight = 1;
combinedPlot.add(plot, weight);
}
}
// Plot P&L
NumberAxis valueAxis = new NumberAxis();
valueAxis.setAutoRangeIncludesZero(false);
TimeSeries ts = createProfitAndLossSeries(strategy.getPositionManager().getProfitAndLossHistory());
TimeSeriesCollection tsCollection = new TimeSeriesCollection();
tsCollection.addSeries(ts);
StandardXYItemRenderer renderer = new StandardXYItemRenderer();
renderer.setSeriesPaint(0, Color.WHITE);
renderer.setStroke(new BasicStroke(2));
profitAndLossPlot = new FastXYPlot(tsCollection, dateAxis, valueAxis, renderer);
profitAndLossPlot.setBackgroundPaint(Color.BLACK);
combinedPlot.add(profitAndLossPlot, 1);
combinedPlot.setDomainAxis(dateAxis);
// Finally, create the chart
chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, combinedPlot, true);
chart.getLegend().setPosition(RectangleEdge.TOP);
chart.getLegend().setBackgroundPaint(Color.LIGHT_GRAY);
return chart;
}
}