Type/to search
8
Follow
1363
Followers
Projetar uma biblioteca de desenho de linhas com vários gráficos
Discussions
Created 2022-03-31 11:28:35  Updated 2023-09-20 10:04:46
 2
 1945

img

A criação de gráficos e o desenho de gráficos são frequentemente usados ​​ao escrever e projetar estratégias. Para um único gráfico personalizado, podemos usarBiblioteca de desenho de linhas(Alunos que não estão familiarizados com o conceito de biblioteca de modelos no FMZ podem consultar a documentação da API do FMZ), o que é muito conveniente para operações de desenho. No entanto, esta biblioteca de modelos não pode atender às necessidades de cenários que exigem vários gráficos. Em seguida, aprendemos as ideias de design desta biblioteca de desenho linear e projetamos uma versão multigráfico da biblioteca de desenho linear com base nisso.

Funções de exportação de design da "biblioteca de modelos"

Com base no design da função de exportação da "Biblioteca de desenho de linhas", também projetamos funções de exportação semelhantes para a biblioteca de desenho de linhas de vários gráficos.

  • $.PlotMultRecords
    Usado para desenhar gráficos K-line, design de parâmetros: cfgName, seriesName, registros, extensão.
    cfgName: O nome do objeto de configuração como um gráfico separado.
    seriesName: O nome da série de dados da linha K a ser plotada atualmente.
    registros: os dados da linha K recebidos.
    extensão: Informações de configuração do tamanho do gráfico, por exemplo, passe em:{layout: 'single', col: 6, height: '600px'}, ou seja, deixe o gráfico com o nome do objeto de configuração cfgName ser exibido separadamente, com uma largura de 6 e uma altura de 600px.

  • $.PlotMultLine
    Usado para desenhar linhas, design de parâmetros: cfgName, seriesName, dot, ts, extensão
    cfgName: O nome do objeto de configuração como um gráfico separado.
    seriesName: O nome da série de dados a ser desenhada.
    ponto: O valor da coordenada vertical do ponto na linha a ser desenhada.
    ts: Carimbo de data/hora, ou seja, o valor no eixo do tempo x.
    extensão: Informações de configuração para tamanho do gráfico.

  • $.PlotMultHLine
    Usado para desenhar linhas horizontais, design de parâmetros: cfgName, valor, rótulo, cor, estilo
    cfgName: Nome do objeto de configuração do gráfico.
    valor: O valor da coordenada vertical da linha horizontal.
    rótulo: O texto exibido na linha horizontal.
    cor: A cor da linha.
    estilo: O estilo da linha, por exemplo:Solid ShortDash ShortDot ShortDashDot ShortDashDotDot Dot Dash LongDash DashDot LongDashDot LongDashDotDot

  • $.PlotMultTitle
    Usado para modificar o título e o subtítulo do gráfico. Design de parâmetros: cfgName, title, chartTitle
    cfgName: Nome do objeto de configuração do gráfico.
    título: Subtítulo.
    chartTitle: Título do gráfico.

  • $.PlotMultFlag
    Desenhe um pequeno ícone de bandeira, design de parâmetro: cfgName, seriesName, ts, texto, título, forma, cor, onSeriesName
    cfgName: Nome do objeto de configuração do gráfico.
    seriesName: nome da série de dados.
    ts: carimbo de data/hora
    texto: O texto no ícone pequeno.
    título: O título do pequeno ícone.
    forma: O formato do ícone pequeno.
    cor: cor do ícone pequeno.
    onSeriesName: Com base na série de dados exibida, o valor é o id da série de dados.

  • $.GetArrCfg
    Retorna uma matriz de objetos de configuração de gráfico.

Design de função de teste

Para facilitar o entendimento, escrevi comentários diretamente nas funções de teste para explicar o que cada chamada de função faz.

