天天看點

k線圖解決方案

在進行源代碼講解之前,有必要将本人所領悟到的一些關于jfreechart的概念講述下,jfreechart工具基本上把一個圖形分成了幾個邏輯部分。 1.首先是整個的畫布對象(chart),畫布就相當與一個大的容器,包含一些圖形元素,比如為一張白紙

2.然後是畫圖區域對象(plot),就是實際圖形所占據的區域,比如為白紙上的一個長方形

3.然後是圖形所需要兩條軸對象(axis),比如為白紙上的兩條軸線

4.最後是如何進行圖像的繪制對象(render),比如為一隻畫圖的筆

5.除開上面的這些對象,還有一個最主要的就是通過必須的資料集(dataset)來生成圖形了

至于一些具體的對象細節,就隻好請各位去閱讀相關的API文檔了,在這裡就不詳細的說明

package chart;
import java.awt.Color;//顔色系統
import java.text.SimpleDateFormat;//時間格式
import java.awt.Paint;//畫筆系統
import org.jfree.data.time.*;
import org.jfree.data.time.Day;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.*;
public class KLineCombineChart {
public static void main(String[] args) {
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");//設定日期格式
     double highValue = Double.MIN_VALUE;//設定K線資料當中的最大值
     double minValue = Double.MAX_VALUE;//設定K線資料當中的最小值
     double high2Value = Double.MIN_VALUE;//設定成交量的最大值
     double min2Value = Double.MAX_VALUE;//設定成交量的最低值
     OHLCSeries series = new OHLCSeries("");//高開低收資料序列,股票K線圖的四個資料,依次是開,高,低,收
     series.add(new Day(28, 9, 2007), 9.2, 9.58, 9.16, 9.34);
     series.add(new Day(27, 9, 2007), 8.9, 9.06, 8.83, 8.96);
     series.add(new Day(26, 9, 2007), 9.0, 9.1, 8.82, 9.04);
     series.add(new Day(25, 9, 2007), 9.25, 9.33, 8.88, 9.00);
     series.add(new Day(24, 9, 2007), 9.05, 9.50, 8.91, 9.25);
     series.add(new Day(21, 9, 2007), 8.68, 9.05, 8.40, 9.00);
     series.add(new Day(20, 9, 2007), 8.68, 8.95, 8.50, 8.69);
     series.add(new Day(19, 9, 2007), 8.80, 8.94, 8.50, 8.66);
     series.add(new Day(18, 9, 2007), 8.88, 9.17, 8.69, 8.80);
     series.add(new Day(17, 9, 2007), 8.26, 8.98, 8.15, 8.89);
     series.add(new Day(14, 9, 2007), 8.44, 8.45, 8.13, 8.33);
     series.add(new Day(13, 9, 2007), 8.13, 8.46, 7.97, 8.42);
     series.add(new Day(12, 9, 2007), 8.2, 8.4, 7.81, 8.13);
     series.add(new Day(11, 9, 2007), 9.0, 9.0, 8.1, 8.24);
     series.add(new Day(10, 9, 2007), 8.6, 9.03, 8.40, 8.95);
     series.add(new Day(7, 9, 2007), 8.89, 9.04, 8.70, 8.73);
     series.add(new Day(6, 9, 2007), 8.4, 9.08, 8.33, 8.88);
     series.add(new Day(5, 9, 2007), 8.2, 8.74, 8.17, 8.36);
     series.add(new Day(4, 9, 2007), 7.7, 8.46, 7.67, 8.27);
     series.add(new Day(3, 9, 2007), 7.5, 7.8, 7.48, 7.69);
     series.add(new Day(31, 8, 2007), 7.4, 7.6, 7.28, 7.43);
     series.add(new Day(30, 8, 2007), 7.42, 7.56, 7.31, 7.40);
     series.add(new Day(29, 8, 2007), 7.42, 7.66, 7.22, 7.33);
     series.add(new Day(28, 8, 2007), 7.31, 7.70, 7.15, 7.56);
     series.add(new Day(27, 8, 2007), 7.05, 7.46, 7.02, 7.41);
     series.add(new Day(24, 8, 2007), 7.05, 7.09, 6.90, 6.99);
     series.add(new Day(23, 8, 2007), 7.12, 7.16, 7.00, 7.03);
     series.add(new Day(22, 8, 2007), 6.96, 7.15, 6.93, 7.11);
     series.add(new Day(21, 8, 2007), 7.10, 7.15, 7.02, 7.07);
     series.add(new Day(20, 8, 2007), 7.02, 7.19, 6.94, 7.14);
     final OHLCSeriesCollection seriesCollection = new OHLCSeriesCollection();//保留K線資料的資料集,必須申明為final,後面要在匿名内部類裡面用到
     seriesCollection.addSeries(series);
     TimeSeries series2=new TimeSeries("");//對應時間成交量資料
     series2.add(new Day(28, 9, 2007), 260659400/100);
     series2.add(new Day(27, 9, 2007), 119701900/100);
     series2.add(new Day(26, 9, 2007), 109719000/100);
     series2.add(new Day(25, 9, 2007), 178492400/100);
     series2.add(new Day(24, 9, 2007), 269978500/100);
     series2.add(new Day(21, 9, 2007), 361042300/100);
     series2.add(new Day(20, 9, 2007), 173912600/100);
     series2.add(new Day(19, 9, 2007), 154622600/100);
     series2.add(new Day(18, 9, 2007), 200661600/100);
     series2.add(new Day(17, 9, 2007), 312799600/100);
     series2.add(new Day(14, 9, 2007), 141652900/100);
     series2.add(new Day(13, 9, 2007), 221260400/100);
     series2.add(new Day(12, 9, 2007), 274795400/100);
     series2.add(new Day(11, 9, 2007), 289287300/100);
     series2.add(new Day(10, 9, 2007), 289063600/100);
     series2.add(new Day(7, 9, 2007), 351575300/100);
     series2.add(new Day(6, 9, 2007), 451357300/100);
     series2.add(new Day(5, 9, 2007), 442421200/100);
     series2.add(new Day(4, 9, 2007), 671942600/100);
     series2.add(new Day(3, 9, 2007), 349647800/100);
     series2.add(new Day(31, 8, 2007), 225339300/100);
     series2.add(new Day(30, 8, 2007), 160048200/100);
     series2.add(new Day(29, 8, 2007), 247341700/100);
     series2.add(new Day(28, 8, 2007), 394975400/100);
     series2.add(new Day(27, 8, 2007), 475797500/100);
     series2.add(new Day(24, 8, 2007), 297679500/100);
     series2.add(new Day(23, 8, 2007), 191760600/100);
     series2.add(new Day(22, 8, 2007), 232570200/100);
     series2.add(new Day(21, 8, 2007), 215693200/100);
     series2.add(new Day(20, 8, 2007), 200287500/100);
     TimeSeriesCollection timeSeriesCollection=new TimeSeriesCollection();//保留成交量資料的集合
     timeSeriesCollection.addSeries(series2);
  
     //擷取K線資料的最高值和最低值
     int seriesCount = seriesCollection.getSeriesCount();//一共有多少個序列,目前為一個
     for (int i = 0; i < seriesCount; i++) {
      int itemCount = seriesCollection.getItemCount(i);//每一個序列有多少個資料項
      for (int j = 0; j < itemCount; j++) {
       if (highValue < seriesCollection.getHighValue(i, j)) {//取第i個序列中的第j個資料項的最大值
        highValue = seriesCollection.getHighValue(i, j);
       }
       if (minValue > seriesCollection.getLowValue(i, j)) {//取第i個序列中的第j個資料項的最小值
        minValue = seriesCollection.getLowValue(i, j);
       }
      }
     }
     //擷取最高值和最低值
     int seriesCount2 = timeSeriesCollection.getSeriesCount();//一共有多少個序列,目前為一個
     for (int i = 0; i < seriesCount2; i++) {
      int itemCount = timeSeriesCollection.getItemCount(i);//每一個序列有多少個資料項
      for (int j = 0; j < itemCount; j++) {
       if (high2Value < timeSeriesCollection.getYValue(i,j)) {//取第i個序列中的第j個資料項的值
        high2Value = timeSeriesCollection.getYValue(i,j);
       }
       if (min2Value > timeSeriesCollection.getYValue(i, j)) {//取第i個序列中的第j個資料項的值
        min2Value = timeSeriesCollection.getYValue(i, j);
       }
      }
     }
     final CandlestickRenderer candlestickRender=new CandlestickRenderer();//設定K線圖的畫圖器,必須申明為final,後面要在匿名内部類裡面用到
     candlestickRender.setUseOutlinePaint(true); //設定是否使用自定義的邊框線,程式自帶的邊框線的顔色不符合中國股票市場的習慣
     candlestickRender.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_AVERAGE);//設定如何對K線圖的寬度進行設定
     candlestickRender.setAutoWidthGap(0.001);//設定各個K線圖之間的間隔
     candlestickRender.setUpPaint(Color.RED);//設定股票上漲的K線圖顔色
     candlestickRender.setDownPaint(Color.GREEN);//設定股票下跌的K線圖顔色
     DateAxis x1Axis=new DateAxis();//設定x軸,也就是時間軸
     x1Axis.setAutoRange(false);//設定不采用自動設定時間範圍
     try{
      x1Axis.setRange(dateFormat.parse("2007-08-20"),dateFormat.parse("2007-09-29"));//設定時間範圍,注意時間的最大值要比已有的時間最大值要多一天
     }catch(Exception e){
      e.printStackTrace();
     }
     x1Axis.setTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());//設定時間線顯示的規則,用這個方法就摒除掉了周六和周日這些沒有交易的日期(很多人都不知道有此方法),使圖形看上去連續
     x1Axis.setAutoTickUnitSelection(false);//設定不采用自動選擇刻度值
     x1Axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);//設定标記的位置
     x1Axis.setStandardTickUnits(DateAxis.createStandardDateTickUnits());//設定标準的時間刻度機關
     x1Axis.setTickUnit(new DateTickUnit(DateTickUnit.DAY,7));//設定時間刻度的間隔,一般以周為機關
     x1Axis.setDateFormatOverride(new SimpleDateFormat("yyyy-MM-dd"));//設定顯示時間的格式
     NumberAxis y1Axis=new NumberAxis();//設定y軸,就是數字軸
     y1Axis.setAutoRange(false);//不不使用自動設定範圍
     y1Axis.setRange(minValue*0.9, highValue*1.1);//設定y軸值的範圍,比最低值要低一些,比最大值要大一些,這樣圖形看起來會美觀些
     y1Axis.setTickUnit(new NumberTickUnit((highValue*1.1-minValue*0.9)/10));//設定刻度顯示的密度
     XYPlot plot1=new XYPlot(seriesCollection,x1Axis,y1Axis,candlestickRender);//設定畫圖區域對象
  
     XYBarRenderer xyBarRender=new XYBarRenderer(){
     private static final long serialVersionUID = 1L;//為了避免出現警告消息,特設定此值
     public Paint getItemPaint(int i, int j){//匿名内部類用來處理當日的成交量柱形圖的顔色與K線圖的顔色保持一緻
       if(seriesCollection.getCloseValue(i,j)>seriesCollection.getOpenValue(i,j)){//收盤價高于開盤價,股票上漲,選用股票上漲的顔色
        return candlestickRender.getUpPaint();
       }else{
        return candlestickRender.getDownPaint();
       }
     }};
     xyBarRender.setMargin(0.1);//設定柱形圖之間的間隔
     NumberAxis y2Axis=new NumberAxis();//設定Y軸,為數值,後面的設定,參考上面的y軸設定
     y2Axis.setAutoRange(false);
     y2Axis.setRange(min2Value*0.9, high2Value*1.1);
     y2Axis.setTickUnit(new NumberTickUnit((high2Value*1.1-min2Value*0.9)/4));
     XYPlot plot2=new XYPlot(timeSeriesCollection,null,y2Axis,xyBarRender);//建立第二個畫圖區域對象,主要此時的x軸設為了null值,因為要與第一個畫圖區域對象共享x軸
     CombinedDomainXYPlot combineddomainxyplot = new CombinedDomainXYPlot(x1Axis);//建立一個恰當的聯合圖形區域對象,以x軸為共享軸
     combineddomainxyplot.add(plot1, 2);//添加圖形區域對象,後面的數字是計算這個區域對象應該占據多大的區域2/3
        combineddomainxyplot.add(plot2, 1);//添加圖形區域對象,後面的數字是計算這個區域對象應該占據多大的區域1/3
        combineddomainxyplot.setGap(10);//設定兩個圖形區域對象之間的間隔空間
        JFreeChart chart = new JFreeChart("中國聯通", JFreeChart.DEFAULT_TITLE_FONT, combineddomainxyplot, false);
        ChartFrame frame = new ChartFrame("中國聯通股票", chart);
     frame.pack();
     frame.setVisible(true);
}
}
           

