
Al diseñar algunas estrategias de tendencia, el cálculo de indicadores a menudo requiere una cantidad suficiente de barras de línea K. Depende de la API de la plataforma FMZ:exchange.GetRecords()La cantidad de datos proporcionados por la función, yexchange.GetRecords()Es la encapsulación de la interfaz de intercambio K-line. En el diseño inicial de las interfaces API de intercambio de criptomonedas, no había consulta de paginación y la interfaz de línea K del intercambio solo proporcionaba una cantidad limitada de datos, por lo que no se pudieron satisfacer las necesidades de algunos desarrolladores de realizar cálculos de indicadores con parámetros más grandes.
La interfaz K-line de la API de contratos de Binance admite consultas paginadas. Este artículo toma la interfaz de la API K-line de Binance como ejemplo para enseñarle cómo implementar una consulta paginada y especificar la biblioteca de plantillas de la plataforma FMZ para obtener la cantidad de barras.

Primero, debes leer la documentación de la API de intercambio para ver los parámetros específicos de la interfaz. Podemos ver que al llamar a esta interfaz de K-line, es necesario especificar el producto, el período de K-line, el rango de datos (hora de inicio y fin), el número de páginas, etc.
Dado que nuestro requisito de diseño es consultar una cantidad específica de datos de la línea K, por ejemplo, para consultar la línea K de 1 hora, pasar del tiempo actual al tiempo pasado, el número es 5000 barras. De esta manera, obviamente no puedes obtener los datos que deseas llamando a la consulta de la interfaz API de intercambio solo una vez.
Luego realizaremos la consulta en páginas, procesando en segmentos desde el momento actual hasta un momento determinado en la historia. Siempre que conozcamos el período de los datos de la línea K requeridos, es fácil calcular el tiempo de inicio y finalización de cada segmento. Simplemente busque en la dirección de los momentos históricos en secuencia hasta que encuentre suficientes barras. La idea suena sencilla, ¿verdad? ¡Vamos a implementarla!
Función de interfaz de plantilla de diseño:$.GetRecordsByLength(e, period, length)。
/**
* desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
* @param {Object} e - 交易所对象
* @param {Int} period - K线周期,秒数为单位
* @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
* @returns {Array<Object>} - K线数据
*/
diseño$.GetRecordsByLengthEsta función generalmente se utiliza en la etapa inicial de la operación de la estrategia cuando se necesita una línea K larga para calcular el indicador. Después de ejecutar esta función, obtiene datos lo suficientemente largos y luego solo necesita actualizar los nuevos datos de la línea K. No es necesario llamar a esta función para obtener datos de línea K superlargos, lo que provocará llamadas de interfaz innecesarias.
Por lo tanto, también es necesario diseñar una interfaz para actualizaciones de datos posteriores:$.UpdataRecords(e, records, period)。
/**
* desc: $.UpdataRecords 是该模板类库的接口函数,该函数用于更新K线数据
* @param {Object} e - 交易所对象
* @param {Array<Object>} records - 需要更新的K线数据源
* @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
* @returns {Bool} - 是否更新成功
*/
El siguiente paso es implementar estas funciones de interfaz.
/**
* desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
* @param {Object} e - 交易所对象
* @param {Int} period - K线周期,秒数为单位
* @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
* @returns {Array<Object>} - K线数据
*/
$.GetRecordsByLength = function(e, period, length) {
if (!Number.isInteger(period) || !Number.isInteger(length)) {
throw "params error!"
}
var exchangeName = e.GetName()
if (exchangeName == "Futures_Binance") {
return getRecordsForFuturesBinance(e, period, length)
} else {
throw "not support!"
}
}
/**
* desc: getRecordsForFuturesBinance 币安期货交易所获取K线数据函数的具体实现
* @param {Object} e - 交易所对象
* @param {Int} period - K线周期,秒数为单位
* @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
* @returns {Array<Object>} - K线数据
*/
function getRecordsForFuturesBinance(e, period, length) {
var contractType = e.GetContractType()
var currency = e.GetCurrency()
var strPeriod = String(period)
var symbols = currency.split("_")
var baseCurrency = ""
var quoteCurrency = ""
if (symbols.length == 2) {
baseCurrency = symbols[0]
quoteCurrency = symbols[1]
} else {
throw "currency error!"
}
var realCt = e.SetContractType(contractType)["instrument"]
if (!realCt) {
throw "realCt error"
}
// m -> 分钟; h -> 小时; d -> 天; w -> 周; M -> 月
var periodMap = {}
periodMap[(60).toString()] = "1m"
periodMap[(60 * 3).toString()] = "3m"
periodMap[(60 * 5).toString()] = "5m"
periodMap[(60 * 15).toString()] = "15m"
periodMap[(60 * 30).toString()] = "30m"
periodMap[(60 * 60).toString()] = "1h"
periodMap[(60 * 60 * 2).toString()] = "2h"
periodMap[(60 * 60 * 4).toString()] = "4h"
periodMap[(60 * 60 * 6).toString()] = "6h"
periodMap[(60 * 60 * 8).toString()] = "8h"
periodMap[(60 * 60 * 12).toString()] = "12h"
periodMap[(60 * 60 * 24).toString()] = "1d"
periodMap[(60 * 60 * 24 * 3).toString()] = "3d"
periodMap[(60 * 60 * 24 * 7).toString()] = "1w"
periodMap[(60 * 60 * 24 * 30).toString()] = "1M"
var records = []
var url = ""
if (quoteCurrency == "USDT") {
// GET https://fapi.binance.com /fapi/v1/klines symbol , interval , startTime , endTime , limit
// limit 最大值:1500
url = "https://fapi.binance.com/fapi/v1/klines"
} else if (quoteCurrency == "USD") {
// GET https://dapi.binance.com /dapi/v1/klines symbol , interval , startTime , endTime , limit
// startTime 与 endTime 之间最多只可以相差200天
// limit 最大值:1500
url = "https://dapi.binance.com/dapi/v1/klines"
} else {
throw "not support!"
}
var maxLimit = 1500
var interval = periodMap[strPeriod]
if (typeof(interval) !== "string") {
throw "period error!"
}
var symbol = realCt
var currentTS = new Date().getTime()
while (true) {
// 计算limit
var limit = Math.min(maxLimit, length - records.length)
var barPeriodMillis = period * 1000
var rangeMillis = barPeriodMillis * limit
var twoHundredDaysMillis = 200 * 60 * 60 * 24 * 1000
if (rangeMillis > twoHundredDaysMillis) {
limit = Math.floor(twoHundredDaysMillis / barPeriodMillis)
rangeMillis = barPeriodMillis * limit
}
var query = `symbol=${symbol}&interval=${interval}&endTime=${currentTS}&limit=${limit}`
var retHttpQuery = HttpQuery(url + "?" + query)
var ret = null
try {
ret = JSON.parse(retHttpQuery)
} catch(e) {
Log(e)
}
if (!ret || !Array.isArray(ret)) {
return null
}
// 超出交易所可查询范围,查询不到数据时
if (ret.length == 0 || currentTS <= 0) {
break
}
for (var i = ret.length - 1; i >= 0; i--) {
var ele = ret[i]
var bar = {
Time : parseInt(ele[0]),
Open : parseFloat(ele[1]),
High : parseFloat(ele[2]),
Low : parseFloat(ele[3]),
Close : parseFloat(ele[4]),
Volume : parseFloat(ele[5])
}
records.unshift(bar)
}
if (records.length >= length) {
break
}
currentTS -= rangeMillis
Sleep(1000)
}
return records
}
/**
* desc: $.UpdataRecords 是该模板类库的接口函数,该函数用于更新K线数据
* @param {Object} e - 交易所对象
* @param {Array<Object>} records - 需要更新的K线数据源
* @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
* @returns {Bool} - 是否更新成功
*/
$.UpdataRecords = function(e, records, period) {
var r = e.GetRecords(period)
if (!r) {
return false
}
for (var i = 0; i < r.length; i++) {
if (r[i].Time > records[records.length - 1].Time) {
// 添加新Bar
records.push(r[i])
// 更新上一个Bar
if (records.length - 2 >= 0 && i - 1 >= 0 && records[records.length - 2].Time == r[i - 1].Time) {
records[records.length - 2] = r[i - 1]
}
} else if (r[i].Time == records[records.length - 1].Time) {
// 更新Bar
records[records.length - 1] = r[i]
}
}
return true
}
En la plantilla, solo implementamos el soporte de la interfaz K-line del contrato de Binance, es decir,getRecordsForFuturesBinanceFunción que también se puede ampliar para admitir interfaces de línea K de otros intercambios de criptomonedas.
Puedes ver que no hay mucho código en la plantilla para implementar estas funciones, probablemente menos de 200 líneas. Una vez escrito el código de la plantilla, es absolutamente necesario realizar pruebas. Y para esa adquisición de datos, también necesitamos probarlos lo más rigurosamente posible.
La prueba requiere copiar esta “versión JavaScript de la plantilla de datos históricos de K-line de consulta de página” y la plantilla de “biblioteca de dibujo de líneas” a su propia biblioteca de estrategias (enPlaza de la estrategiase puede buscar en ). Luego creamos una nueva estrategia y comprobamos estas dos plantillas:



