if (!window.console || !window.console.log || !window.console.error) { window.console = { log: function(x) { myalert(x) }, error: function(x) { myalert(x) } }; } function array_diff(arr1, arr2) { var diff = []; for (var i = 0; i < arr1.length; i++) { if (!in_array(arr2, arr1[i])) { diff.push(arr1[i]); } } return diff; } function array_equal(arr1, arr2) { if (is_array(arr1) && is_array(arr2)) { if (arr1.length != arr2.length) return false; for (var i = 0; i < arr1.length; i++) { if (!array_equal(arr1[i], arr2[i])) { return false; } } return true; } else { return arr1 == arr2; } } function in_array(arr, value) { for (var i = 0; i < arr.length; i++) { if (array_equal(arr[i], value)) return true; } return false; } //*没有用到 // function playmusic(url) { // var id = "__music__embed__player__"; // var obj = document.getElementById(id); // url = __baseurl__ + "js/music/" + encodeURI(url); // if (obj == null) { // var oDiv = document.createElement("DIV"); // oDiv.id = id; // oDiv.style.position = "absolute"; // oDiv.style.width = "1px"; // oDiv.style.height = "1px"; // oDiv.style.top = "-2000px"; // document.body.appendChild(oDiv); // obj = oDiv; // } // if (url.match(new RegExp("\\.wav$", 'ig'))) { // playwav(obj, url); // } else { // playmp3swf(obj, url); // } // } function isIE() { var ua = navigator.userAgent.toLowerCase(); var s; if (ua.match(new RegExp("msie\\s+([\\d.]+)", "ig"))) { return true; } return false; } // function playwav(obj, url) { // var html; // if (isIE()) { // html = ''; // } else { // html = ''; // } // obj.innerHTML = html; // } // function playmp3swf(obj, url) { // var url = escape(url); // var html = ''; // obj.innerHTML = html; // } function myalert(text) { if (__javascript_debug__) { //alert(text); } } function GetText(value, unit) { if (unit < 1) { unit = -Math.floor(Math.log(unit) / Math.LN10); var text = FormatNumber(value, unit); } else if (unit == 1) { var text = Math.floor(value) + ".0"; } else { var text = value; } return text; } function triggerKeydown(code) { var e = jQuery.Event("keydown"); e.keyCode = code; $(document).trigger(e); } /* function Alert(obj, showFunction) { var str = ""; var key; if (typeof showFunction == "undefined") { showFunction = true; } if (typeof obj == 'object') { for (key in obj) { if (typeof key == 'string') { if (typeof obj[key] == 'function') { if (showFunction) { str += key +" => (\n "+Alert(obj[key])+"\n)\n"; } } else { str += key +" => (\n "+Alert(obj[key])+"\n)\n"; } } } return str; } else { if (typeof obj === "undefined") { return "undefined"; } else if (obj === null) { return "null"; } else { return obj.toString(); } } } */ /*没有用到 function url_concat(base, str) { var last = base.charAt(base.length - 1); if (base.indexOf("?") != -1) { if(last != "?") { return base + "&" + str; } else { return base + str; } } else { return base + "?" + str; } }*/ //controller里面作为判断 function is_undefined(obj) { return typeof obj === "undefined"; } //object copy复制一份数据 function object_copy(obj, copy_array) { var newobj = {}; if (is_array(obj)) { if (copy_array) { return array_copy(obj); } else { return obj; } } if (typeof obj == 'object') { for (var key in obj) { newobj[key] = object_copy(obj[key]); } } else { return obj; } return newobj; } //深度拷贝 function array_copy(arr) { var copy = new Array(arr.length); for (var i = 0; i < arr.length; i++) { if (is_array(arr[i])) { copy[i] = array_copy(arr[i]); } else { copy[i] = arr[i]; } } return copy; } //判断是否是数组 function is_array(a) { return Object.prototype.toString.call(a) === "[object Array]"; } function ClassExtend(sub, base) { //before: //alert("before"); //alert(Alert(oparnet.prototype)); for (var property in base.prototype) { sub.prototype[property] = base.prototype[property]; } sub.prototype.constructor = sub; //获得基类 sub.__parent = base; if (!base.__sub) { base.__sub = []; } //保存继承它的子类 base.__sub.push(sub); } function ClassNew(classname) { var args = Array.prototype.slice.call(arguments, 1); var _this = this; function F() { _this.parent = classname.apply(_this, args); return _this.parent; } F.prototype = classname.prototype; return new F(); } function getClassName(obj) { var str = obj.constructor.toString(); var regexp = new RegExp("function(\\s+)([^\\(]+)", "ig"); str = regexp.exec(str); return $.trim(str[2]); } function sup(_this) { var args = Array.prototype.slice.call(arguments, 1); if (_this.constructor && _this.constructor.__parent) { if (typeof _this.__counter === 'undefined') { // It has not... perform the initilization _this.__counter = 0; } _this.__counter++; if (_this.__counter > 1) //不是第一次调用继承 { var _parent = _this.constructor.__parent; for (var i = 1; i < _this.__counter; i++) { _parent = _parent.__parent; } args.unshift(_parent); ClassNew.apply(_this, args); } else { args.unshift(_this.constructor.__parent); ClassNew.apply(_this, args); } } } function Point(x, y, z) { this.x = x; this.y = y; if (z !== undefined || z !== null) { this.z = z; } } function FormatNumber(srcStr, nAfterDot) { var nten = Math.pow(10, nAfterDot); srcStr = Math.round(parseFloat(srcStr) * nten) / nten; srcStr = srcStr.toString(); var arr = srcStr.split("."); if (arr[1] == null) { return srcStr + "." + bulidZero(nAfterDot); } if (arr[1].length < nAfterDot) { var offset = nAfterDot - arr[1].length; srcStr += bulidZero(offset); } return srcStr; } function bulidZero(num) { var srcStr = ''; for (var i = 0; i < num; i++) { srcStr += "0"; } return srcStr; } function array_copy1d(arr) { var copy = new Array(arr.length); for (var i = 0; i < arr.length; i++) { copy[i] = arr[i]; } return copy; } function array_copy2d(arr) { var copy = new Array(arr.length); for (var i = 0; i < arr.length; i++) { var len = arr[i].length; copy[i] = new Array(len); for (var j = 0; j < len; j++) { copy[i][j] = arr[i][j]; } } return copy; } //得到最大最小值 function minmax(data, start_index, end_index) { if (!start_index) start_index = 0; if (!end_index) end_index = data.length - 1; if (is_array(data[start_index])) { return _minmax2d(data, start_index, end_index); } else { return _minmax1d(data, start_index, end_index); } } //辅助minmax function _minmax2d(data, start_index, end_index) { var max = -Infinity; var min = Infinity; var maxIndex=""; var minIndex=""; for (var i = start_index; i <= end_index; i++) { for (var j = 0; j < data[i].length; j++) { if (data[i][j] >= max) { max = data[i][j]; maxIndex=[i,j]; } if (data[i][j] < min){ min = data[i][j]; minIndex=[i,j] } } } return [min, max,minIndex,maxIndex]; } //辅助minmax function _minmax1d(data, start_index, end_index) { var max = -Infinity; var min = Infinity; var maxIndex=""; var minIndex=""; for (var i = start_index; i <= end_index; i++) { if (data[i] >= max) { max = data[i]; maxIndex=i; } if (data[i] < min) { min = data[i]; minIndex=i; } } return [min, max,minIndex,maxIndex]; } //画行 function drawRow(ctx, point, len, color, width) { var x = point.x; var y = point.y; if (isNaN(y)) { debug("draw row y nan"); } ctx.beginPath(); if (width) ctx.lineWidth = width; if (color) ctx.strokeStyle = color; ctx.moveTo(x, y + 0.5); ctx.lineTo(x + len, y + 0.5); ctx.closePath(); ctx.stroke(); } //画列 function drawCol(ctx, point, len, color, width) { var x = point.x; var y = point.y; ctx.beginPath(); if (width) ctx.lineWidth = width; if (color) ctx.strokeStyle = color; ctx.moveTo(x + 0.5, y); ctx.lineTo(x + 0.5, y + len); ctx.closePath(); ctx.stroke(); } //时间转换成日期格式 function timeToDate(time, format, timezone) { if (!format) format = "Y-m-d H:i:s"; if (!timezone) timezone = 0; var date = new Date(); if (format.indexOf(".") == -1) // time no msecond { time *= 1000; } time += (date.getTimezoneOffset() + timezone * 60) * 60 * 1000; date.setTime(time); var y = date.getFullYear(); var m = date.getMonth() + 1; var d = date.getDate(); var h = date.getHours(); var i = date.getMinutes(); var s = date.getSeconds(); var u = date.getMilliseconds(); format = time_replace(format, "Y", y); format = time_replace(format, "m", m); format = time_replace(format, "d", d); format = time_replace(format, "H", h); format = time_replace(format, "i", i); format = time_replace(format, "s", s); format = time_replace(format, "u", u, 3); return format; } //时间标准模式 function formatTime(time, p) { var conf = Config.getInstance() var unit = conf.Global.xunit[p] if (unit) { unit = unit * 60; return Math.floor(time / unit) * unit } return time } //formatTime 辅助函数 function time_replace(format, search, n, zero_number) { if (typeof zero_number === "undefined") { zero_number = 2; } if (n < 10) n = padingZero(n, zero_number) + n; return format.replace(search, n); } //time_replace 辅助函数 function padingZero(n, zero_number) { if (n < 0) { return n; } var zn = zero_number - 1; if (n > 0) { var zero_current_num = Math.floor(Math.log(n) / Math.LN10) + 1; zn = zero_number - zero_current_num; } var zero = ""; for (var i = 0; i < zn; i++) { zero += "0"; } return zero; } //在k线图上写内容 function writeText(ctx, point, text, color, font) { //console.log(window.do_drawText); if (isIPhone() && window.do_drawText) { //手机内容设置 text = text.toString(); if (color) ctx.strokeStyle = color; do_drawText.call(ctx, text, point.x, point.y - 10, 9); } //电脑内容设置 else if (ctx.fillText) { if (color) ctx.fillStyle = color; if (font) { ctx.font = font; } else { ctx.font = "normal small-caps normal 1.1em Arial" } ctx.fillText(text, point.x, point.y); } else { if (color) ctx.strokeStyle = color; if (font) { ctx.font = font; } else { ctx.font = "normal small-caps normal 1.1em Arial" } ctx.strokeText(text, point.x, point.y); } } /* * 画带箭头的虚线 */ function drawWithArrowheads(x1,y1,x2,y2,ctx){ // arbitrary styling ctx.strokeStyle="#ff6600"; ctx.fillStyle="#ff6600"; ctx.lineWidth=1; // draw the line ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke(); // draw the starting arrowhead var startRadians=Math.atan((y2-y1)/(x2-x1)); startRadians+=((x2>x1)?-90:90)*Math.PI/180; this.drawArrowhead(ctx,x1,y1,startRadians); // draw the ending arrowhead var endRadians=Math.atan((y2-y1)/(x2-x1)); endRadians+=((x2>x1)?90:-90)*Math.PI/180; drawArrowhead(ctx,x2,y2,endRadians); } function drawArrowhead(ctx,x,y,radians){ ctx.save(); ctx.beginPath(); ctx.translate(x,y); ctx.rotate(radians); ctx.moveTo(0,0); ctx.lineTo(3,10); ctx.lineTo(-3,10); ctx.closePath(); ctx.restore(); ctx.fill(); } /* * 画虚线 * */ function drawDashLine(context,x1,y1,x2,y2,pattern) { pattern = pattern === undefined ? 5 : pattern; //得到斜边的总长度 // calculate the delta x and delta y var dx = (x2 - x1); var dy = (y2 - y1); var distance = Math.floor(Math.sqrt(dx*dx + dy*dy)); var dashlineInteveral = (pattern <= 0) ? distance : (distance/pattern); var deltay = (dy/distance) * pattern; var deltax = (dx/distance) * pattern; // draw dash line context.beginPath(); context.lineWidth=0.7; context.strokeStyle="#333"; for(var dl=0; dl"); } } /*貌似没有用到 function debugx(msg) { var d = $("#debugx"); if (d) { var time = (new Date()).getTime(); var msecond = time % 1000; time = Math.floor(time / 1000); d.append(time + "("+ msecond +")" + ": " + msg + "
"); } }*/ //得到当前时间 function getTime() { return (new Date()).getTime(); } /*/从小到大排列的数组 没有使用 function find_first_big(arr, match) { //结束条件是:match >= arr[mid] && match <= arr[mid + 1], 也就是在 mid 和 mid + 1 的之间, 这样,mid + 1 就是first big var start = 0; var end = arr.length - 1; while (start <= end) { mid = Math.floor((end - start) / 2) + start; if (mid + 1 > arr.length -1) //越界了, 只有在 start == end 的时候会发生,这个时候 mid = arr.length - 1 { if (arr.length > 1) { if (arr[mid -1] >= match) //判断一下前面一个数字 { return mid - 1; } } return arr[mid] >= match ? mid : -1; } if (match >= arr[mid] && match <= arr[mid + 1]) { return match == arr[mid] ? mid : mid + 1; } else if (arr[mid] < match) { start = mid + 1; } else { //macth end = mid - 1 } } return -1; }*/ ////查找到第一个点 function find_first_big_r(arr, match) { //结束条件是:match >= arr[mid - 1] && match <= arr[mid + 1], 也就是在 mid 和 mid + 1 的之间, 这样,mid + 1 就是first big var start = 0; var end = arr.length - 1; if (arr[end] > match) { return end; } while (start <= end) { mid = Math.floor((end - start) / 2) + start; if (mid - 1 < 0) //最多有两个数据 start = 0, end = 0, 1 { if (arr.length > 1) { if (arr[1] >= match) { return 1; } } return arr[0] >= match ? 0 : -1; } if (match >= arr[mid] && match <= arr[mid - 1]) { return match == arr[mid] ? mid : mid - 1; } else if (arr[mid] < match) { end = mid - 1; } else { //macth start = mid + 1 } } return -1; } ////判断兼容性 手机 pc的兼容性 function check_textRender(canvas) { //判断是否是iPhone 或者 ipad if (isIPhone()) { return false; } if (!canvas.get(0).getContext) { return true; } var ctx = canvas.get(0).getContext('2d'); // ctx.drawImage('static/img/klogo.png',this.width, this.height); if (typeof ctx.strokeText == 'function' || ctx.fillText == 'function') { return true } else { return false } } //判断是否是iPhone 或者 ipad function isIPhone() //如果是iPhone,那么采用js库绘制, 已知的bug { var agent = navigator.userAgent; //agent = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_3 like Mac OS X; zh-cn) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7E18 Safari/528.16"; if (agent.toLowerCase().indexOf("iphone") >= 0 || agent.toLowerCase().indexOf("ipad") >= 0) { return true; } return false; } //初始化标志位 对于空值做处理 function init_obj(obj, c, p, default_value) { if (typeof obj === "undefined") { obj = {}; } if (typeof obj[c] === "undefined") { obj[c] = {}; } if (typeof obj[c][p] === "undefined") { obj[c][p] = default_value; } return obj; } //判断是否为object类型 function is_object(obj, key) { if (key) { if (typeof obj[key] == "object") { return true; } return false; } return (typeof obj == "object"); } //FzmCookie cookie 内容 FzmCookie = {}; //FzmCookie读函数 从cookie拿数据 FzmCookie.read = function(name) { var cookieValue = "", search = name + "="; if (document.cookie.length > 0) { offset = document.cookie.indexOf(search); if (offset != -1) { if (offset > 0) { //去除空格部分 var myoffset = offset; while (myoffset > 0 && document.cookie.charAt(--myoffset) == ' '); if (myoffset > 0 && document.cookie.charAt(myoffset) != ';') { return ''; } } offset += search.length; end = document.cookie.indexOf(";", offset); if (end == -1) { end = document.cookie.length; } cookieValue = unescape(document.cookie.substring(offset, end)) } } return cookieValue; }; //当前时间 function now() { return (new Date()).getTime(); } //写cookie FzmCookie.write = function(name, value, hours, path, domain, secure) { var expire = ""; if (domain == null) { /* domain = document.domain; if (domain.indexOf(".") == -1) { domain = ''; } else { domain = "." + domain; } domain = domain ? "; domain=" + domain : ""; */ } path = path ? path : "/"; if (hours != null) { expire = new Date(now() + hours * 3600000); expire = "; expires=" + expire.toGMTString(); } document.cookie = name + "=" + escape(value) + expire + domain + "; path=" + path + ";"; }; // FzmCookie.del = function(name, domain, path) { var expire = ""; if (domain == null) { domain = domainName(); if (domain.indexOf(".") == -1) { domain = ''; } else { domain = "." + domain; } domain = domain ? "; domain=" + domain : ""; } path = path ? path : "/"; value = ""; hours = "-10"; if (hours != null) { expire = new Date(Fzm.clientTime() + hours * 3600000); expire = "; expires=" + expire.toGMTString(); } document.cookie = name + "=" + escape(value) + expire + domain + "; path=" + path + ";"; }; //更新cookie FzmCookie.update = function(name, value, hours, path, domain, secure) { var v = Guidv4.get(name); //以guid中保存的值为准 if (!v) v = FzmCookie.read(name); if (v == '') { v = value; } Guidv4.update(name, v); FzmCookie.write(name, v, hours, path, domain, secure); return v; }; //设置plot_area基本样式 function set_style(name) { var conf = Config.getInstance(); if (!name) { name = conf.Global.style; } else { conf.Global.style = name; } conf.merge("Global.Candle.public", "Global.Candle." + name); conf.merge("Global.Grid.public", "Global.Grid." + name); //设置plot_area的背景颜色 $(conf.Global.canvasID).css("background", conf.Global.Grid.public.bgColor); // $(conf.Global.canvasID).css("background-image","url('static/img/Klogo.png')"); // $(conf.Global.canvasID).css("background-repeat","no-repeat"); } /*//create_canvas创建canvas层 没有用 function create_canvas() { // var conf = Config.getInstance(); var count = conf.Global.maxCanvasCount; for (var i = 0; i < count; i++) { var id = conf.Global.canvasIDPerfix + i; var tmp = $(""); tmp.get(0).height = 1; $(conf.Global.canvasID).append(tmp); } } */ //显示加载 function show_loading() { var conf = Config.getInstance(); var id = conf.Global.ajaxloadingID; var view = HTML5StockChartAPI.API.controller.view; var plotWidth = view.plotWidth; var offsetHeight = $("#plot_area").offset().top; var offsetWidth = $("#plot_area").offset().left; var plotHeight = view.plotHeight; $(id).css("top", offsetHeight + plotHeight / 2 - $(id).width() / 2); $(id).css("left", offsetWidth + plotWidth / 2 - $(id).width() / 2); $(id).show(); $("#background").css("top", offsetHeight + plotHeight / 2 - $(id).width() / 2-130); $("#background").css("left", offsetWidth+ plotWidth / 2 - $(id).width() / 2-130 ); // $(id).show(); } //隐藏加载 function hide_loading() { var conf = Config.getInstance(); var id = conf.Global.ajaxloadingID; $(id).hide(); } //原来是放了一个矩形框在那里显示内容,现在做成六边形输入框update by fangxiao from 2016/7/1 次方法主要是为了价格标线来设置的 function writeTextOption(ctx, point, value, option) { if (option) { if (option.bg) { if(option.type=="price"){ ctx.fillStyle = option.bg.fillcolor; ctx.fillRect(point.x+8,point.y-11,80,20); ctx.fill(); } else{ //画线 //做个六边形 ctx.strokeStyle = option.bg.color; //ctx.strokeRect(point.x, point.y - option.bg.height / 2, option.bg.width , option.bg.height); ctx.fillStyle = option.fillcolor; ctx.lineWidth = 0.6; //设置线宽 var a = option.bg.width; var b = option.bg.width / 2 * 0.3; //左小测 //ctx.strokeStyle=gradient; //console.log(point.x); //console.log(point.y); // ctx.strokeText(value, point.x+3,point.y+2.8,option.bg.width); //ctx.translate(point.x+option.bg.width/2,point.y); ctx.moveTo(point.x, point.y);//左边第一个点 ctx.lineTo(point.x + b, point.y + option.bg.height / 2);//左上角第一个点 ctx.lineTo(point.x + a - b, point.y + option.bg.height / 2); ctx.lineTo(point.x + a, point.y); ctx.lineTo(point.x + a - b, point.y - option.bg.height / 2); ctx.lineTo(point.x + b, point.y - option.bg.height / 2); ctx.lineTo(point.x, point.y); // ctx.fillText(text, point.x, point.y); ctx.closePath(); ctx.stroke(); ctx.fill(); // ctx.fillRect(point.x, point.y - option.bg.height / 2, option.bg.width , option.bg.height); // point.y += option.bg.height / 2; } } if (option.unit < 1) { value = FormatNumber(value, -Math.floor(Math.log(option.unit) / Math.LN10)); } } point.y += 5; point.x += 12; writeText(ctx, point, value, option.color, option.font); } //为了光标设置的样式 function writeTextOptionFortoollist(ctx, point, value, option) { ctx.strokeStyle = "#000"; //ctx.strokeRect(point.x, point.y - option.bg.height / 2, option.bg.width , option.bg.height); //ctx.fillStyle = "#000"; ctx.lineWidth = 1; //设置线宽 var a = option.bg.width; var b = option.bg.width / 2 * 0.3; //左小测 //ctx.strokeStyle=gradient; //console.log(point.x); //console.log(point.y); ctx.strokeText(value, point.x + 3, point.y + 2.8, option.bg.width); //ctx.translate(point.x+option.bg.width/2,point.y); ctx.moveTo(point.x, point.y); ctx.lineTo(point.x + b, point.y + option.bg.height / 2); ctx.lineTo(point.x + a - b, point.y + option.bg.height / 2); ctx.lineTo(point.x + a, point.y); ctx.lineTo(point.x + a - b, point.y - option.bg.height / 2); ctx.lineTo(point.x + b, point.y - option.bg.height / 2); ctx.lineTo(point.x, point.y); // ctx.fillText(text, point.x, point.y); ctx.closePath(); ctx.stroke(); } //start 大, end 小的情况 二分法找寻value值位置 function binsearch_r(arr, value, start, end) { if (typeof start === "undefined") { start = 0; } if (typeof end === "undefined") { end = arr.length - 1; } var mid; while (start <= end) { mid = parseInt((start + end) / 2); if (arr[mid] == value) { return mid; } else if (arr[mid] > value) { start = mid + 1; } else if (arr[mid] < value) { end = mid - 1; } } return -1; } //最后大于find最大的点的位置 function find_last_little_r(x, find) { index = find_first_big_r(x, find); //第一个最大值点位置 if (index == -1) //没有找到 { if (x[0] <= find) { return 0; } else { return -1; } } else { if (x[index] == find) { //找寻到 return index; } else { index++; if (index >= x.length) return -1; return index; } } } //找到相邻的find位置 function find_near(x, find) { var index = find_first_big_r(x, find); if (index == -1) return 0; if (index < x.length - 1 && (x[index] - find > find - x[index + 1])) { index++; } return index; } //旋转得到的新坐标 //point是在原来坐标系内的坐标 //cta 是新坐标系旋转的角度 //ab 是新坐标系的远点,在原来坐标系中的坐标 function axis_translate(point, cta, ab) { var newpoint = {}; newpoint.x = (point.x - ab.x) * Math.cos(cta) + (point.y - ab.y) * Math.sin(cta); newpoint.y = (ab.x - point.x) * Math.sin(cta) + (point.y - ab.y) * Math.cos(cta); return newpoint; } //获取flash版本 function get_flash_version() { var UNDEF = "undefined", OBJECT = "object", SHOCKWAVE_FLASH = "Shockwave Flash", SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", FLASH_MIME_TYPE = "application/x-shockwave-flash", EXPRESS_INSTALL_ID = "SWFObjectExprInst"; var playerVersion = [0, 0, 0]; var d = null; if (typeof navigator.plugins != UNDEF && typeof navigator.plugins[SHOCKWAVE_FLASH] == OBJECT) { d = navigator.plugins[SHOCKWAVE_FLASH].description; if (d && !(typeof navigator.mimeTypes != UNDEF && navigator.mimeTypes[FLASH_MIME_TYPE] && !navigator.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { plugin = true; d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0; } } else if (typeof window.ActiveXObject != UNDEF) { try { var a = new ActiveXObject(SHOCKWAVE_FLASH_AX); if (a) { // a will return null when ActiveX is disabled d = a.GetVariable("$version"); if (d) { ie = true; // cascaded feature detection for Internet Explorer d = d.split(" ")[1].split(","); playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; } } } catch (e) { return playerVersion; } } return playerVersion; } //是否支持websocket function support_websocket() { var version = get_flash_version(); if (version[0] >= 9) { return true; } if (window.WebSocket) { return true; } return false; } /*未使用 function init_websocket() { if (window.WebSocket) { return true; } if (get_flash_version()[0] >= 9) { include(__baseurl__ + "js/swfobject.js"); include(__baseurl__ + "js/web_socket.js"); return true; } return false; }*/ //设置宽度 function set_width(obj, w) { if (typeof obj.get == "function" && obj.get(0)) { obj.get(0).width = w; obj.width(w); } else { obj.width = w; } } //设置高度 function set_height(obj, h) { // if(h>1){ // if(obj.attr('id')=="plot_canvas_00"||obj.attr('id')=="plot_canvas_01"||obj.attr('id')=="plot_canvas_02"){ // h=h-60; // } // } if (typeof obj.get == "function" && obj.get(0)) { obj.get(0).height = h; obj.height(h); } else { obj.height = h; } } /*未使用 function __indicator__name__change(obj) { indicator.load(indicator.tab, $(obj).val()); }*/ //从search位置开始 function start_with(search, obj) { return obj.indexOf(search) == 0 } //HTML5StockChartAPI函数 function HTML5StockChartAPI(id) { this.isinit = false; this.id = id; this.tplParams = {}; this.container = $("#" + this.id); // this.container.html( __tpl__base__ + __tpl__indwin__); this.config = Config.getInstance(); //console.log(DataReaderTrendLine.getYminmax()); this.option = { debug: __debug__, jsdebug: __javascript_debug__, datasource: __datasource__, datafile: __datafile__, pair: __symbol__list__, runtime_load: __runtime_load__, staticChat: __static__, startTime: __start_time__, "toolbar.isenable": false, plot_area: $("#plot_container"), ind: [this.config.mainName] }; if (!this.read('model')) this.write("model", "active"); var _this = this; if (window["FlashCanvas"]) { FlashCanvas.ready = function() { FlashCanvas.init(); _this.init(); } } else { $(document).ready(function() { _this.init(); }); } } HTML5StockChartAPI.prototype.setTPLParams = function(key, val) { this.tplParams[key] = val; } HTML5StockChartAPI.prototype.init = function() { if (this.isinit) { return; } this.isinit = true; this.controller = new Controller(this); if (!is_undefined(this.option["controller.staticChat"])) { this.controller.staticChat = this.option["controller.staticChat"]; } //为了兼容以前的版本,添加了这个全局变量 /*this.indicator = indicator = new WinAjax("indicator.add", "指标管理", 700, 500,this); this.indicator.createWindow("__indicator__"); this.order = order = new WinAjax("order.order","订单",320,240,this); this.order.createWindow("__order__");*/ ////////////////////////// if (this.c && this.p) { this.setPair(this.c, this.p); } } HTML5StockChartAPI.prototype.destroyed=function(){ this.isinit = false; this.controller.deinit(); this.controller = null; } HTML5StockChartAPI.prototype.setStaticChart = function(isstatic) { //alert(typeof isstatic === "undefined"); var p = (typeof isstatic === "undefined") ? true : isstatic; if (this.isinit) { this.controller.staticChat = p; } else { this.set("controller.staticChat", p); } } HTML5StockChartAPI.prototype.setSymbolList = function(pair) { return this.set("pair", pair); } HTML5StockChartAPI.prototype.setIndicatorConf = function(indicator) { if (typeof indicator !== "string") { indicator = indicator.join(":"); } indicator = indicator.toLowerCase(); this.write("ind", indicator); this.set("ind", indicator); // this.isinit=false; if (this.isinit) { window.location.reload(); // this.isinit = false; // this.controller.deinit(); // this.controller = null; // this.isinit = false; // this.init(); } } HTML5StockChartAPI.prototype.initSignal = function() { var signal_id = this.read("signal"); //固定一个策略 if (signal_id < 4) { signal_id = 5; } if (signal_id) { this.config.Global.signal = signal_id; } else { signal_id = this.config.Global.signal; } return signal_id; } HTML5StockChartAPI.prototype.setModel = function(model) { this.write("model", model); this.config.Global.app_model = model; if (this.isinit) { window.location.href = ""; } } HTML5StockChartAPI.prototype.setSignal = function(signal_id) { this.write("signal", signal_id); this.config.Global.signal = signal_id; if (this.isinit) { window.location.href = ""; } } HTML5StockChartAPI.prototype.set = function(name, value) { this.option[name] = value; return this; } HTML5StockChartAPI.prototype.setPair = function(c, p) { this.c = c; this.p = p; if (!this.isinit) { return; } this.write("symbol", c); $.cookie("symbol",c); $.cookie("period",p) // sessionStorage.setItem('period',p); this.write("period", p); this.controller.set(this.c, this.p); if (typeof this.option["paircb"] === "function") { this.option["paircb"](this.c, this.p); } // console.log(DataReaderbdensity.prototype.getYminmax()); } HTML5StockChartAPI.prototype.toolbarEnable = function() { this.set("toolbar.isenable", true); if (!this.isinit) { return; } this.controller.toolbar.enable(); } HTML5StockChartAPI.prototype.toolbarDisable = function() { this.set("toolbar.isenable", false); $("#toolbar").hide(); if (!this.isinit) { return; } this.controller.toolbar.disable(); } HTML5StockChartAPI.prototype.read = function(name) { return FzmCookie.read(name); } HTML5StockChartAPI.prototype.write = function(name, data) { if (typeof data === "string") { return FzmCookie.write(name, data, 10000); } console.error("cookie write error"); return false; } HTML5StockChartAPI.prototype.initInd = function(def) { var name = this.read("ind"); if (!name) { name = def; } this.setInd(name); } HTML5StockChartAPI.prototype.setConfig = function(key, val) { return this.config.set(key, val); } HTML5StockChartAPI.prototype.setInd = function(name) { if (name == "tick_price" || name.indexOf("tick_source") === 0) { this.p = "TICK"; } else { if (this.p == "TICK") { this.p = "H1"; $.cookie('period','H1'); // sessionStorage.setItem('period','H1'); } } this.setIndicatorConf(name); } HTML5StockChartAPI.prototype.clearCookie = function() { this.write("period", ""); $.cookie('period','H1'); this.write("ind", ""); } HTML5StockChartAPI.prototype.initPair = function(c, p, ignore) { var symbol = this.read("symbol"); symbol = symbol ? symbol : c; var period = this.read("period"); period = period ? period : p; if (period == ignore) { period = "H1"; $.cookie('period','H1'); } this.setPair(symbol, period); } //坐标轴的设计按照通用配置的形式来设计。 // //输入: // //1. POINT(SCALE): 描点形式:需要,model读出的数据. //2. EXTEND(NOMAL): 扩展的方式,需要指定扩展对应的数据。 //3. MIAN: 需要提供主图的数据 //4. SCALE: 按照比例进行,这个需要提供的是最大值,最小值 // //坐标值的密集阵值:一般指定多小像素内一个坐标,然后根据高度计算大概需要多少个坐标。 //但是这个值不是精确的,而是有弹性的,为了计算一个最佳的坐标值。 // //设计流程: // //首先是:[point 还是 scale] // //如果是scale 给出最大值,最小值 就可以进行计算了。调用 Axis_Scale // //如果是point: 那么闲判断主图有没有计算,如果没有计算,那么先计算主图。 // //计算主图的过程是这样的:闲判断是否需要 扩展,如果需要扩展,那么 //按照主图数据,进行扩展。这个时候,要知道主图是 p是多少。这样就可以扩展了。 // //Axis_ExtendByMain //Axis_ExtendByTime // //主图扩展完成后(注意,这里要把Y的数据进行扩展,所以,要计算X的坐标,再计算Y的坐标), //那就计算扩展后主图的坐标。扩展完成后调用 Axis_Point, 进行描点处理。 // //对于副图。 //如果配置不是按照主图进行绘制的,那么直接报错。暂时不支持这样的情况。 // //如果是按照主图进行扩展,那么 //调用 Axis_ExtendByMain 扩展。 // //最后调用 Axis_PointBYMain 获取坐标数据。 // // //main 是主图坐标,因为附图可能和主图相关,所以要加这个值。 //对于主图的坐标系,这个main值是null function Axis(main) { this.main = main; this.config = Config.getInstance(); } Axis.POINT = 1; Axis.MAIN = 1 << 1; Axis.TIME = 1 << 2; Axis.BIG2SMALL = 1 << 3; Axis.BIG2BIG = 1 << 4; Axis.SCALE = 1 << 5; Axis.CUSTOM = 1 << 6; Axis.CUSTOM_DATA = 1 << 7; Axis.prototype.setViewConfig = function(viewconfig) { if (viewconfig) this.viewconfig = viewconfig; } Axis.prototype.setData = function(data) { if (data) { this.data = data; this.c = data.c; this.p = data.p; $.cookie('period',data.p); } } Axis.prototype.clearData = function() { this.data = null; this.viewconfig = null; } Axis.prototype.getData = function() { this.calc(); var ret = { data: this.data, axis: this.viewconfig }; return ret; } Axis.prototype.calc = function() { //viewconfig 按canvas的ID进行组织,主图默认在序号为 0 的配置里面 //按照数据,现在刚刚传入的数据,每个图还是有关系的.我们要分离这样的关系。 // //1. 读取每个 指标 计算坐标的标准配置 //2. 获取最大最小指。 // //计算主图 var maxY = -Infinity; var maxX = -Infinity; var minX = Infinity; var minY = Infinity; for (var j = 0; j < this.viewconfig.plot.length; j++) { var name = this.viewconfig.plot[j]; //判断是否参与计算 var vc = this.config.Global.View[name]; if (name != this.config.mainName && typeof vc["disable_axis_area"] !== 'undefined' && vc["disable_axis_area"]) { continue; } if (this.data[name].maxX > maxX) maxX = this.data[name].maxX; if (this.data[name].minX < minX) minX = this.data[name].minX; if (this.data[name].maxY > maxY) maxY = this.data[name].maxY; if (this.data[name].minY < minY) minY = this.data[name].minY; } this.viewconfig.x.max = maxX; this.viewconfig.x.min = minX; this.viewconfig.y.max = maxY; this.viewconfig.y.min = minY; if (this.main == null) { this.calcMain(); this.main = this; } for (j = 0; j < this.viewconfig.plot.length; j++) { name = this.viewconfig.plot[j]; if (name == this.config.mainName) continue; this.calcOne(name); } } Axis.prototype.calcOne = function(name) { this.getAxis(name, "x"); this.getAxis(name, "y"); } Axis.prototype.calcMain = function() { //下面的数据时扩展过的数据,这样。 this.getAxisMainX(); this.getAxis(this.config.mainName, "y"); } Axis.prototype.getAxis = function(name, tag) { //tag 标记是 x 轴还是 y轴,取数据的时候用的到。 var data = this.data[name][tag]; if (!data || data.length == 0) return; var plot = this.viewconfig[tag]; //在tag下面的配置 var axisconfig = this.config.Global.Axis[name][tag]; var unit = this.data[name][tag + "unit"]; if (axisconfig & Axis.POINT) { if (plot.axis) debug("plot axis has set."); var data = this.pointAxisValue(data, plot.beg, plot.end, this.viewconfig.one, plot.n, axisconfig); plot.axis = data.axis; plot.axis_option = {}; plot.axis_option.style = axisconfig; plot.axis_option.unit = unit; this.viewconfig[tag] = plot; this.data[name][tag] = data.data; //描点,计算坐标轴的值 } else if (axisconfig & Axis.MAIN) { //通过主图计算 this.pointByMain(name); //这个不需要什么配置了 //不需要生成什么坐标的信息,坐标在主图中生成。 } else if (axisconfig & Axis.SCALE) { //通过比例进行计算,每个都要生成坐标的信息。 if (!plot.axis) { //计算坐标轴的值[比例计算的坐标轴] var scalevalue = this.getScaleValue(plot.min, plot.max, unit, plot.n, axisconfig); var data = this.scaleAxisValue(data, scalevalue, plot.beg, plot.end, axisconfig); plot.axis = data.axis; plot.axis_option = {}; plot.axis_option.style = axisconfig; plot.axis_option.unit = unit; this.viewconfig[tag] = plot; this.data[name][tag] = data.data; } else { if (axisconfig & Axis.CUSTOM) { this.data[name][tag] = this.customTranslate(name, data, plot.axis); } else { this.data[name][tag] = this.lineTranslate(data, plot.axis); } } } if (axisconfig & Axis.CUSTOM_DATA) { if (this.viewconfig.x.axis && this.viewconfig.y.axis) { for (var type in this.data[name].data) { this.data[name].data[type].x = this.lineTranslate(this.data[name].data[type].x, this.viewconfig.x.axis); this.data[name].data[type].y = this.lineTranslate(this.data[name].data[type].y, this.viewconfig.y.axis); } } } } Axis.prototype.customTranslate = function(name, data, axis) { var param = this.lineGetAxisParam(axis); if (!param) { return data; } var a = param[0]; var b = param[1]; if (name == "profit" || start_with("profit|", name)) { for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j++) { data[i][j][1] = a * data[i][j][1] + b; } } return data; } else if (name == "kshapetest" || start_with("kshapetest|", name)) { for (var i = 0; i < data.length; i++) { data[i][0] = a * data[i][0] + b; } return data; } else if (name == "fractals" || start_with("fractals|", name) || name == "advfractals" || start_with("advfractals|", name)) { for (var i = 0; i < data.length; i++) { data[i][1] = a * data[i][1] + b; } return data; } else if (name == "bolling_desity") { for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j += 2) { if (data[i][j] == -1) { continue; } data[i][j] = a * data[i][j] + b; } } return data; } if (name == "wdesity") { for (var i = 0; i < data.length; i++) { for (var j = 0; j < data[i].length; j++) { if (data[i][j] == 0) { data[i][j] = 0; continue; } data[i][j] = a * data[i][j] + b; } } return data; } else if (name == "trend_line") { //计算曲线的斜率 var axis = this.viewconfig.y.axis; dy = -(axis[0].key - axis[1].key) / (axis[0].value - axis[1].value); dx = this.viewconfig.one; var group = {}; for (i = 0; i < data.length; i++) { var g = data[i][4]; if (g > 0) { if (typeof group[g] === "undefined") { group[g] = 0; } else { group[g] += 0; } } else { group[g] = 0; } //斜率 data[i][1] = dy * (data[i][1] - data[i][0]) / dx //位置 data[i][0] = a * data[i][0] + b + group[g]; data[i][7] = a * data[i][7] + b + group[g]; data[i][6] = this.main.axisMap[data[i][6]]; } return data; } } //beg 是坐标的开始值,这里的data必须是一维数组。 Axis.prototype.pointAxisValue = function(data, beg, end, one, n, axisconfig) { var index = 0; var j = 0; var data_axis = []; var axis = []; var total_pixel = beg - end; var plot_number = Math.ceil(total_pixel / one); if (n > plot_number) { var mod = 1; } else { var mod = Math.round(plot_number / n); } //这里还有的一个问题是:大坐标表示小数据,还是大坐标,表示大数据,默认是大坐标表示大数据 if (axisconfig & Axis.BIG2SMALL) { //小的排在前面 if (data.length > 1 && data[data.length - 1] < data[0]) data = data.reverse(); } for (var i = 0; i < data.length; i++) { data_axis[i] = beg; if (i % mod == 0) { axis[j] = { key: beg, value: data[i] }; j++; } beg -= one; } var ret = { data: data_axis, axis: axis }; return ret; } Axis.prototype.scaleAxisValue = function(data, scalevalue, beg, end, axisconfig) { var data_axis = []; var axis = []; if (!scalevalue) { debug("error scalevalue."); return { data: [], axis: [] }; } var n = scalevalue.length; //坐标大小问题,1. 大的坐标表示小的数字,2. 小的坐标表示大的数字 if (axisconfig & Axis.BIG2SMALL) { //小的排在前面 if (n > 1 && scalevalue[n - 1] < scalevalue[0]) { scalevalue = scalevalue.reverse(); } } else { //Axis.BIG2BIG if (n > 1 && scalevalue[n - 1] > scalevalue[0]) { scalevalue = scalevalue.reverse(); } } var one = Math.floor((beg - end) / (n - 1)); var index = 0; while (beg >= end && index < n) { axis.push({ key: beg, value: scalevalue[index++] }); beg -= one; } //下面开始转换数据,这里的data可以是一维,也可以是二维 data = this.lineTranslate(data, axis); var ret = { data: data, axis: axis }; return ret; } Axis.prototype.getXValue = function(x, mode, axis) { /*if (!axis) axis = this; var _axis = axis.viewconfig.x.axis; //从小到大排列 */ //查找x的值 var current = null; var offset = null; var min = Infinity; for (var time in this.main.axisMap) { var pos = this.main.axisMap[time]; if (typeof pos == "number") { offset = Math.abs(pos - x); if (offset < min) { min = offset; current = time; } } } return parseInt(current); //return this.getValue(x, _axis, mode); } Axis.prototype.getValue = function(key, axis, mode) { if (typeof axis === "undefined") return false; var index = this.findNear(axis, key); if (mode == PlotGrid.NEAR) { return axis[index].value; } else if (mode == PlotGrid.INTERPOLATION) { return this.getInterpolation(key, index, axis); } return false; } Axis.prototype.getInterpolation = function(find, index, axis) { //寻找两个差值点: var newaxis = []; var start, end; if (axis.length <= 1) { return axis[index].value; } if (index == 0) { start = 0; end = 1; } else if (index == axis.length - 1) { start = axis.length - 2; end = axis.length - 1; } else { if (axis[index].key > find) { start = index; end = index + 1; } else { start = index - 1; end = index; } } newaxis.push({ key: axis[start].value, value: axis[start].key }); newaxis.push({ key: axis[end].value, value: axis[end].key }); return this.lineTranslate(find, newaxis, true); } Axis.prototype.findNear = function(axis, find) { for (var i = 0; i < axis.length; i++) { if (axis[i].key < find) { // find 在 i 和 i - 1 之间 if (i == 0) return 0; if (find - axis[i].key < axis[i - 1].key - find) { return i; } else { return i - 1; } } } return axis.length - 1; } Axis.prototype.getYValue = function(y, mode, axis) { if (!axis) axis = this; var _axis = axis.viewconfig.y.axis; //从小到大排列 return this.getValue(y, _axis, mode); } Axis.prototype.getX = function(data) { return this.lineTranslate(data, this.viewconfig.x.axis); } Axis.prototype.getY = function(data) { return this.lineTranslate(data, this.viewconfig.y.axis); } Axis.prototype.lineTranslate = function(data, axis, noround) { if (axis.length == 0) return data; if (axis.length == 1) { var a = 0; var b = axis[0].key; } else { var a = (axis[0].key - axis[axis.length - 1].key) / (axis[0].value - axis[axis.length - 1].value); var b = axis[0].key - a * axis[0].value; } var is1d = is_array(data); var is2d = is_array(data[0]); if (is1d) { for (var i = 0; i < data.length; i++) { if (is2d) { for (var j = 0; j < data[i].length; j++) { data[i][j] = Math.round(a * data[i][j] + b); } } else { data[i] = Math.round(a * data[i] + b); } } } else { if (noround) { data = a * data + b; } else { data = Math.round(a * data + b); } } return data; } Axis.prototype.lineGetAxisParam = function(axis) { if (axis.length == 0) return false; if (axis.length == 1) { var a = 0; var b = axis[0].key; } else { var a = (axis[0].key - axis[axis.length - 1].key) / (axis[0].value - axis[axis.length - 1].value); var b = axis[0].key - a * axis[0].value; } return [a, b]; } Axis.prototype.getScaleValue = function(start, end, unit, n, axis_style) { //浮动:这个n是建议的数目 var number = Math.ceil((end - start) / unit) / n; if (number <= 0) { if (start == end) { var vals = [start]; return this.standarAxisValue(vals, start, end, axis_style); } return null; } var closest; if (number <= 1) { closest = unit; } else { var low = Math.pow(10, Math.floor(Math.log(number) / Math.LN10)); var high = low * 10; var i = 0; if (low >= 10) { if (axis_style & Axis.TIME) { var lookup = [1, 1.5, 2, 3.0, 4.5, 6.0, 10]; } else { var lookup = [1, 1.5, 2, 2.5, 4, 5, 8, 10]; } for (i = 0; i < lookup.length; i++) { lookup[i] *= low; } } else { if (axis_style & Axis.TIME) { var lookup = [1, 2, 3, 4, 6, 10]; } else { var lookup = [1, 2, 4, 5, 8, 10]; } } var min_value = Number.MAX_VALUE; var min_index = 0; for (i = 0; i < lookup.length; i++) { var tmp = Math.abs(lookup[i] - number); if (min_value > tmp) { min_value = tmp; min_index = i; } } var closest = unit * lookup[min_index]; } var minindex = Math.floor(start / closest); var maxindex = Math.ceil(end / closest); vals = []; for (var j = minindex; j <= maxindex; j++) { vals.push(j * closest); } return this.standarAxisValue(vals, start, end, axis_style); } Axis.prototype.standarAxisValue = function(axis, beg, end, axis_style) { var abeg = axis[0]; var aend = axis[axis.length - 1]; var delta = 0; if (axis_style & Axis.FIX_BEG) { delta = abeg - beg; } else if (axis_style & Axis.FIX_END) { delta = aend - end; } for (var i = 0; i < axis.length; i++) { axis[i] -= delta; } //下面开始计算坐标的值 return axis; } Axis.prototype.getAxisMainX = function() { //计算之前复制一份: var x = this.data[this.config.mainName].x; this.getAxis(this.config.mainName, "x"); //这个是X轴坐标的映射,其他的坐标要靠这个来计算。 this.axisMap = {}; for (var i = 0; i < x.length; i++) { this.axisMap[x[i]] = this.data[this.config.mainName].x[i]; } //这样时间和坐标成了一一对应 } Axis.prototype.pointByMain = function(name) { //这里主要去除一些毛刺数据,所以统一又处理了一次。 //这个部分,以后如果要调节性能,还要进行修改。 var x = []; var y = []; var index = 0; var data = this.data[name]; for (var i = 0; i < data.x.length; i++) { var item = this.main.axisMap[data.x[i]]; if (item) { //查找到坐标,那么就采用这个坐标 x[index] = item; y[index] = data.y[i]; index++; } else { if (typeof item === "undefined") { continue; } else { x[index] = item; y[index] = data.y[i]; index++; } } } this.data[name].x = x; this.data[name].y = y; } //配置画图的属性 //1. 类映射 //绘制图形的时候,是根据绘图的名称,来绘制的。 //main 是主图,这个是固定的名字。 //其他的是指标图,用各自的名字。每个画图对象通过两个方面来控制。 //1. 是数据读取部分,我们把缓存在内存的数据看成是一个数据库,这个读取 //虽然是千篇一律,但是还是有规律可循。 // //2. 是数据的显示控制,我们会根据plotset的情况,最后转换成坐标的值。通过 //这个坐标的值,来绘制不同的图形。 // //3. 还有一个对象控制数据保存的过程。比如,数据下载怎么处理, // 下载后怎么保存,新数据怎么处理,新数据怎么保存等问题。 // 处理读写数据的模块应该是要和 数据更新模块分离的。 // //update by fangxiao from 2016/6/29 17:17 function Config() { //浏览器测试 this.getBrowserInfo(); this.name = "Config"; this.mainName = "main"; this.main = { reader: DataReaderCandle, writer: DataWriterCandle, plot: PlotCandle, data: DataReader.EXTEND, downdata: 1, newdata: 1 }; //扩展数据。必须有扩展的接口。否则会直接忽略这个选项。 //通过主图读取数据,主图会传入一个时间范围,然后通过函数读取数据,通过主图扩展数据 this.volumes = { reader: DataReadervolumes, writer: DataWriter, plot: Plotvolumes }; this.volumes2 = { reader: DataReadervolumes2, writer: DataWriter, plot: Plotvolumes2 }; // console.log(""+DataReadervolumes.getYminmax); this.ma = { reader: DataReaderIndicatorsMa, writer: null, plot: PlotLine }; this.macd = { reader: DataReaderIndicatorsMacd, writer: null, plot: PlotMacd }; this.bolling = { reader: DataReaderIndicatorsBolling, writer: null, plot: PlotBolling }; this.kdj = { reader: DataReaderIndicatorsKDJ, writer: null, plot: PlotBolling }; //this.order = {reader:DataReaderIndicatorsOrder,write:null,plot:PlotOrder}; this.Global = {}; //这个DataWriter 的基类处理通用的读写。 this.Global.downloadCount = 600; this.Global.maxStoreChunk = 20; // store max = 5 * this.Global.downloadCount this.Global.lowMark = 300; this.Global.Interval = 20; this.Global.newDisable = false; this.Global.boxwidth = 1; this.Global.trend_line_selected = []; this.Global.xunit = { // YMD:1440, //S5:1/12, M1: 1, M2: 2, M3: 3, M3: 4, M5: 5, M15: 15, M30: 30, H1: 60, H2: 120, H4: 240, D1: 1440, W1: 10080, MN1: 4320 }; this.Global.styleName = { "hx": "中国经典", "mt": "MT4" }; //时间选择坐标的时间格式 this.Global.timeformat = { YMD: "Y-m-d", TICK: "i:s.u", //S5:"m-d H:i:s", M1: "m-d H:i", M2: "m-d H:i", M3: "m-d H:i", M5: "m-d H:i", M15: "m-d H:i", M30: "m-d H:i", H1: "m-d H:i", H2: "m-d H:i", H4: "m-d H:i", D1: "m-d", W1: "m-d", MN1: "m-d" }; this.Global.ajaxloadingID = "#loading"; this.Global.yunit = {}; this.Global.yunit.default_value = 0.00001; this.Global.maxstep = 1 * 3600; //最大没有数据的时间 this.Global.maxCanvasCount = 8; this.Global.forcePoll = true; if (!this.Global.forcePoll && support_websocket()) { this.Global.modelName = ModelShortWS; this.Global.WSHost = __broadcast_server_active__; this.Global.WSPort = "2003"; this.Global.pingTime = 5; //如果5s没有数据,就ping一下 this.Global.maxNOData = 10; //如果10s内都没有数据, 那么就close掉。//最好是ping time 的整数倍。也就是 10s 以上 } else { this.Global.modelName = ModelShortPoll; } if (FzmCookie.read('model') == 'passive_mode') { this.Global.modelName = ModelNode; } this.Global.viewName = View; this.Global.canvasID = "#plot_area"; this.Global.canvasIDPerfix = "plot_canvas_"; this.Global.PollInterval = 3000; //30ms 判断一次 this.Global.maxConnectAliveTime = 12; //轮询的链接最多存活的时间 this.Global.DataAPIDB ="https://kdata.fxee.com/kdata?datafile=" + __datafile__; if (__dbreset__) { this.Global.DataAPIDB += "&dbreset=" + __dbreset__ } this.Global.DataAPIUpload = "static.php?datafile=" + __datafile__; if (__dbreset__) { this.Global.DataAPIUpload += "&dbreset=" + __dbreset__ } if (__datasource__ == "db") { this.Global.DataAPI = this.Global.DataAPIDB; //一定要加上这个callbac=?否则json无法调用 } else if (__datasource__ == "upload") { this.Global.DataAPI = this.Global.DataAPIUpload; //一定要加上这个callbac=?否则json无法调用 } if (__refererhost__ != __apihost__) { this.Global.PollMode = "jsonp"; } else { this.Global.PollMode = "json"; } if (this.Global.PollMode == "jsonp") { /* $.ajaxSetup({ //'jsonp' : false, 'jsonpCallback': "_jsonp_" }); */ } //view 模块相关的配置 this.Global.View = { main: { NXPixel: 100, NYPixel: 50, sep: 0 }, sin: { NXPixel: 100, NYPixel: 50, sep: 0 }, cos: { NXPixel: 100, NYPixel: 50, sep: 0 }, tick_source: { NXPixel: 100, NYPixel: 50, sep: 0 }, profit_line: { NXPixel: 100, NYPixel: 50, sep: 0 }, volumes: { sep: 1, height: 0.2 }, volumes2: { sep: 1, height: 0.2 }, ma: { sep: 0, height: 0.5 }, //order:{sep:0,height:0.5}, macd: { sep: 1, height: 0.2 }, bolling: { sep: 0, height: 0.5 }, kdj: { sep: 1, height: 0.2 }, }; this.Global.TPL = { signal: 'main:profit', mac2: 'main:volumes:volumes2:ma|main|10|color=red:ma|main|20|color=green:ma|main|50|color=yellow:ma|main|100|color=white:macd|main|12,26,9|color=white-lime:kdj|main|9|color=Red-blue-green:', mac: "main:volumes", bty: "main:volumes", eth: "main:volumes", ma: 'ma|main|10|color=red:ma|main|20|color=green:ma|main|50|color=yellow:ma|main|100|color=white:', volumes:'volumes:', macd: 'macd|main|12,26,9|color=white-lime:', bolling: 'bolling|main|20,2|color=green-green-green:', kdj: 'kdj|main|9|color=Red-blue-green:', ssi: "main:easyforex:fxtrade:" + "ma|main|10|color=red:" + "ma|easyforex|10|color=red:" + "ma|fxtrade|10|color=red:" + "ma|main|50|color=yellow:" + "ma|easyforex|50|color=yellow:" + "ma|fxtrade|50|color=yellow:" + "ma|main|100:" + "ma|easyforex|100:" + "ma|fxtrade|100:" + "matrend|main|10,20,30,50|color=red:" + " 0) { var base = parseInt(name.substr(1)); if (isNaN(base)) return 0; return base * ratios[perfix]; } return 0; } function Controller(api) { this.config = Config.getInstance(); this.api = api; // console.log("api"+this.api); var opt = this.api.option; var indicators; this.plot_area = opt.plot_area; this.staticChat = opt.staticChat; this.startTime = opt.startTime; this.pair = opt.pair; this.toolbar = new ToolBar(this); this.initInd(opt.ind); this.runtime_load = opt.runtime_load; this.isinit = false; for (var key in this.pair) { if (is_object(this.pair[key])) { this.config.Global.yunit[key] = this.pair[key].yunit; } } this.timerID = null; this.intervalEvent = {}; this.recall = {}; if (this.api.option["toolbar.isenable"]) { this.toolbar.enable(); } else { this.toolbar.disable(); } this.model = this.getModel(); this.view = this.getView(); if (!this.runtime_load) { this.initChat(); } this.isinit = false; this.init(); } Controller.prototype.deinit = function() { this.model.deinit(); this.view.deinit(); this.toolbar.deinit(); this.model = null; this.view = null; this.toolbar = null; } Controller.prototype.initChat = function() { this.indicators = this.api.indicators; var prevlist = []; var notseplist = []; for (var key in this.ind) { if (is_object(this.ind[key])) { name = key; if (this.ind[name].prev) { prevlist.push(name); continue; } if (!this.view.isSeperatePlot(name)) { notseplist.push(name); continue; } // if(name.split('"').length==2){ // name=name.split('"')[1]; // } this.registerChat(name, null, true); } //注册prev 形式的图 } //对prevlist,首先它必须依赖一个 sep 的图。因为,每个not sep 都是,都是依赖某个sep放到某个sep上的。 for (var i = 0; i < prevlist.length; i++) { var name = prevlist[i]; var index = this.view.plotindex[this.ind[name].prev]; if (typeof index === "undefined") { continue; } this.registerChat(name, index); } //对于非seplist的图表,这个性质是固有的,依赖某个图。 for (var i = 0; i < notseplist.length; i++) { var name = notseplist[i]; if (!this.ind[name].param) { index = 0; } else { var index = this.view.plotindex[this.ind[name].param[0][0]]; if (typeof index === "undefined") { index = 0; } } this.registerChat(name, index); } } Controller.prototype.getIndParam = function(name) { if (this.ind[name] && this.ind[name].param) { return this.ind[name].param; } return false; } Controller.prototype.getIndList = function() { var ind = {}; for (var key in this.ind) { ind[key] = 1; } return ind; } Controller.prototype.indTpl = function(indicator) { //判断是不是tpl //alert(disable_selected); if (this.config.Global.TPL[indicator]) { ToolBar.tplselect(indicator); indicator = this.config.Global.TPL[indicator]; for (var key in HTML5StockChartAPI.API.tplParams) { var data = HTML5StockChartAPI.API.tplParams[key]; if (typeof data === "string" || typeof data === "number") { indicator = indicator.replace(key, data); } } } else { ToolBar.tplselect("none"); } // indicator =JSON.stringify(indicator).split(":"); // console.log("indicator:"+indicator.split); if(indicator.split==undefined){ return indicator } else{ } if(indicator.split==undefined){ return indicator; } indicator =indicator.split(':'); return indicator; } Controller.prototype.initInd = function(indlist, disable_parse) { //ind 的结构进行调整: //区别对待下载数据,但是不显示的情况 var indlist = this.indTpl(indlist); this.config.mainName = indlist[0]; // if(this.config.mainName.split('"').length==2){ // this.config.mainName=this.config.mainName.split('"')[1]; // } // this.config.mainName=indlist[0]+'"'; //alert(this.config.mainName); if (disable_parse) { var param_ind = indlist; } else { var param_ind = this.parseInd(indlist); } for (var i = 0; i < param_ind.length; i++) { var ind = param_ind[i]; var name = param_ind[i].join("|"); var ind_name = ind[0]; //注册新的指标值,复制配置。 if (ind[1]) { var depends = ind[1].split(","); for (var j = 0; j < depends.length; j++) { if (depends[j] == this.config.mainName || depends[j] == "null" || this.ind[depends[j]]) { //donothing continue; } if (this.config.Global.View[depends[j]]) { this.ind[depends[j]] = { name: depends[j], view: 0, prev: false }; } } } ind.shift(); //shift 名称 ind.shift(); //shift depends if (depends) { ind.unshift(depends); } else { ind.unshift("none"); } this.ind[name].param = ind; this.config[name] = this.config[ind_name]; this.config.Global.View[name] = this.config.Global.View[ind_name]; this.config.Global.Axis[name] = this.config.Global.Axis[ind_name]; } } Controller.prototype.parseInd = function(indlist) { var curperiod = get_period_second(this.api.p); this.ind = {}; var param_ind = []; for (var i = 0; i < indlist.length; i++) { //判断是否是prev类型的 var prev = false; var name = indlist[i]; if (indlist[i].charAt(0) == "<") //prev { prev = this.config.mainName; if (i > 0) { for (var j = i - 1; j >= 0; j--) { if (indlist[j][0] != "<") { prev = indlist[j]; break; } } } name = name.substr(1); } var ind = name.split("|"); var index = ind[0].indexOf(","); var tper, tname; if (index != -1) { tname = ind[0].substring(0, index); tper = ind[0].substring(index + 1); name = tname + name.substring(ind[0].length); ind[0] = tname; if (tper != curperiod) { continue; } } if (ind.length > 1) { param_ind.push(ind); } this.ind[name] = { name: name, view: 1, prev: prev }; } return param_ind; } Controller.prototype.addIntervalEvent = function(name, obj, recall) { this.intervalEvent[name] = obj; if (recall) { this.recall[name] = obj; } } Controller.prototype.removeIntervalEvent = function(name) { this.intervalEvent[name] = null; if (this.recall[name]) { this.recall[name] = null; } } Controller.prototype.recallEvent = function() { for (var key in this.recall) { if (this.recall[key] && is_object(this.recall[key])) { this.recall[key].recall(this); } } } Controller.prototype.init = function() { if (this.isinit) { return; } this.isinit = true; this.toolbar.init(); this.view.init(); this.setGlobalInterval(); return true; } Controller.prototype.set = function(c, p) { // console.log('p:'+p) if (c && p) { this.c = c.toUpperCase(); this.p = p.toUpperCase(); $.cookie('period',this.p); this.init(); if (this.model) { this.model.set(this.c, this.p); this.model.init(); } } } Controller.prototype.setGlobalInterval = function() { var time = this.config.Global.Interval; if (time < 20) { time = 20; } //以20ms为单位 //规则为:要实现一个功能,就分装一个函数在此处执行 if (this.timerID !== null) { return; } var _this = this; this.timerID = window.setInterval(function() { var events = _this.intervalEvent; for (var key in events) { if (events[key] != null && is_object(events[key])) { events[key].run(_this); } } }, time); } Controller.prototype.flag = function(type) { debug(type); if (type == "new") { this.draw(); this.recallEvent(); if (this.config.Global.autoScroll) { triggerKeydown(Key.KEY_END); } } else if (type == "down") { this.draw(); this.recallEvent(); } } Controller.prototype.getModel = function() { var ModelClass = this.config.Global.modelName; //以后要加入兼容性的判断,最后选择一个正确的model return new ModelClass(this); } Controller.prototype.getView = function() { var ViewClass = this.config.Global.viewName; return new ViewClass(this); } Controller.prototype.registerChat = function(name, index, has_plot_area) { var indinfo = this.ind[name]; if (indinfo.view) //如果没有view ,不会注册显示对象 { this.view.registerPlot(name, index, has_plot_area, indinfo.param); } this.model.registerData(name); } Controller.prototype.unregisetChat = function(name) { this.view.unregisetData(name); this.view.unregisetPlot(name); } Controller.prototype.draw = function() { if(this.model==undefined)return; if (!this.model.ready) return; var viewconfig = this.view.getAxisConfig(); //view 的图形分布情况 //然后。model要根据这个配置,生成相应的坐标数据。 //坐标配置: // (绘图区域) // canvas_id => [width, height, 对应的图形名称:main, ma, easyforex ...] // // 返回的格式:图形名称 => 坐标数据 // //主图要绘制的个数 if (viewconfig.length <= 0) return; //清除原来的view数据 this.view.clearData(); this.model.clearData(); debug("draw_data_init"); var data = this.model.getData(viewconfig); for (var i = 0; i < data.length; i++) { //debug("get_data_beg"); //获取数据内部进行封装:我们只调整这一块。 //对画图来说,是自己计算的,还是服务器下载的是没有差别的。 //这样就不会让问题扩大化。 //debug("get_data_end"); this.view.updateData(data[i], i); //加入数据 //debug("calc_axis"); } debug("draw_data_end"); this.view.draw(); //检查是否要下载数据 this.model.downloadData(); debug("draw_end"); } Controller.prototype.startSet = function(name) { var one = this.view.plot[this.config.mainName].getOneSize(); var area = this.view.grid[0].getPlotArea(); if (name == "Next") { this.model.point(this.model.point() - Math.round(30 / one)); } else if (name == "Prev") { this.model.point(this.model.point() + Math.round(30 / one)); } else if (name == "PageDown") { this.model.point(this.model.point() + Math.round((area.x.beg - area.x.end) / one)); } else if (name == "PageUp") { this.model.point(this.model.point() - Math.round((area.x.beg - area.x.end) / one)); } else if (name == "Home") { this.model.point(this.model.endPos()); this.model.state = 'home'; } else if (name == "End") { this.model.point(0); this.model.state = 'end'; } return this.model.point(); } Controller.prototype.startOffset = function(offset) { var start = this.model.point(); this.model.point(start + offset); } Controller.prototype.zoomIn = function() { this.view.plot[this.config.mainName].zoomIn(); this.draw(); } Controller.prototype.zoomOut = function() { this.view.plot[this.config.mainName].zoomOut(); this.draw(); } function Data() { } Data.OPEN = 0; Data.HIGH = 1; Data.LOW = 2; Data.CLOSE = 3; Data.VOLUMES = 4; //controller 中有一个接口会增加这个 //事件,它提供一个接口给客户端 //setVal() //getVal() //提供给controller 的接口是 //run function IntervalEvent(count, callback, zerocall) { this.count = count; if (this.count <= 0) { this.count = 1; } this.current = 0; this.callback = callback; this.value = 0; this.prev = null; if (!zerocall) { this.zerocall = 0; } else { this.zerocall = zerocall; } } IntervalEvent.prototype.run = function(controller) { this.current++; if (this.count == this.current) { this.current = 0; if (this.zerocall) { this.prev = this.value; this.callback.call(this, controller); } else { if (this.value != 0) { this.prev = this.value; this.callback.call(this, controller); this.value = 0; } } } } IntervalEvent.prototype.getVal = function() { return this.value; } IntervalEvent.prototype.recall = function(controller) { this.value = this.prev; if (this.zerocall) { this.callback.call(this, controller, true); } else { if (this.value != 0) { this.callback.call(this, controller, true); this.value = 0; } } } IntervalEvent.prototype.setVal = function(val) { this.value = val; return this; } //数据更新部分: //广播服务器的设计是集中式的。我们要广播所有的数据,所以需要进行更新。 //这个部分将会分为两个部分:下载K线,更新最新的数据 // // 管理tick图: // 根据货币对k线,会注册一系列的K线,指标。 // 更新或者下载的数据都有下面的属性: // 1. 货币对/时间/名称 // 2. 绘图提供更新接口: // plot = plot_list[currency][time][name]; // updateNew(data); // download(data); // //data.registerPlot(c, p, name, plot); //data.unregisetPlot(c, p, name, plot); // //采用广播协议的数据 function Model(controller) { this.data = {}; this.maindata = {}; this.dataObj = {}; this.realname = {}; this.flagset = {}; this.ready = false; //保存数据指针。标志当前图形读取的位置。 this.datapoint = {}; this.config = Config.getInstance(); this.controller = controller; this.registerData(this.config.mainName); // 注册主图 //alert(this.config.mainName); this.main = this.dataObj[this.config.mainName]; this.downOptions = {}; //这个由数据层来更新 this.leftoffset = 0; this.rightoffset = 0; this.state = ''; } Model.prototype.init = function() { //初始化数据 if (this.c && this.p) { //判断是否这个货币对的数据已经初始化过了 if (this.flag(Model.INITED)) { hide_loading(); this.controller.draw(); if (this.config.Global.newDisable || this.config[this.config.mainName].newdata) { this.newData(); } } else if (this.flag(Model.INITING)) { return; } else { this.flag(Model.FIRST_NEW, 1); show_loading(); this.flag(Model.INITING, 1); //alert(this.config.mainName); if (this.config['main'].downdata) { this.initData(); } else { this.flag(Model.INITED, 1); this.init(); } } } } Model.prototype.deinit = function() { //释放socket 资源 } Model.NEW = 1; Model.DOWN = 1 << 1; Model.NEW_DATA = 1 << 2; Model.DOWN_DATA = 1 << 3; Model.INITED = 1 << 4; Model.INITING = 1 << 5; Model.NEW_DISABLE = 1 << 6; Model.DOWN_END = 1 << 7; Model.FIRST_NEW = 1 << 8; Model.UPDATE = 1 << 9; Model.prototype.registerData = function(name) { // console.log("name:"+name); if(this.config[name] && !this.dataObj[name]) { var newname = name.split("|", 2); if (newname.length == 2) { this.realname[newname[0]] = name; } var Reader = this.config[name].reader; var Writer = this.config[name].writer; this.dataObj[name] = {}; if (Writer) { this.dataObj[name].writer = new Writer(this, name); } if (Reader) { this.dataObj[name].reader = new Reader(this, this.dataObj[name].writer, name); } //reader 要引用write } } Model.prototype.clearData = function() { for (var key in this.dataObj) { if (is_object(this.dataObj[key])) { this.dataObj[key].writer.clear(); this.dataObj[key].reader.clear(); } } } Model.prototype.getDataReader = function(name) { return this.dataObj[name].reader; } Model.prototype.unregisetData = function(name) { this.dataObj[name] = null; } Model.prototype.set = function(c, p) { if (this.c != c || this.p != p) { if (this.newDataXHR) this.newDataXHR.abort(); //标记不在更新数据 this.isNewing = false; if (this.subscribe) { this.subscribe(); } this.downOptions = {}; } this.c = c; this.p = p; $.cookie('period',p); //初始化标志位 this.flagset = init_obj(this.flagset, this.c, this.p, 0); this.datapoint = init_obj(this.datapoint, this.c, this.p, 0); //set 所有的reader对象 for (var key in this.dataObj) { if (is_object(this.dataObj[key])) { this.dataObj[key].reader.set(); } } } Model.prototype.clearData = function() { this.data = {}; } Model.prototype.getShift = function(plot) { var maxshift = -Infinity; for (var i = 0; i < plot.length; i++) { var name = plot[i]; var shift = this.dataObj[name].reader.getShift(); if (shift > maxshift) { maxshift = shift; } } return maxshift; } Model.prototype.getData = function(viewconfig) { //all plot list. var plot = []; this.plotnum = viewconfig[0].plotnum; for (var i = 0; i < viewconfig.length; i++) { var arr = viewconfig[i].plot; for (var j = 0; j < arr.length; j++) { plot.push(arr[j]); } } //初始化参数 for (var i = 0; i < plot.length; i++) { var name = plot[i]; //if (name == this.config.mainName) continue; var param = this.controller.getIndParam(name); if (param) { this.dataObj[name].reader.setParam(param); } } var shift = this.getShift(plot); //先计算普通的: for (var i = 0; i < viewconfig.length; i++) { this._getData(viewconfig[i].plot, viewconfig[i].plotnum + shift); } //再计算关联,但是不会在前台显示的数据 var nodatalist = []; for (var i = 0; i < plot.length; i++) { var param = this.controller.getIndParam(plot[i]); if (param && param[0] != "none") { for (var k = 0; k < param[0].length; k++) { if (this.data[param[0][k]]) { continue; } else { nodatalist.push(param[0][k]); } } } } this._getData(nodatalist, viewconfig[0].plotnum + shift); //再计算通过这些数据得到“计算的数据” for (var j = 0; j < viewconfig.length; j++) { for (var i = 0; i < viewconfig[j].plot.length; i++) { var name = viewconfig[j].plot[i]; var param = this.controller.getIndParam(name); if (param && param[2] != "__ignore__") { if (name == this.config.mainName) { continue; } if (param[0] != "none") { for (var k = 0; k < param[0].length; k++) { this.dataObj[name].reader.setDependData(param[0][k], this.data[param[0][k]]); } } this.data[name] = this.dataObj[name].reader.getData(this.config[name].data); } } } //计算完成了,这些数据就没有用了,释放掉内存 for (var i = 0; i < nodatalist.length; i++) { this.data[nodatalist[i]] = null; } return this.formatData(viewconfig, shift); } Model.prototype._getData = function(plotlist, num) { if (!this.data[this.config.mainName]) { this.data[this.config.mainName] = this.dataObj[this.config.mainName].reader.getData(this.plotnum, this.config[this.config.mainName].data, true); this.maindata = this.dataObj[this.config.mainName].reader.getData(this.num, this.config[this.config.mainName].data, true); } //计算所有没有参数的情况 for (var i = 0; i < plotlist.length; i++) { var name = plotlist[i]; if (name == this.config.mainName) continue; var param = this.controller.getIndParam(name); if (!param || param[2] === "__ignore__") { this.data[name] = this.dataObj[name].reader.getDataByMain(this.config[name].data); } } } Model.prototype.shiftData = function(shift) { //先shift main,main直接按照数字shift //其他相关的shift按照时间进行shift。因为,不一定是对齐的。 var main = this.config.mainName; if (shift > 0 && shift < this.data[main].x.length) { //this.data[main].x = this.data[main].x.slice(0, -shift); //this.data[main].y = this.data[main].y.slice(0, -shift); //var minx = this.data[main].x[this.data[main].x.length -1]; /*for (var key in this.data) { if (key == main) continue; if (this.data[key] && is_object(this.data[key])) { var shift = this.getShiftNumber(this.data[key].x, minx); this.data[key].x = this.data[key].x.slice(0, -shift); this.data[key].y = this.data[key].y.slice(0, -shift); } }*/ } for (var key in this.data) { if (this.data[key] && is_object(this.data[key])) { this.data[key] = this.dataObj[key].reader.formatData(this.data[key].x, this.data[key].y); } } } Model.prototype.getShiftNumber = function(x, minx) { var count = 0; for (var i = x.length - 1; i >= 0; i--) { if (x[i] >= minx) { break; } else { count++; } } return count; } //如果是主图的数据,因为要用于对齐,所以,复制一份 //防止外界对其进行修改操作。 Model.prototype.formatData = function(viewconfig, shift) { this.shiftData(shift); //再计算通过这些数据得到“计算的数据” var data = []; //var text; for (var j = 0; j < viewconfig.length; j++) { data[j] = {}; //text = ""; for (var i = 0; i < viewconfig[j].plot.length; i++) { var name = viewconfig[j].plot[i]; if (name == this.config.mainName) { data[j][name] = object_copy(this.data[this.config.mainName]); data[j][name].x = array_copy1d(this.data[this.config.mainName].x); } else { data[j][name] = this.data[name]; } //text += "[" + this.getShowName(name) + " " + this.lastData(this.data[name].y[0]) + "] "; } data[j].c = this.c; data[j].p = this.p; //data[j].text = text; } return data; } Model.prototype.update = function(data, type) { if (!data) { if (type == Model.DOWN) { this.ready = true; this.controller.flag("down"); this.flag(Model.DOWN_END, 1); } return; } if (data.code) { if (data.code == "goto" && data.msg) { window.location.href = data.msg; } return; } var is_new = false; var is_down = false; var is_update = false; if (!data.c || !data.p) { return; } var c = data.c.toUpperCase(); var p = data.p.toUpperCase(); //数据已经下载到末尾了 var end = data.end; this.flag(Model.DOWN_END, end); if (data.code) { debug(data.code); //发生了错误 return; } this.downOptions = data.options || {}; for (var key in data) { //下载的数据 if (!is_object(data[key])) continue; //这三个是保留的 if (key == "c" || key == "p" || key == "code" || key == "end") { continue; } var keyreal = key; if (typeof this.dataObj[key] === "undefined") { key = this.realname[key]; } if (typeof key === "undefined") { continue; } if (this.dataObj[key]) { if (type == Model.NEW) { is_new = true; if (this.dataObj[key].writer) this.dataObj[key].writer.setNewData(c, p, data[keyreal]); } else if (type == Model.UPDATE) { is_update = true; if (this.dataObj[key].writer) this.dataObj[key].writer.setUpdateData(c, p, data[keyreal]); } else if (this.downOptions.total_size) { is_down = true; if (this.dataObj[key].writer && this.dataObj[key].writer.setDownloadDataByOffset) { this.dataObj[key].writer.setDownloadDataByOffset(c, p, data[keyreal], this.downOptions); } else { if (this.dataObj[key].writer) this.dataObj[key].writer.setDownloadData(c, p, data[keyreal]); } } else { //判断是否存在数据对象,那么更新图形的数据。 is_down = true; if (this.dataObj[key].writer) this.dataObj[key].writer.setDownloadData(c, p, data[keyreal]); } } } if (is_new) { this.ready = true; this.controller.flag("new"); } if (is_down) { this.ready = true; this.controller.flag("down"); } if (is_update) { this.ready = true; this.controller.flag("new"); } } Model.prototype.getIndicatorList = function() { var list = []; for (var key in this.dataObj) { if (key == this.config.mainName) continue; //主图的数据,数据接口会默认更新 if (is_object(this.dataObj[key])) { list.push(key); } } return list; } //数据集合中,按照时间,或者某个x轴一路更新 //肯定会有一个边界。这个数据边界就从这里取。 Model.prototype.startXValue = function() { var start = this.main.reader.startXValue(); if (start <= 0 && this.controller.startTime) { return this.controller.startTime; } return start; } Model.prototype.endXValue = function() { return this.main.reader.endXValue(); } Model.prototype.leftDataCount = function() { return this.endPos() - this.point() - this.plotnum; } Model.prototype.rightDataCount = function() { return this.point(); } Model.prototype.endPos = function() { return this.main.reader.getEndPos(); } Model.prototype.flag = function(key, value, c, p) { if (!c) { c = this.c; } if (!p) { p = this.p; } if (!c || !p) { return; } c = c.toUpperCase(); p = p.toUpperCase(); if (typeof value === "undefined") { return (this.flagset[c][p] & key) == key; //判断标志位是否已经被设置了 } else if (value == 0) { this.flagset[c][p] = this.flagset[c][p] & (~key); //清除标志位 } else { this.flagset[c][p] = this.flagset[c][p] | key; //设置标志位 } } Model.prototype.point = function(value) { if (typeof value === "undefined") { return this.datapoint[this.c][this.p]; } else { this.datapoint[this.c][this.p] = value; } } Model.prototype.getLeftOffset = function() { return this.leftoffset; } Model.prototype.getRightOffset = function() { return this.rightoffset; } Model.prototype.getDownOffset = function(down_flag) { this.count = this.config.Global.downloadCount; if (down_flag == -1 && this.getRightOffset() == 0) { return -1; } if (down_flag == 1 && this.getLeftOffset() >= this.downOptions.total_size) { return -1; } if (down_flag == 1) { var offset = this.getLeftOffset() + 1; //读取下一个数据 //超出范围 if (offset >= this.downOptions.total_size) offset = -1; } if (down_flag == -1) { var offset = this.getRightOffset() - this.config.Global.downloadCount; if (offset < 0) { offset = 0; this.count = this.getRightOffset() - offset; } } return offset; } Model.prototype.getDataByTime = function(name, time, near) { var x = this.dataObj[name].writer.getX(this.c, this.p); var y = this.dataObj[name].writer.getY(this.c, this.p); var index = binsearch_r(x, time); var obj = {}; obj.time = timeToDate(time, this.config.Global.timeformat[this.p], this.config.Global.timezone); if (index == -1 && near == "candle") { index = find_last_little_r(x, time); if (index == -1) return false; var close = y[index][Data.CLOSE]; obj.data = [close, close, close, close, y[index][Data.VOLUMES]]; } else { if (index == -1) return false; obj.data = y[index]; } return obj; } __javascript_debug__ = false; __datasource__ = "db"; __datafile__ = "db"; __debug__ = false; __static__ = false; __runtime_load__ = false; __start_time__ = false; __baseurl__ = "http:\/\/locaohost:9001\/"; __broadcast_server_active__ = "115.236.165.18"; __loadhost__ = "localhost:9001"; __refererhost__ = "localhost:9001"; __orderhost__ = "localhost:9001"; __apihost__ = "rsi.33.cn"; __dbreset__ = "" __musicselect__ = { "alert.wav": "alert", "alert2.wav": "alert2", "connect.wav": "connect", "disconnect.wav": "disconnect", "email.wav": "email", "expert.wav": "expert", "ok.wav": "ok", "request.wav": "request", "stops.wav": "stops", "tick.wav": "tick", "timeout.wav": "timeout", "wait.wav": "wait", "": "", "\u05a3\u0534 - ": "\u05a3\u0534 - " }; //plot base function. process base plot function. function Key() { } Key.KEY_PREV = 37; Key.KEY_UP = 38; Key.KEY_NEXT = 39; Key.KEY_DOWN = 40; Key.KEY_HOME = 36; Key.KEY_END = 35; Key.KEY_PAGE_DOWN = 34; Key.KEY_PAGE_UP = 33; function Plot(view, grid, name) { this.view = view; this.name = name; this.controller = this.view.controller; this.grid = grid; this.getPlotArea(); this.ctx = this.grid.ctx; this.ctxBG = this.grid.ctxBG; this.ctxFront = this.grid.ctxFront; this.axis = this.grid.axis; this.canvas = this.grid.canvas; this.config = Config.getInstance(); this.param = {}; classname = getClassName(this); classname = classname.substr(4); if (this.config.Global[classname]) { this.conf = this.config.Global[classname].public; } else { this.conf = {}; } //对K线图的操作是全局性的。也就是说,这里的不会像grid里面一样复制一份全局配置。 //而是直接操作全局配置 } //默认一个时间占据一个像素,这个是线条默认的占用值。 Plot.prototype.getOneSize = function() { return 1; } //默认一个时间占据一个像素,这个是线条默认的占用值。 Plot.prototype.getPlotArea = function() { var area = this.grid.getPlotArea(); this.top = area.y.end; this.left = area.x.end; this.bottom = area.y.beg; this.right = area.x.beg + this.grid.conf.paddingX; } Plot.prototype.getShowName = function() { var name = this.name.toUpperCase(); name = name.replace("MAIN", "OHLC").replace("FXTRADE", "OANDA").replace("||", " "); if (name.indexOf("PROFIT_LINE") != -1) { name = "PROFIT"; } name = name.split("|"); if (name.length == 1) { return name[0]; } var pre = name.shift(); return pre + " (" + name.join(" , ") + ")"; } Plot.prototype.lastData = function() { var data = this.view.model.getDataReader(this.name).last(); if (typeof data === "undefined") { return ""; } try { var yunit = this.axis.viewconfig.y.axis_option.unit; } catch (e) { return ""; } var ret = []; if (is_array(data)) { for (var i = 0; i < data.length; i++) { ret[i] = this.grid.getText(0, data[i], yunit); } return ret; } else if (!isNaN(Number(data))) { return this.grid.getText(0, data, yunit); } else { return data; } } Plot.prototype.setParam = function(param) { if (param.length >= 3) { this.param = this.parseParam(param[2]); } } Plot.prototype.parseParam = function(pstr) { pstr = pstr.split(","); var ret = {}; for (var i = 0; i < pstr.length; i++) { var tmp = pstr[i].split("=", 2); ret[tmp[0]] = tmp[1]; } return ret; } Plot.prototype.lastDataShow = function() { var data = this.lastData(); if (is_array(data)) { return data.join(", "); } return data; } //价格标线 Plot.prototype.priceFlag = function(close_price, conf) { var close_y = this.axis.getY(close_price); drawWithArrowheads(this.grid.xEnd.x-14,close_y,this.grid.xEnd.x+30,close_y,this.ctx); // drawRow(this.ctx, new Point(this.grid.xEnd.x , close_y), 10, conf.lineColor, 1); //能直接访问grid的内容 var height = conf.bg.height / 2; if (close_y >= 0 && close_y - this.grid.O.y < height) { close_y = this.grid.O.y + height; } if (close_y <= this.grid.xyEnd.y && this.grid.xyEnd.y - close_y < height) { close_y = this.grid.xyEnd.y - height; } conf.unit = this.yunit; writeTextOption(this.ctx, new Point(this.grid.xEnd.x-6, close_y), close_price, conf); } function Shape(ctx, width, height, opt) { this.ctx = ctx; this.width = width ? width : 0; this.height = height ? height : 70; if (!opt) { opt = {}; } this.boder = opt.boder ? opt.boder : "black"; this.fill = opt.fill ? opt.fill : "black"; this.cta = opt.cta ? opt.cta : 0; } __symbol__list__ = { // "BTYBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.001 }, // "BTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 }, "BCCBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 }, "ETHBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 }, "ETCBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 }, // "SC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.0001 }, // "WTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.0001 }, // "NYCC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.001 }, // "BTS": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.001 }, "LTCBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 }, "ZECBTC": { "time": ["M1", "M2", "M3", "M4", "M5", "M15", "M30", "H1", "H2", "H4", "D1", "W1", "MN1"], "yunit": 0.01 } } function ToolBar(controller) { this.controller = controller; this.pair = this.controller.pair; // this.initPair(); // this.hideSymbol(); //隐藏没有数据的货币对 this.config = Config.getInstance(); this.api=this.controller.api; this.isenable = true; } ToolBar.prototype.deinit = function() { } //显示出BTY 和ETH标签 ToolBar.prototype.initPair = function() { // $("#symbol").html(''); for (var c in this.pair) { if (!is_object(this.pair[c])) { continue; } } } ToolBar.prototype.enable = function() { $("#toolbar").show(); this.isenable = true; } ToolBar.prototype.disable = function() { $("#toolbar").hide(); this.isenable = false; } ToolBar.prototype.hideSymbol = function() { var _this = this; $("#symbol2").find("li").each( function() { var text = $(this).text(); text = $.trim(text); $(this).show(); if (typeof _this.pair[text] !== "object") { $(this).hide(); } } ); } ToolBar.prototype.hidePeriod = function(c) { var _this = this; if (!c) return; var _timeList = _this.pair[c]; if (!_timeList) return; _timeList = _timeList.time; _timeMap = {}; for (var i = 0; i < _timeList.length; i++) { _timeMap[_timeList[i]] = 1; } $("#period").find("li").each( function() { var text = $(this).text(); text = $.trim(text); $(this).show(); if (_timeMap[text] !== 1) { $(this).hide(); } } ); } ToolBar.prototype.init = function() { this.InitIndicatorstatus(); //添加事件 var _this=this; $("#Indicator_list label").click(function () { var name=$(this).text(); _this.IndicatorStatusChange(this); }) // $("#Indicator_list li").click(function () { // var name=$(this).attr('name'); // if($(this).find('a').attr('class')=='active'){ // $(this).find('a').removeClass('active'); // } // else{ // $(this).find('a').addClass('active'); // } // _this.IndicatorStatusChange(name); // }) //bty,eth选择切换 $("#symbol2 a").click(function(){ $("#symbol2 a").removeClass("active"); var uid=$.cookie().id; $(this).addClass("active"); _this.newsymbol(this); }); $("#getsymbol").click(function(){ console.log($.cookie('symbol')); if(sessionStorage.getItem('symbol')=='YCC'){ HTML5StockChartAPI.API.setPair('NYCC', sessionStorage.getItem('period')); }else{ HTML5StockChartAPI.API.setPair($.cookie('symbol'), sessionStorage.getItem('period')); } }) $("#ethsymbol").click(function(){ HTML5StockChartAPI.API.setPair("ETHBTC", $.cookie('period')); }) $("#etcsymbol").click(function(){ HTML5StockChartAPI.API.setPair("ETCBTC", $.cookie('period')); }) $("#bccsymbol").click(function(){ HTML5StockChartAPI.API.setPair("BCCBTC", $.cookie('period')); }) /* $("#Indicator_list a").click(function(){ var name=$(this).text(); _this.IndicatorStatusChange(name); });*/ $("#period li").click(function() { $("#period a").removeClass("active"); _this.period(this); }); $("#plot_area").bind('click', function() { _this.toolList(); }); $('body').not($("#plot_area")).unbind('mouseover', function() { // console.log($(this)); $('body').css("cursor", "hand"); _this.toolList(); //$("#dropzone").slideUp("normal"); }); // this.addSelectEvent($("#symbol2"), this.symbol); this.addSelectEvent($("#period"), this.period); //this.addSelectEvent($("#tool_list"), this.toolList);//加号功能,指标 this.addClickEvent($("#zoom"), this.zoom); //选择: ToolBar.selectItem($("#period"), $.cookie('period')); } ToolBar.prototype.InitIndicatorstatus = function() { // console.log("this.api:"+this.api); var inds = $.cookie('ind'); if (this.config.Global.TPL[inds]) { inds = this.config.Global.TPL[inds]; } if (inds.indexOf("ma|main|") != -1) { this.SetIndicatorStatus("MA"); } if (inds.indexOf("macd|main|") != -1) { this.SetIndicatorStatus("MACD"); } if (inds.indexOf("volumes") != -1) { this.SetIndicatorStatus("VOLUMES"); } if (inds.indexOf("bolling") != -1) { this.SetIndicatorStatus("BOLLING"); } if (inds.indexOf("kdj") != -1) { this.SetIndicatorStatus("KDJ"); } } ToolBar.prototype.SetIndicatorStatus = function(indname) { $("#Indicator_list label").each(function() { if ($(this).text() == indname) { $(this).addClass("active"); } }); } ToolBar.prototype.IndicatorStatusChange = function(obj) { var name=$(obj).text(); if($(obj).attr("class")=='active'){ $(obj).removeClass('active'); } else{ $(obj).addClass('active') } var indstr = "main:"; $("#Indicator_list label").each(function(){ if ($(this).attr("class")=='active') { switch ($(this).text()) { case "MA": indstr += Config.getInstance().Global.TPL["ma"]; break; case "VOLUMES": indstr += Config.getInstance().Global.TPL["volumes"]; break; case "MACD": indstr += Config.getInstance().Global.TPL["macd"]; break; case "BOLLING": indstr += Config.getInstance().Global.TPL["bolling"]; break; case "KDJ": indstr += Config.getInstance().Global.TPL["kdj"]; break; } } }) // var indstr = "main:"; // var indstr = this.api.read('ind'); // if(this.config.Global.TPL[indstr]) { // indstr = this.config.Global.TPL[indstr]; // } // switch (obj) { // case "MA": // indstr += Config.getInstance().Global.TPL["ma"]; // break; // case "VOLUMES": // indstr += Config.getInstance().Global.TPL["volumes"]; // break; // case "MACD": // indstr += Config.getInstance().Global.TPL["macd"]; // break; // case "BOLLING": // indstr += Config.getInstance().Global.TPL["bolling"]; // break; // case "KDJ": // indstr += Config.getInstance().Global.TPL["kdj"]; // break; // } // console.log("IndicatorStatusChange:"+name); // var indstr = "main:"; // // 选中那个a 标签就执行什么 // $("input[name = Indicator]:checkbox").each(function() { // if ($(this).is(":checked")) { // switch ($(this).val()) { // case "MA": // indstr += Config.getInstance().Global.TPL["ma"]; // break; // case "VOLUMES": // indstr += Config.getInstance().Global.TPL["volumes"]; // break; // case "MACD": // indstr += Config.getInstance().Global.TPL["macd"]; // break; // case "BOLLING": // indstr += Config.getInstance().Global.TPL["bolling"]; // break; // case "KDJ": // indstr += Config.getInstance().Global.TPL["kdj"]; // break; // } // } // }); this.api.setInd(indstr); } ToolBar.prototype.signal = function(obj) { var signal_id = $.trim(obj.attr('name')); //write cookie of signal api.setSignal(signal_id); } ToolBar.prototype.model = function(obj) { var model_id = $.trim(obj.attr('name')); //write cookie of model api.setModel(model_id); } ToolBar.prototype.tplswitch = function(obj) { var tpl = $.trim(obj.attr('name')); if (tpl == "none") return; var api = HTML5StockChartAPI.API; api.setIndicatorConf(tpl); } ToolBar.tplselect = function(name) { ToolBar.selectItem($("#tpl_list"), name); //alert(name); } ToolBar.prototype.clickFirst = function(obj) { var obj = obj.find("li"); for (var i = 0; i < obj.length; i++) { var item = $(obj.get(i)); if (item.css("display") == "none") { continue; } item.click(); break; } } ToolBar.prototype.clickByName = function(obj, name) { var obj = obj.find("li"); for (var i = 0; i < obj.length; i++) { var item = $(obj.get(i)); if ($.trim(item.text()) == name) { item.click(); break; } } } ToolBar.prototype.clickByLiName = function(obj, name) { var obj = obj.find("li"); for (var i = 0; i < obj.length; i++) { var item = $(obj.get(i)); if (item.attr("name") == name) { item.click(); break; } } } ToolBar.prototype.symbol = function(obj) { var c = $.trim(obj.text()); //c="MAC"; var c = this.findSelected($("#symbol2")); //if(c = "ETH") window.location.href="http://33.cn/ethcny"; //if(c = "BTY") window.location.href="http://33.cn/"; this.hidePeriod(c); //隐藏没有数据的时间周期 var p = this.findSelected($("#period")); $.cookie('period',p); // console.log("p=", p); if (!p) { //this.clickFirst($("#period")); HTML5StockChartAPI.API.setPair(c, this.controller.p); } else { HTML5StockChartAPI.API.setPair(c, p); } } ToolBar.prototype.newsymbol = function(obj) { var c = $(obj).attr("name"); if ($.cookie('symbol') != c) { $.cookie("symbol", null); $.cookie("symbol", c); } //$("#period").children().first(); // $("#period").children().first().text(c); //c="MAC"; //var c = this.findSelected($("#symbol")); //if(c = "ETH") window.location.href="http://33.cn/ethcny"; //if(c = "BTY") window.location.href="http://33.cn/"; // this.hidePeriod(c); //隐藏没有数据的时间周期 var p = this.findSelected($("#period")); $.cookie('period',p); // console.log("p=", p); if (!p) { //this.clickFirst($("#period")); HTML5StockChartAPI.API.setPair(c, this.controller.p); } else { HTML5StockChartAPI.API.setPair(c, p); } } ToolBar.prototype.toolList = function(obj) { var name = obj.attr("name"); var _this = this; if (name == "cross") //cross情况 { $("#plot_area").css("cursor", "crosshair"); var view = this.controller.view; if (window["FlashCanvas"]) { var count = 5; } else { var count = 2; } var crossMoveInterval = new IntervalEvent( count, function(controller, recall) //可以重新被call { var _this = controller.view; if (typeof recall == "undefined") { _this.draw(); } if (!this.value || this.value.length != 3) { return; } var x = this.value[0]; var y = this.value[1]; var index = this.value[2]; for (var i = 0; i < _this.canvasActiveCount; i++) { _this.grid[i].drawCrossX(x, _this.axis[0]); } _this.grid[index].drawCrossY(y); } ); for (var i = 0; i < view.canvasActiveCount; i++) { view.canvas[i].bind("mousemove", crossMoveInterval, this.crossMove); } //if the page is redraw, call it by controller.recall(). now, only new tick //redraw need to recall. this.controller.addIntervalEvent("crossMove", crossMoveInterval, true); $(document).bind("mouseover", function(e) { // console.log(e.target); $("body").css("cursor", "default"); for (var i = 0; i < view.canvasActiveCount; i++) { view.canvas[i].unbind("mousemove", _this.crossMove); } view.draw(); _this.controller.removeIntervalEvent("crossMove"); _this.clickByLiName($("#tool_list"), "none"); $(document).unbind("mouseover"); }); } else if (name == "kline_first") { // var view = this.config.Global.View; for (var key in view) { if (is_object(view[key]) && typeof view[key].disable_axis_area !== "undefined") { view[key].disable_axis_area = 1; } } this.controller.draw(); } else if (name == "ind_first") { var view = this.config.Global.View; for (var key in view) { if (is_object(view[key]) && typeof view[key].disable_axis_area !== "undefined") { view[key].disable_axis_area = 0; } } this.controller.draw(); } else if (name == "indicator") { $("#tool_list").find("li[name='indicator']").css('cursor', 'pointer'); indicator.show(); } else if (name == "tick_price") { } } ToolBar.prototype.toolList = function() { var _this = this; $("#plot_area").css("cursor", "crosshair"); this.controller=this.api.controller; var view = this.controller.view; if (window["FlashCanvas"]) { var count = 5; } else { var count = 2; } var crossMoveInterval = new IntervalEvent( count, function(controller, recall) //可以重新被call { var _this = controller.view; if (typeof recall == "undefined") { _this.draw(); } if (!this.value || this.value.length != 3) { return; } var x = this.value[0]; var y = this.value[1]; var index = this.value[2]; for (var i = 0; i < _this.canvasActiveCount; i++) { _this.grid[i].drawCrossX(x, _this.axis[0]); } _this.grid[index].drawCrossY(y); } ); for (var i = 0; i < view.canvasActiveCount; i++) { view.canvas[i].bind("mousemove", crossMoveInterval, this.crossMove); } //if the page is redraw, call it by controller.recall(). now, only new tick //redraw need to recall. this.controller.addIntervalEvent("crossMove", crossMoveInterval, true); $(document).bind("mouseover", function(e) { $("#plot_area").css("cursor", "default"); if(e.target.id.indexOf("plot")==-1){ for (var i = 0; i < view.canvasActiveCount; i++) { view.canvas[i].unbind("mousemove", _this.crossMove); } view.draw(); _this.controller.removeIntervalEvent("crossMove"); _this.clickByLiName($("#tool_list"), "none"); $(document).unbind("mouseover"); } }); } ToolBar.prototype.crossMove = function(event) { var crossMoveInterval = event.data; var id = $(this).attr("id"); var index = id.split("_").pop(); index = index.substr(0, index.length - 1); index = parseInt(index); var x = event.pageX - $(this).offset().left; var y = event.pageY - $(this).offset().top; crossMoveInterval.setVal([x, y, index]); } ToolBar.prototype.period = function(obj) { var c =$.cookie('symbol'); if ($(obj).find("a").length ==1) { var p = $.trim($(obj).text()); $.cookie('period',p); } else { // $(obj).find("a").setAttribute('class','active'); var elemt = obj.firstChild; elemt.setAttribute('class', 'active');// obj拿到的是类似
  • M1
  • ,所以需要拿到M1也就是a标签下的text var p = $.trim($(elemt).text()); $.cookie('period',p); $.cookie('period',p); } // console.log("period:"+p) HTML5StockChartAPI.API.setPair(c, p); /* if (!c) { return; //没有意义,直接忽略 } else { HTML5StockChartAPI.API.setPair("MAC", p); }*/ } ToolBar.prototype.zoom = function(obj) { var name = obj.attr("name"); if (name == "in") { this.controller.zoomIn(); } else if (name == "out") { this.controller.zoomOut(); } } ToolBar.prototype.style = function(obj) { var name = obj.attr("name"); set_style(name); this.controller.draw(); } ToolBar.prototype.findSymbolSelected = function(obj) { var text = ""; $(obj).find("a").each(function() { if ($(this).attr("class") == "active") { text = $(this).attr("name"); } }) // console.log("findSymbolSelected:"+$.trim(text)); return $.trim(text); } ToolBar.prototype.findSelected = function(obj) { var text = obj.find("li > strong").text(); return $.trim(text); } ToolBar.prototype.addSelectEvent = function(obj, callback) { //获取li var _this = this; var _obj = obj.find("li"); _obj.each( function() { var __callback = callback; var __obj = _obj; $(this).bind("click", _this, function(event) { var _this = event.data; __obj.each( function() { var text = $(this).text(); text = $.trim(text); // $(this).html("" + text + ""); } ); $(this).html('' + $.trim($(this).text()) + ''); if (__callback) __callback.call(_this, $(this)); } ); } ); } ToolBar.prototype.addClickEvent = function(obj, callback) { var _this = this; var _obj = obj.find("li"); _obj.each( function() { var __callback = callback; $(this).bind("click", _this, function(event) { var _this = event.data; if (__callback) __callback.call(_this, $(this)) } ); } ); } //select item , not trigger event. ToolBar.selectItem = function(obj, name) { var __obj = obj.find("li"); var _this = obj.find("li[name='" + name + "']"); __obj.each( function() { var text = $(this).text(); text = $.trim(text); $(this).html("" + text + ""); } ); //alert(_this.length); $(_this).html('' + $.trim($(_this).text()) + ''); } //view 视图,负责绘制图形。 //控制器把从model 读取数据后,做处理完成。然后更新到view里面。 //view 再利用这些数据,绘制出图形,view 不能直接读取model 的数据。 //而是读取control 的数据,这样有一个中间的层次,便于扩展。 //view 没有c . p 的概念。每个图形的model都会有一个getData接口。 //每个图形的 view 都有一个setData 的接口。 //关于图形的显示格式。我想任何选项,包括view 包括 model的都通过 //更改config的形式来实现。对象本身只是读取配置,不提供更改接口。 //比如:view 的样式的修改。每次在之前会读取配置。 //比如:model改成静态图形了,也是类似的修改配置,就可以自动完成。 //在不同对象之间传递配置信息过于繁琐。我想直接读取配置是最好的方式。 // function View(controller) { //当前的图像 this.controller = controller; this.model = this.controller.model; this.config = Config.getInstance(); this.plot_area = this.controller.plot_area; this.setPlotArea(); this.canvas = []; this.canvasBG = []; this.canvasFront = []; this.canvasContainer = []; this.resize = []; this.grid = []; this.axis = []; //给每个canvas分配一个grid对象 var main_axis = new Axis(null); for (var i = 0; i < this.config.Global.maxCanvasCount; i++) { var prefix = "#" + this.config.Global.canvasIDPerfix + i; var id = prefix + "2"; var bg = prefix + "1"; var front = prefix + "0"; var id_div = "#" + this.config.Global.canvasIDPerfix + "div" + "_" + i; //canvas var tmp = $(id); if (!tmp) { continue; } if (!this.config.isIE6) { tmp.hide(); } this.canvas.push(tmp); this.canvasBG.push($(bg).hide()); this.canvasFront.push($(front).hide()); this.canvasContainer.push($(prefix).hide()); //resize var resize = $(id_div); resize.hide(); this.resize.push(resize); if (i == 0) { //主图 var axis_obj = main_axis; } else { var axis_obj = new Axis(main_axis); } this.axis.push(axis_obj); this.grid.push(new PlotGrid(this, i)); } //设置宽度 this.setWidth(); this.setHeight(); set_style(); //主图被注册了 this.canvasActiveCount = 1; //判断兼容性 if (!check_textRender(this.canvas[0])) { //console.log('loading stroke text' + __baseurl__ + "js/strokeText.js"); //include(__baseurl__ + "js/strokeText.js"); } this.plot = {}; this.plotindex = {}; this.plotArea = {}; this.plotArea[this.config.mainName] = true; this.viewconfig = []; //注册主图 this.registerPlot(this.config.mainName, null, true, this.controller.ind[this.config.mainName].param); } View.prototype.deinit = function() { } View.prototype.setPlotArea = function(fixd) { if (this.plot_area != window) { var pa = $(this.plot_area); this.plotWidth = pa.attr("fw") ? pa.attr("fw") : pa.width(); this.plotHeight = pa.attr("fh") ? pa.attr("fh") : pa.height(); $("#plot_area").height(this.plotHeight); $("#plot_area").width(this.plotWidth); $("#plot_content").show(); return; } if (!fixd) { fixd = {}; fixd.x = 10; fixd.y = 3; if (this.config.browser == "ie" && this.config.bversion[0] >= 8) { fixd.y = 8; } } $("#plot_content").hide(); this.plotWidth = $(this.plot_area).width() - fixd.x; this.offsetHeight = $("#plot_area").offset().top; this.plotHeight = $(this.plot_area).height() - this.offsetHeight - fixd.y; $("#plot_area").height(this.plotHeight + 1); $("#plot_area").width($(this.plot_area).width()); if (this.plotWidth < 200) this.plotWidth = 200; if (this.plotHeight < 100) this.plotHeight = 100; $("#plot_content").show(); } View.prototype.setWidth = function(w) { if (!w) { w = this.plotWidth; } for (var i = 0; i < this.config.Global.maxCanvasCount; i++) { set_width(this.canvas[i], w); set_width(this.canvasBG[i], w); set_width(this.canvasFront[i], w); set_width(this.resize[i], w); this.grid[i].setWidth(w); } } View.prototype.setHeight = function() { // console.log() for (var i = 0; i < this.config.Global.maxCanvasCount; i++) { set_height(this.canvas[i], 1); } } View.prototype.init = function() { //注册全局事件 //resize 和 canvas 的事件放在grid中 this.lastW = $(this.plot_area).width(); this.lastH = $(this.plot_area).height(); $(document).bind("keydown", this, this.keydown); $(document).bind("keyup", this, this.keyup); $(this.plot_area).bind("resize", this, this.resizeWindow); var _this = this; _this.wheelEvent = new IntervalEvent(3, function(controller) { var mainName = controller.config.mainName; var one = controller.view.plot[mainName].getOneSize(); var offset = Math.round(-this.value * 40 / one); controller.startOffset(offset); controller.draw(); } ); this.controller.addIntervalEvent("wheelEvent", _this.wheelEvent); var wheelobj; if (this.plot_area == window) { wheelobj = document; } else { wheelobj = this.plot_area; } $(wheelobj).mousewheel( function(event, delta) { var e = window.event||event; console.log("滚轮事件:"+e) if (e.wheelDelta == 120) { _this.controller.zoomIn(); return true; } else if (e.wheelDelta == -120) { _this.controller.zoomOut(); return true; } if (e.detail == -3) { _this.controller.zoomIn(); return true; } else if (e.detail == 3) { _this.controller.zoomOut(); return true; } if (e.originalEvent.deltaY == -3) { _this.controller.zoomIn(); return true; } else if (e.originalEvent.deltaY == 3) { _this.controller.zoomOut(); return true; } //return false; } ); //注册十字光标事件 } View.prototype.registerPlot = function(name, index, has_plot_area, param) { if (this.config[name] && !this.plot[name]) { var Plot = this.config[name].plot; var index = this.getCanvas(name, index); this.plot[name] = new Plot(this, this.grid[index], name); if (param) this.plot[name].setParam(param); if(index==0){ this.canvas[index].css('display', 'block'); this.canvasBG[index].css('display', 'block'); this.canvasFront[index].css('display', 'block'); // this.canvas[index].css('padding-top', '60px'); // this.canvasBG[index].css('padding-top', '60px'); // this.canvasFront[index].css('padding-top', '60px'); } else{ this.canvas[index].css('display', 'block'); this.canvasBG[index].css('display', 'block'); this.canvasFront[index].css('display', 'block'); // this.canvas[index].css('padding-top', '10px'); // this.canvasBG[index].css('padding-top', '10px'); // this.canvasFront[index].css('padding-top', '10px'); } this.canvasContainer[index].css('display', 'block'); this.resize[index].show(); this.plotindex[name] = index; if (has_plot_area) { this.plotArea[name] = has_plot_area; } this.grid[index].addPlot(name, this.plot[name]); this.setLast(); } } View.prototype.isSeperatePlot = function(name) { if (!this.config.Global.View[name]) { return null; } return this.config.Global.View[name].sep; } View.prototype.setLast = function() { for (var i = 0; i < this.canvasActiveCount; i++) { this.grid[i].clearLast(); } this.grid[this.canvasActiveCount - 1].setLast(); } View.prototype.unregisetPlot = function(name) { this.plot[name] = null; this.plotindex[name] = null; this.setLast(); var index = this.plotindex[name]; this.grid[index].removePlot(name); } View.prototype.draw = function() { //draw grid //绘制Y轴 for (var i = 0; i < this.canvasActiveCount; i++) { if (is_object(this.grid[i])) { if (this.data[i].axis.y.axis) { this.grid[i].drawBg(); this.grid[i].drawY(); this.grid[i].drawX(this.axis[0]); //画canvas分割折线 var ctx = this.grid[i].ctx; if(i != this.canvasActiveCount - 1){ drawLever(ctx,this.grid[i].xEnd.x-20,this.grid[i].yEnd.y-2,'#999',10,10); drawLever(ctx,this.grid[i].xEnd.x-20,this.grid[i].yEnd.y+2,'#999',10,-10); } //绘制底部线条 // this.grid[i].drawLine(this.ctx, data.x, data.y, color); //this.grid[i].drawBoder(this.axis[0]); } else { this.grid[i].drawBg(); this.grid[i].drawX(this.axis[0]); // drawLever(ctx,this.grid[i].xEnd.x,this.grid[i].yEnd.y,'#999',10,10); //绘制底部线条 //this.grid[i].drawBoder(this.axis[0]); } } if (i == this.canvasActiveCount - 1) { //画线 //绘制底线没有其他图层了画线 var ctx = this.grid[i].ctx; ctx.beginPath(); ctx.moveTo(this.grid[i].O.x, this.grid[i].yEnd.y+20 - this.grid[i].O.y); ctx.lineTo(this.grid[i].xEnd.x, this.grid[i].yEnd.y+20 - this.grid[i].O.y); ctx.closePath(); ctx.lineWidth = 0.3; ctx.strokeStyle = "#fff"; ctx.stroke(); } } //绘制x。目前,x只需要绘制最后一个图形x轴部分 //开始绘制每个图形 for (i = 0; i < this.data.length; i++) { var plot = this.data[i].data; var text = ""; for (var key in plot) { if (is_object(plot[key])) { //坐标轴的选项对绘图中的 转换是有用的。在通过数字 转换 //成固定格式的数据的时候非常有用。比如时间转换,4舍五入等。 if (plot[key].x && plot[key].x.length > 0) { this.plot[key].draw(plot[key]); //顶部内容 update by fangxiao from 2016/6/29/17:47 // text += "[" + this.plot[key].getShowName() + " " + this.plot[key].lastDataShow() + "] "; } } } if (text.length > 0) { // console.log(text); this.grid[i].writeHeadText(text); } } } //获取坐标轴的配置: // //1.附图,那么要和主图对应,这样时间应该是通过查询产生的。所以,不需要提供配置。 //2.计算x轴,完全是利用主图的配置,这样计算主图的配置就可以了。 // View.prototype.getAxisConfig = function() { var one = this.plot[this.config.mainName].getOneSize(); //获取一个点占用的点数 var nx = this.config.Global.View[this.config.mainName].NXPixel; //x轴的数目推荐坐标的数目 var ny = this.config.Global.View[this.config.mainName].NYPixel; //x轴的数目推荐坐标的数目 var viewconfig = []; for (var i = 0; i < this.canvasActiveCount; i++) { if (is_object(this.grid[i])) { viewconfig[i] = {}; var area = this.grid[i].getPlotArea(); var plotnum = Math.ceil((area.x.beg - area.x.end) / one); viewconfig[i].plotnum = plotnum; viewconfig[i].one = one; viewconfig[i].x = {}; viewconfig[i].x.n = Math.round((area.x.beg - area.x.end) / nx); viewconfig[i].x.beg = area.x.beg; viewconfig[i].x.end = area.x.end; viewconfig[i].y = {}; viewconfig[i].y.n = Math.round((area.y.beg - area.y.end) / ny); viewconfig[i].y.beg = area.y.beg; viewconfig[i].y.end = area.y.end; } } for (var key in this.plotindex) { var index = this.plotindex[key]; if (index === null) continue; if (!viewconfig[index]) continue; if (typeof viewconfig[index].plot === "undefined") viewconfig[index].plot = []; viewconfig[index].plot.push(key); } this.viewconfig = viewconfig; return viewconfig; } View.prototype.getAxis = function(name) { var index = this.plotindex[name]; return this.axis[index]; } View.prototype.getCanvas = function(name, index) { //为指标和主函数分配canvas. var plotconfig = this.config.Global.View; //判断是否要增加canvas if (name == this.config.mainName) { this.grid[0].setHeight(this.plotHeight); } if ((typeof index !== "undefined") && index !== null && index < this.canvasActiveCount) { return index; } if (!plotconfig[name] || !plotconfig[name].sep) { return 0; } var h = this.getUnitHeight(name); // console.log("getUnitHeight:"+h); var index = this.setExistPlotHeight(h); // console.log("getUnitHeight:"+h * plotconfig[name].height); this.grid[index++].setHeight(h * plotconfig[name].height); this.canvasActiveCount = index; return index - 1; } //设置已经存在的图像的高度 View.prototype.setExistPlotHeight = function(unit_h) { //设置所有的canvas 的高度为0 var plotconfig = this.config.Global.View; for (i = 0; i < this.grid.length; i++) { this.grid[i].setHeight(1); } this.grid[0].setHeight(unit_h); var index = 1; //返回新加入名称的canvas for (var key in this.plot) { if (is_object(this.plot[key])) { if (key == this.config.mainName) continue; //主图, 直接pass if (!plotconfig[key]) continue; //没有配置,直接pass if (plotconfig[key].sep) { //分离 // console.log("unit_h * plotconfig[key].height:"+unit_h * plotconfig[key].height) this.grid[index++].setHeight(unit_h * plotconfig[key].height); } } } return index; //新图加在末尾 } //新家图像 View.prototype.getUnitHeight = function(newname) { var x = 1; var plotconfig = this.config.Global.View; for (var key in this.plot) { if (is_object(this.plot[key])) { if (key == this.config.mainName) continue; //主图, 直接pass if (!plotconfig[key]) continue; //没有配置,直接pass if (!this.plotArea[key]) { continue; } if (plotconfig[key].sep) { //分离 x += plotconfig[key].height; } } } if (newname) { x += plotconfig[newname].height; } var h = this.plotHeight * (1 / x); return h; } View.prototype.clearData = function() { this.data = []; //清除原来的main值 this.axis[0].main = null; for (var i = 0; i < this.canvasActiveCount; i++) { this.axis[i].clearData(); } } View.prototype.updateData = function(data, index) { var axis = this.axis[index]; axis.setViewConfig(this.viewconfig[index]); axis.setData(data); this.data[index] = axis.getData(); } //这里的PlotGrid 的名字是暂时使用的,具体什么名字还没有取好 //因为事件主要是在plotgrid中处理,所以,我用了这个名字 View.prototype.keydown = function(event) { var _this = event.data; PlotGrid.setMoveInterval("key", _this.grid[0]); debug("keydown"); if (event.keyCode == Key.KEY_NEXT) { var offset = _this.controller.startSet("Next"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_PREV) { var offset = _this.controller.startSet("Prev"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_PAGE_DOWN) { var offset = _this.controller.startSet("PageDown"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_PAGE_UP) { var offset = _this.controller.startSet("PageUp"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_HOME) { var offset = _this.controller.startSet("Home"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_END) { var offset = _this.controller.startSet("End"); PlotGrid.moveQueue.push(offset); return false; } else if (event.keyCode == Key.KEY_UP) { _this.controller.zoomIn(); return false; } else if (event.keyCode == Key.KEY_DOWN) { _this.controller.zoomOut(); return false; } } View.prototype.keyup = function(event) { debug("keyup"); var _this = event.data; PlotGrid.clearMoveInterval(_this.grid[0]); } View.prototype.resizeGrid = function(offset, index) { debug("set resize offset " + offset); if (offset == 0) return; var grid1 = this.grid[index]; var grid2 = this.grid[index + 1]; // if(index==0){ // var margin1 = grid1.marginTop[grid1.TOP] + grid1.marginTop[grid1.BOTTOM]; // var margin2 = grid2.marginTop[grid2.TOP] + grid2.marginTop[grid2.BOTTOM]; // }else{ // var margin1 = grid1.margin[grid1.TOP] + grid1.margin[grid1.BOTTOM]; // var margin2 = grid2.margin[grid2.TOP] + grid2.margin[grid2.BOTTOM]; // } var margin1 = grid1.margin[grid1.TOP] + grid1.margin[grid1.BOTTOM]; var margin2 = grid2.margin[grid2.TOP] + grid2.margin[grid2.BOTTOM]; if (!grid1 || !grid2) return; if (offset > 0 && grid2.height - margin2 - offset <= 5) { offset = grid2.height - 5 - margin2; } if (offset < 0 && grid1.height - margin1 + offset <= 5) { offset = -(grid1.height - 5 - margin1); } grid1.setHeight(grid1.height + offset); grid2.setHeight(grid2.height - offset); } View.prototype.resizeWindow = function(e) { var _this = e.data; if (!_this.lastW) { return; } if (_this.lastW == $(window).width() && _this.lastH == $(window).height()) { return; } //debugx("resize0: " + _this.lastW + "," + _this.lastH); //debugx("resize1: " + $(window).width() + "," + $(window).height()); _this.setPlotArea(); _this.setWidth(); //_this.setExistPlotHeight(_this.getUnitHeight()); //debugx("resize2: " + $(window).width() + "," + $(window).height()); //保存最新的window值 _this.lastW = $(window).width(); _this.lastH = $(window).height(); _this.controller.draw(); } function Win(title, width, height, opt) { this.title = title; this.width = width ? width : 400; this.height = height ? height : 300; if (!opt) opt = {}; this.autoclose = opt.autoclose ? opt.autoclose : 0; this.left = opt.left ? opt.left : -1; //-1表示默认向左,其实就是窗口居中 this.top = opt.top ? opt.top : -1; //top 的意义和 left相同 this.config = Config.getInstance(); this.tpl = $(this.config.Global.WinTplID); this.cache = {}; } Win.zindex = 1000; Win.prototype.getHtml = function() { var html = this.tpl.html(); html = html.replace("{|$title|}", this.title); html = html.replace("{|$autoclose|}", this.autoclose); return html; } Win.prototype.createWindow = function(targetID) { if ($("#" + targetID).length) { return; } this.conf = this.config.Global.Win[targetID]; var html var newhtml if (targetID == "__order__") { this.win = $("
    "); html = this.getHtml(); newhtml = html.replace("class=\"content\">", "id='popwin' class=\"content\">"); } else { this.win = $("
    "); newhtml = this.getHtml(); } this.targetID = targetID; $("body").append(this.win); this.win.html(newhtml); this.dragArea = this.getClassQuery("popTop"); this.win.dragdrop(this.dragArea); this.setWidth(this.width).setHeight(this.height); this.win.css("position", "absolute"); this.win.css("z-index", Win.zindex); this.setPotion(); Win.zindex++; this.addTab(); this.addEvent(); this.clickFirst(this.getClassQuery("cc")); this.win.hide(); } Win.prototype.addTab = function() { var query = this.getClassQuery("cc"); query = $(query); for (var key in this.conf.tab) { if (typeof this.conf.tab[key] == "string") { query.append('
  • ' + this.conf.tab[key] + '
  • '); } } } Win.prototype.clickFirst = function(query) { query = $(query); query.children().first().click(); } Win.prototype.setPotion = function() { if (this.top == -1) { var top = Math.max(0, ($(window).height() - this.height) / 2); } if (this.left == -1) { var left = Math.max(0, ($(window).width() - this.width) / 2); } this.win.css("top", top + "px"); this.win.css("left", left + "px"); } Win.prototype.setWidth = function(w) { var mainContainer = this.getClassQuery("windowBody"); $(mainContainer).width(w); return this; } Win.prototype.setHeight = function(h) { var mainContainer = this.getClassQuery("windowBody"); $(mainContainer).height(h); return this; } Win.prototype.getClassQuery = function(classname) { var targetID = this.targetID; if (targetID.charAt(0) != "#") { targetID = "#" + this.targetID } if (classname.charAt(0) != ".") { classname = " ." + classname; } var query = targetID + classname; return query } Win.prototype.show = function() { this.win.show(); this.win.css("z-index", Win.zindex++); } Win.prototype.addEvent = function() { //close var query = this.getClassQuery("adel"); var _this = this; $(query).click( function() { $("#tool_dl").find("li[name=none]").click(); _this.win.hide(); } ); //tab var query = this.getClassQuery("cc"); var _this = this; $(query).children().click(function(e) { $(query).children().removeClass("current"); $(this).addClass("current"); if (_this.load) { _this.load($(this).attr("name")); } }); } function WinAjax(name, title, width, height, opt) { sup(this, title, width, height, opt); } ClassExtend(WinAjax, Win); WinAjax.prototype.load = function(name, flag) { //这里可以根据name 做一些特殊的处理。 //但是,目前,我们采用的是 ajax 从服务器下载相关的配置 this.tab = name; this.flag = flag; var query = this.getClassQuery("content"); if (this.cache[name + flag]) { $(query).html(this.cache[name + flag]); return; } var api if (name == "order") { api = this.config.Global.WinOrder; } else { api = this.config.Global.WinAPI; } var _this = this; if (this.ajax) { this.ajax.abort(); } var param = { win: this.targetID, tab: name, __tmp: Math.random() }; if (this.flag) { param.flag = this.flag; } if (name == "order") { this.ajax = $.get(api, function(data) { //_this.cache[data.tab + data.flag] = data.data; $(query).html(data); }, "html"); } else { this.ajax = $.get(api, param, function(data) { _this.cache[data.tab + data.flag] = data.data; $(query).html(data.data); }, "jsonp"); } } function ShapeArrow(ctx, width, height, opt) { sup(this, ctx, width, height, opt); } ClassExtend(ShapeArrow, Shape); ShapeArrow.prototype.arrowhalf = function(ctx, width, height) { var pi = Math.PI; ctx.save(); ctx.translate(0, -height); ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(width, 0); var head = (0.618) * height; ctx.lineTo(width, head); if (width <= 3) { var add = 3; } else { var add = width / 0.618; } ctx.lineTo(width + add, head - width * Math.tan(pi / 3)); ctx.lineTo(0, height); ctx.closePath(); ctx.stroke(); ctx.fill(); ctx.restore(); } ShapeArrow.prototype.basic_arrow = function(ctx, width, height) { ctx.save(); this.arrowhalf(ctx, width, height); ctx.transform(-1, 0, 0, 1, 0, 0); this.arrowhalf(ctx, width, height); ctx.restore(); } //箭头的定位在于两个方面。一个方面是旋转的角度,一个是箭头指向的位置 ShapeArrow.prototype.draw = function(point) { var ctx = this.ctx; var cta = this.cta; ctx.save(); ctx.translate(point.x, point.y); ctx.rotate(cta); ctx.strokeStyle = this.boder; ctx.fillStyle = this.fill; this.basic_arrow(ctx, this.width, this.height); ctx.restore(); } function PlotAdvFractals(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotAdvFractals, Plot); PlotAdvFractals.prototype.draw = function(data) { var color; if (this.param.lineColor) { color = this.param.lineColor; } else { color = this.conf.lineColor; } /* for(var i = 0 ;i y[i][j]) { min = y[i][j] } } } this.maxD = max; this.minD = min; } PlotBollingDesity.prototype.drawOne = function(time, data) { var price, desity, color, left, top; try { for (var i = 0; i < 16; i += 2) { price = data[i]; if (price == -1 || data[i + 1] == -1) { continue; } desity = this.getDesity(data[i + 1]); color = this.color[i / 2]; this.ctx.fillStyle = color; left = time - this.left; top = price - desity; if (top < this.top || top > this.bottom) { continue; } this.ctx.fillRect(left, top, this.box, desity); } } catch (e) {} } PlotBollingDesity.prototype.getDesity = function(d) { return (Math.floor(((d - this.minD) / (this.maxD - this.minD)) * 6) + 1); } PlotBollingDesity.prototype.lastDataShow = function() { var data = this.lastData(); var newdata = []; for (var i = 0; i < data.length; i += 2) { newdata[i / 2] = data[i]; } return newdata; } function PlotCandle(view, grid, name) { sup(this, view, grid, name); //放大缩小尺寸控制更加灵活。采用配置数组的形式 this.box = [1, 2, 3, 5, 7, 9, 11, 13]; this.space = [0, 1, 1, 2, 2, 3, 4, 5]; this.left = [0, 1, 1, 2, 3, 4, 5, 6] // this.box = [ 7, 9, 11, 13,15,18,21,23]; // this.space = [ 2, 3, 4, 5,6,8,9,10]; // this.left = [ 3, 4, 5, 6,7,9,10,11] // this.box = [3, 5, 9, 13, 17, 19, 23, 25]; // this.space = [1, 2, 3, 5, 7, 8, 10, 11]; // this.left = [1, 2, 4, 6, 8, 9, 11, 12] } ClassExtend(PlotCandle, Plot); PlotCandle.prototype.zoomIn = function() { if (this.conf.box_pixel >= 7 && this.conf.space_pixel >= 7) { return; } if (this.conf.box_pixel < this.conf.space_pixel) { this.conf.box_pixel++; } else if (this.conf.box_pixel > this.conf.space_pixel) { this.spaceOffset(1); } else { this.conf.box_pixel++; } } PlotCandle.prototype.spaceOffset = function(offset) { var prev = this.space[this.conf.space_pixel]; this.conf.space_pixel = this.conf.space_pixel + offset; var current = this.space[this.conf.space_pixel]; if (prev == current) { this.conf.box_pixel = this.conf.box_pixel + offset; } } PlotCandle.prototype.zoomOut = function() { if (this.conf.box_pixel <= 0 && this.conf.space_pixel <= 0) { return; } if (this.conf.box_pixel < this.conf.space_pixel) { this.spaceOffset(-1); } else if (this.conf.box_pixel > this.conf.space_pixel) { this.conf.box_pixel--; } else { this.spaceOffset(-1); } this.conf.box_pixel = this.conf.box_pixel < 0 ? 0 : this.conf.box_pixel; } PlotCandle.prototype.getOneSize = function() { return this.box[this.conf.box_pixel] + 2*this.space[this.conf.space_pixel]; } PlotCandle.prototype.getBoxLeft = function() { return this.left[this.conf.box_pixel]; } PlotCandle.prototype.getBoxSize = function() { return this.box[this.conf.box_pixel]; } PlotCandle.prototype.draw = function(data) { this.data = data; this.X = data.x; this.Y = data.y; this.xunit = this.data.xunit; this.yunit = this.data.yunit; this.closePriceFlag(); var width = this.box[this.conf.box_pixel]; var left = this.left[this.conf.box_pixel]; if (width > 2) { width--; } this.config.Global.boxwidth = width; //最高最低点标签 var maxy=this.Y[this.data.maxIndex][1]-5; var miny=this.Y[this.data.minIndex][2]+15; writeText(this.ctx,new Point(this.X[this.data.maxIndex],maxy),parseFloat(this.data.maxY).toFixed(3), "#ff6600", "#fff"); writeText(this.ctx,new Point(this.X[this.data.minIndex],miny),parseFloat(this.data.minY).toFixed(3), "#ff6600", "#fff"); //画个分界线 for (var i = 0; i < this.X.length; i++) { this.drawOne(this.X[i], this.Y[i], width, left); // var ohlc=this.Y[i]; // var maxy=parseFloat(GetText(ohlc[1],this.yunit)).toFixed(3); // var miny=parseFloat(GetText(ohlc[2],this.yunit)).toFixed(3); // if(maxy==this.data.maxY){ // writeText(this.ctx, new Point(this.X[i] + this.conf.ledgeLen + 2, this.Y[i]), this.data.maxY, this.conf.fontColor, this.conf.font); // } // if(miny==this.data.minY){ // writeText(this.ctx, new Point(this.X[i] + this.conf.ledgeLen + 2, this.Y[i]), this.data.maxY, this.conf.fontColor, this.conf.font); // } } //console.log(data.text); } //按照道理,这些数据都应该是计算好了之后, //会送给客户端的。这个部分,还是要对数据层进行改进 //才能比较好的封装。现在暂时这样处理。 PlotCandle.prototype.closePriceFlag = function() { //view 一般不调用model的数据。因为,这样使得view 和 model层直接联系起来。 //但是,我们知道,我们的Controller 是一般性的分发。 //而且这样的一般性的分发在大多数情况下面是够用的。 //出于这样的考虑,我们允许在view里面调用model,因为,的确很多时候需要这样的调用。 //每个图都有对应的 model plot var close_price = this.view.model.getDataReader(this.name).close(0); this.priceFlag(close_price, this.conf.close); } PlotCandle.prototype.over = function(x, y) { //查找最接近的 // console.log("1"); if (!this.X || !this.Y) return; var index = find_near(this.X, x); if (index == -1) return false; if (Math.abs(this.X[index] - x) > this.getOneSize()) { return false; } var ohlc = this.Y[index]; // console.log("text PlotCandle"); //找到此金额的y轴坐标 //if (ohlc[Data.HIGH] - this.config.Global.overPadding <= y && ohlc[Data.LOW] + this.config.Global.overPadding >= y) { var findTime = this.axis.getXValue(x, PlotGrid.NEAR); if (findTime === false) return; var cdata = this.view.model.getDataByTime(this.name, findTime, "candle"); if (!cdata) return false; var time = cdata.time; var ohlc = cdata.data; var yunit = this.axis.viewconfig.y.axis_option.unit; var fallOrDegrees = parseFloat(GetText(ohlc[Data.CLOSE], yunit) - GetText(ohlc[Data.OPEN], yunit)) / parseFloat(GetText(ohlc[Data.OPEN], yunit)); var fallOrDegrees = fallOrDegrees.toFixed(3); $(".freetimebox li:first-child").text("时间:"+time); $(".freetimebox li:nth(1)").text("开:"+GetText(ohlc[Data.OPEN], yunit)); $(".freetimebox li:nth(2)").text("高:"+GetText(ohlc[Data.HIGH], yunit)); $(".freetimebox li:nth(3)").text("低:"+GetText(ohlc[Data.LOW], yunit)); $(".freetimebox li:nth(4)").text("收:"+GetText(ohlc[Data.LOW], yunit)); $(".freetimebox li:nth(4)").text("收:"+GetText(ohlc[Data.CLOSE], yunit)); $(".freetimebox li:nth(5)").text("涨幅:"+fallOrDegrees); $(".freetimebox li:nth(6)").text("振幅:"+fallOrDegrees); $(".freetimebox li:nth(7)").text("量:"+GetText(ohlc[Data.VOLUMES], yunit)); $('.freetimebox').show(); //timeLine Data // timelineData(time, GetText(ohlc[Data.OPEN], yunit), GetText(ohlc[Data.HIGH], yunit), GetText(ohlc[Data.LOW], yunit), GetText(ohlc[Data.CLOSE], yunit), GetText(ohlc[Data.VOLUMES], yunit)); // console.log("text PlotCandle"); // return time + "
    " + "O: " + GetText(ohlc[Data.OPEN], yunit) + "
    " + "H: " + GetText(ohlc[Data.HIGH], yunit) + "
    " + "L : " + GetText(ohlc[Data.LOW], yunit) + "
    " + "C: " + GetText(ohlc[Data.CLOSE], yunit) + "
    " + "V: " + GetText(ohlc[Data.VOLUMES], yunit) + "
    "; // } else { // return false; // } } PlotCandle.prototype.drawOne = function(time, ohlc, width, left) { var ctx = this.ctx; var open = ohlc[Data.OPEN]; var high = ohlc[Data.HIGH]; var low = ohlc[Data.LOW]; var close = ohlc[Data.CLOSE]; time += 0.5; open += 0.5; high += 0.5; low += 0.5; close += 0.5; if (high == low) { //画个横线就可以了 ctx.strokeStyle = this.conf.os_border_color; ctx.beginPath(); ctx.moveTo(time - left, open); ctx.lineTo(time + width, open); ctx.stroke(); return; } if (open < close) { var up = open; var down = close; var fill = this.conf.open_big_color; var border = this.conf.ob_border_color; } else { var up = close; var down = open; var fill = this.conf.open_small_color; var border = this.conf.os_border_color; } ctx.strokeStyle = border; ctx.beginPath(); ctx.moveTo(time, high); ctx.lineTo(time, low); ctx.stroke(); if (this.conf.space_pixel > 0) { ctx.fillStyle = fill; ctx.fillRect(time - left, up, width, down - up); ctx.strokeRect(time - left, up, width, down - up); } } function PlotFractals(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotFractals, Plot); PlotFractals.prototype.draw = function(data) { var color; if (this.param.lineColor) { color = this.param.lineColor; } else { color = this.conf.lineColor; } /* for(var i = 0 ;i 0) { debug("setInterval draw beg"); PlotGrid.moveTo(PlotGrid.moveQueue.pop(), _this); debug("setInterval draw end"); PlotGrid.moveQueue = []; } }, _time); debug(this.moveTimeid); } PlotGrid.clearMoveInterval = function(_this) { if (PlotGrid.moveQueue.length > 0) { PlotGrid.moveTo(PlotGrid.moveQueue.pop(), _this); PlotGrid.moveQueue = []; } if (PlotGrid.moveTimeid !== null) window.clearInterval(PlotGrid.moveTimeid); debug("clearMoveInterval"); PlotGrid.moveTimeid = null; } PlotGrid.prototype.getYValue = function(y, axis) { if (typeof axis === "undefined") { axis = this.axis; } var value = this.axis.getYValue(y, this.conf.crossYMode, axis); if (value === false) return; var opt = axis.viewconfig.y.axis_option; value = this.getText(opt.style, value, opt.unit); return value; } PlotGrid.prototype.addEvent = function() { this.active = true; //这两个事件在主图中注册就可以了 $(this.canvasContainer).bind('mousedown touchstart', this, function(e) { debug('mousedown'); var _this = e.data; //PlotGrid.xleft = e.pageX; PlotGrid.xleft = isPC() ? e.pageX - $(this).offset().left : e.originalEvent.targetTouches[0].pageX - $(this).offset().left; PlotGrid.setMoveInterval('mouse', _this); var canvas_mousemove = function(e) { //console.log("move"); var _this = e.data; //var newxleft = e.pageX - $(this).offset().left; var newxleft = isPC() ? e.pageX - $(this).offset().left : e.originalEvent.targetTouches[0].pageX - $(this).offset().left; PlotGrid.moveQueue.push(newxleft); debug("move to " + newxleft); return false; }; $(_this.canvasContainer).bind('mousemove touchmove', _this, canvas_mousemove); var _canvas = _this.canvas; if (_canvas.selector == "#plot_canvas_02") { /*var x = e.pageX - $(_canvas).offset().left;*/ var x = isPC() ? e.pageX - $(_canvas).offset().left : e.originalEvent.targetTouches[0].pageX - $(_canvas).offset().left; /*var y = e.pageY - $(_canvas).offset().top;*/ var y = isPC() ? e.pageY - $(_canvas).offset().top : e.originalEvent.targetTouches[0].pageY - $(_canvas).offset().top; /* if(_this.xEnd.x >x && (_this.xEnd.x-x)<40){ $("#tool_list").find("li[name='order']").css('cursor', 'pointer'); $("#ordershow").val(_this.getYValue(y)); $("#orderprice").val( $("#ordershow").val()); $("#orderdirection").html("买"); order.show(); }else if(_this.xEnd.x = PlotGrid._this.config.Global.hoverTime) { var show = 1; this.value[2] = -1; } else { var show = 0; } // console.log("time:"+time+",newtime:"+newtime+",show:"+show+",x:"+x+",y:"+y);//头部标题内容 if (this.X && this.Y) { var index = find_near(this.X, x); if ((index != -1) && (Math.abs(this.X[index] - x) <= this.getOneSize())) { var ohlc = this.Y[index]; if (ohlc[Data.HIGH] - this.config.Global.overPadding <= y && ohlc[Data.LOW] + this.config.Global.overPadding >= y) { var findTime = this.axis.getXValue(x, PlotGrid.NEAR); if (findTime === false) return; var cdata = this.view.model.getDataByTime(this.name, findTime, "candle"); if (!cdata) return false; var time = cdata.time; var ohlc = cdata.data; var yunit = this.axis.viewconfig.y.axis_option.unit; var fallOrDegrees = parseFloat(GetText(ohlc[Data.CLOSE], yunit) - GetText(ohlc[Data.OPEN], yunit)) / parseFloat(GetText(ohlc[Data.OPEN], yunit)); var fallOrDegrees = fallOrDegrees.toFixed(3); //timeLine Data // timelineData(time, GetText(ohlc[Data.OPEN], yunit), GetText(ohlc[Data.HIGH], yunit), GetText(ohlc[Data.LOW], yunit), GetText(ohlc[Data.CLOSE], yunit), fallOrDegrees, GetText(ohlc[Data.VOLUMES], yunit)); // console.log("text PlotCandle"); } } } PlotGrid.doHover(x, y, this.value[3], show); }, true ); $(this.canvas).bind("mousemove touchmove", this, this.mouseMove); this.controller.addIntervalEvent("mouseMoveInterval", mouseMoveInterval); $(this.canvas).bind("mouseout touchend", this, function(e) { $("#msgbox").hide(); }); } PlotGrid.prototype.mouseMove = function(e) { var _this = e.data; PlotGrid._this = _this; var _canvas = _this.canvas; //var x = e.pageX - $(_canvas).offset().left; var x = isPC() ? e.pageX - $(_canvas).offset().left : e.originalEvent.targetTouches[0].pageX - $(_canvas).offset().left; //var y = e.pageY - $(_canvas).offset().top; var y = isPC() ? e.pageY - $(_canvas).offset().top : e.originalEvent.targetTouches[0].pageY - $(_canvas).offset().top; var time = (new Date()).getTime(); var interval = _this.controller.intervalEvent.mouseMoveInterval; //位置没有变化,不进行处理 if (interval.value && interval.value[0] == x && interval.value[1] == y) { return; } interval.setVal([x, y, time, e]); } //十字光标经过操作 PlotGrid.doHover = function(x, y, e, showhover) { var _this = PlotGrid._this; if (!showhover) { if ($("#msgbox").css('display') != "none") { $("#msgbox").css("top", -1000).css("left", -1000); $("#msgbox").hide(); //debugx("hide1: " + x + "," + y); } return false; } //_this.controller.intervalEvent.mouseMoveInterval.setVal(null); var overstr = ''; for (var i = _this.plot.length - 1; i >= 0; i--) { if (_this.plot[i].obj.over) { //将内容加入头部 overstr = _this.plot[i].obj.over(x, y); if (overstr) break; } } if (overstr) { $("#msgbox").html(overstr); //debugx("show: " + x + "," + y); var top = e.pageY + 10; var left = e.pageX + 10; var w = $("#msgbox").width(); var h = $("#msgbox").height(); if (top + h + 10 > $(window).height()) { top = $(window).height() - h - 12; } if (left + w + 10 > $(window).width()) { left = $(window).width() - w - 12; } $("#msgbox").css("top", top).css("left", left); $("#msgbox").show(); } else { $("#msgbox").css("top", -1000).css("left", -1000); $("#msgbox").hide(); //debugx("hide2: " + x + "," + y); } } PlotGrid.prototype.removeEvent = function() { this.active = false; $(document).unbind("keydown"); $(this.canvas).unbind('mousedown uchstartto'); $(this.canvas).unbind('mousemove touchmove'); $(document).unbind('mouseup touchend'); } PlotGrid.moveTo = function(newxleft, _this) { if (PlotGrid.mode == "mouse") { if (newxleft - PlotGrid.xleft > PlotGrid.minwidth) //move to left { _this.controller.startOffset(Math.round((newxleft - PlotGrid.xleft) / PlotGrid.minwidth)); _this.controller.draw(); PlotGrid.xleft = newxleft; } else if (PlotGrid.xleft - newxleft > PlotGrid.minwidth) { _this.controller.startOffset(-Math.round((PlotGrid.xleft - newxleft) / PlotGrid.minwidth)); _this.controller.draw(); PlotGrid.xleft = newxleft; } } else if (PlotGrid.mode == "key") { _this.controller.draw(); } else if (PlotGrid.mode == "resize") { _this.view.resizeGrid(newxleft - PlotGrid.ytop, _this.index); _this.controller.draw(); PlotGrid.ytop = newxleft; } } PlotGrid.prototype.addPlot = function(name, plot) { var obj = { name: name, obj: plot }; this.plot.push(obj); } PlotGrid.prototype.removePlot = function(name) { var plot = []; for (var i = 0; i < plot.length; i++) { var item = plot[i]; if (item.name != name) { plot.push(item); } } this.plot = plot; } function isPC() { var userAgentInfo = navigator.userAgent; //alert(userAgentInfo) var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod" ]; var flag = true; for (var v = 0; v < Agents.length; v++) { if (userAgentInfo.indexOf(Agents[v]) > 0) { flag = false; break; } } return flag; } function PlotKshape(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotKshape, Plot); PlotKshape.tagMap = { 1: "A", 2: "a", 3: "B", 4: "b", 5: "C", 6: "c", 7: "D", 8: "d", 9: "E", 10: "e", 11: "N", 12: "n", 13: "W", 14: "w", 15: "X", 16: "x", 17: "F", 18: "f", 19: "L", 20: "l", 21: "T", 22: "t", 23: "G", 24: "g", 25: "Z", 26: "z", 27: "V", 28: "v", 29: "J", 30: "j", 31: "Y", 32: "y" }; PlotKshape.prototype.draw = function(data) { //建立点 var color; if (this.param.lineColor) { color = this.param.lineColor; } else { color = this.conf.lineColor; } for (var i = 0; i < data.x.length; i++) { if (data.y[i][1] != -1) { writeText(this.ctx, new Point(data.x[i], data.y[i][0]), PlotKshape.tagMap[data.y[i][1]], color[0], this.param.font); } if (data.y[i][2] != -1) { writeText(this.ctx, new Point(data.x[i], data.y[i][0] + 12), PlotKshape.tagMap[data.y[i][2]], color[1], this.param.font); } } } function PlotLine(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotLine, Plot); PlotLine.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } drawLine(this.ctx, data.x, data.y, color); } function PlotMacd(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotMacd, Plot); PlotMacd.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color.split("-"); } else { color = this.conf.lineColor; } var y1 = [] var y2 = [] for (var i = 0; i < data.y.length; i++) { y1[i] = data.y[i][0]; y2[i] = data.y[i][1]; } //color[1] = "rgb(112,207,88)"; drawLine(this.ctx, data.x, y2, color[0],1.5); this.plotBar(data.x, y1, color[1]); } PlotMacd.prototype.getShowName = function() { var name = this.name.toUpperCase(); name = name.replace("MAIN", "").replace("||", " "); name = name.split("|"); if (name.length == 1) { return name[0]; } var pre = name.shift(); return pre + " (" + name.join(" , ") + ")"; } PlotMacd.prototype.plotBar = function(x, H, colorbar) { var zero_y = this.axis.getY(0); drawRow(this.ctx, { x: this.left, y: zero_y }, this.right - this.left, this.conf.lineColorZero); this.ctx.beginPath(); this.ctx.strokeStyle = colorbar; for (var i = 0; i < x.length; i++) { try { if (isNaN(H[i])) continue; this.ctx.moveTo(x[i] + 0.5, zero_y); this.ctx.lineTo(x[i] + 0.5, H[i]); } catch (e) { // console.log(e); // console.log(x[i]); // console.log(H[i]); } } this.ctx.stroke(); } function Plotmatrend(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotmatrend, Plot); Plotmatrend.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } var y1 = [] var y2 = [] var y3 = [] var y4 = [] var y5 = [] var y6 = [] for (var i = 0; i < data.y.length; i++) { y1[i] = data.y[i][0]; y2[i] = data.y[i][1]; y3[i] = data.y[i][2]; y4[i] = data.y[i][3]; y5[i] = data.y[i][4]; y6[i] = data.y[i][5] } drawLine(this.ctx, data.x, y1, color[0]); drawLine(this.ctx, data.x, y2, color[1]); drawLine(this.ctx, data.x, y3, color[2]); drawLine(this.ctx, data.x, y4, color[3]); drawLine(this.ctx, data.x, y5, color[4]); drawLine(this.ctx, data.x, y6, color[5]); } function PlotMLine(view, grid, name) { sup(this, view, grid, name); this.laststatus = ""; this.lastquick = ""; } ClassExtend(PlotMLine, Plot); PlotMLine.prototype.draw = function(data) { //建立点,mline的格式 // var color, width; this.data = data; var last = this.lastData(); for (var key in data.data) { if (this.param[key.toLowerCase() + "_color"]) { color = this.param[key.toLowerCase() + "_color"]; } else { color = this.conf.lineColor; } if (data.width && data.width[key]) { width = data.width[key]; //console.log(width); } if (data.data[key].x.length > 0) { drawLine(this.ctx, data.data[key].x, data.data[key].y, color, width); } if (this.param[key.toLowerCase() + "_flag"]) { this.priceFlag(last[key].bidask[0], this.config.Global.Candle["public"].close); } } } PlotMLine.prototype.lastDataShow = function() { var text = []; text.push("Local-QuickT = " + this.data.offset.local); text.push("QuickT-SlowT = " + this.data.offset.delt); if (1) { var last = this.lastData(); var laststatus = "BID("; for (var key in last) { laststatus += key + "=" + this.grid.getText(0, last[key].bidask[0], this.data.yunit) + " "; } this.laststatus = laststatus + ")"; } if (!this.data.quick.inquick) { var quick = this.data.quick; if (quick.start && quick.end) { this.lastquick = " ([" + quick.start + ", " + quick.end + "] Point: " + quick.point / 10 + " Time: " + quick.delt + ") " } } return this.lastquick + this.laststatus + text.join(" , "); } PlotMLine.prototype.getShowName = function() { var text = ""; var keyarr; var name = this.name.split("|", 2)[0]; for (var key in this.param) { if (key.indexOf("_color") !== -1) { keyarr = key.split("_"); text += keyarr[0] + "(" + this.param[key] + ") "; } } var ret = name + " " + text; ret = ret.toUpperCase(); return ret; } function PlotOrder(view, grid, name) { sup(this, view, grid, name); this.arrow = {}; var conf = this.config.Global.arrow[DataWriterProfit.BUY_OPEN]; this.arrow[DataWriterProfit.BUY_OPEN] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.BUY_CLOSE]; this.arrow[DataWriterProfit.BUY_CLOSE] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.SELL_OPEN]; this.arrow[DataWriterProfit.SELL_OPEN] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.SELL_CLOSE]; this.arrow[DataWriterProfit.SELL_CLOSE] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); } ClassExtend(PlotProfit, Plot); PlotProfit.prototype.draw = function(data) { var x, y, type; this.X = data.x; this.Y = data.y; for (var i = 0; i < data.x.length; i++) { for (var j = 0; j < data.y[i].length; j++) { y = data.y[i][j]; type = y[0]; y = y[1]; x = this.fixedX(data.x[i], type); this.arrow[type].draw(new Point(x, y)); } } } PlotProfit.prototype.getShowName = function() { return this.controller.c + " " + this.controller.p; } PlotProfit.prototype.lastDataShow = function() { return ""; } PlotProfit.prototype.fixedX = function(x, type) { x += 0.5; var box = this.view.plot[this.config.mainName].getBoxSize(); var left = this.view.plot[this.config.mainName].getBoxLeft(); if (type == DataWriterProfit.BUY_OPEN || type == DataWriterProfit.SELL_OPEN) { x -= left; } else { x += box - left; } return x; } PlotProfit.prototype.over = function(x1, y1) { if (!this.X) { return false; } var x, y, type, bk = false; x1 += 0.5; for (var i = 0; i < this.X.length; i++) { for (var j = 0; j < this.Y[i].length; j++) { y = this.Y[i][j][1]; type = this.Y[i][j][0]; x = this.fixedX(this.X[i], type); if (this.inarrow(new Point(x, y), type, x1, y1)) { bk = true; break; } } if (bk) { break; } } if (!bk) { return false; } var findTime = this.axis.getXValue(this.X[i], PlotGrid.NEAR); if (findTime === false) return; var cdata = this.view.model.getDataByTime(this.name, findTime); if (!cdata) { return false; } cdata.data = cdata.data[j]; /* var data = cdata.data[2]; var open_time = timeToDate(data[1], null, this.config.Global.timezone); var close_time = timeToDate(data[8], null, this.config.Global.timezone); return "Ticket: " + data[0] + "
    " + "Open Time: " + open_time + "
    " + "Close Time: " + close_time + "
    " + "Open Price: " + data[5] + "
    " + "Close Price:" + data[9] + "
    " + "Size: " + data[3] + "
    " + "Profit: " + data[13] + "
    "; */ return "Type:      " + (DataWriterProfit.nameMap[cdata.data[0]]) + "
    " + "Price:     " + cdata.data[1] + "
    " + "OrderID: " + cdata.data[2] + "
    "; } //某个坐标是否在某个点内部 PlotProfit.prototype.inarrow = function(point, type, x, y) { var conf = this.config.Global.arrow[type]; var cta = conf.incta; var len = conf.height; var width = conf.width + 5; point.y = -point.y; y = -y; var newpoint = axis_translate(new Point(x, y), cta, point); if (newpoint.x >= 0 && newpoint.x <= len && Math.abs(newpoint.y) <= width) { return true; } else { return false; } } function Plotcombinparam1001(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotcombinparam1001, Plot); Plotcombinparam1001.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } var y1 = [] var y2 = [] var y3 = [] var y4 = [] var y5 = [] var y6 = [] for (var i = 0; i < data.y.length; i++) { y1[i] = data.y[i][0]; y2[i] = data.y[i][1]; y3[i] = data.y[i][2]; y4[i] = data.y[i][3]; y5[i] = data.y[i][4]; y6[i] = data.y[i][5]; } drawLine(this.ctx, data.x, y1, color[0]); drawLine(this.ctx, data.x, y2, color[1]); drawLine(this.ctx, data.x, y3, color[2]); drawLine(this.ctx, data.x, y4, color[3]); drawLine(this.ctx, data.x, y5, color[4]); drawLine(this.ctx, data.x, y6, color[5]); } function PlotProfit(view, grid, name) { sup(this, view, grid, name); this.arrow = {}; var conf = this.config.Global.arrow[DataWriterProfit.BUY_OPEN]; this.arrow[DataWriterProfit.BUY_OPEN] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.BUY_CLOSE]; this.arrow[DataWriterProfit.BUY_CLOSE] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.SELL_OPEN]; this.arrow[DataWriterProfit.SELL_OPEN] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); var conf = this.config.Global.arrow[DataWriterProfit.SELL_CLOSE]; this.arrow[DataWriterProfit.SELL_CLOSE] = new ShapeArrow(this.ctx, conf.width, conf.height, conf); } ClassExtend(PlotProfit, Plot); PlotProfit.prototype.draw = function(data) { var x, y, type; this.X = data.x; this.Y = data.y; for (var i = 0; i < data.x.length; i++) { for (var j = 0; j < data.y[i].length; j++) { y = data.y[i][j]; type = y[0]; y = y[1]; x = this.fixedX(data.x[i], type); this.arrow[type].draw(new Point(x, y)); } } } PlotProfit.prototype.getShowName = function() { return this.controller.c + " " + this.controller.p; } PlotProfit.prototype.lastDataShow = function() { return ""; } PlotProfit.prototype.fixedX = function(x, type) { x += 0.5; var box = this.view.plot[this.config.mainName].getBoxSize(); var left = this.view.plot[this.config.mainName].getBoxLeft(); if (type == DataWriterProfit.BUY_OPEN || type == DataWriterProfit.SELL_OPEN) { x -= left; } else { x += box - left; } return x; } PlotProfit.prototype.over = function(x1, y1) { if (!this.X) { return false; } var x, y, type, bk = false; x1 += 0.5; for (var i = 0; i < this.X.length; i++) { for (var j = 0; j < this.Y[i].length; j++) { y = this.Y[i][j][1]; type = this.Y[i][j][0]; x = this.fixedX(this.X[i], type); if (this.inarrow(new Point(x, y), type, x1, y1)) { bk = true; break; } } if (bk) { break; } } if (!bk) { return false; } var findTime = this.axis.getXValue(this.X[i], PlotGrid.NEAR); if (findTime === false) return; var cdata = this.view.model.getDataByTime(this.name, findTime); if (!cdata) { return false; } cdata.data = cdata.data[j]; /* var data = cdata.data[2]; var open_time = timeToDate(data[1], null, this.config.Global.timezone); var close_time = timeToDate(data[8], null, this.config.Global.timezone); return "Ticket: " + data[0] + "
    " + "Open Time: " + open_time + "
    " + "Close Time: " + close_time + "
    " + "Open Price: " + data[5] + "
    " + "Close Price:" + data[9] + "
    " + "Size: " + data[3] + "
    " + "Profit: " + data[13] + "
    "; */ return "Type:      " + (DataWriterProfit.nameMap[cdata.data[0]]) + "
    " + "Price:     " + cdata.data[1] + "
    " + "OrderID: " + cdata.data[2] + "
    "; } //某个坐标是否在某个点内部 PlotProfit.prototype.inarrow = function(point, type, x, y) { var conf = this.config.Global.arrow[type]; var cta = conf.incta; var len = conf.height; var width = conf.width + 5; point.y = -point.y; y = -y; var newpoint = axis_translate(new Point(x, y), cta, point); if (newpoint.x >= 0 && newpoint.x <= len && Math.abs(newpoint.y) <= width) { return true; } else { return false; } } function Plotrsi(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotrsi, Plot); Plotrsi.prototype.draw = function(data) { this.plotBar(data.x, data.y); } Plotrsi.prototype.lastDataShow = function() { var data = this.lastData(); return data; } Plotrsi.prototype.plotBar = function(x, H) { var zero_y = this.axis.getY(50); drawRow(this.ctx, { x: this.left, y: zero_y }, this.right - this.left, this.conf.lineColorZero); this.ctx.beginPath(); this.ctx.strokeStyle = this.conf.lineColorH; for (var i = 0; i < x.length; i++) { try { if (isNaN(H[i])) continue; this.ctx.moveTo(x[i] + 0.5, zero_y); this.ctx.lineTo(x[i] + 0.5, H[i]); } catch (e) { // console.log(e); // console.log(x[i]); // console.log(H[i]); } } this.ctx.stroke(); } function Plotrsisma(view, grid, name) { sup(this, view, grid, name); //放大缩小尺寸控制更加灵活。采用配置数组的形式 this.box = [1, 2, 3, 5, 7, 9, 11, 13]; this.space = [0, 1, 1, 2, 2, 3, 4, 5]; this.left = [0, 1, 1, 2, 3, 4, 5, 6] } ClassExtend(Plotrsisma, Plot); Plotrsisma.prototype.zoomIn = function() { if (this.conf.box_pixel >= 7 && this.conf.space_pixel >= 7) { return; } if (this.conf.box_pixel < this.conf.space_pixel) { this.conf.box_pixel++; } else if (this.conf.box_pixel > this.conf.space_pixel) { this.spaceOffset(1); } else { this.conf.box_pixel++; } } Plotrsisma.prototype.spaceOffset = function(offset) { var prev = this.space[this.conf.space_pixel]; this.conf.space_pixel = this.conf.space_pixel + offset; var current = this.space[this.conf.space_pixel]; if (prev == current) { this.conf.box_pixel = this.conf.box_pixel + offset; } } Plotrsisma.prototype.zoomOut = function() { if (this.conf.box_pixel <= 0 && this.conf.space_pixel <= 0) { return; } if (this.conf.box_pixel < this.conf.space_pixel) { this.spaceOffset(-1); } else if (this.conf.box_pixel > this.conf.space_pixel) { this.conf.box_pixel--; } else { this.spaceOffset(-1); } this.conf.box_pixel = this.conf.box_pixel < 0 ? 0 : this.conf.box_pixel; } Plotrsisma.prototype.getOneSize = function() { return this.box[this.conf.box_pixel] + 2 * this.space[this.conf.space_pixel]; } Plotrsisma.prototype.getBoxLeft = function() { return this.left[this.conf.box_pixel]; } Plotrsisma.prototype.getBoxSize = function() { return this.box[this.conf.box_pixel]; } Plotrsisma.prototype.draw = function(data) { this.data = data; this.X = data.x; this.Y = data.y; this.xunit = this.data.xunit; this.yunit = this.data.yunit; var width = this.box[this.conf.box_pixel]; var left = this.left[this.conf.box_pixel]; if (width > 2) { width--; } for (var i = 0; i < this.X.length; i++) { this.drawOne(this.X[i], this.Y[i], width, left); } var ySma = [] for (var i = 0; i < data.y.length; i++) { ySma[i] = data.y[i][7]; } drawLine(this.ctx, data.x, ySma, this.conf.SmaColor); } //鼠标经过十字光标显示的内容框 Plotrsisma.prototype.over = function(x, y) { //查找最接近的 // console.log("1"); if (!this.X || !this.Y) return; var index = find_near(this.X, x); if (index == -1) return false; if (Math.abs(this.X[index] - x) > this.getOneSize()) { return false; } var ohlc = this.Y[index]; if (ohlc[Data.HIGH] - this.config.Global.overPadding <= y && ohlc[Data.LOW] + this.config.Global.overPadding >= y) { // console.log("Plotrsisma"); var findTime = this.axis.getXValue(x, PlotGrid.NEAR); if (findTime === false) return; var cdata = this.view.model.getDataByTime(this.name, findTime, "candle"); if (!cdata) return false; var time = cdata.time; var ohlc = cdata.data; var yunit = this.axis.viewconfig.y.axis_option.unit; var fallOrDegrees=parseFloat(GetText(ohlc[Data.CLOSE], yunit)-GetText(ohlc[Data.OPEN], yunit))/parseFloat(GetText(ohlc[Data.OPEN], yunit)) ; var fallOrDegrees=fallOrDegrees.toFixed(3); //timeLine Data // timelineData(time,GetText(ohlc[Data.OPEN], yunit),GetText(ohlc[Data.HIGH], yunit),GetText(ohlc[Data.LOW], yunit),GetText(ohlc[Data.CLOSE], yunit),fallOrDegrees,GetText(ohlc[Data.VOLUMES], yunit)); // return time + "
    " + "O: " + GetText(ohlc[Data.OPEN], yunit) + "
    " + "H: " + GetText(ohlc[Data.HIGH], yunit) + "
    " + "L : " + GetText(ohlc[Data.LOW], yunit) + "
    " + "C: " + GetText(ohlc[Data.CLOSE], yunit) + "
    " + // +"V: " + GetText(ohlc[Data.VOLUMES], yunit) + "
    "; } else { return false; } } Plotrsisma.prototype.drawOne = function(time, ohlc, width, left) { var ctx = this.ctx; var open = ohlc[Data.OPEN]; var high = ohlc[Data.HIGH]; var low = ohlc[Data.LOW]; var close = ohlc[Data.CLOSE]; time += 0.5; open += 0.5; high += 0.5; low += 0.5; close += 0.5; if (high == low) { //画个横线就可以了 ctx.strokeStyle = this.conf.hx.os_border_color; ctx.beginPath(); ctx.moveTo(time - left, open); ctx.lineTo(time + width, open); ctx.stroke(); return; } if (open < close) { var up = open; var down = close; var fill = this.conf.hx.open_big_color; var border = this.conf.hx.ob_border_color; } else { var up = close; var down = open; var fill = this.conf.hx.open_small_color; var border = this.conf.hx.os_border_color; } ctx.strokeStyle = border; ctx.beginPath(); ctx.moveTo(time, high); ctx.lineTo(time, low); ctx.stroke(); if (this.conf.space_pixel > 0) { ctx.fillStyle = fill; ctx.fillRect(time - left, up, width, down - up); ctx.strokeRect(time - left, up, width, down - up); } } function PlotTrendLine(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotTrendLine, Plot); PlotTrendLine.prototype.draw = function(data) { this.X = data.x; this.Y = data.y; var plot_list = this.config.Global.trend_line_selected; if (plot_list == null) { return; } for (var i = 0; i < data.x.length; i++) { if (plot_list.indexOf("" + data.y[i][2]) == -1) { continue; } this.drawTrendLine(data.x[i], data.y[i][0], data.y[i][1], data.y[i][2], data.y[i]); } } PlotTrendLine.prototype.drawTrendLine = function(x, y, tan, type, data) { //曲线方程 y = ax + b var a = -tan; var b = y + tan * x; var p = []; p[0] = {}; p[1] = {}; var i = 0; var o = this.grid.O; var xy = this.grid.xyEnd; var plot_point = {}; if (data[5] == 0 && typeof data[6] !== "undefined") { plot_point.x = data[6]; plot_point.y = data[7]; } else { if (tan != 0) { //和坐标轴的交点的计算 //o 点 , 和o点的对角点 //计算曲线与坐标轴的绘图区域的焦点 //top ytop = o.y; xtop = (ytop - b) / a; if (i < 2 && xtop >= o.x && xtop <= xy.x) { p[i].x = xtop; p[i].y = ytop; i++; } //bottom ybot = xy.y; xbot = (ybot - b) / a; if (i < 2 && xbot >= o.x && xbot <= xy.x) { p[i].x = xbot; p[i].y = ybot; i++; } //left xleft = o.x; yleft = xleft * a + b; if (i < 2 && yleft >= o.y && yleft <= xy.y) { p[i].x = xleft; p[i].y = yleft; i++; } //right xright = xy.x; yright = xright * a + b; if (i < 2 && yright >= o.y && yright <= xy.y) { p[i].x = xright; p[i].y = yright; i++; } } else { p[0].x = o.x; p[0].y = y; p[1].x = xy.x; p[1].y = y; } //从 x ,y 开始,然后绘制到边界上。 if (p[0].x > x) { plot_point = p[0]; } else { plot_point = p[1]; } } plotLine(this.ctx, { x: x, y: y }, plot_point, this.conf.lineColor[type]); } PlotTrendLine.prototype.over = function(x1, y1) { //console.log(this.X); //console.log(this.Y); mind = Infinity; mini = 0; if (typeof this.X === "undefined") { return; } for (var i = 0; i < this.X.length; i++) { var d = this.distance(x1, y1, this.X[i], this.Y[i]); if (d < mind) { mind = d; mini = i; } } //i 是最短距离了 if (mind <= 3) { return "index:" + this.Y[mini][3] + "
    "; } } PlotTrendLine.prototype.distance = function(x1, y1, x, y) { //曲线方程 A = -y[1]; B = -1; C = y[0] + y[1] * x; d = Math.abs(A * x1 + B * y1 + C) / Math.sqrt(A * A + B * B); return d; } function Plotvolumes(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotvolumes, Plot); Plotvolumes.prototype.draw = function(data) { //偏移量配置 根据function Plotrsisma(view, grid, name)函数的配置 var left = [0, 1, 1, 2, 3, 4, 5, 6]; // var left = [ 3, 4, 5, 6,7,9,10,11] //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } var zero_y = this.axis.getY(0); for (var i = 0; i < data.y.length - 1; i++) { if (isNaN(data.y[i][0])) { data.y[i][0] = 1; } /*if (data.y[i][0]>data.y[i+1][0]){ this.ctx.strokeStyle = color[1]; //green }else if (data.y[i][0]data.y[i][0]){ var height=zero_y-data.y[i][0]; } else{ var height=data.y[i][0]-zero_y; } this.ctx.strokeStyle = color[1]; //green //绘制矩形 参考Plotrsisma.prototype.drawOne函数 this.ctx.closePath(); this.ctx.beginPath(); this.ctx.lineWidth = 1; this.ctx.fillStyle = color[1]; this.ctx.fillRect(data.x[i]+0.5-left[this.config.Global.Candle.public.box_pixel],zero_y-height,this.config.Global.boxwidth,height); this.ctx.strokeRect(data.x[i]+0.5-left[this.config.Global.Candle.public.box_pixel],zero_y-height,this.config.Global.boxwidth,height); this.ctx.closePath(); // this.ctx.moveTo(data.x[i] + 0.5, zero_y); // this.ctx.lineTo(data.x[i] + 0.5, data.y[i][0]); // this.ctx.closePath(); // this.ctx.lineWidth = this.config.Global.boxwidth; // this.ctx.stroke(); // this.ctx.closePath(); } else { this.ctx.strokeStyle = color[0]; //red this.ctx.closePath(); this.ctx.beginPath(); if(zero_y>data.y[i][0]){ var height=zero_y-data.y[i][0]; } else{ var height=data.y[i][0]-zero_y; } this.ctx.lineWidth = 1; this.ctx.fillStyle = "#0a0a0a"; this.ctx.fillRect(data.x[i]+0.5-left[this.config.Global.Candle.public.box_pixel],zero_y-height,this.config.Global.boxwidth,height); this.ctx.strokeRect(data.x[i]+0.5-left[this.config.Global.Candle.public.box_pixel],zero_y-height,this.config.Global.boxwidth,height); this.ctx.closePath(); // if(zero_y>data.y[i][0]) // { // var height=zero_y-data.y[i][0]; // this.ctx.strokeRect(data.x[i] + 0.5-this.config.Global.boxwidth,data.y[i][0],this.config.Global.boxwidth,height) // this.ctx.closePath(); // } // else{ // var height=data.y[i][0]-zero_y; // this.ctx.lineWidth=5; // if(height!=0){ // this.ctx.strokeRect(data.x[i] + 0.5-this.config.Global.boxwidth,data.y[i][0],this.config.Global.boxwidth,height); // this.ctx.closePath(); // } // // } // this.ctx.closePath(); } // this.ctx.beginPath(); // this.ctx.moveTo(data.x[i] + 0.5, zero_y); // this.ctx.lineTo(data.x[i] + 0.5, data.y[i][0]); // this.ctx.closePath(); // this.ctx.lineWidth = this.config.Global.boxwidth; // this.ctx.stroke(); // this.ctx.closePath(); } this.ctx.beginPath(); this.ctx.moveTo(data.x[data.y.length - 1] + 0.5, zero_y); this.ctx.lineTo(data.x[data.y.length - 1] + 0.5, data.y[data.y.length - 1][0]); this.ctx.closePath(); //this.ctx.lineWidth = this.config.Global.boxwidth; this.ctx.stroke(); } function Plotvolumes2(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotvolumes2, Plot); Plotvolumes2.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } var zero_y = this.axis.getY(0); for (var i = 0; i < data.x.length; i++) { if (isNaN(data.y[i][0])) continue; this.ctx.beginPath(); this.ctx.moveTo(data.x[i] + 0.5, zero_y); if (data.y[i][0] > zero_y) { this.ctx.strokeStyle = color[1]; this.ctx.lineTo(data.x[i] + 0.5, 2 * zero_y - data.y[i][0]); } else { this.ctx.strokeStyle = color[0]; this.ctx.lineTo(data.x[i] + 0.5, data.y[i][0]); } this.ctx.closePath(); this.ctx.stroke(); } } function PlotWave(view, grid, name) { sup(this, view, grid, name); } ClassExtend(PlotWave, Plot); PlotWave.prototype.draw = function(data) { //建立点 var color; if (this.param.color) { color = this.param.color; } else { color = this.conf.lineColor; } var y1 = [] for (var i = 0; i < data.y.length; i++) { y1[i] = data.y[i][0]; } drawLine(this.ctx, data.x, y1, color[0]); } function Plotwdensity(view, grid, name) { sup(this, view, grid, name); } ClassExtend(Plotwdensity, Plot); Plotwdensity.prototype.draw = function(data) { //建立点 var color; if (this.param.lineColor) { color = this.param.lineColor; } else { color = this.conf.lineColor; } for (var i = 0; i < data.y.length; i++) { for (var j = 0; j < data.y[i].length / 2; j++) { if (data.y[i][2 * j] != 0 && data.y[i][j * 2 + 1] != 0) { writeText(this.ctx, new Point(data.x[i], data.y[i][2 * j]), "~", color[j], this.param.font) } } } } //Node 版本的数据接收 //这里主要针对本地传送数据。[直接一次性加载的策略] function ModelNode(controller) { sup(this, controller); this.host = this.config.Global.NodeHost; this.port = this.config.Global.NodePort; this.isinit = false; } ClassExtend(ModelNode, Model); ModelNode.prototype.initData = function() { if (this.isinit) return; this.isinit = true; var socket = io.connect('http://' + document.domain); this.socket = socket; var _this = this; socket.on("error", function(data) { // console.log("error"); }); socket.on("init", function(data) { // console.log("init"); _this.updateData(data, "init"); }); socket.on("data", function(data) { if (data.main && data.main.options.prev_calculated == 0) console.log("data"); _this.updateData(data, "new"); }); var _this = this; _this.isconnect = false; socket.on("connect", function() { _this.isconnect = true; _this.subscribe(); }); this.socket = socket; } ModelNode.prototype.subscribe = function() { if (this.isconnect) { this.socket.emit("subscribe", { c: this.controller.c, p: this.controller.p }); } } ModelNode.prototype.downloadData = function() { } ModelNode.prototype.newData = function() { } ModelNode.prototype.updateData = function(data, action_name) { // console.log("data.p:"+data.p); if (data && data.c && data.p) { if (data.c != this.c || data.p != this.p) { //$("#symbol li[name="+data.c+"]").click(); $("#period li[name=" + data.p + "]").click(); action_name = "init"; } } if (action_name == "init") { if (data && data.c && data.p) { this.flag(Model.INITED, 1, data.c, data.p); } else { this.flag(Model.INITED, 1); } hide_loading(); } else { debug("update"); } this.update(data, Model.UPDATE); } //采用短连接下载数据的接口,有两个子类,一个子类是poll 采用轮询的方式更新数据。 //还有一个 WS采用websocket 的方式更新数据, //如果在配置中,指定采用poll,那么我们就采用poll //如果在配置中 指定采用websokect,但是发现,有些浏览器不支持,我们还是要退回轮询的方式。 //而服务器端 会有两套服务器。 //为了便于扩展。我们没有直接重写 data.set 类。 function ModelShort(controller) { sup(this, controller); this.controller=controller; this.api = this.config.Global.DataAPI; this.prevPoint = 0; } ClassExtend(ModelShort, Model); ModelShort.prototype.getAPI = function() { console.log($.cookie('symbol')); if($.cookie('symbol')=="YCC"){ return "https://kdata.fxee.com/kdata?datafile=db" + "&c=NYCC&p="+ this.p; } else{ return "https://kdata.fxee.com/kdata?datafile=db" + "&c=" + $.cookie('symbol') + "&p=" + this.p; } // if(this.p==null){ // return "https://tick.33.cn:4062/kdata?datafile=db" + "&c=" + $.cookie('symbol') + "&p=H1" ; // } // else{ // return "https://tick.33.cn:4062/kdata?datafile=db" + "&c=" + $.cookie('symbol') + "&p=" + this.p; // } // if(this.c=="ETC"){ // return etcUrl + "&c=" + this.c + "&p=" + this.p; // } // else{ // if (this.api.indexOf("?") == -1) { // return this.api + "?" + "c=" + this.c + "&p=" + this.p; // } else { // return this.api + "&c=" + this.c + "&p=" + this.p; // } // } } ModelShort.prototype.download = function(action_name) { this.count = this.config.Global.downloadCount; if (action_name == "down") { if (this.flag(Model.DOWN_END)) return; //判断左边是否需要下载(左边的优先下载) var down_flag = 0; if (this.leftDataCount() < this.config.Global.lowMark) { if (this.downOptions.total_size) { if (this.getLeftOffset() < this.downOptions.total_size - 1) down_flag = 1; } else { down_flag = 1; //left } } //判断右边是否需要下载。对于以时间下载的模式,不会消去数据。所以,右边的永远不需要下载。 if (down_flag == 0 && this.rightDataCount() < this.config.Global.lowMark) { if (this.downOptions.total_size) { if (this.getRightOffset() > 0) down_flag = -1; } } if (!down_flag) return; } if (this.config[this.config.mainName].downdata == 0) return; var api = this.getAPI(); if (!api) return; var _this = this; var ind = this.getIndicatorList(); //用一个数字,来标记这些状态. init_beg 和 init_end //是一对,设置了一个,就会清除另外的一个 if (this.flag(Model.DOWN) == 1) return; //如果数据正在下载中,那么忽略新的下载任务 if (this.downOptions.total_size) { //以位置分页 if (this.state.length == '' && action_name == 'down') { var offset = this.getDownOffset(down_flag); if (offset < 0) return; } else if (this.state == 'home') { offset = this.downOptions.total_size - this.count; } else if (this.state == "end") { offset = 0; } if (this.getRightOffset() <= offset && offset <= this.getLeftOffset()) { this.state = ''; return; } var baseparam = { action: action_name, offset: offset, count: this.count, 'ind': ind.join(":") } } else { //以时间分页 var baseparam = { action: action_name, ts: this.startXValue(), count: this.count, 'ind': ind.join(":") } } var pollmode = this.config.Global.PollMode; baseparam.out = pollmode; this.flag(Model.DOWN, 1); $.ajax({ type: "post", url: api, data: baseparam, success: function(data) { // console.log("ajax data:"+data); if (data) { _this.processDown(data, data.action); } }, error : function() { // view("异常!"); alert("异常!"); //光标移至输入用户名框 $("#loginusername").focus(); return -1; }, complete: function(xhr) { _this.flag(Model.DOWN, 0); _this.state = ''; }, dataType: baseparam.out }); } ModelShort.prototype.processDown = function(data, action_name) { if (action_name == "init") { if (data && data.c && data.p) { this.flag(Model.INITED, 1, data.c, data.p); } else { this.flag(Model.INITED, 1); } hide_loading(); } else { debug("down ok"); } this.update(data, Model.DOWN); this.flag(Model.DOWN_DATA, 1); //标记下载的数据可用了 if (action_name == "init") { //this.newData(); } } ModelShort.prototype.initData = function() { return this.download("init"); } ModelShort.prototype.downloadData = function() { this.download("down"); } //通过广播的方式得到tick数据。在本地计算出K线的数据。(方案1) //但是本地计算有些麻烦,比较简单的解决方案是:支持一个new 命令 //把相关的数据下载下来。并注册广播该数据。这个广播服务器需要复杂一点。 //暂时采用第二种方案,个人认为这个方案在不需要tick报价的情况下,客户端 //逻辑最少,数据最不容易出错。 // //tick广播服务器,只要缓存24小时内的数据,一般就不会有出现断层。【如果new //命令在的时间是在24小时以外的。可以提醒客户端,刷新。但是不考虑这样的异常。】 // //下载数据:会收集所有注册指标的名称,一起下载。下载的数据,按照主K线图对齐。 //没有对齐的填上NULL,如果某个点为NULL,那么这个点就直接跳过,不会进行绘制 //function 继承 function ModelShortPoll(controller) { sup(this, controller); this.interval = new IntervalEvent(Math.round(this.config.Global.PollInterval / this.config.Global.Interval), function(controller) { if(controller.model==undefined)return; controller.model.newData(); }, true); controller.addIntervalEvent("poll", this.interval); } ClassExtend(ModelShortPoll, ModelShort); ModelShortPoll.prototype.newData = function() { var api = this.getAPI(); if (!api) return; //如果是静态图形 if (this.controller.staticChat) return true; var _this = this; if (this.isNewing) { if (getTime() - this.lastSend > 1000 * this.config.Global.maxConnectAliveTime) { //reconnect. may be the connect is error } else { return false; } } //如果没有初始化,那么返回 if (!this.flag(Model.INITED)) { return; } this.isNewing = true; this.lastSend = getTime(); var ind = this.getIndicatorList(); //更新时间按照主图:的最新数据 var first = this.flag(Model.FIRST_NEW) ? 1 : 0; this.c = this.c; console.log(this); var baseparam = { action: 'new', "ind": ind.join(":"), te: this.endXValue(), first: first, __tmp: Math.random() }; var pollmode = this.config.Global.PollMode; baseparam.out = pollmode; this.newDataXHR = $.ajax({ type: "post", url: api, data: baseparam, success: function(data) { _this.processNew(data); _this.isNewing = false; }, complete: function(xhr) { _this.isNewing = false; }, dataType: baseparam.out }); return true; } ModelShortPoll.prototype.processNew = function(data) { if (data != null) { this.update(data, Model.NEW); this.flag(Model.FIRST_NEW, 0, data.c, data.p); } } //通过广播的方式得到tick数据。在本地计算出K线的数据。(方案1) //但是本地计算有些麻烦,比较简单的解决方案是:支持一个new 命令 //把相关的数据下载下来。并注册广播该数据。这个广播服务器需要复杂一点。 //暂时采用第二种方案,个人认为这个方案在不需要tick报价的情况下,客户端 //逻辑最少,数据最不容易出错。 // //tick广播服务器,只要缓存24小时内的数据,一般就不会有出现断层。【如果new //命令在的时间是在24小时以外的。可以提醒客户端,刷新。但是不考虑这样的异常。】 // //下载数据:会收集所有注册指标的名称,一起下载。下载的数据,按照主K线图对齐。 //没有对齐的填上NULL,如果某个点为NULL,那么这个点就直接跳过,不会进行绘制 //function 继承 //这个是通过 websocket 广播进行数据更新的模式。数据下载部分不变,只是更新了数据更新的部分。 function ModelShortWS(controller) { sup(this, controller); this.wsinit = false; if (!this.config.Global.WSHost) { return; } this.url = "ws://" + this.config.Global.WSHost + ":" + this.config.Global.WSPort + "/"; this.interval = new IntervalEvent(Math.round(1000 / this.config.Global.Interval), function(controller) { var model = controller.model; if (getTime() - model.lastRead > model.config.Global.maxNOData * 1000) { model.lastRead = getTime(); if (model.wsinit) { if (model.ws) { model.ws.close(); } model.ws = null; model.wsinit = false; model.newData(); } else { model.ws = null; model.wsinit = false; model.newData(); } return; } if (getTime() - model.lastRead > model.config.Global.pingTime * 1000) { if (model.ws) { try { model.ws.send("ping"); } catch (e) {} } } }, true); if (!this.config.Global.newDisable) { controller.addIntervalEvent("ws", this.interval); } this.ws = null; } ClassExtend(ModelShortWS, ModelShort); ModelShortWS.prototype.initWS = function() { var _this = this; if (this.ws) return; if (this.url) { this.ws = new WebSocket(this.url); this.ws.onopen = function() { _this.open(); }; this.ws.onmessage = function(e) { _this.message(e); }; this.ws.onclose = function(e) { _this.message(e); }; } } ModelShortWS.prototype.newData = function() { //如果是静态图形 if (this.controller.staticChat || this.config.Global.newDisable) return true; if (!this.flag(Model.INITED)) { return; } //货币对+指标列表 var ind = this.getIndicatorList(); this.cmd = []; var hasmain = false; var super_ind = []; for (var i = 0; i < ind.length; i++) { if (ind[i] == this.config.mainName) { hasmain = true; break; } } if (!hasmain) ind.push(this.config.mainName); for (var i = 0; i < ind.length; i++) { var mycmd = this.dataObj[ind[i]].reader.getWSCmd(); if (mycmd) { this.cmd.push(mycmd); } else { super_ind.push(ind[i]); } } if (super_ind.length != 0) { super_ind = super_ind.join(','); this.cmd.push("super_sub:" + this.c + "_" + this.p + ";" + super_ind + ":" + this.endXValue()); } this.initWS(); if (this.wsinit) { this.sendcmd(); this.cmd = null; } } ModelShortWS.prototype.sendcmd = function() { if (this.cmd) { try { for (var i = 0; i < this.cmd.length; i++) { this.ws.send(this.cmd[i]); } } catch (e) {} } } ModelShortWS.prototype.open = function() { this.wsinit = true; this.sendcmd(); } ModelShortWS.prototype.message = function(e) { this.lastRead = getTime(); if (e.data == "here") { return; } var data = eval("(" + e.data + ")"); this.update(data, Model.NEW); } ModelShortWS.prototype.close = function(e) { this.ws = null; this.wsinit = false; this.initWS(); } //一般任务reader是一个很简单的东西,但是它实际上也不是非常简单,它涉及预处理。定位等。 //现在我们考虑一般的情况,那就是main 图 和 这个指标图并不是时间对齐的。 //我们需要通过time查找。这里,我们采用二分查找。 // //基本的reader会提供两个函数,一个是通过一个start值和number 读取数据 //一个是通过一个时间范围。读取这个时间范围内的数据。 // //通过精心的组织代码,我们发现这个代码还是可以控制的。虽然,你很难想像用Javascript //写这样大型的绘图程序到底能否成功。 // function DataReader(model, writer, name) { //每个datreader 对象建立的时候。有一个data write 对象 this.model = model; this.controller = this.model.controller; /*if (name!="volumes"){ this.setParam(this.controller.ind[name].param); }*/ this.writer = writer; this.name = name; this.config = Config.getInstance(); this.shift = 0; } DataReader.EXTEND = 1; DataReader.MAIN = 1 << 1; DataReader.prototype.setWriter = function(writer) { this.writer = writer; } DataReader.prototype.setParam = function(param) { this.param = param; } DataReader.prototype.getShift = function() { //为了计算精确, 从图表中多读取这样多的数据。同时,兼容K线这一端对齐的 和 非对齐的情况,这样,指标的计算只和图表数据相关 和原始数据无关。 //为了不影响画图,最好不要影响画图 //时间刻度一般以主图为准。 return this.shift; } DataReader.prototype.getStart = function(number) { //数据指针 var start = this.model.point(); var endpos = this.getEndPos(); if (endpos < 0) { return -1; } //alert(start); if (start < 0) { start = 0; } if (start > endpos) { start = endpos; } if (!number) return start; if (endpos - start < number) { start = endpos - number + 1; } if (start < 0) { start = 0; } if (start > endpos) { start = endpos - number + 1; } //修正 this.model.point(start); return start; } DataReader.prototype.getEndPos = function() { var x = this.main.getX(this.c, this.p); return x.length - 1; } DataReader.prototype.startXValue = function() { var x = this.main.getX(this.c, this.p); return x[x.length - 1]; } DataReader.prototype.endXValue = function() { var x = this.main.getX(this.c, this.p); return x[0]; } DataReader.prototype.set = function() { this.c = this.model.c; // console.log("this.model.p:"+this.model.p); this.p = this.model.p; this.xunit = this.config.Global.xunit[this.p] * 60; //这个是根据时间决定其单位 if (this.config[this.name] && this.config[this.name].yunit) { this.yunit = this.config[this.name].yunit; } else { this.yunit = this.config.get("Global.yunit." + this.c, this.config.Global.yunit.default_value); //这个是根据货币对决定其单位 } if (this.writer) { this.store = this.writer.getStore(this.c, this.p); } this.config = Config.getInstance(); // this.config.mainName="main"; this.main = this.model.dataObj[this.config.mainName].writer; //这个对main是引用关系,所以只要c,p 没有切换 this.mainData = this.model.data[this.config.mainName]; //main刚刚读取的数据 this.step = this.xunit; this.maxstep = this.config.Global.maxstep; //(5个小时没有数据,那么不进行补充) //这个数据,依然是引用了main的最新的数据。 } DataReader.prototype.getMainData = function() { this.mainData = this.model.data[this.config.mainName]; return this.mainData; } DataReader.prototype.getYminmax = function(y) { return minmax(y); } DataReader.prototype.getWSCmd = function() { return false; } //抽象函数,不提供任何的功能。//主图数据 DataReader.prototype.getDataByMain = function(readconfig) { this.mainData = this.getMainData(); var nLen = this.mainData.x.length; if (nLen > this.model.plotnum) { nLen = this.model.plotnum } var start = this.mainData.x[nLen - 1]; var end = this.mainData.x[0]; var x = new Array(); var y = new Array(); var xdata = this.writer.getX(this.c, this.p); var ydata = this.writer.getY(this.c, this.p); //这个部分是数据,首先查找xdata,当然还要判断是否扩展。 //这里的扩展方式是扩展K线图。其他的扩展,在其他的图里面表示。 //先获取数据: var count = 0; var index = find_first_big_r(xdata, end); //查找到第一个点 if (index == -1) { index = 0; } if (xdata[index] > end) index++; //去掉第一个大于的点 for (var i = index; i < xdata.length; i++) { if (xdata[i] >= start) { x[count] = xdata[i]; y[count] = ydata[i]; count++; } else { break; } } if (readconfig & DataReader.EXTEND) { //多添加一个最后时间的数据,这个数据如果不存在,那么就复制前一个数据的 open 值,这个时间值肯定小于 start if (xdata[i]) { x.push(xdata[i]); y.push(ydata[i]); } else if (x[x.length - 1] > start) { x.push(start); var ylast = y[y.length - 1][Data.OPEN]; y.push([ylast, ylast, ylast, ylast, y[y.length - 1][Data.VOLUMES]]); } return this.extendByMain(x, y); } //深度拷贝 y = array_copy(y); // } return { x: x, y: y }; } DataReader.prototype._formatData = function(x, y) { var data = {}; data.x = x; data.y = y; data.xunit = this.xunit; data.yunit = this.yunit; data.maxX = x[0]; data.minX = x[x.length - 1]; data.maxIndex=0; data.minIndex=0; var _minmax = this.getYminmax(y); data.maxY = _minmax[1]; data.minY = _minmax[0]; data.maxIndex=_minmax[3]; data.minIndex=_minmax[2]; return data; } DataReader.prototype.formatData = function(x, y) { return this._formatData(x, y); } DataReader.prototype.last = function(index) { if (!index || index < 0) index = 0; var y = this.writer.getY(this.c, this.p); return y[index]; } function DataWriter(model, name) { this.model = model; this.name = name; this.config = this.model.config; this.storeSet = {}; } DataWriter.prototype.getStore = function(c, p) { this.storeSet = init_obj(this.storeSet, c, p, { x: [], y: [] }); return this.storeSet[c][p]; } DataWriter.prototype.clear = function() { this.storeSet = {}; } DataWriter.prototype.setNewData = function(c, p, data) { //do nothing. } DataWriter.prototype.setDownloadData = function(c, p, data) { //do nothing. } DataWriter.prototype.getX = function(c, p) { var store = this.getStore(c, p); return store.x; } DataWriter.prototype.getY = function(c, p) { var store = this.getStore(c, p); return store.y; } function DataWriterbdensity(model, name) { sup(this, model, name); } ClassExtend(DataWriterbdensity, DataWriter); DataWriterbdensity.prototype.setNewData = function(c, p, data) { if (!is_array(data) || data.length == 0) { return; } var store = this.getStore(c, p); } DataWriterbdensity.prototype.notify = function(items) { if (items && items.length) { /*var typename = DataWriterProfit.nameMap[items[items.length - 1][0]]; typename = typename.replace("/", "_").toLowerCase(); var url = $("#music_list select[name="+typename+"]").val(); playmusic(url); */ } } DataWriterbdensity.prototype.parseItem = function(items) { var newarr = []; var type = null; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item[0] == "buy" && item[1] == "open") { type = DataWriterProfit.BUY_OPEN; } else if (item[0] == "buy" && item[1] == "close") { type = DataWriterProfit.BUY_CLOSE; } else if (item[0] == "sell" && item[1] == "open") { type = DataWriterProfit.SELL_OPEN; } else if (item[0] == "sell" && item[1] == "close") { type = DataWriterProfit.SELL_CLOSE; } if (type) { newarr[i] = [type, parseFloat(item[2]), item[3]]; } } if (newarr.length == 0) { return false; } newarr = this.formatItem(newarr); return newarr; } DataWriterbdensity.prototype.formatItem = function(arr) { for (var i = 0; i < arr.length; i++) {} return arr; } DataWriterbdensity.prototype.setDownloadData = function(c, p, data) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data.x)) { return; } var store = this.getStore(c, p); var item; for (var i = 0; i < data.x.length; i++) { //format time var time = formatTime(data.x[i], p); store.x.push(time); item = data.y[i]; //this.parseItem(data[i][1]); if (item) store.y.push(item); } } function DataWriterCandle(model, name) { sup(this, model, name); this.chunks = []; //数据在服务器端的[开始,结束,total,在本地数组中的,开始位置,长度,方便删除] } ClassExtend(DataWriterCandle, DataWriter); //重写父类. //写类不能像读类一样,直接读取model.c 个 model.p //因为,数据属于什么类,由服务器端返回的值决定 // DataWriterCandle.prototype.getStore = function(c, p) { this.storeSet = init_obj(this.storeSet, c, p, { time: [], ohlc: [], v: [] }); return this.storeSet[c][p]; } DataWriterCandle.prototype.setDownloadDataByOffset = function(c, p, data, options) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data.x)) { return; } this.c = c; this.p = p; var store = this.getStore(c, p); data.x = data.x.reverse(); data.y = data.y.reverse(); //判断offset 是否衔接,如果不衔接,那么重置所有的数据。 //如果衔接。要判断一下左衔接 还是 右衔接。 //如果是左衔接,并且缓存数目超过一个配置的值,那么删除一部分数据 //每个数据用一个chunks来表示 options.astart = 0; options.alen = data.x.length; if (this.chunks.length && options.end + 1 == this.chunks[0].start) { //右衔接[在头部加入数据] var len = data.x.length; store.time = data.x.concat(store.time); store.ohlc = data.y.concat(store.ohlc); this.shiftLeftChunk(store, options); //把chunk往左移 } else if (this.chunks.length && this.chunks[this.chunks.length - 1].end + 1 == options.start) { //左衔接 var len = data.x.length; store.time = store.time.concat(data.x); store.ohlc = store.ohlc.concat(data.y); this.shiftRightChunk(store, options); } else { //不衔接 store.time = data.x; store.ohlc = data.y; this.chunks = []; this.chunks[0] = options; } //更新数据offset 的范围 this.model.leftoffset = this.chunks[this.chunks.length - 1].end; this.model.rightoffset = this.chunks[0].start; } DataWriterCandle.prototype.shiftLeftChunk = function(store, options) { //update chunk data for (var i = 0; i < this.chunks.length; i++) { this.chunks[i].astart += options.alen; } this.chunks.unshift(options); //更新数据指针,头部数据被加入 this.model.point(this.model.point() + options.alen); if (store.time.length > this.config.Global.maxStoreChunk * this.config.Global.downloadCount) { var chunk = this.chunks.pop(); store.time.splice(chunk.astart); store.ohlc.splice(chunk.astart); } } DataWriterCandle.prototype.shiftRightChunk = function(store, options) { //处理chunk var lastchunk = this.chunks[this.chunks.length - 1]; options.astart += lastchunk.astart + lastchunk.alen; this.chunks.push(options); if (store.time.length > this.config.Global.maxStoreChunk * this.config.Global.downloadCount) { var chunk = this.chunks.shift(); for (var i = 0; i < this.chunks.length; i++) { this.chunks[i].astart -= chunk.alen; } //头部数据被删除 store.time.splice(0, chunk.alen); store.ohlc.splice(0, chunk.alen); //更新数据指针 var point = this.model.point(); point -= chunk.alen; this.model.point(point); } } DataWriterCandle.prototype.setUpdateData = function(c, p, data) { //更新第一个数据 if (!is_array(data.x) || data.x.length == 0) { return; } var store = this.getStore(c, p); var mintime = data.x[0]; data.x = data.x.reverse(); data.y = data.y.reverse(); if (store.time.length == 0 || parseInt(data.options.prev_calculated) == 0) { store.time = data.x; store.ohlc = data.y; //console.log(store.time); return; } var i = 0; while (i < store.time.length && store.time[i] >= mintime) { i++; } if (i == store.time.length) { store.time = data.x; store.ohlc = data.y; } else { store.time.splice(0, i); store.ohlc.splice(0, i); store.time = data.x.concat(store.time); store.ohlc = data.y.concat(store.ohlc); } } /* DataWriterCandle.prototype.setNewData = function(c, p, data) { console.log("newdata=",data,is_array(data),data.length); //更新第一个数据 if (!is_array(data) || data.length == 0) { return; } data = data.reverse(); var store = this.getStore(c, p); var time = parseInt(data[0][0]); while (data.length && time < store.time[0]) { data.shift(); time = parseInt(data[0][0]); } if (data.length == 0) { return; } // for (var i = 1; i < data[0].length; i++) { data[0][i] = parseFloat(data[0][i]); } var first = data[0].slice(1); if (time == store.time[0]) { store.time[0] = time; store.ohlc[0] = first; } else { store.time.unshift(time); store.ohlc.unshift(first); } if (isNaN(store.ohlc[0][0])) { alert("update new NaN"); } //store.v[0] = parseFloat(data[0][5]); for (var i = 1; i < data.length; i++) { if (!is_array(data[i])) { continue; } for (var j = 1; j < data[i].length; j++) { data[i][j] = parseFloat(data[i][j]); } store.time.unshift(parseInt(data[i][0])); var newdata = data[i].slice(1); store.ohlc.unshift(newdata); } }*/ DataWriterCandle.prototype.setNewData = function(c, p, data) { //更新第一个数据 if (!is_array(data.x) || data.x.length == 0) { return; } var store = this.getStore(c, p); var time = parseInt(data.x[0]); while (data.x.length && time < store.time[0]) { data.x.shift(); data.y.shift(); time = parseInt(data.x[0]); } if (data.x.length == 0) { return; } var first = data.y[0]; if (time == store.time[0]) { store.time[0] = time; store.ohlc[0] = data.y[0] } else { store.time.unshift(time); store.ohlc.unshift(data.y[0]); } if (isNaN(store.ohlc[0][0])) { alert("update new NaN"); } for (var i = 1; i < data.x.length; i++) { store.time.unshift(data.x[i]); store.ohlc.unshift(data.y[i]); } } DataWriterCandle.prototype.getX = function(c, p) { var store = this.getStore(c, p); return store.time; } DataWriterCandle.prototype.getY = function(c, p) { var store = this.getStore(c, p); return store.ohlc; } DataWriterCandle.prototype.setDownloadData = function(c, p, data) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data)) { if (is_array(data.x) && is_array(data.y)) { var newdata = []; for (var i = data.x.length -1; i >= 0; i--) { var item = []; item.push(data.x[i]); item = item.concat(data.y[i]); newdata.push(item); } data = newdata; } else { return; } } var store = this.getStore(c, p); for (var i = 0; i < data.length; i++) { if (!is_array(data[i])) { continue; } for (var j = 1; j < data[i].length; j++) { data[i][j] = parseFloat(data[i][j]); } store.time.push(parseInt(data[i][0])); store.ohlc.push(data[i].slice(1)); } } function DataWriterFractals(model, name) { sup(this, model, name); } ClassExtend(DataWriterFractals, DataWriter); DataWriterFractals.prototype.setNewData = function(c, p, data) { if (!is_array(data) || data.length == 0) { return; } //data = data.reverse(); var store = this.getStore(c, p); /* for (var i = 0; i < data.length; i++) { if (!is_array(data[i])) { continue; } var item = this.parseItem(data[i][1]); var newitem; if (item) {} else {continue;} var time = formatTime(data[i][0], p); if (store.x[0] == time) { //可能会有新的买卖点 newitem = array_diff(item, store.y[0]); store.y[0] = item; } else { store.x.unshift(time); store.y.unshift(item); newitem = item; } this.notify(newitem); } */ } DataWriterFractals.prototype.notify = function(items) { if (items && items.length) { /*var typename = DataWriterProfit.nameMap[items[items.length - 1][0]]; typename = typename.replace("/", "_").toLowerCase(); var url = $("#music_list select[name="+typename+"]").val(); playmusic(url); */ } } DataWriterFractals.prototype.parseItem = function(items) { var newarr = []; var type = null; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item[0] == "buy" && item[1] == "open") { type = DataWriterProfit.BUY_OPEN; } else if (item[0] == "buy" && item[1] == "close") { type = DataWriterProfit.BUY_CLOSE; } else if (item[0] == "sell" && item[1] == "open") { type = DataWriterProfit.SELL_OPEN; } else if (item[0] == "sell" && item[1] == "close") { type = DataWriterProfit.SELL_CLOSE; } if (type) { newarr[i] = [type, parseFloat(item[2]), item[3]]; } } if (newarr.length == 0) { return false; } newarr = this.formatItem(newarr); return newarr; } DataWriterFractals.prototype.formatItem = function(arr) { for (var i = 0; i < arr.length; i++) {} return arr; } DataWriterFractals.prototype.setDownloadData = function(c, p, data) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data.x)) { return; } var store = this.getStore(c, p); var item; for (var i = 0; i < data.x.length; i++) { //format time var time = formatTime(data.x[i], p); store.x.push(time); item = data.y[i]; //this.parseItem(data[i][1]); if (item) store.y.push(item); } } function DataWriterKshape(model, name) { sup(this, model, name); } ClassExtend(DataWriterKshape, DataWriter); DataWriterKshape.prototype.setNewData = function(c, p, data) { if (!is_array(data) || data.length == 0) { return; } //data = data.reverse(); var store = this.getStore(c, p); /* for (var i = 0; i < data.length; i++) { if (!is_array(data[i])) { continue; } var item = this.parseItem(data[i][1]); var newitem; if (item) {} else {continue;} var time = formatTime(data[i][0], p); if (store.x[0] == time) { //可能会有新的买卖点 newitem = array_diff(item, store.y[0]); store.y[0] = item; } else { store.x.unshift(time); store.y.unshift(item); newitem = item; } this.notify(newitem); } */ } DataWriterKshape.prototype.notify = function(items) { if (items && items.length) { /*var typename = DataWriterProfit.nameMap[items[items.length - 1][0]]; typename = typename.replace("/", "_").toLowerCase(); var url = $("#music_list select[name="+typename+"]").val(); playmusic(url); */ } } DataWriterKshape.prototype.parseItem = function(items) { var newarr = []; var type = null; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item[0] == "buy" && item[1] == "open") { type = DataWriterProfit.BUY_OPEN; } else if (item[0] == "buy" && item[1] == "close") { type = DataWriterProfit.BUY_CLOSE; } else if (item[0] == "sell" && item[1] == "open") { type = DataWriterProfit.SELL_OPEN; } else if (item[0] == "sell" && item[1] == "close") { type = DataWriterProfit.SELL_CLOSE; } if (type) { newarr[i] = [type, parseFloat(item[2]), item[3]]; } } if (newarr.length == 0) { return false; } newarr = this.formatItem(newarr); return newarr; } DataWriterKshape.prototype.formatItem = function(arr) { for (var i = 0; i < arr.length; i++) {} return arr; } DataWriterKshape.prototype.setDownloadData = function(c, p, data) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data.x)) { return; } var store = this.getStore(c, p); var item; for (var i = 0; i < data.x.length; i++) { //format time var time = formatTime(data.x[i], p); store.x.push(time); item = data.y[i]; //this.parseItem(data[i][1]); if (item) store.y.push(item); } } function DataWriterMath(model, name) { sup(this, model, name); } ClassExtend(DataWriterMath, DataWriter); function DataWriterProfit(model, name) { sup(this, model, name); } ClassExtend(DataWriterProfit, DataWriter); DataWriterProfit.BUY_OPEN = 1; DataWriterProfit.BUY_CLOSE = 2; DataWriterProfit.SELL_OPEN = 3; DataWriterProfit.SELL_CLOSE = 4; DataWriterProfit.nameMap = { 1: "Open/Buy", 2: "Close/Buy", 3: "Open/Sell", 4: "Close/Sell" }; DataWriterProfit.prototype.setNewData = function(c, p, data) { if (!is_array(data) || data.length == 0) { return; } data = data.reverse(); var store = this.getStore(c, p); for (var i = 0; i < data.length; i++) { if (!is_array(data[i])) { continue; } var item = this.parseItem(data[i][1]); var newitem; if (item) {} else { continue; } var time = formatTime(data[i][0], p); if (store.x[0] == time) { //可能会有新的买卖点 newitem = array_diff(item, store.y[0]); store.y[0] = item; } else { store.x.unshift(time); store.y.unshift(item); newitem = item; } this.notify(newitem); } } DataWriterProfit.prototype.notify = function(items) { if (items && items.length) { var typename = DataWriterProfit.nameMap[items[items.length - 1][0]]; typename = typename.replace("/", "_").toLowerCase(); var url = $("#music_list select[name=" + typename + "]").val(); // playmusic(url); } } DataWriterProfit.prototype.parseItem = function(items) { var newarr = []; var type = null; for (var i = 0; i < items.length; i++) { var item = items[i]; if (item[0] == "buy" && item[1] == "open") { type = DataWriterProfit.BUY_OPEN; } else if (item[0] == "buy" && item[1] == "close") { type = DataWriterProfit.BUY_CLOSE; } else if (item[0] == "sell" && item[1] == "open") { type = DataWriterProfit.SELL_OPEN; } else if (item[0] == "sell" && item[1] == "close") { type = DataWriterProfit.SELL_CLOSE; } if (type) { newarr[i] = [type, parseFloat(item[2]), item[3]]; } } if (newarr.length == 0) { return false; } newarr = this.formatItem(newarr); return newarr; } DataWriterProfit.prototype.formatItem = function(arr) { for (var i = 0; i < arr.length; i++) {} return arr; } DataWriterProfit.prototype.setDownloadData = function(c, p, data) { //数据按照时间倒序序进行排列 var beg, end; //处理指标 if (!is_array(data)) { return; } var store = this.getStore(c, p); var item; for (var i = 0; i < data.length; i++) { if (!is_array(data[i])) { continue; } //format time var time = formatTime(data[i][0], p); store.x.push(time); item = this.parseItem(data[i][1]); if (item) store.y.push(item); } } function DataWriterTickSource(model, name) { sup(this, model, name); this.lastSource = null; } ClassExtend(DataWriterTickSource, DataWriter); DataWriterTickSource.prototype.setNewData = function(c, type, data) { if (!is_array(data) || !is_array(data[0])) return; this.lastSource = type; var store = this.getStore(c, type); for (var i = 0; i < data.length; i++) { store.x.push(parseInt(data[i][0]) * 1000 + parseInt(data[i][1])); store.y.push([parseFloat(data[i][2]), parseFloat(data[i][3])]); } if (store.x.length > this.maxdata) { store.x = store.x.slice(this.maxdata / 2); store.y = store.y.slice(this.maxdata / 2); } } function DataReaderadx(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderadx, DataReader); function DataReaderAjax(model, writer, name) { this.isinit = false; sup(this, model, writer, name); this.data = {}; this.lastdata = {}; } ClassExtend(DataReaderAjax, DataReader); DataReaderAjax.prototype.setParam = function(param) { if (this.isinit) return; this.isinit = true; var url = param[1]; var _this = this; $.get(url, function(data) { _this.setAjaxData(data); }, "json"); } DataReaderAjax.prototype.setAjaxData = function(data) { this.data = data; this.model.ready = true; this.model.controller.flag("down"); //this.model.ready = false; //不更新图表了 this.main = this; return this; } DataReaderAjax.prototype.getX = function() { return this.data.x; } DataReaderAjax.prototype.getY = function() { return this.data.y; } DataReaderAjax.prototype.getData = function(number, readconfig) { var start = this.getStart(number); this.lastdata = this.data.y[0]; start = this.getStart(number); index = 0; var x = []; var y = []; for (var i = start; i < this.data.x.length; i++) { x[index] = this.data.x[i]; y[index] = this.data.y[i]; index++; if (index >= number) { break; } } var data = {}; data.x = x; data.y = array_copy2d(y); return data; } DataReaderAjax.prototype.last = function() { return this.lastdata; } DataReaderAjax.prototype.close = function(index) { if (!index || index < 0) index = 0; return this.data.y[index][Data.CLOSE]; } DataReaderAjax.prototype.getEndPos = function() { var x = this.data.x; return x.length - 1; } DataReaderAjax.prototype.startXValue = function() { var x = this.data.x; return x[x.length - 1]; } DataReaderAjax.prototype.endXValue = function() { var x = this.data.x; return x[0]; } //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderatr(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderatr, DataReader); //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReaderbdensity(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderbdensity, DataReader); DataReaderbdensity.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var maxIndex=0; var minIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } var nIndex = 0; for (var i = 0; i < y.length; i++) { for (var j = 0; j < y[i].length; j++) { nIndex = (j / 4) * 4 + 2 if (j % 4 <= 1 && y[i][nIndex] != 0) { var value = y[i][j]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } } } console.log("ymin:"+ymin+",ymax:"+ymax); return [ymin, ymax,minIndex,maxIndex]; } //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderBolling(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderBolling, DataReader); //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderBollingDesity(model, writer, name) { sup(this, model, writer, name); this.sourcedata = null; } ClassExtend(DataReaderBollingDesity, DataReader); DataReaderBollingDesity.prototype.formatData = function(x, y) { var data = {}; data.x = x; data.y = y; data.xunit = this.xunit; data.yunit = this.yunit; data.maxX = x[0]; data.minX = x[x.length - 1]; data.maxIndex=0; data.minIndex=0; var min = Infinity var max = -Infinity; for (var i = 0; i < y.length; i++) { for (var j = 0; j < y.length; j += 2) { if (y[i][j] == -1) { continue; } if (max < y[i][j]) { max = y[i][j]; data.maxIndex=i; } if (min > y[i][j]) { min = y[i][j]; data.minIndex=i; } } } data.maxY = max; data.minY = min; return data; } // //读取蜡烛图的数据 // function DataReaderCandle(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderCandle, DataReader); //抽象函数,不提供任何的功能。 DataReaderCandle.prototype.getDataByNumber = function(number, readconfig) { if (readconfig & DataReader.EXTEND) { //以扩展的方式读取 return this.getDataByNumberExtend(number, readconfig); } //普通的读取方式 //这里,如果读取到末尾了,要修正start var x = new Array(number); var y = new Array(number);; var index = 0; start = this.getStart(number); for (var i = start; i < this.store.time.length; i++) { x[index] = this.store.time[i]; y[index] = this.store.ohlc[i]; index++; if (index >= number) { break; } } if (index < number) { x = x.slice(0, index - 1); y = y.slice(0, index - 1); } //深度复制 y = array_copy2d(y); return { x: x, y: y }; } DataReaderCandle.prototype.getData = function(number, readconfig) { return this.getDataByNumber(number, readconfig); } DataReaderCandle.prototype.getDataByNumberExtend = function(number, readconfig) { // console.log("number:"+number); var x = new Array(number); var y = new Array(number); var index = 0; //普通的读取方式 //这里,如果读取到末尾了,要修正start var start = this.getStart(number); var xdata = this.writer.getX(this.c, this.p); var ydata = this.writer.getY(this.c, this.p); var length = xdata.length; if (length == 0) { return { x: [], y: [] }; } x[index] = xdata[start]; y[index] = ydata[start]; for (var i = start; i < length - 1; i++) { var ctime = xdata[i]; //当前时间 var ptime = xdata[i + 1]; //前一个时间 //1. if (ctime - ptime == this.step || ctime - ptime > this.maxstep) { //连续,或者相差太大。加入 index++; x[index] = xdata[i + 1]; y[index] = ydata[i + 1]; } else { //需要补全,x值每次 加step y用 ptime对应的值 var close = ydata[i + 1][Data.CLOSE]; var candle = [close, close, close, close, ydata[i + 1][Data.VOLUMES]]; for (var newtime = xdata[i] - this.step; newtime > xdata[i + 1]; newtime -= this.step) { index++; x[index] = newtime; y[index] = candle; } index++; x[index] = xdata[i + 1]; y[index] = ydata[i + 1]; } if (index + 1 >= number) { break; } } index++; if (index < number) { x = x.slice(0, index); y = y.slice(0, index); } y = array_copy2d(y); return { x: x, y: y }; } //和main的时间进行对齐,对于稀疏的数据,这个非常重要 DataReaderCandle.prototype.extendByMain = function(xdata, ydata) { var mainX = this.getMainData().x; var x = new Array(); var y = new Array(); var j = 0; //1. 如果时间值超过了Main[0],那么直接忽略 //2. 如果某个值开始小于main[0],那么,main 开始加。直到main[i] < 这个值。 //3. 我们的索引加 if (xdata.length == 0 || xdata[xdata.length - 1] > mainX[mainX.length - 1]) { //无效,这样的情况无法进行补全。 return { x: [], y: [] }; } //计算第一个数据 var current = 0, count = 0; var close = ydata[current][Data.CLOSE]; candle = [close, close, close, close, ydata[current][Data.VOLUMES]]; for (var i = 0; i < mainX.length; i++) { if (xdata[current] > mainX[i]) //这样的情况是无效的,从程序的条件可以看出,current 不会越界。 { while (xdata[current] > mainX[i]) { current++; } if (!ydata[current]) { alert(current); //debug 的信息 } close = ydata[current][Data.CLOSE]; candle = [close, close, close, close, ydata[current][Data.VOLUMES]]; } if (mainX[i] > xdata[current]) { y[count++] = candle; } else if (mainX[i] == xdata[current]) { y[count++] = ydata[current]; } } x = array_copy1d(mainX); y = array_copy2d(y); return { x: x, y: y }; } DataReaderCandle.prototype.close = function(index) { if (!index || index < 0) index = 0; return this.store.ohlc[index][Data.CLOSE]; } DataReaderCandle.prototype.formatData = function(x, y) { var data = {}; data.x = x; data.y = y; data.xunit = this.xunit; data.yunit = this.yunit; data.maxX = x[0]; var nLen = y.length; if (nLen > this.model.plotnum) { nLen = this.model.plotnum; } data.minX = x[nLen - 1]; data.maxIndex=0; data.minIndex=0; var min = Infinity var max = -Infinity; //console.log("model=",this.model.plotnum,x); for (var i = 0; i < y.length && i < this.model.plotnum; i++) //for(var i=y.length-1;i>=0&& (y.length-i)<=this.model.plotnum;i--) { if (y[i][Data.HIGH] > max) { max = y[i][Data.HIGH]; data.maxIndex=i; } if (y[i][Data.LOW] < min) { min = y[i][Data.LOW]; data.minIndex=i; } } data.maxY = max; data.minY = min; return data; } DataReaderCandle.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var minIndex=0; var maxIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } //for(var i=y.length-1;i>=0&& (y.length-i)<=this.model.plotnum;i--) for (var i = 0; i < y.length && i < this.model.plotnum; i++) { for (var j = 0; j < 4; j++) { var value = y[i][j]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } } return [ymin, ymax,minIndex,maxIndex]; } function PlotEasyforex(plot, canvas) { this.plot = plot; this.canvas = canvas; this.last = last; this.initMyPlot(); } //X Y 轴都要根据数据,绘制图形。 //分离 主绘图区域 的数据 和 candle的 绘制。 //使得数据 和 绘图分离,提高复用能力。 // PlotEasyforex.prototype.initMyPlot = function() { } //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReaderFractals(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderFractals, DataReader); DataReaderFractals.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var minIndex=0; var maxIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length; i++) { var value = y[i][1]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minmax=i; } } return [ymin, ymax,minIndex,maxIndex]; } function DataReaderIndicators(model, writer, name) { sup(this, model, writer, name); this.data = {}; this.lastdata = {}; } ClassExtend(DataReaderIndicators, DataReader); DataReaderIndicators.prototype.minmax = function(start, n) { var high = -Infinity; var low = Infinity; for (var i = start; i < start + n && i < this.y.length; i++) { if (this.y[i][Data.HIGH] > high) { high = this.y[i][Data.HIGH]; } if (this.y[i][Data.LOW] < low) { low = this.y[i][Data.LOW]; } } // console.log(this.name, high, low); return [high, low]; } DataReaderIndicators.prototype.sum = function(start, length) { if (this.y.length < start + length) { return false; } var sum = 0; for (var i = start; i < start + length; i++) { sum += this.y[i][Data.CLOSE]; } return sum; } DataReaderIndicators.prototype.setParam = function(param) { this.param = param; } DataReaderIndicators.prototype.setDependData = function(name, data) { this.data[name] = data; } //大多数指标可以这样初始化。如果子类初始化不一样,可以做处理 DataReaderIndicators.prototype.dataInit = function(name) { //init 先计算前n个值 //y是一个二维数组 if (!name) { name = this.param[0][0]; } if (this.data[name] && this.data[name].y) { //[read only] 如果需要修改,请先复制。否则会改动原始数据。 this.y = this.data[name].y; this.x = this.data[name].x; } else { return false; } //默认数据源是倒序排列的 if (this.n > this.y.length) { return false; } if (!this.y) { return false; } if (this.y && this.y.length == 0) { return false; } return true; } //常用指标的实现 DataReaderIndicators.prototype.wpr = function(n) { var data = []; var minmax = this.minmax(0, n); var low = minmax[1]; var high = minmax[0]; var wr; if (low == high) { wr = 0; } else { wr = 100 * (this.y[0][Data.CLOSE] - high) / (high - low); } data.push(wr); var index = 0; for (var i = 1; i < this.y.length - n; i++) { index = i + n - 1; //from i to index, total n number data. if (this.y[index][Data.LOW] > low && this.y[index][Data.HIGH] < high && this.y[i][Data.LOW] > low && this.y[i][Data.HIGH] < high) { //do nothing, high and low 不会变化 } else { minmax = this.minmax(i, n); low = minmax[1]; high = minmax[0]; } if (low == high) { wr = 0; } else { wr = 100 * (this.y[i][Data.CLOSE] - high) / (high - low); } data.push(wr); } //后面为空的补零,这样和时间才能对齐。 for (var i = 0; i < n; i++) { data.push(0); } return data; } DataReaderIndicators.prototype.ma = function(n) { var avg = []; var sum = this.sum(0, n); avg.push(sum / n); for (var i = 1; i < this.y.length - n; i++) { sum -= this.y[i - 1][Data.CLOSE]; sum += this.y[i + n - 1][Data.CLOSE]; avg.push(sum / n); } for (var i = 0; i < n; i++) { avg.push(0); } return avg; } DataReaderIndicators.prototype.strength = function(n) { var data = []; var high = -Infinity; var low = Infinity; var index, item; for (var i = 0; i < this.y.length - n; i++) { index = i + n - 1; item = this.y[i][Data.CLOSE] - this.y[index][Data.CLOSE]; if (high < item) { high = item; } if (low > item) { low = item; } data.push(item); } for (var i = 0; i < data.length; i++) { data[i] = (data[i] / (high - low)) * 100; } //后面为空的补零,这样和时间才能对齐。 for (var i = 0; i < n; i++) { data.push(0); } return data; } DataReaderIndicators.prototype.last = function() { return this.lastdata; } //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReaderKshape(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderKshape, DataReader); DataReaderKshape.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var maxIndex=0; var minIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length; i++) { var value = y[i][0]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } //console.log(ymax) //console.log(ymin) return [ymin, ymax,minIndex,maxIndex]; } function DataReaderMa(data, period, shift) { if (!period) { period = 14; } if (!shift) { shift = 0; } this.data = data; this.period = period; $.cookie('period',period); this.shift = shift; this.X = []; this.Y = []; } ClassExtend(DataReaderMa, DataReader); DataReaderMa.prototype.calc = function(start, count, time, data) { //计算移动平均线 //start: var sum = 0; var i, j; for (i = start, j = 0; j < count; i++, j++) { this.X[j] = time[i]; } } //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderMacd(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderMacd, DataReader); function DataReaderMaex(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderMaex, DataReader); function DataReaderMath(model, writer, name) { this.data = {}; this.lastdata = {}; sup(this, model, writer, name); this.start = 0; this.end = 0; this.number = 0; this.xunit = 0; this.yunit = 0; } ClassExtend(DataReaderMath, DataReader); //对数学公式来说,只要设置一个 x轴的区间 和 要划分的 数目就可以了 DataReaderMath.prototype.setXArea = function(start, end, number) { this.start = start; this.end = end; this.number = number; if (number <= 0) { return; } var x = []; var step = (this.end - this.start) / this.number; var i = this.start; while (i <= this.end) { x.push(i); i += step; } this.data.x = x.reverse(); } DataReaderMath.prototype.setParam = function(param) { if (param.length >= 2) { param = param[1].split(","); for (var i = 0; i < param.length; i++) { param[i] = parseInt(param[i]); } this.setXArea(param[0], param[1], param[2]); } } DataReaderMath.prototype.last = function() { return this.lastdata; } DataReaderMath.prototype.getEndPos = function() { var x = this.data.x; return x.length - 1; } DataReaderMath.prototype.startXValue = function() { var x = this.data.x; return x[x.length - 1]; } DataReaderMath.prototype.endXValue = function() { var x = this.data.x; return x[0]; } DataReaderMath.prototype.formatData = function(x, y) { var data = {}; data.x = x; data.y = y; data.maxX = x[0]; data.minX = x[x.length - 1]; data.maxIndex=0; data.minIndex=0; var _minmax = minmax(y); data.maxY = _minmax[1]; data.minY = _minmax[0]; data.maxIndex=_minmax[3]; data.minIndex=_minmax[2]; data.xunit = this.getUnit(data.minX, data.maxX); data.yunit = this.getUnit(data.minY, data.maxY); return data; } DataReaderMath.prototype.getUnit = function(min, max) { var step = (max - min) / this.number; return Math.pow(10, Math.floor(Math.log(step) / Math.LN10)); } //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderOrder(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderOrder, DataReader); // //读取蜡烛图的数据 // function DataReaderProfit(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderProfit, DataReader); DataReaderProfit.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var maxIndex=0; var minIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length; i++) { for (var j = 0; j < y[i].length; j++) { var value = y[i][j][1]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } } return [ymin, ymax,minIndex,maxIndex]; } function DataReaderTickSource(model, writer, name) { sup(this, model, writer, name); this.maxdata = this.config[name].maxdata; this.data = {}; this.cmd = ""; this.number = 60; this.nameToId = { 'FXDD': 0, 'FXPRO': 1, 'EXNESS': 2, 'IRONFX': 3, "FXCM": 4 }; this.idToName = ['FXDD', 'FXPRO', 'EXNESS', 'IRONFX', "FXCM"]; this.microsecond = 0; this.lastStatus = { width: {}, offset: {}, time: {}, quick: { queue: {}, inquick: false } }; this.lastdata; } ClassExtend(DataReaderTickSource, DataReader); DataReaderTickSource.prototype.setParam = function(param) { this.param = param; var cmd = this.param[1].split(","); var tmp = cmd.shift(); tmp = tmp.split("/"); this.bidask = tmp[0]; this.number = parseInt(tmp[1]); this.microsecond = parseInt(tmp[2]); if (!this.c) { return; } var r = []; for (var i = 0; i < cmd.length; i++) { r.push(this.c + "_" + cmd[i]); } this.cmd = "tick_source:" + r.join(","); } DataReaderTickSource.prototype.getWSCmd = function() { this.setParam(this.param); return this.cmd; } DataReaderTickSource.prototype.getData = function() { var type, id; var store, len; var x, y, bidask; var data = { x: [], y: {} }; this.lastdata = {}; this.lastStatus.time[this.writer.lastSource] = now(); for (var i = 0; i < this.idToName.length; i++) { x = []; y = []; type = this.idToName[i]; id = this.nameToId[type]; store = this.writer.getStore(this.c, type); len = store.x.length; if (len == 0) { continue; } if (this.bidask == "bid") { bidask = 0; } else if (this.bidask == "bid-ask") { bidask = -1; } else { bidask = 1; } this.lastdata[type] = {}; this.lastdata[type].bidask = store.y[len - 1]; for (var j = 0; j < this.number && j < len; j++) { var second = store.x[len - 1 - j]; if (this.microsecond) { x[j] = second; } else { x[j] = parseInt(second / 1000) * 1000; } if (bidask < 0) { y[j] = (store.y[len - 1 - j][0] + store.y[len - 1 - j][1]) / 2; } else { y[j] = store.y[len - 1 - j][bidask]; } } this.lastdata[type].x = x[0]; this.lastdata[type].y = y[0]; data.y[type] = { x: x, y: y }; } return data; } DataReaderTickSource.prototype.formatData = function(x, y) { var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; var hasdata = false; var len = 0; for (var type in y) { if (!is_object(y[type])) continue; len = y[type].x.length; if (len == 0) { continue; } hasdata = true; var data = this._formatData(y[type].x, y[type].y); if (minX > data.minX) minX = data.minX; if (minY > data.minY) minY = data.minY; if (maxX < data.maxX) maxX = data.maxX; if (maxY < data.maxY) maxY = data.maxY; } if (!hasdata) { return { x: [], y: [] }; } var data = this.lastInfo(); data.x = [minX, maxX]; data.xunit = 1; data.yunit = this.yunit; data.minX = minX; data.minY = minY; data.maxY = maxY; data.maxX = maxX; data.y = [minY, maxY] data.data = y; return data; } DataReaderTickSource.prototype.last = function() { return this.lastdata; } DataReaderTickSource.prototype.lastInfo = function() { //计算平均值 var avg = 0; var num = 0; var timemax = -Infinity; var timemin = Infinity; var tick_timemax = -Infinity; var tick_timemin = Infinity; var maxname, minname; var tick_maxname, tick_minname; var last = this.lastdata; for (var key in last) { if (!is_object(last[key])) { continue; } if (this.lastStatus.time[key] > timemax) { timemax = this.lastStatus.time[key]; maxname = key; } if (this.lastStatus.time[key] < timemin) { timemin = this.lastStatus.time[key]; minname = key; } if (last[key].x > tick_timemax) { tick_timemax = last[key].x; tick_maxname = key; } if (last[key].x < tick_timemin) { tick_timemin = last[key].x; tick_minname = key; } if (typeof this.lastStatus.width[key] === "undefined") { this.lastStatus.width[key] = 3; } num++; avg += last[key].y; } avg = avg / num; this.lastStatus.offset.local = now() - tick_timemax; this.lastStatus.offset.delt = timemax - timemin; //存在突破,要显示最后的bid ask 时间 if (this.lastStatus.quick.inquick == true) { var quick = this.lastStatus.quick; quick.queue[this.writer.lastSource] = 1; qnumber = 0; for (var key in quick.queue) { if (quick.queue[key] === 1) { qnumber++; } } if (qnumber == num) { quick.end = maxname; quick.inquick = false; quick.delt = this.lastStatus.time[quick.end] - this.lastStatus.time[quick.start]; } } var show = false; var point = Math.abs(Math.floor(last[maxname].y / this.yunit) - Math.floor(avg / this.yunit)); if (this.lastStatus.quick.inquick == false && point >= 4) { show = true; this.lastStatus.quick = {}; this.lastStatus.quick.bidask = {}; this.lastStatus.quick.start = maxname; this.lastStatus.quick.inquick = true; this.lastStatus.quick.queue = {}; this.lastStatus.quick.point = point; this.lastStatus.quick.queue[maxname] = 1; for (var key in this.lastStatus.width) { if (this.lastStatus.width[key] === 6) this.lastStatus.width[key] = 3; this.lastStatus.quick.bidask[key] = this.lastdata[key] ? this.lastdata[key].bidask : []; } this.lastStatus.width[maxname] = 6; } if (Math.abs(Math.floor(last[minname].y / this.yunit) - Math.floor(avg / this.yunit)) >= 4) { show = true; for (var key in this.lastStatus.width) { if (this.lastStatus.width[key] === 0.5) this.lastStatus.width[key] = 3; } this.lastStatus.width[minname] = 0.5; } this.lastStatus.show = show; return this.lastStatus; //计算最快值和最慢值 } function PlotCandleBollingDesity(plot, canvas) { this.name = "BollingDesity"; this.plot = plot; this.grid = this.plot.grid; this.api = plot.api; this.time = []; this.data = []; //所有的数据 this.cdata = new Point([], []); //当前要绘制的点 this.config = { Long: { Up: 0, Low: 2, Color: "Yellow" }, Mid1: { Up: 4, Low: 6, Color: "Red" }, Mid2: { Up: 8, Low: 10, Color: "Purple" }, Short1: { Up: 12, Low: 14, Color: "Green" }, Short2: { Up: 16, Low: 18, Color: "LightSeaGreen" } }; this.init = false; this.databuf = []; } //PlotCandle 有一个指标列表,在相应的处理上,加上这个这些接口 //提供下面的接口,用于下载数据,更新数据: 先更新time_start 和 mid_time //再更新剩下来的部分 PlotCandleBollingDesity.prototype.init = function(time_start, mid_time, time_end) { if (time_start == mid_time) { this.initComplete(); return; } var _this = this; $.get(this.api, { action: 'indicator', name: this.name, 'ts': time_start, 'te': mid_time }, function(data) { _this.processHistoryData(data); if (mid_time == time_end) { this.initComplete(); } }, 'json'); if (mid_time < time_end) { $.get(this.api, { action: 'indicator', name: this.name, 'ts': mid_time + 1, 'te': time_end }, function(data) { _this.processHistoryData(data); //处理原来下载的数据 this.initComplete(); }, 'json'); } } PlotCandleBollingDesity.prototype.initComplete = function() { for (var i = 0; i < this.databuf.length; i++) { this.processHistoryData(this.databuf[i]); } this.databuf = []; this.init = true; } PlotCandleBollingDesity.prototype.draw = function() { var X = array_copy(this.cdata.x); var Y = array_copy(this.cdata.y); //分成5种线来绘制 var yset = {}; for (var i = 0; i < X.length; i++) { for (var key in this.config) { if (typeof key == "string" && key != "undefined") { var max_index = this.config[key].Up; var min_index = this.config[key].Low; var color = this.config[key].Color; if (yset[key] === undefined) { yset[key] = {}; } if (Y[i][max_index] != 0) { if (yset[key].Up === undefined) { yset[key].Up = new Point([], [], []); } yset[key].Up.x.push(X[i]); yset[key].Up.y.push(Y[i][max_index]); yset[key].Up.z.push(Y[i][max_index + 1]); } if (Y[i][min_index] != 0) { if (yset[key].Low === undefined) { yset[key].Low = new Point([], [], []); } yset[key].Low.x.push(X[i]); yset[key].Low.y.push(Y[i][min_index]); yset[key].Low.z.push(Y[i][min_index + 1]); } } } } var minmax = this.getMinMaxDensity(); for (var key in yset) { if (typeof key == "string" && key != "undefined") { if (yset[key].Up) { this.drawOne(yset[key].Up, this.config[key].Color, minmax); } if (yset[key].Low) { this.drawOne(yset[key].Low, this.config[key].Color, minmax); } } } } PlotCandleBollingDesity.prototype.getLineHeight = function(min, max, data) { for (var i = 0; i < data.length; i++) { data[i] = Math.floor(((data[i] - min) / (max - min)) * 5) + 1; } return data; } PlotCandleBollingDesity.prototype.drawOne = function(data, color, minmax) { data.y = this.grid.getY(data.y); data.z = this.getLineHeight(minmax[0], minmax[1], data.z); var ctx = this.grid.ctx; ctx.fillStyle = color; for (var i = 0; i < data.x.length; i++) { ctx.fillRect(data.x[i] - this.plot.box_pixel, data.y[i] - data.z[i], 2 * this.plot.box_pixel + 1, data.z[i]); } } PlotCandleBollingDesity.prototype.updateNew = function(time) { //do nothing } PlotCandleBollingDesity.prototype.down = function(time_start, time_end) { var _this = this; $.get(this.api, { action: 'indicator', name: this.name, 'ts': time_start, 'te': time_end }, function(data) { if (this.init) { _this.processHistoryData(data); } else { _this.databuf.push(data); } }, 'json'); } PlotCandleBollingDesity.prototype.processHistoryData = function(data) { var newdata = data[this.name]; if (!is_array(newdata) || newdata.length == 0) { return; } for (var i = 0; i < newdata.length; i++) { var item = newdata[i]; this.time.push(parseInt(item.pop())); var flag = item.shift(); if (flag == null) { item = null; } else { for (var j = 0; j < item.length; j++) { item[j] = parseFloat(item[j]); } } this.data.push(item); } } PlotCandleBollingDesity.prototype.getDrawData = function(start_index, end_index) { var y = this.data.slice(start_index, end_index + 1); this.cdata.y = []; this.cdata.x = []; for (var i = 0; i < y.length - 1; i++) { if (y[i] != null) { this.cdata.y.push(y[i]); this.cdata.x.push(this.plot.X[i]); } } return this.cdata; } PlotCandleBollingDesity.prototype.getMinMax = function() { var data = this.cdata.y; var max = -Infinity; var min = Infinity; for (var i = 0; i < data.length; i++) { for (var key in this.config) { if (typeof key == "string" && key != "undefined") { var max_index = this.config[key].Up; var min_index = this.config[key].Low; if (data[i][max_index] > 0 && max < data[i][max_index]) { max = data[i][max_index] } if (data[i][min_index] > 0 && min > data[i][min_index]) { min = data[i][min_index]; } } } } return [min, max]; } PlotCandleBollingDesity.prototype.getMinMaxDensity = function() { var data = this.cdata.y; var max = -Infinity; var min = Infinity; for (var i = 0; i < data.length; i++) { for (var key in this.config) { if (typeof key == "string" && key != "undefined") { var max_index = this.config[key].Up; var min_index = this.config[key].Low; max = Math.max(max, data[i][max_index + 1], data[i][min_index + 1]); min = Math.min(min, data[i][max_index + 1], data[i][min_index + 1]); } } } return [min, max]; } //arr 从小到大 //reverse = true arr 从大到小 function bsearch(arr, match, reverse) { var start = 0; var end = arr.length - 1; if (!reverse) { reverse = false; } while (start <= end) { mid = Math.floor((end - start) / 2) + start; if (arr[mid] > match) { if (reverse) { start = mid + 1; } else { end = mid - 1; } } else if (arr[mid] < match) { if (reverse) { end = mid - 1; } else { start = mid + 1; } } else { //macth return mid; } } return -1; } // //读取蜡烛图的数据 // function DataReaderTrendLine(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderTrendLine, DataReader); DataReaderTrendLine.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var maxIndex=0; var minIndex=0; //console.log("DataReaderTrendLine.prototype.getYminmax"); if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length; i++) { var value = y[i][0]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } return [ymin, ymax,minIndex,maxIndex]; } //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReadervolumes(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReadervolumes, DataReader); DataReadervolumes.prototype.getDataByMain = function(readconfig) { this.mainData = this.getMainData(); //var start = this.mainData.x[this.mainData.x.length - 1]; //var end = this.mainData.x[0]; var x = new Array(); var y = new Array(); for (var i = 0; i < this.mainData.x.length && i < this.model.plotnum; i++) { x.push(this.mainData.x[i]); if (this.mainData.y[i][Data.OPEN] <= this.mainData.y[i][Data.CLOSE]) { y.push([this.mainData.y[i][4]]); } else { y.push([this.mainData.y[i][4], 0]); } } y = array_copy(y); return { x: x, y: y }; } DataReadervolumes.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = 0; var minIndex=0; var maxIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length && i < this.model.plotnum; i++) //for(var i=y.length-1;i>=0&& (y.length-i)<=this.model.plotnum;i--) { var value = y[i][0]; if (value > ymax) { ymax = value; maxIndex=i; } } return [0, ymax,minIndex,maxIndex]; } //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReadervolumes2(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReadervolumes2, DataReader); DataReadervolumes2.prototype.getDataByMain = function(readconfig) { this.mainData = this.getMainData(); var x = new Array(); var y = new Array(); for (var i = 0; i < this.mainData.x.length && i < this.model.plotnum; i++) { x.push(this.mainData.x[i]); y.push([this.mainData.y[i][5]]); } y = array_copy(y); return { x: x, y: y }; } DataReadervolumes2.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var minIndex=0; var maxIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } //for(var i=y.length-1;i>=0&& (y.length-i)<=this.model.plotnum;i--) for (var i = 0; i < y.length && i < this.model.plotnum; i++) { var value = y[i][0]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } return [0, ymax,minIndex,maxIndex]; } function DataReaderWave(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderWave, DataReader); //���ڣ�����������һ��ָ�걻ע���ˣ� //�ͻ���������֪K���Ͻ��г�ʼ��������������ʼ������������K��ʱ�䷶Χ������ //���������ݣ��ͻ�������K�ߵ����ؽ������� //��ָ����ʼ��δ������ʱ������������ʱ����ָ���������������ݣ���ô�ͻᱣ����һ�����������档 //ָ����ʼ����������K�ߵij�ʼ���������£������ص�һ��1000��K�ߵ�ָ�ꡣ��ʾ������ //Ȼ�����غ�����ָ��,���ؽ����Ժ󣬺ϲ�������������ָ�꣬���󣬳�ʼ�������� // //��ȡ����ͼ������ // function DataReaderwdensity(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderwdensity, DataReader); DataReaderwdensity.prototype.getYminmax = function(y) { var ymax = -Infinity; var ymin = Infinity; var minIndex=0; var maxIndex=0; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 0; i < y.length; i++) { for (var j = 0; j < y[i].length / 2; j++) { if (y[i][j * 2] != 0 && y[i][j * 2 + 1] != 0) { var value = y[i][j * 2]; if (value > ymax) { ymax = value; maxIndex=i; } if (value < ymin) { ymin = value; minIndex=i; } } } } return [ymin, ymax,minIndex,maxIndex]; } //现在,这样处理,一旦指标被注册了, //就会首先在已知K线上进行初始化操作。这个初始化操作是已有K线时间范围的数据 //后面的数据,就会绑定在K线的下载结果里面 //在指标初始化未结束的时候,如果这个时候,指标绑定下载了数据,那么就会保存在一个缓冲区里面。 //指标初始化过程类似K线的初始化过程如下:先下载第一个1000个K线的指标。显示出来, //然后下载后面的指标,下载结束以后,合并缓冲区里面的指标,最后,初始化结束。 function DataReaderWpr(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderWpr, DataReader); function DataReaderMathCos(model, writer, name) { sup(this, model, writer, name); this.setXArea(-10, 10, 100); } ClassExtend(DataReaderMathCos, DataReaderMath); DataReaderMathCos.prototype.getData = function() { if (this.number <= 0 || this.start == this.end) { return { x: [], y: [] }; } var y = []; var x = this.data.x; for (var i = 0; i < x.length; i++) { y.push(Math.cos(x[i])); } this.lastdata = y[0]; this.data.y = y; return this.data; } function DataReaderMathSin(model, writer, name) { sup(this, model, writer, name); this.setXArea(-10, 10, 100); } ClassExtend(DataReaderMathSin, DataReaderMath); DataReaderMathSin.prototype.getData = function() { if (this.number <= 0 || this.start == this.end) { return { x: [], y: [] }; } var x = this.data.x; var y = []; for (var i = 0; i < x.length; i++) { y.push(Math.sin(x[i])); } this.lastdata = y[0]; this.data.y = y; return this.data; } function DataReaderIndicatorsBolling(model, writer, name) { sup(this, model, writer, name); this.nPeriod = 12; this.nDev = 2; this.vlast = [0, 0, 0]; } ClassExtend(DataReaderIndicatorsBolling, DataReaderIndicators); DataReaderIndicatorsBolling.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; n = param[1].split(","); this.nPeriod = parseInt(n[0]); this.nDev = parseInt(n[1]); this.shift = this.nPeriod; } DataReaderIndicatorsBolling.prototype.getData = function() { var mx = this.model.maindata.x; var my = this.model.maindata.y if (mx.length < this.nPeriod) { return { x: [], y: [] }; } var sum = 0; for (var i = 0; i < this.nPeriod; i++) { sum += my[i][Data.CLOSE]; } var avg = []; var matime = []; var sma = 0.0, dev = 0.0; sma = sum / this.nPeriod; for (var i = 0; i < this.nPeriod; i++) { dev += Math.pow((my[i][Data.CLOSE] - sma), 2); } dev = Math.sqrt(dev / this.nPeriod); avg.push([sma, (sma + this.nDev * dev), (sma - this.nDev * dev)]); matime.push(mx[0]); for (var i = 1; i <= (mx.length - this.nPeriod) && i < this.model.plotnum; i++) { sum -= my[i - 1][Data.CLOSE]; sum += my[i + this.nPeriod - 1][Data.CLOSE]; sma = sum / this.nPeriod; dev = 0; for (var j = 0; j < this.nPeriod; j++) { dev += Math.pow((my[i + j][Data.CLOSE] - sma), 2); } dev = Math.sqrt(dev / this.nPeriod); avg.push([sma, (sma + this.nDev * dev), (sma - this.nDev * dev)]); matime.push(mx[i]); } this.vlast = [sma, (sma + this.nDev * dev), (sma - this.nDev * dev)]; return { x: matime, y: avg }; } DataReaderIndicatorsBolling.prototype.last = function() { return this.vlast; } function DataReaderIndicatorsDWPR(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderIndicatorsDWPR, DataReaderIndicators); //对指标,数据部分由 DataReaderIndicatorsDWPR.prototype.setN = function(n) { this.n = parseInt(n); this.shift = parseInt(n); } DataReaderIndicatorsDWPR.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; this.n = param[1].split(","); this.n[0] = parseInt(this.n[0]); this.n[1] = parseInt(this.n[1]); this.reffer = this.param[0]; this.shift = Math.max(this.n[0], this.n[1]); } DataReaderIndicatorsDWPR.prototype.getData = function() { // if (!this.dataInit(this.reffer[0])) { return { x: [], y: [] }; } var data1 = this.wpr(this.n[0]); if (!this.dataInit(this.reffer[1])) { return { x: [], y: [] }; } var data2 = this.wpr(this.n[1]); if (data1.length != data2.length) { return { x: [], y: [] }; } var data = []; for (var i = 0; i < data1.length; i++) { data[i] = data1[i] - data2[i]; } this.lastdata = data[0]; return { x: this.x, y: data }; } function DataReaderIndicatorsKDJ(model, writer, name) { sup(this, model, writer, name); this.nPeriod = 9; this.factor_1 = 2.0 / 3.0; this.factor_2 = 1.0 / 3.0; this.vlast = [0, 0, 0]; } ClassExtend(DataReaderIndicatorsKDJ, DataReaderIndicators); DataReaderIndicatorsKDJ.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; n = param[1].split(","); this.nPeriod = parseInt(n[0]); this.shift = this.nPeriod; } DataReaderIndicatorsKDJ.prototype.getData = function() { var mx = this.model.maindata.x.reverse(); var my = this.model.maindata.y.reverse(); if (mx.length < this.nPeriod) { return { x: [], y: [] }; } var k, d, j, rsv; var yvalue = []; var matime = []; var cn, ln, hn; var kpre = 50; var dpre = 50; for (i = this.nPeriod - 1; i < mx.length; i++) { ln = Infinity; hn = -Infinity; cn = my[i][Data.CLOSE]; for (j = 0; j < this.nPeriod; j++) { if (ln > my[i - j][Data.LOW]) { ln = my[i - j][Data.LOW]; } if (hn < my[i - j][Data.HIGH]) { hn = my[i - j][Data.HIGH]; } } if ((hn - ln) == 0) { rsv = 50; } else { rsv = (cn - ln) / (hn - ln) * 100; } k = this.factor_1 * kpre + this.factor_2 * rsv; d = this.factor_1 * dpre + this.factor_2 * k; j = 3 * d - 2 * k; kpre = k; dpre = d; yvalue.push([k, d, j]); matime.push(mx[i]); } matime = matime.reverse(); yvalue = yvalue.reverse(); this.vlast = [k, d, j]; return { x: matime, y: yvalue }; } DataReaderIndicatorsKDJ.prototype.last = function() { return this.vlast; } function DataReaderIndicatorsMa(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderIndicatorsMa, DataReaderIndicators); //对指标,数据部分由 DataReaderIndicatorsMa.prototype.setN = function(n) { this.n = parseInt(n); this.shift = parseInt(n); } DataReaderIndicatorsMa.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; this.setN(this.param[1]); } DataReaderIndicatorsMa.prototype.getData = function() { var mx = this.model.maindata.x; var my = this.model.maindata.y if (mx.length < this.n) { return { x: [], y: [] }; } var sum = 0; //---- initial accumulation //移动平均线的做法:前n个平均数目,那可以从 0 遍历到 length - this.n //和一般求解指标不同,我这里采用倒过来求解的办法 //先初始化平均值 for (var i = 0; i < this.n; i++) { sum += my[i][Data.CLOSE]; } var avg = []; var matime = []; avg.push(sum / this.n); matime.push(mx[0]); for (var i = 1; i <= (mx.length - this.n) && i < this.model.plotnum; i++) { sum -= my[i - 1][Data.CLOSE]; sum += my[i + this.n - 1][Data.CLOSE]; avg.push(sum / this.n); matime.push(mx[i]); } //后面为空的补零,这样和时间才能对齐。 /*for (var i = 0; i < this.n; i++) { avg.push(0); }*/ this.lastdata = avg[0]; return { x: matime, y: avg }; } function DataReaderIndicatorsMacd(model, writer, name) { sup(this, model, writer, name); this.nFastPeriod = 12; this.nSlowPeriod = 26; this.nSignalPeriod = 9; this.vlast = [0, 0]; } ClassExtend(DataReaderIndicatorsMacd, DataReaderIndicators); DataReaderIndicatorsMacd.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; n = param[1].split(","); this.nFastPeriod = parseInt(n[0]); this.nSlowPeriod = parseInt(n[1]); if (this.nFastPeriod > this.nSlowPeriod) { nTmp = this.nFastPeriod; this.nFastPeriod = this.nSlowPeriod; this.nSlowPeriod = nTmp; } this.nSignalPeriod = parseInt(n[2]); this.shift = this.nSlowPeriod + this.nSignalPeriod; } DataReaderIndicatorsMacd.prototype.getData = function() { var mx = this.model.maindata.x; var my = this.model.maindata.y if (mx.length < this.shift) { return { x: [], y: [] }; } var sumfast = 0.0; for (var i = 1; i <= this.nFastPeriod; i++) { sumfast += my[mx.length - i][Data.CLOSE]; } var sumslow = sumfast; var vfast = sumfast / this.nFastPeriod; var prfast = 2.0 / (this.nFastPeriod + 1.0); for (var i = this.nFastPeriod + 1; i <= this.nSlowPeriod; i++) { sumslow += my[mx.length - i][Data.CLOSE]; vfast = vfast * (1 - prfast) + prfast * my[mx.length - i][Data.CLOSE]; } var vslow = sumslow / this.nSlowPeriod; var prslow = 2.0 / (this.nSlowPeriod + 1.0); var matime = []; var macdy = []; matime.push(mx[mx.length - this.nSlowPeriod]); macdy.push([vfast - vslow, NaN]); var summacd = vfast - vslow; for (var i = this.nSlowPeriod + 1; i < this.nSlowPeriod + this.nSignalPeriod; i++) { vfast = vfast * (1 - prfast) + prfast * my[mx.length - i][Data.CLOSE]; vslow = vslow * (1 - prslow) + prslow * my[mx.length - i][Data.CLOSE]; summacd += vfast - vslow; matime.push(mx[mx.length - i]); if (i < (this.nSlowPeriod + this.nSignalPeriod - 1)) { macdy.push([vfast - vslow, NaN]); } else { macdy.push([vfast - vslow, summacd / this.nSignalPeriod]); } } for (var i = this.nSlowPeriod + this.nSignalPeriod; i <= mx.length; i++) { vfast = vfast * (1 - prfast) + prfast * my[mx.length - i][Data.CLOSE]; vslow = vslow * (1 - prslow) + prslow * my[mx.length - i][Data.CLOSE]; summacd += vfast - vslow; summacd -= macdy[macdy.length - this.nSignalPeriod][0]; matime.push(mx[mx.length - i]); macdy.push([vfast - vslow, summacd / this.nSignalPeriod]); } this.vlast = [vfast - vslow, summacd / this.nSignalPeriod] return { x: matime, y: macdy }; } DataReaderIndicatorsMacd.prototype.last = function() { return this.vlast; } // DataReaderIndicatorsMacd.prototype.getYminmax = function(y) { // var ymax = -Infinity; // var ymin = Infinity; // if (!y) { // return [ymin, ymax]; // } // for (var i = 1; i <= y.length && i <= this.model.plotnum; i++) { // for (var j = 0; j < y[y.length - i].length; j++) { // var value = y[y.length - i][j]; // if (!isNaN(value)) { // if (value > ymax) { // ymax = value; // } // if (value < ymin) { // ymin = value; // } // } // } // } // return [ymin, ymax]; // } DataReaderIndicatorsMacd.prototype.getYminmax = function(y) { var ymax = -Infinity; var maxIndex=0; var minIndex=0; var ymin = Infinity; if (!y) { return [ymin, ymax,minIndex,maxIndex]; } for (var i = 1; i <= y.length && i <= this.model.plotnum; i++) { for (var j = 0; j < y[y.length - i].length; j++) { var value = y[y.length - i][j]; if (!isNaN(value)) { if (value > ymax) { maxIndex=i; ymax = value; } if (value < ymin) { minIndex=i; ymin = value; } } } } return [ymin, ymax,minIndex,maxIndex]; } function DataReaderIndicatorsMaTrend(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderIndicatorsMaTrend, DataReaderIndicators); DataReaderIndicatorsMaTrend.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; this.n = param[1].split(","); for (var i = 0; i < this.n.length; i++) { this.n[i] = parseInt(this.n[i]); } this.n = this.n.sort(function(a, b) { return a - b }); this.reffer = this.param[0]; this.shift = Math.max.apply(this, this.n); } DataReaderIndicatorsMaTrend.prototype.getData = function() { var ma = []; var trend = []; if (!this.dataInit()) { return { x: [], y: [] }; } for (var i = 0; i < this.n.length; i++) { // alert(this.n[i]); ma[i] = this.ma(this.n[i]); } var num, delt; for (var i = 0; i < this.y.length - 1; i++) { num = 0; delt = 0; for (var j = 0; j < this.n.length; j++) { if (ma[j][i] > ma[j][i + 1]) { num++; } else if (ma[j][i] < ma[j][i + 1]) { num--; } delt++; for (var k = j + 1; k < this.n.length; k++) { if (ma[j][i] > ma[k][i]) { num++; } else if (ma[j][i] < ma[k][i]) { num--; } delt++; } } num = num / delt; trend[i] = num; } trend[this.y.length - 1] = 0; this.lastdata = trend[0]; return { x: this.x, y: trend }; } function DataReaderIndicatorsRSI(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderIndicatorsRSI, DataReaderIndicators); //对指标,数据部分由 DataReaderIndicatorsRSI.prototype.setN = function(n) { this.n = parseInt(n); this.shift = parseInt(n); } DataReaderIndicatorsRSI.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; this.reffer = this.param[0]; this.setN(param[1]); } DataReaderIndicatorsRSI.prototype.empty = function() { return { x: [], y: [] }; } DataReaderIndicatorsRSI.prototype.getData = function() { if (!this.dataInit(this.reffer[1])) { return this.empty(); } var K = this.strength(this.n); if (!this.dataInit(this.reffer[0])) { return this.empty(); } var E = this.strength(this.n); if (K.length != E.length) { return this.empty(); } var H = []; for (var i = 0; i < K.length; i++) { H[i] = (K[i] - E[i] + 200) / 4; } this.lastdata = H[0]; return { x: this.x, y: H }; } function DataReaderIndicatorsWPR(model, writer, name) { sup(this, model, writer, name); } ClassExtend(DataReaderIndicatorsWPR, DataReaderIndicators); //对指标,数据部分由 DataReaderIndicatorsWPR.prototype.setN = function(n) { this.n = parseInt(n); this.shift = parseInt(n); } DataReaderIndicatorsWPR.prototype.setParam = function(param) { //这个函数里面做一定的初始化工作。特别是shift this.param = param; this.setN(this.param[1]); } DataReaderIndicatorsWPR.prototype.getData = function() { if (!this.dataInit()) { return { x: [], y: [] }; } var data = this.wpr(this.n); this.lastdata = data[0]; return { x: this.x, y: data }; } function html5chart(api,type,period) { if (type&&period) { $('#'+type.toLowerCase()).addClass('active'); //$.clearCookie(); api.initPair(type, period); $.cookie('symbol', type); $.cookie('period',period); // $.cookie('period',period); // var indstr="main:ma|main|5|color=black:ma|main|10|color=yellow:ma|main|20|color=red:ma|main|30|color=green:"; // api.setInd(indstr); api.initInd('MAC'); // console.log($('li[name="ETH"]')); } else { $.cookie('symbol', 'BCCBTC'); $.cookie('period', 'H1'); // sessionStorage.setItem('period','H1'); api.initPair('BCCBTC', "H1"); api.initInd('MAC'); } return; }