Highlow in several colors

Discussion about JFreeChart related to stockmarket charts.
Locked
Lina Larsson
Posts: 5
Joined: Tue Apr 01, 2008 10:45 am

Highlow in several colors

Post by Lina Larsson » Mon Jun 30, 2008 2:45 pm

In some diagrams of Bollinger Bands the highlows are colored in red or green depending on if it's in sell trend och buy trend. Is this possible to do in jfreechart?

Like in this picture but without the blue color (I'm only (interested in the green and the red color)

Image

RoyW
Posts: 93
Joined: Wed Apr 23, 2008 7:42 pm
Contact:

Post by RoyW » Fri Jul 11, 2008 9:18 pm

The following code reproduces the chart in the image (the top price chart).
If you look around line 58 you can uncomment the line that will remove the blue line.

Code: Select all

import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SegmentedTimeline;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.general.DatasetChangeEvent;
import org.jfree.data.general.DatasetChangeListener;
import org.jfree.data.xy.*;

import java.awt.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.StringTokenizer;

public class CandlesticBBDemo{
    public static void main(String[] args) {
        ChartFrame chartFrame = new ChartFrame("Candlestick + Bollinger Bands", buildChart("IBM"));
        chartFrame.setSize(600,360);
        chartFrame.setVisible(true);
    }

    public static JFreeChart buildChart(String symbol) {
        DateAxis   domainAxis        = new DateAxis();
        NumberAxis rangeAxis         = new NumberAxis("Price");

        CandlestickRenderer priceRenderer = new CandlestickRenderer();
        XYDataset           priceDataset  = getDataSet(symbol);

        XYLineAndShapeRenderer bbRenderer = new XYLineAndShapeRenderer(true, false);
        XYDataset              bbDataset  = new BollingerBandsDataset((OHLCDataset)priceDataset );

        XYPlot mainPlot = new XYPlot(priceDataset, domainAxis, rangeAxis, priceRenderer);
        //This is the code that adds the line data to the candlestick chart
        mainPlot.setRenderer(1, bbRenderer);
        mainPlot.setDataset (1, bbDataset);

        //Do some setting up, see the API Doc
        rangeAxis.setAutoRangeIncludesZero(false);
        domainAxis.setTimeline( SegmentedTimeline.newMondayThroughFridayTimeline() );

        priceRenderer.setSeriesPaint(0, Color.BLACK);
        priceRenderer.setSeriesToolTipGenerator(0, new HighLowItemLabelGenerator());
        priceRenderer.setDrawVolume(false);

        bbRenderer.setSeriesPaint(0, Color.GREEN);
        bbRenderer.setSeriesPaint(1, Color.BLUE);
        bbRenderer.setSeriesPaint(2, Color.RED);
 //        bbRenderer.setSeriesVisible(1, false); //Uncomment this line to remove the moving average (center line)

        return new JFreeChart(symbol, null, mainPlot, false);
    }

    protected static AbstractXYDataset getDataSet(String symbol) {
        java.util.List<OHLCDataItem> dataItems = new ArrayList<OHLCDataItem>();
        try {
            String strUrl= "http://ichart.finance.yahoo.com/table.csv?s="+symbol+"&a=2&b=1&c=2005&d=6&e=22&f=2005&ignore=.csv";
            URL url = new URL(strUrl);
            BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
            DateFormat df = new SimpleDateFormat("y-M-d");
            String inputLine;
            in.readLine();
            while ((inputLine = in.readLine()) != null) {
                StringTokenizer st = new StringTokenizer(inputLine, ",");
                Date date       = df.parse( st.nextToken() );
                double open     = Double.parseDouble( st.nextToken() );
                double high     = Double.parseDouble( st.nextToken() );
                double low      = Double.parseDouble( st.nextToken() );
                double close    = Double.parseDouble( st.nextToken() );
                double volume   = Double.parseDouble( st.nextToken() );
                double adjClose = Double.parseDouble( st.nextToken() );
                OHLCDataItem item = new OHLCDataItem(date, open, high, low, close, volume);
                dataItems.add(item);
            }
            in.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        Collections.reverse(dataItems);
        OHLCDataItem[] data = dataItems.toArray(new OHLCDataItem[dataItems.size()]);
        return new DefaultOHLCDataset(symbol, data);
    }

    private static class BollingerBandsDataset extends AbstractXYDataset implements DatasetChangeListener {
        protected OHLCDataset ohlcDataset;
        protected int maxLength;
        protected int upperBandwidth;
        protected int lowerBandwidth;

        Double[] upperValues;
        Double[] lowerValues;
        Double[] averageValues;

        public BollingerBandsDataset(OHLCDataset ohlcDataset) {
            this(ohlcDataset, 20, 2, 2);
        }

        public BollingerBandsDataset(OHLCDataset ohlcDataset, int maxLength, int upperBandwidth, int lowerbandwidth) {
            this.maxLength      = maxLength;
            this.upperBandwidth = upperBandwidth;
            this.lowerBandwidth = lowerbandwidth;
            this.setOhlcDataset(ohlcDataset);
        }

        public OHLCDataset getOhlcDataset() {
            return ohlcDataset;
        }

        public void setOhlcDataset(OHLCDataset ohlcDataset) {
            if (this.ohlcDataset != null)
                this.ohlcDataset.removeChangeListener(this);
            this.ohlcDataset = ohlcDataset;
            this.ohlcDataset.addChangeListener(this);
            calculateBollingerBands();
            fireDatasetChanged();
        }

        protected void calculateBollingerBands() {
            int size = ohlcDataset.getItemCount(0);
            upperValues   = new Double[size];
            averageValues = new Double[size];
            lowerValues   = new Double[size];

            for(int i=maxLength, n = ohlcDataset.getItemCount(0) ; i<n ; i++){
                double sma = this.calculateSMA( i );
                double stdDev = this.calculateStdDev( i, sma);

                averageValues[i] = sma;
                upperValues  [i] = sma+(stdDev * upperBandwidth);
                lowerValues  [i] = sma-(stdDev * lowerBandwidth);
            }
        }

        protected double calculateSMA(int end) {
            double total = 0.0;
            for (int i = end-maxLength;  i<end; i++) {
                total += getSourceValue(i);
            }
            return total / maxLength;
        }


        protected  double calculateStdDev(int end, double sma) {
            double stdDev = 0.0;
            double total = 0.0;
            for (int i = end-maxLength;  i<end; i++) {
                double dev = getSourceValue(i) - sma;
                total += (dev * dev);
            }
            total = (total / maxLength);
            stdDev = Math.sqrt(total);
            return stdDev;
        }

        protected double getSourceValue(int item){
            return ohlcDataset.getCloseValue(0, item);
        }

        public int getSeriesCount() {
            return 3;
        }

        public Comparable getSeriesKey(int series) {
            switch (series) {
                case 0: return "Bollinger Bands Lower";
                case 1: return "Bollinger Bands Average";
                case 2: return "Bollinger Bands Upper";
                default : return null;
            }
        }

        public int getItemCount(int series) {
            return ohlcDataset.getItemCount(0);
        }

        public Number getX(int series, int item) {
            return ohlcDataset.getX(0, item);
        }

        public Number getY(int series, int item) {
            switch (series) {
                case 0: return lowerValues[item];
                case 1: return averageValues[item];
                case 2: return upperValues[item];
                default : return null;
            }
        }

        public void datasetChanged(DatasetChangeEvent event) {
            calculateBollingerBands();
            fireDatasetChanged();
        }
    }
}

The answer does not come from thinking outside the box, rather the answer comes from realizing the truth; There is no Box. my js site

Lina Larsson
Posts: 5
Joined: Tue Apr 01, 2008 10:45 am

Post by Lina Larsson » Thu Jul 17, 2008 8:22 am

Hi!

I tried your code and it acts the way I want except that I would like HighLows instead of CandleSticks. If I implement highlows they are only shown in one color, i'd like it to be like this picture:

http://chart.ecovision.se/scripts/chart ... 0000108656

RoyW
Posts: 93
Joined: Wed Apr 23, 2008 7:42 pm
Contact:

Post by RoyW » Thu Jul 17, 2008 3:33 pm

Well, in that chart the color depends on a buy or sell signal. I don't know how to calculate a buy or sell signal but the following code will change the color of the high/low. You just have to figure out what to put in the method getBuySellColor().

Use this for your high/low renderer.

Code: Select all

        HighLowRenderer priceRenderer = new HighLowRenderer(){
            public void drawItem(Graphics2D g2, 
                                 XYItemRendererState state, 
                                 Rectangle2D dataArea, 
                                 PlotRenderingInfo info, 
                                 XYPlot plot, 
                                 ValueAxis domainAxis, 
                                 ValueAxis rangeAxis, 
                                 XYDataset dataset, 
                                 int series, 
                                 int item, 
                                 CrosshairState crosshairState, 
                                 int pass) {
                if (dataset instanceof OHLCDataset) {
                    Color color = getBuySellColor((OHLCDataset) dataset, series, item);
                    setSeriesPaint(series, color);
                }
                super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass);
            }

            protected Color getBuySellColor(OHLCDataset hld, int series, int item){
                if(20 < item && item <50){
                    return Color.RED;
                }else{
                    return Color.GREEN;
                }
            }
        };
The answer does not come from thinking outside the box, rather the answer comes from realizing the truth; There is no Box. my js site

Lina Larsson
Posts: 5
Joined: Tue Apr 01, 2008 10:45 am

Post by Lina Larsson » Fri Jul 18, 2008 12:11 pm

It works! Thank you very much for all your help :)

Locked