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; 
 
    } 
 
}