// test function main() { LogReset(10) var i = 0 var prePrintTs = 0 while (true) { var r = exchange.GetRecords() // 获取K线数据 var t = exchange.GetTicker() // 获取实时的tick数据 $.PlotMultRecords("chart1", "kline1", r, {layout: 'single', col: 6, height: '600px'}) // 创建一个名为chart1的K线图表,独立显示,宽度是6,高度是600px,K线数据系列名称为kline1,使用上面获取的r作为数据源画图 $.PlotMultRecords("chart2", "kline2", r, {layout: 'single', col: 6, height: '600px'}) // 创建第二个K线图表,名为chart2 $.PlotMultLine("chart2", "line1", t.Last, r[r.length - 1].Time) // 在K线图表即chart2上增加一条线,数据系列名称为line1,使用当前的tick数据的最新价Last作为线上的点的Y值。K线数据的最后一个BAR的时间戳作为X值 $.PlotMultLine("chart3", "line2", t.Last) // 创建一个只画线的图表,图表名称chart3,数据系列名称line2,使用实时tick数据的Last最新价格在当前时间(X值)画一个点(t.Last为Y值),注意图表不是独立显示 $.PlotMultLine("chart6", "line6", t.Time) // 创建一个只画线的图表chart6,注意图表不是独立显示,会和chart3在一起分页显示 $.PlotMultLine("chart4", "line3", t.Sell, new Date().getTime(), {layout: 'single', col: 4, height: '300px'}) // 创建一个只画线的图表chart4,独立显示,宽度4,高度300px $.PlotMultLine("chart5", "line4", t.Volume, new Date().getTime(), {layout: 'single', col: 8, height: '300px'}) // 创建一个只画线的图表chart5,独立显示,宽度8,高度300px $.PlotMultHLine("chart1", r[r.length - 1].Close, "HLine1", "blue", "ShortDot") // 给图表chart1增加水平横线 $.PlotMultHLine("chart4", t.Sell, "HLine2", "green") // 给图表chart4增加水平横线 $.PlotMultTitle("chart3", "change : chart3->test1", "test1") // 修改chart3的标题 var ts = new Date().getTime() if (ts - prePrintTs > 1000 * 20) { prePrintTs = ts // 触发时,给chart3图表上画小图标 $.PlotMultFlag("chart3", "flag1", new Date().getTime(), "flag test", "flag1") } if (i == 10) { Log("i == 10") // 触发时,给chart4,chart1上画小图标 $.PlotMultFlag("chart4", "flag2", new Date().getTime(), "flag test", "flag2") $.PlotMultFlag("chart1", "flag3", new Date().getTime(), "flag test", "flag3", "squarepin", "green", "kline1") } else if (i == 20) { Log("i == 20") // 触发时,给chart1上添加一条线,但是只画了这条线的一个点,X坐标时间戳,Y坐标为t.Last值 $.PlotMultLine("chart1", "line5", t.Last, r[r.length - 1].Time) } else if (i == 30) { Log("i == 30") // 触发时,给chart2上画小图标 $.PlotMultFlag("chart2", "flag4", new Date().getTime(), "flag test", "flag4", "circlepin", "black", "kline2") } Sleep(1000 * 5) i++ } }

Executando testes

img

img

Você pode ver que com apenas uma linha de chamadas de função, você pode facilmente desenhar um gráfico e exibir vários gráficos ao mesmo tempo.

Código fonte da estratégia completa

Configuração de parâmetros:
img

Implementação do código-fonte da biblioteca de classes:

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) } }

Endereço estratégico: https://www.fmz.com/strategy/353264

Aqui estou apenas dando algumas ideias. Se estiver interessado, você pode continuar adicionando tipos de gráficos suportados e continuar a atualizar, como desenhar gráficos de profundidade de mercado, gráficos de barras, gráficos de pizza, etc.

Related Recommendations
Comment
All comments (2)

    梦大,超赞 [赞],图表名称如果用for遍历的变量代替,直接就会自动分页显示不同的表了。[呲牙]

    4 years ago

    是的,只要标题不同,就会分开显示了。不过要看模版导出函数传入的参数,可以指定叠加或者平铺。

    4 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)