Se utiliza la “biblioteca de dibujo de líneas” porque necesitamos dibujar los datos de la línea K obtenidos para la observación.
function main() {
LogReset(1)
var testPeriod = PERIOD_M5
Log("当前测试的交易所:", exchange.GetName())
// 如果是期货则需要设置合约
exchange.SetContractType("swap")
// 使用$.GetRecordsByLength获取指定长度的K线数据
var r = $.GetRecordsByLength(exchange, testPeriod, 8000)
Log(r)
// 使用画图测试,方便观察
$.PlotRecords(r, "k")
// 检测数据
var diffTime = r[1].Time - r[0].Time
Log("diffTime:", diffTime, " ms")
for (var i = 0; i < r.length; i++) {
for (var j = 0; j < r.length; j++) {
// 检查重复Bar
if (i != j && r[i].Time == r[j].Time) {
Log(r[i].Time, i, r[j].Time, j)
throw "有重复Bar"
}
}
// 检查Bar连续性
if (i < r.length - 1) {
if (r[i + 1].Time - r[i].Time != diffTime) {
Log("i:", i, ", diff:", r[i + 1].Time - r[i].Time, ", r[i].Time:", r[i].Time, ", r[i + 1].Time:", r[i + 1].Time)
throw "Bar不连续"
}
}
}
Log("检测通过")
Log("$.GetRecordsByLength函数返回的数据长度:", r.length)
// 更新数据
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
}
Aquí usamosvar testPeriod = PERIOD_M5Esta sentencia establece el período de la línea K en 5 minutos y especifica que se deben obtener 8000 barras. Entonces paravar r = $.GetRecordsByLength(exchange, testPeriod, 8000)La interfaz devuelve datos de línea K largos para realizar pruebas de dibujo:
// 使用画图测试,方便观察
$.PlotRecords(r, "k")
A continuación, probaremos estos datos de la línea K muy larga:
// 检测数据
var diffTime = r[1].Time - r[0].Time
Log("diffTime:", diffTime, " ms")
for (var i = 0; i < r.length; i++) {
for (var j = 0; j < r.length; j++) {
// 检查重复Bar
if (i != j && r[i].Time == r[j].Time) {
Log(r[i].Time, i, r[j].Time, j)
throw "有重复Bar"
}
}
// 检查Bar连续性
if (i < r.length - 1) {
if (r[i + 1].Time - r[i].Time != diffTime) {
Log("i:", i, ", diff:", r[i + 1].Time - r[i].Time, ", r[i].Time:", r[i].Time, ", r[i + 1].Time:", r[i + 1].Time)
throw "Bar不连续"
}
}
}
Log("检测通过")
Después de pasar estas comprobaciones, verifique la interfaz utilizada para actualizar la línea K$.UpdataRecords(exchange, r, testPeriod)Normal:
// 更新数据
while (true) {
$.UpdataRecords(exchange, r, testPeriod)
LogStatus(_D(), "r.length:", r.length)
$.PlotRecords(r, "k")
Sleep(5000)
}
Este código generará continuamente la línea K en el gráfico de estrategia cuando se esté ejecutando en operaciones reales, de modo que podamos verificar si los datos de la barra de la línea K se actualizan y agregan normalmente.


Utilice la función de adquisición de línea K diaria y configure la adquisición en 8000 (sabiendo que no hay datos de mercado antes de 8000 días) y realice una prueba de fuerza bruta como esta:

Solo hay 1309 líneas diarias, en comparación con los datos del gráfico de intercambio:


Se puede ver que los datos son consistentes.
Dirección de la plantilla:「Versión JavaScript de la plantilla de datos históricos de K-line de consulta paginada」 Dirección de la plantilla:Biblioteca de dibujos lineales
Las plantillas y los códigos de estrategia anteriores son solo para fines de enseñanza y aprendizaje. Optimícelos y modifíquelos según sus necesidades reales.