Enseñarle a diseñar una biblioteca de clases de plantillas para obtener datos de línea K de longitud especificada

El autor:- ¿ Por qué?, Creado: 2023-06-29 17:27:59, Actualizado: 2023-09-18 19:33:33

img

Enseñarle a diseñar una biblioteca de clases de plantillas para obtener datos de línea K de longitud especificada

Cuando se diseñan estrategias de tendencia, a menudo es necesario disponer de un número suficiente de barras de la línea K para el cálculo de los indicadores.exchange.GetRecords()En el diseño inicial de las API de intercambio de criptomonedas, no había soporte para la paginación en la interfaz de línea K, y la interfaz de línea K de la bolsa solo proporcionó una cantidad limitada de datos.

La interfaz de línea K de la API de contratos de Binance admite la paginación. En este artículo, utilizaremos la interfaz de API de línea K de Binance como ejemplo para enseñarle cómo implementar la paginación y especificar el número de barras para recuperar utilizando la biblioteca de plantillas de la plataforma FMZ.

Interfaz de línea K de Binance

Datos de la línea K

La hora de apertura de cada línea K en elGET /dapi/v1/klinesel punto final puede considerarse como un identificador único.

El peso de la solicitud depende del valor del parámetro LIMIT.

img

Parámetros:

img

En primer lugar, necesitamos consultar la documentación de la API del exchange para entender los parámetros específicos de la interfaz de línea K. Podemos ver que al llamar a este punto final de línea K, necesitamos especificar el tipo, el período de línea K, el rango de datos (tiempo de inicio y final) y el número de páginas, etc.

Dado que nuestro requisito de diseño es consultar un número específico de datos de línea K, por ejemplo, consultar la línea K de 1 hora, 5000 bares de datos de línea K de 1 hora desde el momento actual hacia el pasado, es evidente que hacer una sola llamada de API al intercambio no recuperará los datos deseados.

Para lograr esto, podemos implementar la paginación y dividir la consulta en segmentos desde el momento actual hacia un momento histórico específico. Dado que conocemos el período de datos de la línea K deseado, podemos calcular fácilmente el tiempo de inicio y final para cada segmento. Luego podemos consultar cada segmento en secuencia hacia el momento histórico hasta que recuperemos suficientes barras.

Diseño Versión JavaScript de la plantilla de datos históricos de línea K de la consulta paginada

Función de interfaz para plantillas de diseño:$.GetRecordsByLength(e, period, length).

/**
 * desc: $.GetRecordsByLength is the interface function of this template library, this function is used to get the K-line data of the specified K-line length
 * @param {Object} e - exchange object
 * @param {Int} period - K-line period, in seconds
 * @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
 * @returns {Array<Object>} - K-line data
 */

Diseñar la función$.GetRecordsByLength, que se utiliza típicamente en la etapa inicial de la ejecución de la estrategia para calcular indicadores basados en un largo período de datos de línea K. Una vez que esta función se ejecuta y se obtienen datos suficientes, solo se necesitan actualizar nuevos datos de línea K. No es necesario llamar a esta función nuevamente para recuperar datos de línea K excesivamente largos, ya que daría lugar a llamadas innecesarias de API.

Por lo tanto, también es necesario diseñar una interfaz para las actualizaciones de datos posteriores:$.UpdataRecords(e, records, period).

/**
 * desc: $.UpdataRecords is the interface function of this template library, this function is used to update the K-line data.
 * @param {Object} e - exchange object
 * @param {Array<Object>} records - K-line data sources that need to be updated
 * @param {Int} period - K-line period, needs to be the same as the K-line data period passed in the records parameter
 * @returns {Bool}  - Whether the update was successful
 */

El siguiente paso es implementar estas funciones de interfaz.

/**
 * desc: $.GetRecordsByLength is the interface function of this template library, this function is used to get the K-line data of the specified K-line length
 * @param {Object} e - exchange object
 * @param {Int} period - K-line period, in seconds
 * @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
 * @returns {Array<Object>} - K-line data
 */
$.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, the specific implementation of the function to get K-line data for Binance Futures Exchange
 * @param {Object} e - exchange object
 * @param {Int} period - K-line period, in seconds
 * @param {Int} length - Specify the length of the acquired K-line data, which is related to the exchange interface limits
 * @returns {Array<Object>} - K-line data
 */
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 -> minute; h -> hour; d -> day; w -> week; M -> month
    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 maximum value: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
        // The difference between startTime and endTime can be up to 200 days.
        // limit maximum value: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) {
        // Calculate 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
        }

        // When the data cannot be searched because it is beyond the searchable range of the exchange
        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 is the interface function of this template library, this function is used to update the K-line data.
 * @param {Object} e - exchange object
 * @param {Array<Object>} records - K-line data sources that need to be updated
 * @param {Int} period - K-line period, needs to be the same as the K-line data period passed in the records parameter
 * @returns {Bool}  - Whether the update was successful
 */
