
La creación de gráficos y diagramas se utiliza a menudo al escribir y diseñar estrategias. Para un único gráfico personalizado, podemos utilizarBiblioteca de dibujos lineales(Los estudiantes que no estén familiarizados con el concepto de biblioteca de plantillas en FMZ pueden consultar la documentación de la API de FMZ), lo cual es muy conveniente para operaciones de dibujo. Sin embargo, esta biblioteca de plantillas no puede satisfacer las necesidades de escenarios que requieren múltiples gráficos. Luego aprendemos las ideas de diseño de esta biblioteca de dibujo lineal y diseñamos una versión de múltiples gráficos de la biblioteca de dibujo lineal basada en esto.
Basándonos en el diseño de la función de exportación de la “Biblioteca de dibujo de líneas”, también diseñamos funciones de exportación similares para la biblioteca de dibujo de líneas de múltiples gráficos.
$.PlotMultRecords
Se utiliza para dibujar gráficos de líneas K, diseño de parámetros: cfgName, seriesName, registros, extensión.
cfgName: el nombre del objeto de configuración como un gráfico separado.
seriesName: El nombre de la serie de datos de la línea K que se va a trazar actualmente.
registros: los datos entrantes de la línea K.
extensión: Información de configuración del tamaño del gráfico, por ejemplo, pasar:{layout: 'single', col: 6, height: '600px'}, es decir, que el gráfico con el nombre del objeto de configuración cfgName se muestre por separado, con un ancho de 6 y una altura de 600 px.
$.PlotMultLine Se utiliza para dibujar líneas, diseño de parámetros: cfgName, seriesName, dot, ts, extensión cfgName: el nombre del objeto de configuración como un gráfico separado. seriesName: El nombre de la serie de datos que se dibujará. punto: El valor de la coordenada vertical del punto en la línea que se va a dibujar. ts: Marca de tiempo, es decir, el valor en el eje de tiempo x. extensión: Información de configuración para el tamaño del gráfico.
$.PlotMultHLine
Se utiliza para dibujar líneas horizontales, diseño de parámetros: cfgName, valor, etiqueta, color, estilo
cfgName: Nombre del objeto de configuración del gráfico.
valor: El valor de la coordenada vertical de la línea horizontal.
etiqueta: El texto que se muestra en la línea horizontal.
color: El color de la línea.
estilo: El estilo de la línea, por ejemplo:Solid ShortDash ShortDot ShortDashDot ShortDashDotDot Dot Dash LongDash DashDot LongDashDot LongDashDotDot。
$.PlotMultTitle Se utiliza para modificar el título y el subtítulo del gráfico. Diseño de parámetros: cfgName, title, chartTitle cfgName: Nombre del objeto de configuración del gráfico. Título: Subtítulo. chartTitle: Título del gráfico.
$.PlotMultFlag Dibuje un pequeño icono de bandera, diseño de parámetros: cfgName, seriesName, ts, texto, título, forma, color, onSeriesName cfgName: Nombre del objeto de configuración del gráfico. seriesName: nombre de la serie de datos. ts : marca de tiempo texto: El texto en el icono pequeño. título: El título del pequeño icono. forma: La forma del icono pequeño. color: color de icono pequeño. onSeriesName: según la serie de datos que se muestra, el valor es el ID de la serie de datos.
$.GetArrCfg Devuelve una matriz de objetos de configuración de gráficos.
Para que sea más fácil de entender, escribí comentarios directamente en las funciones de prueba para explicar qué hace cada llamada de función.
// 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++
}
}


Puede ver que con solo una línea de llamadas de función, puede dibujar fácilmente un gráfico y puede mostrar varios gráficos al mismo tiempo.
Configuración de parámetros:

Implementación del código fuente de la biblioteca de clases:
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)
}
}
Dirección de estrategia: https://www.fmz.com/strategy/353264
Aquí solo estoy lanzando algunas ideas. Si estás interesado, puedes seguir agregando tipos de gráficos compatibles y continuar con las actualizaciones, como dibujar gráficos de profundidad de mercado, gráficos de barras, gráficos circulares, etc.