為了能夠保證K線圖的上影線和下影線能夠和K線圖本身的顔色保持一緻,找了很多的方法設定都不成功,可以說目前暫不提供單獨繪制K線邊框顔色的方法,是以,經過本人的實驗,對其中的源代碼進行了下修改,具體修改的地方是在下載下傳回來的檔案目中source\org\jfree\chart\renderer\xy\CandlestickRenderer.java檔案,将

Paint p = getItemPaint(series, item);

Paint outlinePaint = null;

outlinePaint = getItemOutlinePaint(series, item);

}

修改為如下的代碼,這樣就會保證K線圖的上影線和下影線能夠和K線圖本身的顔色保持一緻,符合中國股票市場K線圖形顔色的習慣:

Paint p = getItemPaint(series, item);

Paint outlinePaint = null;

if (this.useOutlinePaint) {

if(yClose>yOpen){

if (this.upPaint != null) {

outlinePaint=this.upPaint;

}

else {

outlinePaint=p;

}

}else{

if (this.downPaint != null) {

outlinePaint=this.downPaint;

}

else {

outlinePaint=p;

}

}

}

然後使用ant內建編譯工具,編譯重新生成最新的開發包就可以了,如果各位在使用過程中遇到什麼問題,歡迎留言探讨,也希望這個執行個體對大家有所幫助,等有時間了再給大家介紹下jpgraph的K線圖的繪制,敬請期待,呵呵!

最終的效果圖如下: