Apprendre à concevoir une bibliothèque de classes de modèle pour obtenir des données de ligne K de longueur spécifiée

Auteur:Je ne sais pas., Créé à: 2023-06-29 17:27:59, Mis à jour à: 2023-09-18 19:33:33

img

Apprendre à concevoir une bibliothèque de classes de modèle pour obtenir des données de ligne K de longueur spécifiée

Lors de la conception des stratégies de tendance, il est souvent nécessaire d'avoir un nombre suffisant de barres de ligne K pour le calcul des indicateurs.exchange.GetRecords()Dans la conception initiale des API d'échange de crypto-monnaie, il n'y avait pas de support pour la pagination dans l'interface K-line, et l'interface K-line de l'échange ne fournissait qu'une quantité limitée de données.

L'interface K-line de l'API de contrat de Binance prend en charge la pagination. Dans cet article, nous utiliserons l'interface API K-line de Binance comme exemple pour vous apprendre à implémenter la pagination et à spécifier le nombre de barres à récupérer à l'aide de la bibliothèque de modèles de plateforme FMZ.

Interface K-line de Binance

Données de ligne K

L'heure d'ouverture de chaque ligne K dans leGET /dapi/v1/klinesle point final peut être considéré comme un identifiant unique.

Le poids de la demande dépend de la valeur du paramètre LIMIT.

img

Paramètres:

img

Tout d'abord, nous devons nous référer à la documentation de l'API de l'échange pour comprendre les paramètres spécifiques de l'interface K-line. Nous pouvons voir que lors de l'appel de ce point final K-line, nous devons spécifier le type, la période K-line, la plage de données (heure de début et de fin) et le nombre de pages, etc.

Étant donné que notre exigence de conception consiste à interroger un nombre spécifique de données de ligne K, par exemple, à interroger la ligne K d'une heure, 5000 barres de données de ligne K d'une heure du moment actuel vers le passé, il est évident que faire un seul appel API à l'échange ne récupérera pas les données souhaitées.

Pour ce faire, nous pouvons implémenter la pagination et diviser la requête en segments à partir du moment actuel vers un moment historique spécifique. Puisque nous connaissons la période des données de la ligne K souhaitée, nous pouvons facilement calculer le début et la fin de chaque segment. Nous pouvons ensuite interroger chaque segment en séquence vers le moment historique jusqu'à ce que nous récupérions suffisamment de barres. L'approche semble simple, alors allons-y et implémentons-la!

Conception Version JavaScript de la requête paginée modèle de données historiques en ligne K

Fonction d'interface pour les modèles de conception:$.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
 */

Concevoir la fonction$.GetRecordsByLength, qui est généralement utilisé dans la phase initiale de l'exécution de la stratégie pour calculer des indicateurs basés sur une longue période de données de ligne K. Une fois que cette fonction est exécutée et que suffisamment de données sont obtenues, seules de nouvelles données de ligne K doivent être mises à jour. Il n'est pas nécessaire d'appeler cette fonction à nouveau pour récupérer des données de ligne K excessivement longues, car cela entraînerait des appels API inutiles.

Par conséquent, il est également nécessaire de concevoir une interface pour les mises à jour ultérieures des données:$.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
 */

L'étape suivante consiste à mettre en œuvre ces fonctions d'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
}

Dans le modèle, nous avons seulement mis en œuvre le support pour le Binance contrat à terme K-line interface, c'est à dire, legetRecordsForFuturesBinanceIl peut également être étendu pour prendre en charge les interfaces K-line d'autres échanges de crypto-monnaie.

Séance de test

Comme vous pouvez le voir, le code pour la mise en œuvre de ces fonctionnalités dans le modèle n'est pas étendu, totalisant moins de 200 lignes. Après avoir écrit le code du modèle, le test est crucial et ne doit pas être négligé.

Pour le tester, vous devez copier à la fois le Version JavaScript de la page de requête K-Line Template de données historiques et le Plot Library modèles à votre bibliothèque de stratégie (qui peut être trouvé dans lePlace de la stratégieEnsuite, créer une nouvelle stratégie et sélectionner ces deux modèles.

img

img

La Plot Library est utilisée, car nous devons dessiner les données de ligne K obtenues pour l'observation.

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

Ici, nous utilisons la lignevar testPeriod = PERIOD_M5Ensuite, nous pouvons effectuer un test de tracé sur les données longues de la ligne K renvoyées par levar r = $.GetRecordsByLength(exchange, testPeriod, 8000) interface.

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

Le test suivant pour les données de longue ligne K est:

    // 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. Vérifiez s'il y a des barres en double dans les données de la ligne K.
  2. Vérifiez la cohérence des données de ligne K (si la différence d'horodatage entre les barres adjacentes est égale).

Après avoir passé ces contrôles, vérifiez si l'interface utilisée pour mettre à jour les données de ligne K,$.UpdateRecords(exchange, r, testPeriod), fonctionne correctement.

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

Ce code fournira continuellement des données de ligne K sur le graphique de stratégie pendant la négociation en direct, ce qui nous permet de vérifier si les mises à jour et les ajouts de données de ligne K fonctionnent correctement.

img

img

En utilisant les données quotidiennes de la ligne K, nous l'avons réglé pour récupérer 8000 barres (sachant qu'il n'y a pas de données de marché disponibles pour 8000 jours auparavant).

img

Étant donné qu'il n'y a que 1309 lignes K quotidiennes, comparez les données des graphiques de change:

img

img

Vous pouvez voir que les données correspondent aussi.

Résultats

Adresse du modèle:Version JavaScript du modèle de données historiques de requête de pagination K-LineAdresse du modèle: Bibliothèque de parcelles

Le modèle et le code de stratégie ci-dessus sont uniquement destinés à l'enseignement et à l'apprentissage, veuillez optimiser et modifier en fonction des besoins spécifiques du trading en direct.


Relationnée

Plus de