Type/to search
8
Follow
1363
Followers
Concevoir une bibliothèque de dessins au trait multi-graphiques
Discussions
Created 2022-03-31 11:28:35  Updated 2023-09-20 10:04:46
 2
 1945

img

La création de diagrammes et le dessin de graphiques sont souvent utilisés lors de la rédaction et de la conception de stratégies. Pour un seul graphique personnalisé, nous pouvons utiliserBibliothèque de dessins au trait(Les étudiants qui ne sont pas familiers avec le concept de bibliothèque de modèles sur FMZ peuvent consulter la documentation de l'API FMZ), ce qui est très pratique pour les opérations de dessin. Cependant, cette bibliothèque de modèles ne peut pas répondre aux besoins des scénarios nécessitant plusieurs graphiques. Nous apprenons ensuite les idées de conception de cette bibliothèque de dessins au trait et concevons une version multi-graphiques de la bibliothèque de dessins au trait basée sur celle-ci.

Fonctions d'exportation de conception de la « bibliothèque de modèles »

En nous appuyant sur la conception de la fonction d'exportation de la « Bibliothèque de dessins au trait », nous avons également conçu des fonctions d'exportation similaires pour la bibliothèque de dessins au trait multi-graphiques.

  • $.PlotMultRecords
    Utilisé pour dessiner des graphiques K-line, conception de paramètres : cfgName, seriesName, enregistrements, extension.
    cfgName : Le nom de l'objet de configuration sous forme de graphique séparé.
    seriesName : le nom de la série de données K-line actuellement à tracer.
    enregistrements : les données entrantes de la ligne K.
    extension : informations de configuration de la taille du graphique, par exemple, à transmettre :{layout: 'single', col: 6, height: '600px'}, c'est-à-dire, laissez le graphique avec le nom d'objet de configuration cfgName être affiché séparément, avec une largeur de 6 et une hauteur de 600 px.

  • $.PlotMultLine
    Utilisé pour dessiner des lignes, conception des paramètres : cfgName, seriesName, dot, ts, extension
    cfgName : Le nom de l'objet de configuration sous forme de graphique séparé.
    seriesName : Le nom de la série de données à dessiner.
    point : la valeur de la coordonnée verticale du point sur la ligne à dessiner.
    ts : Horodatage, c'est-à-dire la valeur sur l'axe des x-temps.
    extension : informations de configuration pour la taille du graphique.

  • $.PlotMultHLine
    Utilisé pour dessiner des lignes horizontales, conception des paramètres : cfgName, valeur, étiquette, couleur, style
    cfgName : nom de l'objet de configuration du graphique.
    valeur : La valeur de la coordonnée verticale de la ligne horizontale.
    étiquette : Le texte affiché sur la ligne horizontale.
    couleur : La couleur de la ligne.
    style : Le style de la ligne, par exemple :Solid ShortDash ShortDot ShortDashDot ShortDashDotDot Dot Dash LongDash DashDot LongDashDot LongDashDotDot

  • $.PlotMultTitle
    Permet de modifier le titre et le sous-titre du graphique. Conception des paramètres : cfgName, title, chartTitle
    cfgName : nom de l'objet de configuration du graphique.
    titre : Sous-titre.
    chartTitle : Titre du graphique.

  • $.PlotMultFlag
    Dessinez une petite icône de drapeau, conception des paramètres : cfgName, seriesName, ts, texte, titre, forme, couleur, onSeriesName
    cfgName : nom de l'objet de configuration du graphique.
    seriesName : nom de la série de données.
    ts : horodatage
    texte : Le texte dans la petite icône.
    titre : Le titre de la petite icône.
    forme : La forme de la petite icône.
    couleur : couleur de la petite icône.
    onSeriesName : En fonction de la série de données affichée, la valeur est l'ID de la série de données.

  • $.GetArrCfg
    Renvoie un tableau d'objets de configuration de graphique.

Conception de la fonction de test

Pour faciliter la compréhension, j'ai écrit des commentaires directement sur les fonctions de test pour expliquer ce que fait chaque appel de fonction.

// 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++ } }

Exécution de tests

img

img

Vous pouvez voir qu’avec une seule ligne d’appels de fonction, vous pouvez facilement dessiner un graphique et afficher plusieurs graphiques en même temps.

Code source complet de la stratégie

Configuration des paramètres :
img

Implémentation du code source de la bibliothèque 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) } }

Adresse de la stratégie : https://www.fmz.com/strategy/353264

Je ne fais ici que lancer quelques idées. Si vous êtes intéressé, vous pouvez continuer à ajouter des types de graphiques pris en charge et à effectuer des mises à niveau, comme dessiner des graphiques de profondeur de marché, des graphiques à barres, des graphiques à secteurs, 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)