highstock K线图 深入研究

好久没写博客了,这次来个质量点的。

K线图,相信每个股民都不陌生,如何用SVG画好一个K线图是一个难题。

我选择用highstock做为画图组件,适当的修改了一下源码,参考了数个财经网站的案例,完成了一个不太成熟的K线图,欢迎大家批评指正。

highstock K线图 深入研究_第1张图片


highstock K线图 深入研究_第2张图片



上图就是整个K线图的样子,图的上半部分是K线图和5日均线,10日均线,30日均线,下半部分是成交量,用柱状图显示,tooltips显示了用户选择点的股票指标,所有颜色符合红涨绿跌的原则。

实现的功能主要有:

1.根据用户选择的时间区间,显示最高价和最低价。

2.点击最高价或最低价的flags会显示出相应的时间。

3.动态改变X轴时间显示格式(%Y       %Y-%m        %m-%d),防止样式重叠在一起。

4. 动态改变Y轴的最大值最小值,防止K线图画出去。

5.根据当前点的开盘价和收盘价改变柱状图的颜色。

6.本地化一些常量,本地化日期格式。

7.根据鼠标指向的当前点的位置。动态改变tooltip的位置


下面附上源码

//highstock K线图
var highStockChart = function(divID,result,crrentData){
	var $reporting = $("#report");
	var firstTouch = true;
	//开盘价^最高价^最低价^收盘价^成交量^成交额^涨跌幅^换手率^五日均线^十日均线^20日均线^30日均线^昨日收盘价 ^当前点离左边的相对距离
	var  open,high,low,close,y,zde,zdf,hsl,MA5,MA10,MA20,MA30,zs,relativeWidth; 
	//定义数组
	var ohlcArray = [],volumeArray = [],MA5Array = [],MA10Array=[],MA20Array=[],MA30Array=[],zdfArray=[],zdeArray=[],hslArray=[],data=[],dailyData = [],data =[];	
	/*
	 * 这个方法用来控制K线上的flags的显示情况,当afterSetExtremes时触发该方法,通过flags显示当前时间区间最高价和最低价
	 * minTime  当前k线图上最小的时间点
	 * maxTime  当前k线图上最大的时间点
	 * chart  当前的highstock对象
	 */
	var showTips = 	function (minTime,maxTime,chart){
	//	console.log( Highcharts.dateFormat('%Y-%m-%d %H:%M',minTime));
	//	console.log( Highcharts.dateFormat('%Y-%m-%d %H:%M',maxTime));
		chart.showLoading();
		//定义当前时间区间中最低价的最小值,最高价的最大值 以及对应的时间
		var lowestPrice,highestPrice,array=[],highestArray=[],lowestArray=[],highestTime,lowestTime,flagsMaxData_1=[],flagsMaxData_2=[],flagsMinData_1,flagsMinData_2; 
//		var chartData = chart.series[0].data;
//		for(var i=0;iminTime && chartData[i].x<=maxTime){
//				array.push([
//				            chartData[i].x,
//				            chartData[i].high, //最高价
//				            chartData[i].low //最低价
//				            ])
//			}
//		}
		for(var i=0;i=minTime && ohlcArray[i][0]<=maxTime){
				array.push([
				            ohlcArray[i][0],
				            ohlcArray[i][2], //最高价
				            ohlcArray[i][3] //最低价
				            ])
			}
		}
		if(!array.length>0){
			return;
		}
		highestArray = array.sort(function(x, y){  return y[1] - x[1];})[0];// 根据最高价降序排列
		highestTime =highestArray[0];  
		highestPrice =highestArray[1].toFixed(2);  
		lowestArray = array.sort(function(x, y){  return x[2] - y[2];})[0]; //根据最低价升序排列
		lowestTime =lowestArray[0];  
		lowestPrice =lowestArray[2].toFixed(2); 
		var formatDate1 = Highcharts.dateFormat('%Y-%m-%d',highestTime)
		var formatDate2 = Highcharts.dateFormat('%Y-%m-%d',lowestTime)
		flagsMaxData_1 = [
		               			{
		               			 x : highestTime,
		               			title : highestPrice+"("+formatDate1+")"
		               			}
		               		];
		
		flagsMaxData_2 = [
					               {
					                x : highestTime,
					                title : highestPrice
					               }
		               ];
		flagsMinData_1 = [
		                  {
		                	  x : lowestTime,
		                	  title : lowestPrice+"("+formatDate2+")"
		                  }
		                  ];
		
		flagsMinData_2 = [
		               {
		            	   x : lowestTime,
		            	   title : lowestPrice
		               }
		               ];
		var min =  parseFloat(flagsMinData_2[0].title) - parseFloat(flagsMinData_2[0].title)*0.05;
		var max =  parseFloat(flagsMaxData_2[0].title)+parseFloat(flagsMaxData_2[0].title)*0.05;
		var tickInterval = (( max-min)/5).toFixed(1)*1;
		var oneMonth = 1000*3600*24*30;
		var oneYear = 1000*3600*24*365;
		var tickIntervalTime,dataFormat='%Y-%m';
		if(maxTime-minTime>oneYear*2){
			tickIntervalTime = oneYear*2
			dataFormat = '%Y';
		}else if(maxTime-minTime>oneYear){
			tickIntervalTime = oneMonth*6
		}else if(maxTime-minTime>oneMonth*6){
			tickIntervalTime = oneMonth*3
		}else{
			tickIntervalTime = oneMonth
			dataFormat = '%m-%d'
		}
			
		//Y轴坐标自适应
		 chart.yAxis[0].update({
	    	   	min : min,
	    	   	max : max,
	    	   	tickInterval: tickInterval
	       });
		//X轴坐标自适应
		 chart.xAxis[0].update({
			 min : minTime,
			 max : maxTime,
			 tickInterval: tickIntervalTime,
			 labels: {
				   	y:-78,//调节y偏移
	             formatter: function(e) {
	             		 return Highcharts.dateFormat(dataFormat, this.value);
	             }
	         }
		 });
	 //动态update flags(最高价)
       chart.series[5].update({
    	   data : flagsMaxData_2,
            point:{
         	   events:{
         		  click:function(){
	         			 chart.series[5].update({
	         					data : flagsMaxData_1,
	         					width : 100
	         			 });
	         			 chart.series[6].update({
	         					data : flagsMinData_1,
	         					width : 100
	         			 });
                    }
                }
         },
         events:{
             mouseOut:function(){
		             	 chart.series[5].update({
		   					data :flagsMaxData_2,
		   					width : 25
		             	 });
		             	 chart.series[6].update({
			   					data :flagsMinData_2,
			   					width : 25
			   			 });
             	}
         	}
		});
       
       //动态update flags(最低价)
       chart.series[6].update({
    		data : flagsMinData_2,
    		   point:{
             	   events:{
             		  click:function(){
             			 chart.series[6].update({
	         					data : flagsMinData_1,
	         					width : 100
	         			 });
    	         			 chart.series[5].update({
    	         					data : flagsMaxData_1,
    	         					width : 100
    	         			 });
                        }
                    }
             },
             events:{
                 mouseOut:function(){
		                	 chart.series[6].update({
				   					data :flagsMinData_2,
				   					width : 25
				   			 });
    		             	 chart.series[5].update({
    		   					data :flagsMaxData_2,
    		   					width : 25
    		             	 });
                 	}
             	}
		});
   	chart.hideLoading();
	}
	
	  //修改colum条的颜色(重写了源码方法)
	 var originalDrawPoints = Highcharts.seriesTypes.column.prototype.drawPoints;
	    Highcharts.seriesTypes.column.prototype.drawPoints = function () {
	        var merge  = Highcharts.merge,
	            series = this,
	            chart  = this.chart,
	            points = series.points,
	            i      = points.length;
	        
	        while (i--) {
	            var candlePoint = chart.series[0].points[i];
	            if(candlePoint.open != undefined && candlePoint.close !=  undefined){  //如果是K线图 改变矩形条颜色,否则不变
		            var color = (candlePoint.open < candlePoint.close) ? '#DD2200' : '#33AA11';
		            var seriesPointAttr = merge(series.pointAttr);
		            seriesPointAttr[''].fill = color;
		            seriesPointAttr.hover.fill = Highcharts.Color(color).brighten(0.3).get();
		            seriesPointAttr.select.fill = color;
	            }else{
	            	var seriesPointAttr = merge(series.pointAttr);
	            }
	            
	            points[i].pointAttr = seriesPointAttr;
	        }
	
	        originalDrawPoints.call(this);
	    }

	//常量本地化
	Highcharts.setOptions({
		global : {
			useUTC : false
		},
		lang: {
			rangeSelectorFrom:"日期:",
			rangeSelectorTo:"至",
			rangeSelectorZoom:"范围",
			loading:'加载中...',
			/*decimalPoint:'.',
         downloadPNG:'下载PNG图片',
         downloadJPEG:'下载JPG图片',
         downloadPDF:'下载PDF文件',
         exportButtonTitle:'导出...',
         printButtonTitle:'打印图表',
         resetZoom:'还原图表',
         resetZoomTitle:'还原图表为1:1大小',
         thousandsSep:',',*/
		shortMonths:['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
         weekdays:['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
		},
	});
	//格式化数据,准备绘图 
	dailyData = result.vl.split("~");	
	for(i=0;i
'; tip +=stockName+"
"; if(open>zs){ tip += '开盘价:'+open+'
'; }else{ tip += '开盘价:'+open+'
'; } if(high>zs){ tip += '最高价:'+high+'
'; }else{ tip += '最高价:'+high+'
'; } if(low>zs){ tip += '最低价:'+low+'
'; }else{ tip += '最低价:'+low+'
'; } if(close>zs){ tip += '收盘价:'+close+'
'; }else{ tip += '收盘价:'+close+'
'; } if(zde>0){ tip += '涨跌额:'+zde+'
'; }else{ tip += '涨跌额:'+zde+'
'; } if(zdf>0){ tip += '涨跌幅:'+zdf+'
'; }else{ tip += '涨跌幅:'+zdf+'
'; } if(y>10000){ tip += "成交量:"+(y*0.0001).toFixed(2)+"(亿股)
"; }else{ tip += "成交量:"+y+"(万股)
"; } /* tip += "换手率:"+hsl+"
";*/ $reporting.html( ' '+stockName+'' + ' 开盘:'+ open +' 收盘:'+close +' 最高:'+ high +' 最低:'+ low +' '+ Highcharts.dateFormat('%Y-%m-%d',this.x) +'
MA5 '+ MA5 +' MA10 '+ MA10 +' MA30 '+ MA30 ); return tip; }, //crosshairs: [true, true]//双线 crosshairs: { dashStyle: 'dash' }, borderColor: 'white', positioner: function () { //设置tips显示的相对位置 var halfWidth = this.chart.chartWidth/2;//chart宽度 var width = this.chart.chartWidth-155; var height = this.chart.chartHeight/5-8;//chart高度 if(relativeWidth
html页面调用的话需要这样写

其中container是highstock的renderID , retTrade是需要组装的数组,crrentData是当天需要实时更新的数组(也就是图上的最后一个点,需要过一段时间更新一遍,因为当天的K线指标一直在变)
retTrade的数据格式如下:

{日期^1开盘价^2收盘价^3最高价^4最低价^5成交量^6成交额^7涨跌幅^8涨跌额^9换手率^10昨日收盘价^11MA5^12MA10^13MA20^14MA30^15MA60}
crrentData的数据格式如下:

{日期^1开盘价^2收盘价^3最高价^4最低价^5成交量^MA5^MA10^MA20^MA30}

当然您也可以自定义,只需要把js中的相应数组下标调整好就可以了。

最后,别忘了引入highstock.js和较高版本的JQUERY,good luck!


附上demo,点开html就能看到效果


你可能感兴趣的:(highcharts,web前端)