Highcharts.setOptions({lang :{ rangeSelectorZoom : "", /*rangeSelectorFrom : "", rangeSelectorTo : "",*/ }}); $(document).ajaxStart(function() { $(stockChart.loadingContainer).show(); }); $(document).ajaxStop(function() { $(stockChart.loadingContainer).hide(); }); $(window).on('resize.stockChart', function(e) { if (isMobileView){ stockChart.resize(e); } }); var stockChart = { chart : null, cur_chart : null, data : null, period: 5, usedSlot : ["","","","","",""], usedMASlot : ["","","","",""], //maColors : ["#61A1C6","#a1af00","#82389b","#e60028","#fbc189"], maColors : ["#78c043","#865bbc","#da0c6f","#7a004c","#4c227c"], maIndex : {10: 0, 20: 1, 50: 2, 100:3, 250:4}, maIndex2 : [10,20,50,100,250], navigatorUpdated : null, toolTipMoved : null, selectedRange : [-1,-1], stockcode: "", comparecode : "", backgroundColor: "", selected_ts:0, loadingContainer:null, charttype: "candlestick", lastTooltipPoint:null, isMobile:false, init : function(code, callback, navUpdated, ttMoved){ var self = this; self.navigatorUpdated = (navUpdated!==undefined)?navUpdated:null; self.toolTipMoved = (ttMoved!==undefined)?ttMoved:null; self.isMobile = (isMobileView || $("body").attr("data-broswer-view") == "mobile")?true:false; if (self.isMobile){ self.options.subChartHeight = 75; self.options.chartHeight = 238; } if (code.toLowerCase() == "zzzb" || code.toLowerCase() == "zzzc1" || code.toLowerCase() == "ssea"){ this.period = 0; this.options.groupingUnits = [["day",[1]],]; } $.getJSON('/'+lang+'/data/chart/stockChart/code/'+code+'/period/'+self.period, function(_data) { $("#chartContainer").show(); if (_data.mainData.underlying.length == 0){ $("#chartContainer").hide(); return; } self.data = self.decode(_data); var htmlContainer = null; if ($('#stockChartContainer .highcharts-html-container').length>0){ htmlContainer = $('#stockChartContainer .highcharts-html-container'); } $(".chart-stime").html(_data.stime); $(".isdelayed").show(); $('#stockChartContainer').highcharts('StockChart',self.getJSON(self.data), function (chart){ if ($(chart.renderTo).attr("id")){ if (!$('#stockChartContainer').hasClass("noSetting")){ if (htmlContainer){ $('#stockChartContainer').append(htmlContainer); }else{ $('#stockChartContainer').append("
"); $('#stockChartContainer .highcharts-html-container').append("
"); self.loadingContainer = $('#stockChartContainer .highcharts-html-container .highcharts-loading'); } } self.chart = chart; self.cur_chart = chart; self.stockcode = code; self.drawBackground(); if (self.period == 0){ self.setPeriod(self.data.last[self.data.last.length-1][0]- (60*60*24*1000)*180,self.data.last[self.data.last.length-1][0]); }else if(self.data.last[0]){ self.setPeriod(self.data.last[Math.max(self.data.last.length-71,0)][0],self.data.last[self.data.last.length-1][0]); } self.changeChartType(self.charttype); if (callback){ callback(); } }else{ chart.reflow(); } }); }); }, decode : function(_data){ var data = _data.mainData.underlying; var ohlc = [], volume = [],turnover = [],last = []; for (i = 0; i < data.length; i++) { ohlc.push({ x: data[i].ts, y: data[i].last, high: data[i].high, low: data[i].low, open: data[i].open, close: data[i].last, }); last.push([ data[i].ts, // the date data[i].last // close ]); volume.push([ data[i].ts, // the date data[i].vol // the volume ]); turnover.push([ data[i].ts, // the date data[i].turnover // the turnover ]); } return {ohlc : ohlc, vol:volume, turnover: turnover, last:last}; }, options : { groupingUnits : [["minute",[5]],],//[['day',[1]],], chartHeight : 400, chartTitle : 45, subChartHeight : 100, subChartTitle : 23, dateBarHeight : 25, footerPadding : 5, }, resize: function(e){ var self = this; var chart = this.chart; var count = 0; //usedSlot for(var i=0;i'+this.points[0].y+""; }else{ self.lastTooltipPoint = null; html += '
'+self.labels()[lang].toolTip.underlyinghigh+''+this.points[0].point.high+"
"; html += '
'+self.labels()[lang].toolTip.underlyinglow+''+this.points[0].point.low+"
"; html += '
'+self.labels()[lang].toolTip.underlyingopen+''+this.points[0].point.open+"
"; html += '
'+self.labels()[lang].toolTip.underlyinglast+''+this.points[0].point.close+"
"; } var k=3; for(var i =0; i0){ if (self.usedMASlot[i].indexOf("_bol-")>0){ while(_index>0 && typeof this.points[_index].point.high == "undefined"){ _index--; } var val = formatPrice(this.points[_index+1].y); var _val1 = formatPrice(this.points[_index].point.high); var _val2 = formatPrice(this.points[_index].point.low); html += '
'+self.labels()[lang].toolTip.MASlot[5][0]+''; html += ''+val+"
"; html += '
'+self.labels()[lang].toolTip.MASlot[5][2]+''; html += ''+_val2+"
"; html += '
'+self.labels()[lang].toolTip.MASlot[5][1]+''; html += ''+_val1+"
"; }else if(self.usedMASlot[i].indexOf("_compare")>0){ var val = formatPrice(this.points[_index].y); html += '
'+self.labels()[lang].toolTip.MASlot[i]+'
'; html += ''+val+"
"; }else{ var val = formatPrice(this.points[_index].y); html += '
'+self.labels()[lang].toolTip.MASlot[i]+''; html += ''+val+"
"; } k++; } } } html = '
'+stime+'
'+html+'
'; return html;*/ }, shadow: false, }, rangeSelector : { enabled: false, selected: 1 }, xAxis:{ type: 'datetime', gridLineWidth: 0, lineWidth: 0, tickColor: '#00000000', crosshair: { width: 1, color: "#000", zIndex: 4, dashStyle: "Dash", }, labels: { style:{"color": "#000"}, formatter: function () { if (Highcharts.dateFormat('%H:%M', this.value) != "00:00"){ if (Highcharts.dateFormat('%H%M', this.value)*1 < 920){ return Highcharts.dateFormat('09:20', this.value); } return Highcharts.dateFormat('%H:%M', this.value); }else{ return Highcharts.dateFormat('%d/%m', this.value); } } }, opposite: true, offset: this.options.chartHeight*-1-30, }, yAxis: [{ labels:{ style:{"color": "#000"}, formatter: function () { return formatPrice(this.value); }, align:'left', x: 3, y: 4, }, gridLineWidth: (self.toolTipMoved !=null)?0:1, height: this.options.chartHeight, top:this.options.chartTitle, opposite: true, showFirstLabel: false, showLastLabel: true, crosshair: { width: 1, color: "#000", snap:true, zIndex: 4, dashStyle: "Dash", label: { backgroundColor: "#575757", enabled: true, format: '{value:.2f}', style:{"color": "#fff"}, } }, }, { labels:{ formatter: function () { return formatPrice(this.value); }, align:"left", x: 3, }, opposite: false, height: this.options.chartHeight, top:this.options.chartTitle, }, { top:-100,//this.options.chartHeight-43, height: 90, gridLineWidth: 0, labels:{ enabled: false }, }, { top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, },{ top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, },{ top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, },{ top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, },{ top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, },{ top:-100,height: this.options.subChartHeight,title: {text: null,},offset: 0,tickAmount : 5,opposite: true,showFirstLabel: false,showLastLabel: false, labels:{formatter: this.subChartLabelFormatter,align:'left',x: 3,y: 4,style:{"color": "#000"}}, crosshair: {width: 0}, }], navigator : { enabled : (this.isMobile || this.period != 0)?false:true, height : 60, baseSeries: 0, outlineColor: '#00716C', maskFill: 'rgba(36, 130, 125, 0.1)', handles: { backgroundColor: "#a8d0ce", borderColor: "#00716C" }, xAxis: { gridLineWidth: 0, labels:{ enabled: false } }, yAxis: { gridLineWidth: 0, labels:{ enabled: false } }, series: { data: data.last, type: 'area', fillColor: 'rgba(237,237,237,1)', lineColor: '#00000000' } }, scrollbar : { enabled : false }, series : [{ id : "vol", type: 'column', name: 'vol', data: data.vol, yAxis: 2, color: "#cad3da", dataGrouping: { approximation: "sum", units: self.options.groupingUnits, groupPixelWidth: 1000 } },{ name: 'primary', type: 'candlestick', id: 'primary', data : data.ohlc, yAxis: 0, zIndex: 1, dataGrouping: { units: self.options.groupingUnits, groupPixelWidth: 1000 } }], }; }, refreshSeries : function(){ var chart = this.cur_chart; var _series = []; for (var i=0;i0 && this.cur_chart!=this.chart){ for (var j=0;j0){ chart.tooltip.refresh(_series); } }, setPeriod : function(_from,_to){ if (this.data.last[0][0]){ this.chart.xAxis[0].setExtremes(Math.max(_from,this.data.last[0][0]), _to); } }, drawToolTip: function(obj){ //console.log("start drawToolTip"); //console.log(obj); //var chart = this.chart; var chart = this.cur_chart; var fontstyle = {color: '#000', fontSize: '13px'}; var textattr = {zIndex: 7, class: "tooltipElement"}; var xoffset = 0; //if ($(".tooltipElement.s"+obj.points[0].x).length>0){ if ($(".tooltipElement.s"+obj.points[0].x).length>0 && this.cur_chart == this.chart){ return; } if (this.cur_chart == this.chart){ $(".tooltipElement").remove(); } if (this.toolTipMoved){ this.toolTipMoved(obj.points[0].x); } if (this.period!=0){ chart.renderer.text(Highcharts.dateFormat('%y/%m/%d %H:%M',obj.points[0].x), 10, 18).css(fontstyle).attr(textattr).add(); xoffset = 50; }else{ chart.renderer.text(Highcharts.dateFormat('%y/%m/%d',obj.points[0].x), 10, 18).css(fontstyle).attr(textattr).add(); } //chart.renderer.text(Highcharts.dateFormat('%y/%m/%d',obj.points[0].x), 15, 18).css(fontstyle).attr(textattr).add(); try { var _points = []; for(var j = 0; j b.series.name ? 1 : -1; }); for(var j = 0; j<_points.length;j++){ /*if (this.points[j].series.name.startsWith(self.usedMASlot[i])){ _index = j; break; }*/ this.selected_ts = _points[0].x; if (_points[j].series.name == "primary"){ //chart.renderer.text(Highcharts.dateFormat('%y/%m/%d',_points[j].x), 15, 18).css(fontstyle).attr(textattr).add(); if (_points[j].point.close){ chart.renderer.text(this.labels()[lang].toolTip.open + formatPrice(_points[j].point.open), 10, 37).css(fontstyle).attr(textattr).add(); chart.renderer.text(this.labels()[lang].toolTip.high + formatPrice(_points[j].point.high), 90, 37).css(fontstyle).attr(textattr).add(); chart.renderer.text(this.labels()[lang].toolTip.low + formatPrice(_points[j].point.low), 170, 37).css(fontstyle).attr(textattr).add(); chart.renderer.text(this.labels()[lang].toolTip.last + formatPrice(_points[j].point.close), 250, 37).css(fontstyle).attr(textattr).add(); }else{ chart.renderer.text(this.labels()[lang].toolTip.last+formatPrice(_points[j].point.y), 10, 37).css(fontstyle).attr(textattr).add(); } }else if (_points[j].series.name == "vol"){ /* chart.renderer.text(this.labels()[lang].toolTip.volume+(_points[0].y/1000000).toFixed(2)+this.labels()[lang].unit.M, (_points[1].series.userOptions.type=="line")?170:390, 18).css(fontstyle).attr(textattr).add(); */ }else if (_points[j].series.name.indexOf("_sma")>0 || _points[j].series.name.indexOf("_ema")>0 ){ for(var i =0; i0){ chart.renderer.text("SMA("+days+"):"+formatPrice(_points[j].y), 145*_index+105+xoffset, 18).css(fontstyle).attr(textattr).add(); }else{ chart.renderer.text("EMA("+days+"):"+formatPrice(_points[j].y), 145*_index+105+xoffset, 18).css(fontstyle).attr(textattr).add(); } }else if (_points[j].series.name.indexOf("_compare")>0){ /*chart.renderer.text(this.labels()[lang].toolTip.MASlot.compare, 15, 38).css(fontstyle).attr(textattr).add(); chart.renderer.text("C:"+formatPrice(_points[j].y), 90, 38).css(fontstyle).attr(textattr).add(); */ }else if (_points[j].series.name.indexOf("_bol")>0 && _points[j].point.high){ var _label1 = this.labels()[lang].toolTip.MASlot[5][0]+": "; //"("+_points[j].series.userOptions.p1+","+_points[j].series.userOptions.p2+"): "; var _label2 = this.labels()[lang].toolTip.MASlot[5][1]+": "; var _label3 = this.labels()[lang].toolTip.MASlot[5][2]+": "; _label1 += (_points[j-1].y).toFixed(2); _label2 += (_points[j].point.high).toFixed(2); _label3 += (_points[j].point.low).toFixed(2); chart.renderer.text(_label1, 25+60+xoffset, 18).css(fontstyle).attr(textattr).add(); chart.renderer.text(_label2, 185+60+xoffset, 18).css(fontstyle).attr(textattr).add(); chart.renderer.text(_label3, 345+60+xoffset, 18).css(fontstyle).attr(textattr).add(); }else if (_points[j].series.name.indexOf("_sar")>0){ var _label = this.labels()[lang].toolTip.MASlot.sar+"("+_points[j].series.options.p1+","+_points[j].series.options.p2+"): "; _label += (_points[j].y).toFixed(3); chart.renderer.text(_label, 15, 36).css(fontstyle).attr(textattr).add(); }else{ var segment = _points[j].series.name.split("_"); var days = ""; if (segment.length==2){ textattr.class = "tooltipElement text_subchart"; var index = segment[0].replace("s","")*1; var _top = (this.options.chartHeight+this.options.chartTitle)+((this.options.subChartHeight+this.options.subChartTitle)*index)+this.options.dateBarHeight; for(var i =0; i=0;k--){ if (chart.series[k].name && chart.series[k].name == this.usedSlot[i]){ if (chart.series[k].userOptions.p1){ days = "("+chart.series[k].userOptions.p1; if (chart.series[k].userOptions.p2){ days += ","+chart.series[k].userOptions.p2; if (chart.series[k].userOptions.p3){ days += ","+chart.series[k].userOptions.p3; } } days += ")"; } } } chart.renderer.text(this.labels()[lang].subTitle[chartType]+days, 0, _top+16).css(fontstyle).attr(textattr).add(); j++; } }*/ }, changeChartType: function (type){ if (type != this.chart.series[1].type){ this.charttype = type; if(type == "line") { _width = 2; } else { _width = 1; } if(type == 'candlestick') { _color = "#e60028"; }else{ _color = "#000000"; } if(type == "line") { this.chart.series[1].update({ type: type, lineWidth: _width, color: _color, dataGrouping: { approximation: 'average' }, keys: ['x', 'y'], marker:{ enabled:false, }, }); }else{ this.chart.series[1].update({ type: type, lineWidth: _width, color: _color, dataGrouping: { approximation: 'ohlc' }, keys: ['x', 'open', 'high', 'low', 'close'], }); } } }, changePeriodType: function (type,callback){ var self = this; if (type.endsWith("min")){ this.period = type.replace("min","")*1; this.options.groupingUnits = [["minute",[this.period]],]; this.init(this.stockcode,function(){ self.setPeriod(self.data.last[Math.max(self.data.last.length-71,0)][0],self.data.last[self.data.last.length-1][0]); callback(); },this.navigatorUpdated,this.toolTipMoved); return; }else if (this.period != 0){ this.period = 0; this.options.groupingUnits = [[type,[1]],]; this.init(this.stockcode,callback,this.navigatorUpdated,this.toolTipMoved); return; } this.chart.series.forEach(function(ser) { ser.update({ dataGrouping: { units: [ [type, [1]] ] } }, false); }); this.options.groupingUnits = [[type,[1]],]; this.chart.redraw(); this.selected_ts = this.chart.xAxis[0].tickPositions[this.chart.xAxis[0].tickPositions.length-1]; }, removeMAline: function(id){ var chart = this.chart; var _id = []; if (Array.isArray(id)){ _id = id; }else{ _id[0] = id; } for (var j=0;j<_id.length;j++){ if (this.usedMASlot[_id[j]] != ""){ for (var i=chart.series.length-1;i>=0;i--){ if (chart.series[i].name && (chart.series[i].name == this.usedMASlot[_id[j]] || chart.series[i].name.startsWith(this.usedMASlot[_id[j]]+"_"))){ chart.series[i].remove(); } } } this.setUsedMASlot(_id[j], ""); } }, addMAline: function(type, id){ var chart = this.chart; var self = this; //return; if (type!=""){ var tempArr = type.split("-"); var _type = tempArr[0]; var _para = "?type="+_type; for (var i=1; i0){ self.setUsedMASlot(_index, series[i].name); } }else{ series[i].name = "t"+id+"_"+type; series[i].p1 = tempArr[1]; series[i].p2 = tempArr[2]; self.setUsedMASlot(id, "t"+id+"_"+type); } chart.addSeries(series[i]); } self.addSettingBox(_type); chart.yAxis[0].update(); setTimeout(function(){ self.refreshSeries(); }, 500); }); } }else{ self.removeMAline(id); } //chart.setSize(this.options.chartWidth,this.getChartHeight()); }, removeSeries: function(id){ var chart = this.chart; var yAxisID = id + 3; if (this.usedSlot[id] != ""){ for (var i=chart.series.length-1;i>=0;i--){ if (chart.series[i].name && (chart.series[i].name == this.usedSlot[id] || chart.series[i].name.startsWith(this.usedSlot[id]+"_"))){ chart.series[i].remove(); chart.yAxis[yAxisID].userOptions.top = -100; chart.yAxis[yAxisID].userOptions.min = null; chart.yAxis[yAxisID].userOptions.max = null; } } } this.setUsedSlot(id, ""); }, addSeries: function(type, id){ var chart = this.chart; var yAxisID = id + 3; var self = this; $(".tooltipElement").remove(); if (id>=0 && type!="" && !(type == "vol" || type == "turnover")){ var tempArr = type.split("-"); var _type = tempArr[0]; var _para = "?type="+_type; for (var i=1; i0){ return; } var chartWidth = $('#stockChartContainer').width(); var sbox = '
'; var cbox = ''; container.append(sbox+cbox);*/ }, updateSeriesPosition: function(){ var chart = this.chart; var j=0; for (var i=0;i=0 && id=0 && id0 || self.usedMASlot[0].indexOf("_ema")>0 ){ // command = (self.usedMASlot[0].split("_"))[1]; // for(var i=1;i0){ var _value = (self.usedMASlot[i].split("-")); for(var j=1; j<_value.length;j++){ command += "-"+_value[j]; } }else{ if(self.usedMASlot[i]!=""){ command += "-"+(self.usedMASlot[i].split("-"))[1]; // self.addMAline(command, i); } } } if (command){ self.addMAline(command, -1); } /*for(var i=0;i0){ $("#confirm_btn").trigger("click"); }else if (typeof updateChartLayout == "function"){ updateChartLayout(); } }, getChartHeight : function(){ var height = this.options.chartHeight+this.options.chartTitle+25; if (!(this.isMobile || this.period != 0)){ height += 90; } for(var i = 0; i < this.usedSlot.length; i++) { if (this.usedSlot[i] != ""){ height += this.options.subChartHeight+this.options.subChartTitle; } } /*this.chart.xAxis[0].userOptions.top = height-70; this.chart.xAxis[0].update();*/ //return height-((this.isIntraday)?70:0); return height+this.options.footerPadding; }, labels : function (){ var _out = { tc :{ currency: "港元", unit:{ K:"千", M:"百萬", B:"十億", }, yTitle1 : "價格", subTitle : { macd : "平滑異同移動平均線", rsi : "相對強弱指數", turnover : "成交額", }, toolTip : { open:"O:", high:"H:", low:"L:", last:"C:", volume:"成交量:", underlyinghigh: this.stockcode + " 最高價", underlyinglow: this.stockcode + " 最低價", underlyingopen: this.stockcode + " 開市價", underlyinglast: this.stockcode + " 價格", MASlot: ["10天移動平均線","20天移動平均線","50天移動平均線","200天移動平均線","250天移動平均線",["保力加平均線","保力加下限線","保力加上限線"],adddigit(this.comparecode) + " 價格"], TASlot: { turnover:"", rsi:"", macd:["MACD","MACD-訊號","MACD-差異"], }, }, calllv : "收回價", maturity : "到期日", strike : "行使價", gearing : "有效槓桿", egearing : "有效槓桿", x: "倍", call : "認購", put : "認沽", bull : "牛證", bear : "熊證", }, sc :{ currency: "港元", unit:{ K:"千", M:"百万", B:"十亿", }, yTitle1 : "价格", subTitle : { macd : "平滑异同移动平均线", rsi : "相对强弱指数", turnover : "成交额", }, toolTip : { open:"O:", high:"H:", low:"L:", last:"C:", volume:"成交量:", underlyinghigh: this.stockcode + " 最高价", underlyinglow: this.stockcode + " 最低价", underlyingopen: this.stockcode + " 开市价", underlyinglast: this.stockcode + " 价格", MASlot: ["10天移动平均线","20天移动平均线","50天移动平均线","200天移动平均线","250天移动平均线",["保力加平均线","保力加下限线","保力加上限线"],adddigit(this.comparecode) + " 价格"], TASlot: { turnover:"", rsi:"", macd:["MACD","MACD-讯号","MACD-差异"], }, }, calllv : "收回价", maturity : "到期日", strike : "行使价", gearing : "有效杠杆", egearing : "有效杠杆", x: "倍", call : "认购", put : "认沽", bull : "牛证", bear : "熊证", }, en :{ currency: "HKD", unit:{ K:"K", M:"M", B:"B", }, yTitle1 : "last", subTitle : { macd : "MACD", rsi : "RSI", turnover : "Turnover", }, toolTip : { open:"O:", high:"H:", low:"L:", last:"C:", volume:"Volume:", underlyinghigh: this.stockcode + " high", underlyinglow: this.stockcode + " low", underlyingopen: this.stockcode + " open", underlyinglast: this.stockcode + " last", MASlot: ["SMA-10","SMA-20","SMA-50","SMA-200","SMA-250",["Moving average","Bollinger lower bound","Bollinger upper bound"],adddigit(this.comparecode) + " last"], TASlot: { turnover:"", rsi:"", macd:["MACD","MACD-signal","MACD-Diff"], }, }, calllv : "Call level", maturity : "Maturity", strike : "Strike", gearing : "Gearing", egearing : "Eff.Gearing", x: "X", call : "Call", put : "Put", bull : "Bull", bear : "Bear", }, }; if (typeof currency == "string"){ _out[lang].currency = currency; } return _out; }, }