$.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) {
            // Add a new Bar
            records.push(r[i])
            // Update the previous 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) {
            // Update Bar
            records[records.length - 1] = r[i]
        }
    }
    return true
}

En la plantilla, sólo hemos implementado soporte para el contrato de futuros Binance interfaz K-line, es decir, elgetRecordsForFuturesBinanceTambién se puede extender para soportar interfaces K-line de otros intercambios de criptomonedas.

Sesión de prueba

Como puede ver, el código para implementar estas funcionalidades en la plantilla no es extenso, con un total de menos de 200 líneas. Después de escribir el código de la plantilla, la prueba es crucial y no debe pasarse por alto. Además, para la recuperación de datos como esta, es importante realizar pruebas exhaustivas.

Para probarlo, debe copiar tanto la Versión JavaScript de la Plantilla de Datos Históricos K-Line de Preguntas de Paginación como las plantillas Plot Library a su biblioteca de estrategias (que se puede encontrar en elPlaza EstrategiaLuego, cree una nueva estrategia y seleccione estas dos plantillas.

img

img

La Plot Library se utiliza, porque necesitamos dibujar los datos de la línea K obtenidos para la observación.

function main() {
	LogReset(1)
	var testPeriod = PERIOD_M5
    Log("Current exchanges tested:", exchange.GetName())

    // If futures, you need to set up a contract
    exchange.SetContractType("swap")

    // Get K-line data of specified length using $.GetRecordsByLength
    var r = $.GetRecordsByLength(exchange, testPeriod, 8000)
    Log(r)

    // Use the Plot test for easy observation
    $.PlotRecords(r, "k")

    // Test data
    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++) {
            // Check the repeat bar
            if (i != j && r[i].Time == r[j].Time) {
                Log(r[i].Time, i, r[j].Time, j)
                throw "With duplicate Bar"
            }
        }
        
        // Check Bar continuity
        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 discontinuity"
            }            
        }
    }
    Log("Test passed")

    Log("The length of the data returned by the $.GetRecordsByLength function:", r.length)

    // Update data
    while (true) {
        $.UpdataRecords(exchange, r, testPeriod)
        LogStatus(_D(), "r.length:", r.length)
        $.PlotRecords(r, "k")
        Sleep(5000)
    }
}

Aquí, usamos la líneavar testPeriod = PERIOD_M5Entonces podemos realizar una prueba de gráficos en los datos de línea K largos devueltos por elvar r = $.GetRecordsByLength(exchange, testPeriod, 8000) interface.

    // Use the plot test for easy observation
    $.PlotRecords(r, "k")

El siguiente ensayo para los datos de la línea K larga es:

    // Test data
    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++) {
            // Check the repeat Bar
            if (i != j && r[i].Time == r[j].Time) {
                Log(r[i].Time, i, r[j].Time, j)
                throw "With duplicate Bar"
            }
        }
        
        // Check Bar continuity
        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 discontinuity"
            }            
        }
    }
    Log("Test passed")
  1. Compruebe si hay barras duplicadas en los datos de la línea K.
  2. Compruebe la coherencia de los datos de la línea K (si la diferencia de marca horaria entre las barras adyacentes es igual).

Después de pasar estos controles, comprobar si la interfaz utilizada para actualizar los datos de la línea K,$.UpdateRecords(exchange, r, testPeriod), funciona correctamente.

    // Update data
    while (true) {
        $.UpdataRecords(exchange, r, testPeriod)
        LogStatus(_D(), "r.length:", r.length)
        $.PlotRecords(r, "k")
        Sleep(5000)
    }

Este código emitirá continuamente datos de línea K en el gráfico de estrategia durante el comercio en vivo, lo que nos permite verificar si las actualizaciones y adiciones de datos de línea K funcionan correctamente.

img

img

Utilizando los datos diarios de la línea K, lo configuramos para recuperar 8000 barras (sabiendo que no hay datos de mercado disponibles para 8000 días atrás).

img

Visto que sólo hay 1309 líneas K diarias, comparar los datos en los gráficos de cambio:

img

img

Se puede ver que los datos también coinciden.

Enlace a la sección

Dirección del modelo:Versión JavaScript de la consulta de paginación K-Line Template de datos históricosDirección del modelo:Biblioteca de tramas

La plantilla y el código de estrategia anteriores son solo para uso de enseñanza y aprendizaje, por favor optimice y modifique de acuerdo con las necesidades específicas del comercio en vivo.


Relacionados

Más.