Ensinar a projetar biblioteca de classe modelo para obter dados de linha K de comprimento especificado

Autora:Lydia., Criado: 2023-06-29 17:27:59, Atualizado: 2023-09-18 19:33:33

img

Ensinar a projetar biblioteca de classe modelo para obter dados de linha K de comprimento especificado

Para a elaboração de estratégias de tendência, é frequentemente necessário dispor de um número suficiente de barras de linha K para o cálculo dos indicadores.exchange.GetRecords()A função foi desenvolvida para fornecer uma interface de interfaces para a plataforma FMZ, que é um envoltório para a interface K-line da exchange.

A interface de linha K da API de contrato da Binance suporta paginação. Neste artigo, usaremos a interface de API de linha K da Binance como exemplo para ensinar como implementar a paginação e especificar o número de barras a serem recuperadas usando a biblioteca de modelos da plataforma FMZ.

Interface K-line da Binance

Dados da linha K

A hora de abertura de cada linha K noGET /dapi/v1/klinesO ponto final pode ser considerado como uma identificação única.

O peso do pedido depende do valor do parâmetro LIMIT.

img

Parâmetros:

img

Primeiro, precisamos nos referir à documentação da API do exchange para entender os parâmetros específicos da interface da linha K. Podemos ver que, ao chamar este ponto final da linha K, precisamos especificar o tipo, o período da linha K, o intervalo de dados (hora de início e final) e o número de páginas, etc.

Uma vez que nosso requisito de projeto é consultar um número específico de dados de linha K, por exemplo, para consultar a linha K de 1 hora, 5000 bares de dados de linha K de 1 hora do momento atual para o passado, é evidente que fazer uma única chamada de API para a troca não recuperará os dados desejados.

Para conseguir isso, podemos implementar a paginação e dividir a consulta em segmentos a partir do momento atual em direção a um momento histórico específico. Como sabemos o período desejado dos dados da linha K, podemos calcular facilmente o tempo de início e fim para cada segmento. Podemos então consultar cada segmento em sequência em direção ao momento histórico até recuperar barras suficientes. A abordagem parece simples, então vamos avançar e implementá-la!

Projeto versão JavaScript da consulta paginada modelo de dados históricos de linha K

Função de interface para modelos de projeto:$.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
 */

Projetar a função$.GetRecordsByLength, que é tipicamente usado na fase inicial da execução da estratégia para calcular indicadores com base em um longo período de dados de linha K. Uma vez que esta função é executada e dados suficientes são obtidos, apenas novos dados de linha K precisam ser atualizados. Não há necessidade de chamar esta função novamente para recuperar dados de linha K excessivamente longos, pois resultaria em chamadas desnecessárias de API.

Por conseguinte, é igualmente necessário conceber uma interface para as actualizações de dados subsequentes:$.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
 */

O próximo passo é implementar estas funções de interface.

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

No modelo, nós só implementamos suporte para o Binance contrato de futuros K-line interface, ou seja, ogetRecordsForFuturesBinanceEle também pode ser estendido para suportar interfaces K-line de outras exchanges de criptomoedas.

Sessão de teste

Como você pode ver, o código para implementar essas funcionalidades no modelo não é extenso, totalizando menos de 200 linhas. Depois de escrever o código do modelo, o teste é crucial e não deve ser negligenciado. Além disso, para recuperação de dados como este, é importante realizar testes completos.

Para testá-lo, você precisa copiar tanto o JavaScript Versão de Paginação Query K-Line Histórico Modelo de Dados e o Plot Library modelos para a sua biblioteca de estratégia (que pode ser encontrado noPraça da Estratégia) Em seguida, crie uma nova estratégia e selecione estes dois modelos.

img

img

A Plot Library é usada, porque precisamos desenhar os dados de linha K obtidos para observação.

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

Aqui, usamos a linhavar testPeriod = PERIOD_M5Então, podemos realizar um teste de gráfico nos dados de longa linha K devolvidos pelovar r = $.GetRecordsByLength(exchange, testPeriod, 8000) interface.

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

O próximo ensaio para os dados de linha K longa é:

    // 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. Verifique se há barras duplicadas nos dados da linha K.
  2. Verificar a coerência dos dados da linha K (se a diferença de marca de hora entre as barras adjacentes é igual).

Após passar por estas verificações, verificar se a interface utilizada para atualizar os dados da linha K,$.UpdateRecords(exchange, r, testPeriod), está a funcionar corretamente.

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

Este código produzirá continuamente dados de linha K no gráfico de estratégia durante a negociação ao vivo, permitindo-nos verificar se as atualizações e adições de dados de linha K estão funcionando corretamente.

img

img

Usando os dados diários da linha K, configuramos para recuperar 8000 barras (sabendo que não há dados de mercado disponíveis para 8000 dias atrás).

img

Visto que existem apenas 1309 linhas K diárias, compare os dados dos gráficos de câmbio:

img

img

Podem ver que os dados também coincidem.

Fim de ano

Endereço do modelo:Versão JavaScript do Modelo de Dados Históricos de Perguntas de Paginação K-LineEndereço do modelo:Plot Library

O modelo e o código de estratégia acima são apenas para uso de ensino e aprendizagem, por favor, otimize e modifique de acordo com as necessidades específicas da negociação ao vivo.


Relacionados

Mais.