Candlestick Timeline problem

Discussion about JFreeChart related to stockmarket charts.
Locked
Kalenzo
Posts: 1
Joined: Tue Apr 21, 2009 4:02 pm

Candlestick Timeline problem

Post by Kalenzo » Tue Apr 21, 2009 4:19 pm

Hello guys!
I have some problem with Timeline function (at least i think so). I want to draw candlestick chart, with no gaps between the candles. This means that i can haveeg. 1 minute candles like this:
8:00
8:01
8:02
8:05

8:08
8:09
8:10
8:11
and so on. Also i have gaps during weekends. I tried to use SegmentedTimeLine with default exclude weekends mechanism, but this don't clears the empty bars problem in minute chart, or some other charts. I found some workaround with this problem using addException(start.getTime(), end.getTime()) to timeline, but then performance of the chart - of moving chart forth and backward has been highly decerased. Here is how i did the coding part:

Code: Select all

        private void clearGaps(DateAxis domainAxis, ArrayList<String[]> ListOfDates)
	{
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        
		try 
		{
			SegmentedTimeline stml = new SegmentedTimeline(SegmentedTimeline.MINUTE_SEGMENT_SIZE,this.TF,0);
			
			
			  for(String[]d:ListOfDates)
			  {
				Date start = df.parse(d[0]);
				Calendar cal = Calendar.getInstance();
				cal.setTime(start);
				cal.add(Calendar.MINUTE, this.TF);
				
				start = cal.getTime();
			 	Date end = df.parse(d[1]);
			    
			 	stml.addException(start.getTime(), end.getTime());
			  }
		      domainAxis.setTimeline(stml);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	} 




	private JFreeChart CandleStickChart(String title)
	{
		JFreeChart chart;
		
		DbEngeine dbe = new DbEngeine(); 
		dbe.fillArrays("SELECT FIRST 500 a.TS_, a.\"OPEN_\", a.\"HIGH_\", a.\"LOW_\", a.\"CLOSE_\" FROM \""+this.Symbol+""+this.TF+"\" a order by a.TS_ desc");
		
		//GET OHLC DATA FROM DATABASE
		ArrayList<OHLCDataItem> candles = new ArrayList<OHLCDataItem>();
		
		for(int i = 0;i<dbe.open.size();i++)
		{
			candles.add(new OHLCDataItem(dbe.ts.get(i),dbe.open.get(i),dbe.high.get(i),dbe.low.get(i),dbe.close.get(i),0));
		}
		
		//CREATE DATA TO DISPLAY
		OHLCDataItem[] data = candles.toArray(new OHLCDataItem[candles.size()]);
		
		
		//FOR GAP CLEARING
		//CHECK EACH BAR BY BAR COMPARING TIME
		//IF IT IS MISSING BAR (if the difference between time of current bar and time of prior bar is > than timeframe
		//then it means that some data is missing and should be hidden. Eg. if we are on 1 minute chart, and
		//we have space between this and prior bar higher than 1 minute then it means that bar is missing.
		//I'm writing the date & time of such bars to this arraylist
		ArrayList<String[]> ListOfDates = new ArrayList<String[]>();
		for(int i = 0;i<data.length-1;i++)
		{
			OHLCDataItem item = data[i];
			OHLCDataItem pitem = data[i+1];
			
			if(item.getDate().getTime()-pitem.getDate().getTime()>(1000 * 60 * this.TF))
			{
				String[] restriction = {pitem.getDate().toString(),item.getDate().toString()};
				ListOfDates.add(restriction);
			}
		}
		
		//CREATE DATASET
		DefaultOHLCDataset dataset = new DefaultOHLCDataset(title,data);
		 
		DateAxis  domainAxis        = new DateAxis("");//new DateAxis("");
        NumberAxis  rangeAxis         = new NumberAxis("");
        CandlestickRenderer renderer  = new CandlestickRenderer();
       
        //USING PREPARED LIST I'M EXCLUDING EMPTY BARS WITH clearGaps FUNCTION
        clearGaps(domainAxis, ListOfDates);
        
         
        
        XYPlot mainPlot = new XYPlot(dataset, domainAxis, rangeAxis, null);
        
         
        Color dnColor = Color.green.darker();
        Color upColor = Color.red;
        
        renderer.setSeriesPaint(0, Color.black);
        renderer.setAutoWidthMethod(1);
        renderer.setUpPaint(upColor);
        renderer.setDownPaint(dnColor);
        renderer.setDrawVolume(false);
        
        mainPlot.setRenderer(0, renderer);
        mainPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
        
        Paint gradient = new GradientPaint(0, 0, Color.white, 1000, 0, Color.white);
        
        mainPlot.setBackgroundPaint(gradient);
        
        mainPlot.getRangeAxis().setLabelPaint(Color.black);
        mainPlot.getRangeAxis().setTickLabelPaint(Color.black);
        mainPlot.getRangeAxis().setTickLabelFont(new Font("sansserif", Font.BOLD, 12));
        
        mainPlot.getDomainAxis().setTickLabelPaint(Color.black);
        mainPlot.getDomainAxis().setLabelPaint(Color.black);
        mainPlot.getDomainAxis().setTickLabelFont(new Font("sansserif", Font.BOLD, 12));
        
        mainPlot.setDomainGridlinesVisible(false);
        mainPlot.setRangeGridlinesVisible(false);
        
        mainPlot.setOutlinePaint(Color.black);
        
        rangeAxis.setAutoRangeIncludesZero(false); 
        
        chart = new JFreeChart(title, null, mainPlot, false);
        
        Paint gradient2 = new GradientPaint(0, 0, Color.white, 1000, 0, Color.white);
        
        chart.setBackgroundPaint(gradient2);
		chart.getTitle().setPaint(new Color(192,64,0));
		
        RenderingHints rh = chart.getRenderingHints();
        rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        rh.put(RenderingHints.KEY_TEXT_LCD_CONTRAST, 250);
        
        
        //ADJUSTMENT OF THE VISIBLE BARS
        //TO SCROLL THE CHART I ADDED <> BUTTONS AND I'm LOADING MORE DATA
        //EG 500 bars, but at once i'm showing 100 - restricting view by range.
        //Then if some click > or < i'm updating visible range dynamicly
        domainAxis.setMinimumDate(candles.get(100).getDate());
        domainAxis.setMaximumDate(candles.get(0).getDate());
        
        double max = 0;
        double min = 99999;
        
        for(int i = 0;i<=100;i++)
        {
        	if(candles.get(i).getHigh().doubleValue() > max) max = candles.get(i).getHigh().doubleValue();
        	if(candles.get(i).getLow().doubleValue() < min) min = candles.get(i).getLow().doubleValue();
        }
        
    	
        if(this.Symbol.contains("JPY") )
        	rangeAxis.setRange(min-0.05, max+0.05);
        else
        	rangeAxis.setRange(min-0.0005, max+0.0005);
        	 
		return chart;
	}



Now, my question is: is there any other - simple way to remove gaps between 1 minute candles ? Or, how to improve performance of the SegmentedTimeLine?

GeniuZ
Posts: 9
Joined: Sun Apr 05, 2009 9:14 am

Re: Candlestick Timeline problem

Post by GeniuZ » Wed Jun 10, 2009 12:31 pm

Hello, I have this problem too.
Do you plane increase exceptions performance?
Is it possible add any to source code some "question" to my gap? Will be performace better with this solution?
Thank you.

gregory
Posts: 4
Joined: Thu Dec 17, 2009 1:07 pm
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by gregory » Thu Dec 17, 2009 2:07 pm

Hi, got a similar if not same issue.

I want to show intraday candles, lines and shapes on a chart, but only for selected time windows of the day (not necessarily regular trading hours, but windows generally defined by application logic through data selection).

I'd like to have something more simple than segmented timeline, which just puts the shapes on the chart one after another in an equi-distant manner, no matter what the time is.
On the other hand, I guarantee, that the time of the series elements in the dataset is right, the time should just be shown on the axis with reasonable ticks.
I will also guarantee, that only those elements are part of the series, which shall be shown. As a resulting feature of the new timeline, the plot shall simply show every element in the series.
If there is any resulting non-linearity on the time/domain axis, I will handle this on the application level. The new axis shall just show the elements one by one and put appropriate tick marks on the bottom, even if there is a gap of say 3 days between two 10 min elements.

Any idea, which axis/timeline combination shall be used for this purpose?
Or how to fix the following to approach it?

Up to now, I try using the following code based on segmented timeline, but this even fails if only regular hours (w/o weekend) shall be shown. There seem to be DS issues, in spite of DS switched on, and weekend issues.
In winter time (now), data from summer time are compressed from 9:00 to 10:00 and show as a 'tower' at 10:00. The weekend window is not supressed at all.

Code: Select all

		this.dateAxis = new DateAxis(null);
		
		SegmentedTimeline mondayFridayTimeline = SegmentedTimeline.newMondayThroughFridayTimeline();
		mondayFridayTimeline.setAdjustForDaylightSaving(true);
		int noOfWorkingSegments = 9;
		int noOfSleepingSegments = 24-noOfWorkingSegments;
		SegmentedTimeline workingHoursTimeline = new SegmentedTimeline(SegmentedTimeline.HOUR_SEGMENT_SIZE, noOfWorkingSegments, noOfSleepingSegments);
		workingHoursTimeline.setStartTime(8 * SegmentedTimeline.HOUR_SEGMENT_SIZE);  // 8 a.m. GMT on 1/1/1970
		workingHoursTimeline.setBaseTimeline(mondayFridayTimeline);
		workingHoursTimeline.setAdjustForDaylightSaving(true);

		this.dateAxis.setTimeline(workingHoursTimeline);
		this.dateAxis.setTimeZone(TimeZone.getDefault());

Thanks.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Re: Candlestick Timeline problem

Post by skunk » Thu Dec 17, 2009 8:23 pm

gregory wrote:Hi, got a similar if not same issue.

I want to show intraday candles, lines and shapes on a chart, but only for selected time windows of the day (not necessarily regular trading hours, but windows generally defined by application logic through data selection).

I'd like to have something more simple than segmented timeline, which just puts the shapes on the chart one after another in an equi-distant manner, no matter what the time is.
On the other hand, I guarantee, that the time of the series elements in the dataset is right, the time should just be shown on the axis with reasonable ticks.
I will also guarantee, that only those elements are part of the series, which shall be shown. As a resulting feature of the new timeline, the plot shall simply show every element in the series.
If there is any resulting non-linearity on the time/domain axis, I will handle this on the application level. The new axis shall just show the elements one by one and put appropriate tick marks on the bottom, even if there is a gap of say 3 days between two 10 min elements.

Any idea, which axis/timeline combination shall be used for this purpose?
Or how to fix the following to approach it?
You should be able to achieve the result I think you want by ditching DateAxis completely and using a org.jfree.chart.axis.SymbolAxis instead.

gregory
Posts: 4
Joined: Thu Dec 17, 2009 1:07 pm
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by gregory » Thu Dec 17, 2009 9:31 pm

Thanks, Skunk, I gave it a hack. Now I have to manage min/max date myself, because symbol axis only translates between integer and a (limited) list of symbol strings. Also I think, a time in ms as practically unbounded value does not fit into this.
For testing, I used some symbol values, nothing is shown at all on the chart at all.

Hmh, any other ideas?

For my part, I ' am dreaming of a more flexible, adaptive SegementedTimeline for DateAxis, which retains all the date range and axis labelling stuff.
Actually there must be a simple solution, because the problem is pretty straight forward. I looked into the time series / axis / timeline function split. This makes it difficult, because in timeline, I have no access to the sequential order information of the series, which is actually available in series. This I would need to compute a straight reversible index in function Timeline.toTimelineValue() based on the sequential order of the series...I need to sleep over it.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Re: Candlestick Timeline problem

Post by skunk » Thu Dec 17, 2009 10:12 pm

SymbolAxis assumes that your x values are 0, 1, 2 etc It is up to your code to generate an array containing the String formatted timestamps and supply them to the SymbolAxis constructor. Then the SymbolAxis merely interprets the xvalue 0,1,2 as an index into the array.

Another approach is to use a NumberAxis and a custom formatter. But it will require an extension to XYDataset to implement generically.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Re: Candlestick Timeline problem

Post by skunk » Sat Dec 19, 2009 12:54 am

Here is a reasonably generic way to implement it

Firstly, you need to subclass your existing XYDataset

Code: Select all

public class TranslatingDataset extends <WhateverXYDatsetYouAreUsing> {
    public double getXValue(int series, int item) {
        if ((item < 0) || (item >= getItemCount(series))
            return Double.NaN;
        return item;
    }
    public double getDisplayXValue(int series, int item) {
        if ((item < 0) || (item >= getItemCount(series))
            return Double.NaN;
        return super.getXValue(series, item);
    }
// These are commented out but you WILL need to implement them as soon as you
//  need to call setRange() and need to convert timevalues to data indexes
//
//    public int findFirstXValueGE(long timeval);
//    public int findLastXValueLE(long timeval);
}
Then you need a formatter that translates index to time

Code: Select all

public class TranslatingFormat extends DecimalFormat {
    final TranslatingDataset    xlateDS;
    final DateFormat fmt = new SimpleDateFormat("HH:mm:ss");

    public TranslatingFormat(TranslatingDataset ds) {
        xlateDS = ds;
    }
    public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
        if (Double.isNaN(number))
            return toAppendTo;
        double timeval = xlateDS.getDisplayXValue(0, (int)number);
        if (Double.isNaN(timeval)
            return toAppendTo;
        return toAppendTo.append(fmt.format(new Date((long)timeval)));
    }
}
Finally, replace the domain axis and install custom formatter

Code: Select all

NumberAxis axis = new NumberAxis();
xyplot = chartPanel.getChart().getXYPlot();
xyplot.setDomainAxis(axis);
axis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
axis.setNumberFormatOverride(new TranslatingFormat((TranslatingDataset)xyplot.getDataset()));
You will notice that the chart renders much faster because there is no DateAxis monster.

You lose all the advantages of a segmented timeline. The plot will just draw each item equally spaced across the domain axis regardless of whether the real time interval between them is 1 millisecond or 1 year. If there is data missing from your timeseries, there will be no visual gaps on the chart.

You lose the flexibility of the built in TickUnitSelection because the current implementation assumes an equal distribution of items to determine which tickunit to apply. Since the individual data items are now no longer symmetrically spaced along the time axis, it becomes very difficult to generically select the correct format.

Zooming / panning etc. works as expected.

Tooltip generation etc. needs to call getDisplayXValue() anywhere you are planning on displaying the formatted time of the item instead of the item index.

Enjoy 8)

gregory
Posts: 4
Joined: Thu Dec 17, 2009 1:07 pm
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by gregory » Mon Dec 21, 2009 2:55 pm

Thanks very much skunk, this looks very, very close to what I needed. I have some other things in the pipe the next days (family business etc), but will come back if I integrated it.

snid3ly
Posts: 23
Joined: Wed Aug 07, 2013 4:21 am
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by snid3ly » Wed Aug 07, 2013 7:19 am

What does "GE" and "LE" represent here?

Code: Select all

// These are commented out but you WILL need to implement them as soon as you
//  need to call setRange() and need to convert timevalues to data indexes
//
//    public int findFirstXValueGE(long timeval);
//    public int findLastXValueLE(long timeval);
Thanks

ricky87
Posts: 2
Joined: Wed Oct 02, 2013 4:43 am
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by ricky87 » Wed Oct 02, 2013 4:59 am

i also agree with lin my candlesticks are placed not right above the minute timelines, but between them! I mean each candle is placed in the XXminutes:30seconds position. Whats up?

csmith
Posts: 1
Joined: Sat Jan 18, 2014 8:18 pm
antibot: No, of course not.

Re: Candlestick Timeline problem

Post by csmith » Sat Jan 18, 2014 8:30 pm

Yesterday I got the TranslatingDataset (extending DefaultOHLCDataset) and TranslatingFormat to work well. The chart now displays 1, 5, 15, 30, 60 minute bars, daily bars and weekly bars with no gaps. Great stuff! But when I added back in the moving averages, each moving average as a new dataset, the moving averages show up really scrunched up on the right side of the chart and the price bars show up scrunched up on the left side of the chart. Any ideas what I am doing wrong. Here is the code to build the OHLC dataset:

Code: Select all

    private TranslatingDataset getDataSet(AnalyticsData analizer, int numBars) {
        setVisualBars(analizer, numBars);

    	int idx = numVisualBars;
    	OHLCDataItem[] data = new OHLCDataItem[numVisualBars];
    	
    	lowestLow = Double.MAX_VALUE;
    	highestHigh = Double.MIN_VALUE;
        for (int i=0; i<numVisualBars; ++i) {
            Date date = new Date(analizer.date[i]);
            OHLCDataItem item = new OHLCDataItem(date, analizer.open[i], analizer.high[i], analizer.low[i], 
            		analizer.close[i], analizer.volume[i]);
            data[--idx] = item;
            if (analizer.low[i] < lowestLow) {
            	lowestLow = analizer.low[i]; 
            }
            if (analizer.high[i] > highestHigh) {
            	highestHigh = analizer.high[i]; 
            }
            Logger.log(Logger.EVENT_LOG, "Price", "D=", date, "h=", analizer.high[i], "l=", analizer.low[i]);
        }
 		TranslatingDataset result = new TranslatingDataset(analizer.getTicker(), data);
        return result;
    }
Here is the code for the moving average time series. Note I have tried many different 'date', 'day', Millisecond, FixedMillisecond objects for the first arg when adding MA data to the time series.

Code: Select all

   protected TimeSeries getEMATimeSeries(AnalyticsData analizer, int period) {
    	TimeSeries ts = new TimeSeries("EMA-" + period);
    	double[] data = null;
    	switch (period) {
    	case 8: data = analizer.ema8; break;
    	case 21: data = analizer.ema21; break;
    	case 34: data = analizer.ema34; break;
    	case 55: data = analizer.ema55; break;
    	}
    	if (data != null) {
	    	for (int i=numVisualBars-1; i>=0; --i) {
                //cal.setTimeInMillis(analizer.date[i]);
                //Day day = new Day(cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR));

	    		Date date = new Date(analizer.date[i]);
	    		Logger.log(Logger.EVENT_LOG, "Adding ema for", date);
	    		ts.add(new FixedMillisecond(date), data[i]);
	    	}
    	}
    	return ts;
    }
Any ideas would be greatly appreciated.

Locked