highstock K线图 深入研究
K线图,相信每个股民都不陌生,如何用SVG画好一个K线图是一个难题。
我选择用highstock做为画图组件,适当的修改了一下源码,参考了数个财经网站的案例,完成了一个不太成熟的K线图,欢迎大家批评指正。
上图就是整个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;i<chartData.length-1;i++){
// if(chartData[i].x>minTime && chartData[i].x<=maxTime){
// array.push([
// chartData[i].x,
// chartData[i].high, //最高价
// chartData[i].low //最低价
// ])
// }
// }
for(var i=0;i<ohlcArray.length-1;i++){
if(ohlcArray[i][0]>=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<dailyData.length-1;i++){
data[i] = dailyData[i].split("^");
}
//把当前最新K线数据加载进来
var length = data.length-1;
var time = parseFloat(data[length][0]);
var crrentTime = crrentData[0];
// if(!(isNaN(crrentData[1]) || isNaN(crrentData[2]) || isNaN(crrentData[3]) || isNaN(crrentData[4]))){
// if(crrentData[1]!=0 || crrentData[2]!=0 || crrentData[3]!=0 || crrentData[4]!=0){
// if(time < crrentTime){
// data.push(crrentData);
// }else if(time == crrentTime){
// data[length] = crrentData;
// }
// }
// } for (i = 0; i < data.length; i++) {
// console.log( Highcharts.dateFormat('%A ,%Y-%m-%d %H:%M',parseInt(data[i][0])));
ohlcArray.push([
parseInt(data[i][0]), // the date
parseFloat(data[i][1]), // open
parseFloat(data[i][3]), // high
parseFloat(data[i][4]), // low
parseFloat(data[i][2]) // close
]); MA5Array.push([
parseInt(data[i][0]), // the date
parseFloat(data[i][11])
]); MA10Array.push([
parseInt(data[i][0]),
parseFloat(data[i][12]),
]);
MA20Array.push([
parseInt(data[i][0]),
parseFloat(data[i][13]),
])
MA30Array.push([
parseInt(data[i][0]),
parseFloat(data[i][14])
]);
volumeArray.push([
parseInt(data[i][0]), // the date
parseInt(data[i][5]) // 成交量
]);
} //开始绘图
return new Highcharts.StockChart( {
chart:{
renderTo : divID,
margin: [30, 30,30, 30],
plotBorderColor: '#3C94C4',
plotBorderWidth: 0.3,
events:{
load:function(){
var length = ohlcArray.length-1;
showTips(ohlcArray[0][0],ohlcArray[length][0],this);
}
}
},
loading: {
labelStyle: {
position: 'relative',
top: '10em',
zindex:1000
}
},
credits:{
enabled:false
},
rangeSelector: {
// selected: 1,
// buttons: [{
// type: 'month',
// count: 1,
// text: '1月'
// }, {
// type: 'month',
// count: 2,
// text: '2月'
// },{
// type: 'all',
// text: 'All'
// }],
enabled:false,
inputDateFormat: '%Y-%m-%d' //设置右上角的日期格式
},
plotOptions: {
//修改蜡烛颜色
candlestick: {
color: '#33AA11',
upColor: '#DD2200',
lineColor: '#33AA11',
upLineColor: '#DD2200',
maker:{
states:{
hover:{
enabled:false,
}
}
}
},
//去掉曲线和蜡烛上的hover事件
series: {
states: {
hover: {
enabled: false
}
},
line: {
marker: {
enabled: false
}
}
}
},
//格式化悬浮框
tooltip: {
formatter: function() {
if(this.y == undefined){
return;
}
for(var i =0;i<data.length;i++){
if(this.x == data[i][0]){
zdf = parseFloat(data[i][7]).toFixed(2);
zde = parseFloat(data[i][8]).toFixed(2);
// hsl = parseFloat(data[i][9]).toFixed(2);
zs = parseFloat(data[i][10]).toFixed(2);
}
}
open = this.points[0].point.open.toFixed(2);
high = this.points[0].point.high.toFixed(2);
low = this.points[0].point.low.toFixed(2);
close = this.points[0].point.close.toFixed(2);
y = (this.points[1].point.y*0.0001).toFixed(2);
MA5 =this.points[2].y.toFixed(2);
MA10 =this.points[3].y.toFixed(2);
MA30 =this.points[4].y.toFixed(2);
relativeWidth = this.points[0].point.shapeArgs.x;
var stockName = this.points[0].series.name;
var tip= '<b>'+ Highcharts.dateFormat('%Y-%m-%d %A', this.x) +'</b><br/>';
tip +=stockName+"<br/>";
if(open>zs){
tip += '开盘价:<span style="color: #DD2200;">'+open+' </span><br/>';
}else{
tip += '开盘价:<span style="color: #33AA11;">'+open+' </span><br/>';
}
if(high>zs){
tip += '最高价:<span style="color: #DD2200;">'+high+' </span><br/>';
}else{
tip += '最高价:<span style="color: #33AA11;">'+high+' </span><br/>';
}
if(low>zs){
tip += '最低价:<span style="color: #DD2200;">'+low+' </span><br/>';
}else{
tip += '最低价:<span style="color: #33AA11;">'+low+' </span><br/>';
}
if(close>zs){
tip += '收盘价:<span style="color: #DD2200;">'+close+' </span><br/>';
}else{
tip += '收盘价:<span style="color: #33AA11;">'+close+' </span><br/>';
}
if(zde>0){
tip += '涨跌额:<span style="color: #DD2200;">'+zde+' </span><br/>';
}else{
tip += '涨跌额:<span style="color: #33AA11;">'+zde+' </span><br/>';
}
if(zdf>0){
tip += '涨跌幅:<span style="color: #DD2200;">'+zdf+' </span><br/>';
}else{
tip += '涨跌幅:<span style="color: #33AA11;">'+zdf+' </span><br/>';
}
if(y>10000){
tip += "成交量:"+(y*0.0001).toFixed(2)+"(亿股)<br/>";
}else{
tip += "成交量:"+y+"(万股)<br/>";
}
/* tip += "换手率:"+hsl+"<br/>";*/
$reporting.html(
' <span style="font-weight:bold">'+stockName+'</span>'
+ ' <span>开盘:</span>'+ open
+' <span>收盘:</span>'+close
+' <span>最高:</span>'+ high
+' <span>最低:</span>'+ low
+' <span style="padding-left:25px;"> </span>'+ Highcharts.dateFormat('%Y-%m-%d',this.x)
+' <br/><b style="color:#1aadce;padding-left:25px">MA5</b> '+ MA5
+' <b style="color: #8bbc21;padding-left:150px">MA10 </b> '+ MA10
+' <b style="color:#910000;padding-left:150px">MA30</b> '+ 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<halfWidth){
return { x: width, y:height };
}else{
return { x: 30, y: height };
}
},
shadow: false
},
title: {
enabled:false
},
exporting: {
enabled: false //设置导出按钮不可用
},
scrollbar: {
barBackgroundColor: 'gray',
barBorderRadius: 7,
barBorderWidth: 0,
buttonBackgroundColor: 'gray',
buttonBorderWidth: 0,
buttonArrowColor: 'yellow',
buttonBorderRadius: 7,
rifleColor: 'yellow',
trackBackgroundColor: 'white',
trackBorderWidth: 1,
trackBorderColor: 'silver',
trackBorderRadius: 7,
//enabled: false,
liveRedraw: false //设置scrollbar在移动过程中,chart不会重绘
},
navigator: {
adaptToUpdatedData: false,
xAxis: {
labels: {
formatter: function(e) {
return Highcharts.dateFormat('%m-%d', this.value);
}
}
},
handles: {
backgroundColor: '#808080',
// borderColor: '#268FC9'
},
margin:-10
},
xAxis: {
type: 'datetime',
tickLength: 0,//X轴下标长度
// minRange: 3600 * 1000*24*30, // one month
events: {
afterSetExtremes: function(e) {
var minTime = Highcharts.dateFormat("%Y-%m-%d", e.min);
var maxTime = Highcharts.dateFormat("%Y-%m-%d", e.max);
var chart = this.chart;
showTips(e.min,e.max,chart);
}
}
},
yAxis: [{
title: {
enable:false
},
height: '70%',
lineWidth:1,//Y轴边缘线条粗细
gridLineColor: '#346691',
gridLineWidth:0.1,
// gridLineDashStyle: 'longdash',
opposite:true
},{
title: {
enable:false
},
top: '75%',
height: '25%',
labels:{
x:-15
},
gridLineColor: '#346691',
gridLineWidth:0.1,
lineWidth: 1,
}],
series: [
{
type: 'candlestick',
id:"candlestick",
name: result.cname,
data: ohlcArray,
dataGrouping: {
enabled: false
}
}
,{
type: 'column',//2
name: '成交量',
data: volumeArray,
yAxis: 1,
dataGrouping: {
enabled: false
}
} ,{
type: 'spline',
name: 'MA5',
color:'#1aadce',
data: MA5Array,
lineWidth:1,
dataGrouping: {
enabled: false
}
},{
type: 'spline',
name: 'MA10',
data: MA10Array,
color:'#8bbc21',
threshold: null,
lineWidth:1,
dataGrouping: {
enabled: false
}
},{
type: 'spline',
name: 'MA30',
data: MA30Array,
color:'#910000',
threshold: null,
lineWidth:1,
dataGrouping: {
enabled: false
}
},{
type : 'flags',
cursor:'pointer',
style:{
fontSize: '11px',
fontWeight: 'normal',
textAlign: 'center'
},
lineWidth:0.5,
onSeries : 'candlestick',
width : 25,
shape: 'squarepin'
},{
type : 'flags',
cursor:'pointer',
y: 33,
style:{
fontSize: '11px',
fontWeight: 'normal',
textAlign: 'center'
},
lineWidth:0.5,
onSeries : 'candlestick',
width : 25,
shape: 'squarepin'
}
]
});
}
html页面调用的话需要这样写
- <script>
- new highStockChart('container',retTrade,crrentData);
- </script>
- <div id="container" style="height: 400px;width: 545px">
其中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!
highstock K线图 深入研究的更多相关文章
- Highstock生成股票K线图
在线演示 本地下载 使用HightStock生成股票K线图例子.
- Highcharts candlestick(K线图)案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 百度 echarts K线图使用
看个效果图先 首先在需要插入图例的HTML中嵌入 <div id="main" style="height:400px"></div> ...
- PHP使用HighChart生成股票K线图详解
本人qq群也有许多的技术文档,希望可以为你提供一些帮助(非技术的勿加). QQ群: 281442983 (点击链接加入群:http://jq.qq.com/?_wv=1027&k=29Lo ...
- K线图学习
本博文(适合入门的股民朋友)内容来自网络,股市有风险,入市需谨慎 一.起源 K线图(Candlestick Charts)又称蜡烛图.日本线.阴阳线.棒线等,常用说法是“K线”,起源于日本十八世纪德川 ...
- [python]沪深龙虎榜数据导入通达信的自选板块,并标注于K线图上
将沪深龙虎榜数据导入通达信的自选板块,并标注于K线图上 原理:python读取前一次处理完的计算5日后涨跌幅输出的csv文件 文件名前加"[paint]" 安照通达信的画图文件和板 ...
- Wijmo金融图表系列之平均K线图&砖形图
2015年7月16日将会发布有史以来最令人兴奋的控件-Wijmo 金融图表,它的一体化设计为单个自定义集合提供了所有主要的金融图表,这是市场上的其他控件都不具备的独一无二的好处.它像Wijmo其他任意 ...
- 如何看K线图基础知识
在日K线图中一般白线.黄线.紫线.绿线依次分别表示:5.10.20.60日移动平均线,但这并不是固定的,会根据设置的不同而不同,比如你也可以在系统里把它们设为5.15.30.60均线. 你看K线图的上 ...
- 功能分解——Android下画分时图与k线图有感
最近工作极度繁忙,已经好久没有更新博客了,总感觉要是再不抽空总结总结点东西,分分钟就会被懒惰的状态给打到了.同时也希望同学们谨记,如果你已经决定要坚持某些正确的东西,比如背完某章单词,看一完本书抑或是 ...
随机推荐
- [Oracle] Insert All的妙用
无条件的插入 Oracle中的insert all是指把同一批数据插入到不同的表中,假如如今有个需求:把t表中的数据分别插入t1,t2,假设你不知道insert all,你可能会使用insert插入2 ...
- Spring+Quartz的版本问题
使用Spring配置管理Quartz的时候会遇到下面的异常: Caused by: java.lang.IncompatibleClassChangeError: class org.springfr ...
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
在本系列的上一篇文章中,我带着大家一起阅读了一遍Glide的源码,初步了解了这个强大的图片加载框架的基本执行流程. 不过,上一篇文章只能说是比较粗略地阅读了Glide整个执行流程方面的源码,搞明白了G ...
- easyui input设置为disabled提交后获取不到属性值
在做网站管理后台的用户修改功能时,由于当前用户修改个人信息时规定用户名不能修改,故使用了input标签的disabled属性,但是在提交数据后却发现用户名显示为空了.后 来一查才知道input设置为d ...
- [Ubuntu] ubuntu的tty下挂载移动硬盘拷贝数据
转载:http://blog.csdn.net/langb2014/article/details/51567460 更换CUDA好多人都更换成功了,我却失败了,然后电脑最后进不了界面了,只有tty端 ...
- iOS:多个单元格的删除(方法一)
采用存取indexPath的方式,来对多个选中的单元格进行删除 删除前: 删除后: 分析:如何实现删除多个单元格呢?这需要用到UITableView的代理方法,即选中单元格时对单元格做的处理,同时我们 ...
- 远程视频监控之应用篇(mjpg-streamer)
这篇文章将主要结合源码介绍mjpg-streamer,使小伙伴们了解视频监控的实现. 一.移植 tar xvf mjpg-streamer-r63.tar.gz cd mjpg-streamer-r6 ...
- 局域网Cesium离线影像及瓦片影像地图加载【转】
http://www.mamicode.com/info-detail-2161992.html 1.Cesium简介 优点: cesium展示地图数据效果比较好,解析2D地图各种不同服务类型的数据源 ...
- C# 实现PNG文件的背景透明显示,解决动态显示闪烁问题 【转】
http://blog.sina.com.cn/s/blog_402c071e0102x4rl.html 以下内容,对于想要使用C#实现PNG图片背景透明显示,同时动态显示时无闪烁问题的人来说, ...
- window安装Scrapy———解决报错问题
系统是WIN10 64位Python是3.5.2今天安装pip install Scrapy 来安装发现报错Microsoft Visual C++ 14.0 is required 检查发现电脑中 ...