En la página web de la biblioteca de clases de dibujo, se puede leer sobre diseño.
Artículo relacionado:https://www.fmz.com/digest-topic/9159
/*backtest start: 2022-03-29 00:00:00 end: 2022-03-29 00:10:00 period: 30m basePeriod: 15m exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}] */ var registerInfo = {} var chart = null var arrCfg = [] function updateSeriesIdx() { var index = 0 var map = {} _.each(arrCfg, function(cfg) { _.each(cfg.series, function(series) { var key = cfg.name + "|" + series.name map[key] = index index++ }) }) for (var cfgName in registerInfo) { _.each(arrCfg, function(cfg, cfgIdx) { if (cfg.name == cfgName) { registerInfo[cfgName].cfgIdx = cfgIdx } }) for (var i in registerInfo[cfgName].seriesIdxs) { var seriesName = registerInfo[cfgName].seriesIdxs[i].seriesName var key = cfgName + "|" + seriesName if (typeof(map[key]) != "undefined") { registerInfo[cfgName].seriesIdxs[i].index = map[key] } if (registerInfo[cfgName].seriesIdxs[i].type == "candlestick") { registerInfo[cfgName].seriesIdxs[i].preBarTime = 0 } else if (registerInfo[cfgName].seriesIdxs[i].type == "line") { registerInfo[cfgName].seriesIdxs[i].preDotTime = 0 } else if (registerInfo[cfgName].seriesIdxs[i].type == "flag") { registerInfo[cfgName].seriesIdxs[i].preFlagTime = 0 } } } if (!chart) { chart = Chart(arrCfg) } chart.update(arrCfg) chart.reset() _G("registerInfo", registerInfo) _G("arrCfg", arrCfg) for (var cfgName in registerInfo) { for (var i in registerInfo[cfgName].seriesIdxs) { var buffer = registerInfo[cfgName].seriesIdxs[i].buffer var index = registerInfo[cfgName].seriesIdxs[i].index if (buffer && buffer.length != 0 && registerInfo[cfgName].seriesIdxs[i].type == "line" && registerInfo[cfgName].seriesIdxs[i].preDotTime == 0) { _.each(buffer, function(obj) { chart.add(index, [obj.ts, obj.dot]) registerInfo[cfgName].seriesIdxs[i].preDotTime = obj.ts }) } else if (buffer && buffer.length != 0 && registerInfo[cfgName].seriesIdxs[i].type == "flag" && registerInfo[cfgName].seriesIdxs[i].preFlagTime == 0) { _.each(buffer, function(obj) { chart.add(index, obj.data) registerInfo[cfgName].seriesIdxs[i].preFlagTime = obj.ts }) } } } } function checkBufferLen(buffer, maxLen) { while (buffer.length > maxLen) { buffer.shift() } } $.PlotMultRecords = function(cfgName, seriesName, records, extension) { if (typeof(cfgName) == "undefined") { throw "need cfgName!" } var index = -1 var eleIndex = -1 do { var cfgInfo = registerInfo[cfgName] if (typeof(cfgInfo) == "undefined") { var cfg = { name: cfgName, __isStock: true, title: { text: cfgName }, tooltip: { xDateFormat: '%Y-%m-%d %H:%M:%S, %A' }, legend: { enabled: true, }, plotOptions: { candlestick: { color: '#d75442', upColor: '#6ba583' } }, rangeSelector: { buttons: [{ type: 'hour', count: 1, text: '1h' }, { type: 'hour', count: 3, text: '3h' }, { type: 'hour', count: 8, text: '8h' }, { type: 'all', text: 'All' }], selected: 2, inputEnabled: true }, series: [{ type: 'candlestick', name: seriesName, id: seriesName, data: [] }], } if (typeof(extension) != "undefined") { cfg.extension = extension } registerInfo[cfgName] = { "cfgIdx": arrCfg.length, "seriesIdxs": [{ seriesName: seriesName, index: arrCfg.length, type: "candlestick", preBarTime: 0 }], } arrCfg.push(cfg) updateSeriesIdx() } if (!chart) { chart = Chart(arrCfg) } else { chart.update(arrCfg) } _.each(registerInfo[cfgName].seriesIdxs, function(ele, i) { if (ele.seriesName == seriesName && ele.type == "candlestick") { index = ele.index eleIndex = i } }) if (index == -1) { arrCfg[registerInfo[cfgName].cfgIdx].series.push({ type: 'candlestick', name: seriesName, id: seriesName, data: [] }) registerInfo[cfgName].seriesIdxs.push({ seriesName: seriesName, index: arrCfg.length, type: "candlestick", preBarTime: 0 }) updateSeriesIdx() } } while (index == -1) for (var i = 0; i < records.length; i++) { if (records[i].Time == registerInfo[cfgName].seriesIdxs[eleIndex].preBarTime) { chart.add(index, [records[i].Time, records[i].Open, records[i].High, records[i].Low, records[i].Close], -1) } else if (records[i].Time > registerInfo[cfgName].seriesIdxs[eleIndex].preBarTime) { registerInfo[cfgName].seriesIdxs[eleIndex].preBarTime = records[i].Time chart.add(index, [records[i].Time, records[i].Open, records[i].High, records[i].Low, records[i].Close]) } } return chart } $.PlotMultLine = function(cfgName, seriesName, dot, ts, extension) { if (typeof(cfgName) == "undefined") { throw "need cfgName!" } var index = -1 var eleIndex = -1 do { var cfgInfo = registerInfo[cfgName] if (typeof(cfgInfo) == "undefined") { var cfg = { name: cfgName, __isStock: true, title: { text: cfgName }, xAxis: { type: 'datetime' }, series: [{ type: 'line', name: seriesName, id: seriesName, data: [], }] } if (typeof(extension) != "undefined") { cfg.extension = extension } registerInfo[cfgName] = { "cfgIdx": arrCfg.length, "seriesIdxs": [{ seriesName: seriesName, index: arrCfg.length, type: "line", buffer: [], preDotTime: 0 }], } arrCfg.push(cfg) updateSeriesIdx() } if (!chart) { chart = Chart(arrCfg) } else { chart.update(arrCfg) } _.each(registerInfo[cfgName].seriesIdxs, function(ele, i) { if (ele.seriesName == seriesName && ele.type == "line") { index = ele.index eleIndex = i } }) if (index == -1) { arrCfg[registerInfo[cfgName].cfgIdx].series.push({ type: 'line', name: seriesName, id: seriesName, data: [], }) registerInfo[cfgName].seriesIdxs.push({ seriesName: seriesName, index: arrCfg.length, type: "line", buffer: [], preDotTime: 0 }) updateSeriesIdx() } } while (index == -1) if (typeof(ts) == "undefined") { ts = new Date().getTime() } var buffer = registerInfo[cfgName].seriesIdxs[eleIndex].buffer if (registerInfo[cfgName].seriesIdxs[eleIndex].preDotTime != ts) { registerInfo[cfgName].seriesIdxs[eleIndex].preDotTime = ts chart.add(index, [ts, dot]) buffer.push({ ts: ts, dot: dot }) checkBufferLen(buffer, maxBufferLen) } else { chart.add(index, [ts, dot], -1) buffer[buffer.length - 1].dot = dot } return chart } $.PlotMultHLine = function(cfgName, value, label, color, style) { if (typeof(cfgName) == "undefined" || typeof(registerInfo[cfgName]) == "undefined") { throw "need cfgName!" } var cfg = arrCfg[registerInfo[cfgName].cfgIdx] if (typeof(cfg.yAxis) == "undefined") { cfg.yAxis = { plotLines: [] } } else if (typeof(cfg.yAxis.plotLines) == "undefined") { cfg.yAxis.plotLines = [] } var obj = { value: value, color: color || 'red', width: 2, dashStyle: style || 'Solid', label: { name: label || '', text: (label + ":" + value) || '', align: 'center' }, } var found = false for (var i = 0; i < cfg.yAxis.plotLines.length; i++) { if (cfg.yAxis.plotLines[i].label.name == label) { cfg.yAxis.plotLines[i] = obj found = true } } if (!found) { cfg.yAxis.plotLines.push(obj) } if (!chart) { chart = Chart(arrCfg) } else { chart.update(arrCfg) } return chart } $.PlotMultTitle = function(cfgName, title, chartTitle) { if (typeof(cfgName) == "undefined" || typeof(registerInfo[cfgName]) == "undefined") { throw "need cfgName!" } var cfg = arrCfg[registerInfo[cfgName].cfgIdx] cfg.subtitle = { text: title } if (typeof(chartTitle) !== 'undefined') { cfg.title = { text: chartTitle } } if (chart) { chart.update(arrCfg) } return chart } $.PlotMultFlag = function(cfgName, seriesName, ts, text, title, shape, color, onSeriesName) { if (typeof(cfgName) == "undefined" || typeof(registerInfo[cfgName]) == "undefined") { throw "need cfgName!" } var index = -1 var eleIndex = -1 do { if (!chart) { chart = Chart(arrCfg) } else { chart.update(arrCfg) } _.each(registerInfo[cfgName].seriesIdxs, function(ele, i) { if (ele.seriesName == seriesName && ele.type == "flag") { index = ele.index eleIndex = i } }) if (index == -1) { arrCfg[registerInfo[cfgName].cfgIdx].series.push({ type: 'flags', name: seriesName, onSeries: onSeriesName || arrCfg[registerInfo[cfgName].cfgIdx].series[0].id, data: [] }) registerInfo[cfgName].seriesIdxs.push({ seriesName: seriesName, index: arrCfg.length, type: "flag", buffer: [], preFlagTime: 0 }) updateSeriesIdx() } } while (index == -1) if (typeof(ts) == "undefined") { ts = new Date().getTime() } var buffer = registerInfo[cfgName].seriesIdxs[eleIndex].buffer var obj = { x: ts, color: color, shape: shape, title: title, text: text } if (registerInfo[cfgName].seriesIdxs[eleIndex].preFlagTime != ts) { registerInfo[cfgName].seriesIdxs[eleIndex].preFlagTime = ts chart.add(index, obj) buffer.push({ ts: ts, data: obj }) checkBufferLen(buffer, maxBufferLen) } else { chart.add(index, obj, -1) buffer[buffer.length - 1].data = obj } return chart } $.GetArrCfg = function() { return arrCfg } $.removeChart = function(cfgName) { var index = -1 for (var i = 0; i < arrCfg.length; i++) { if (arrCfg[i].name == cfgName) { index = i break } } if (index != -1) { arrCfg.splice(index, 1) } if (typeof(registerInfo[cfgName]) != "undefined") { delete registerInfo[cfgName] } updateSeriesIdx() } function init() { if (isChartReset) { Log("重置图表", "#FF0000") chart = Chart(arrCfg) chart.reset() Log("清空持久化数据,key:", "registerInfo、arrCfg #FF0000") _G("registerInfo", null) _G("arrCfg", null) } else { var multChartRegisterInfo = _G("registerInfo") var multChartArrCfg = _G("arrCfg") if (multChartRegisterInfo && multChartArrCfg) { registerInfo = multChartRegisterInfo arrCfg = multChartArrCfg Log("恢复 registerInfo、arrCfg #FF0000") } else { Log("没有数据可以恢复 #FF0000") } } } function onexit() { _G("registerInfo", registerInfo) _G("arrCfg", arrCfg) Log("保存数据,key : registerInfo, arrCfg #FF0000") } // test function main() { LogReset(10) var i = 0 var prePrintTs = 0 var t = _C(exchange.GetTicker) while (true) { var r = _C(exchange.GetRecords) $.PlotMultRecords("chart1", "kline1", r, { layout: 'single', col: 6, height: '600px' }) $.PlotMultRecords("chart2", "kline2", r, { layout: 'single', col: 6, height: '600px' }) $.PlotMultLine("chart2", "line1", t.Last, r[r.length - 1].Time) $.PlotMultLine("chart3", "line2", 10 + i) $.PlotMultLine("chart6", "line6", 100 + i) $.PlotMultLine("chart4", "line3", 1000 + i, new Date().getTime(), { layout: 'single', col: 4, height: '300px' }) $.PlotMultLine("chart5", "line4", 10000 + i, new Date().getTime(), { layout: 'single', col: 8, height: '300px' }) $.PlotMultHLine("chart1", r[r.length - 1].Close, "HLine1", "blue", "ShortDot") $.PlotMultHLine("chart4", t.Sell, "HLine2", "green") $.PlotMultTitle("chart3", "change : chart3->test1", "test1") var ts = new Date().getTime() if (ts - prePrintTs > 1000 * 20) { prePrintTs = ts $.PlotMultFlag("chart3", "flag1", new Date().getTime(), "flag test" + i, "flag1") } if (i == 10) { Log("i == 3") $.PlotMultFlag("chart4", "flag2", new Date().getTime(), "flag test" + i, "flag2") $.PlotMultFlag("chart1", "flag3", new Date().getTime(), "flag test" + i, "flag3", "squarepin", "green", "kline1") } else if (i == 20) { Log("i == 8") $.PlotMultLine("chart1", "line5", t.Last, r[r.length - 1].Time) } else if (i == 30) { Log("i == 10") $.PlotMultFlag("chart2", "flag4", new Date().getTime(), "flag test" + i, "flag4", "circlepin", "black", "kline2") $.removeChart("chart1") } i++ Sleep(1000 * 1) } }