Apprenez à concevoir une bibliothèque de modèles pour obtenir des données de ligne de longueur K spécifiées

Auteur:Le petit rêve, Créé: 2023-06-27 13:37:01, Mis à jour: 2023-09-18 19:34:23

img

Apprenez à concevoir une bibliothèque de modèles pour obtenir des données de ligne de longueur K spécifiées

Lors de la conception de certaines stratégies de tendance, les indicateurs de calcul nécessitent souvent un nombre suffisant de K-Line Bar.exchange.GetRecords()C'est la quantité de données que la fonction donne.exchange.GetRecords()L'interface K-Line n'était pas une requête partagée dans la conception de l'API de l'échange de crypto-monnaie. Les interfaces K-Line des échanges ne fournissaient qu'une quantité limitée de données.

Si l'interface K de l'API des contrats de Binance prend en charge les requêtes de page partagée, alors cet article vous apprend à réaliser une requête partagée en utilisant l'interface K de l'API de Binance et vous permet de spécifier le nombre de barres d'accès à la bibliothèque de modèles de la plate-forme FMZ.

L'interface K de Binance

img

Tout d'abord, vous devez consulter la documentation de l'API de l'échange pour voir les paramètres spécifiques de l'interface. Nous pouvons voir que l'interface K-line nécessite une spécification de la variété, de la période de la ligne K, de la portée des données (heure de début et de fin), du nombre de pages, etc.

Puisque nous avons conçu la requête pour interroger une quantité spécifiée de données de ligne K, par exemple, pour interroger une ligne K de 1 heure, de la direction du moment actuel à la direction du moment passé, le nombre de barres est de 5000. Ainsi, vous n'avez manifestement pas obtenu les données que vous vouliez en n'appelant qu'une seule fois l'interface API de l'échange.

Ensuite, nous partageons les pages et traitons les segments de l'instant présent à un moment donné de l'histoire. Nous savons que le cycle de données de ligne K dont nous avons besoin est bon pour calculer le début et la fin de chaque segment.

Concevoir un modèle de données d'historique en ligne K pour les pages de requêtes JavaScript

Les fonctions d'interface des modèles de conception:$.GetRecordsByLength(e, period, length)

/**
 * desc: $.GetRecordsByLength 是该模板类库的接口函数,该函数用于获取指定K线长度的K线数据
 * @param {Object} e - 交易所对象
 * @param {Int} period - K线周期,秒数为单位
 * @param {Int} length - 指定获取的K线数据的长度,具体和交易所接口限制有关
 * @returns {Array<Object>} - K线数据
 */

La conception$.GetRecordsByLengthCette fonction nécessite généralement une longue ligne K pour calculer les indicateurs au début de l'exécution de la stratégie. Une fois que cette fonction est exécutée, elle obtient suffisamment de données pour mettre à jour les données de la nouvelle ligne K. Il n'est plus nécessaire d'appeler cette fonction pour obtenir des données de ligne K très longues, ce qui entraîne des appels d'interface inutiles.

Il est donc nécessaire de concevoir une interface pour les mises à jour ultérieures:$.UpdataRecords(e, records, period)

/**
 * desc: $.UpdataRecords 是该模板类库的接口函数,该函数用于更新K线数据
 * @param {Object} e - 交易所对象
 * @param {Array<Object>} records - 需要更新的K线数据源
 * @param {Int} period - K线周期,需要和records参数传入的K线数据周期一致
 * @returns {Bool}  - 是否更新成功
 */

La prochaine étape est d'implémenter ces fonctions d'interface.

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

Dans le modèle, nous n'avons mis en œuvre que la prise en charge de l'interface K-Line du contrat Binance, qui est la connexion de base de la transaction.getRecordsForFuturesBinanceLa fonctionnalité peut également être étendue aux interfaces K-Line qui prennent en charge d'autres échanges de crypto-monnaie.

Les étapes de test

Comme vous pouvez le voir, il n'y a pas beaucoup de code pour réaliser ces fonctionnalités dans le modèle, probablement moins de 200 lignes. Une fois le code du modèle écrit, il y a absolument beaucoup de tests. Et pour obtenir ces données, nous avons besoin de tests aussi rigoureux que possible.

Le test a nécessité de copier le modèle "K-Line History Template pour la page de requête JavaScript" et le modèle "Draw Line Library" dans sa propre bibliothèque de stratégies (en anglais seulement).La place des stratégiesNous avons ensuite créé une nouvelle stratégie pour sélectionner les deux modèles:

img

img

img

L'utilisation de la bibliothèque de lignes de dessin est nécessaire car nous avons besoin de dessiner les données de lignes K obtenues pour les observer.

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

Nous utilisons icivar testPeriod = PERIOD_M5Cette phrase, définissez un cycle de ligne K de 5 minutes, spécifiez l'obtention de 8 000 bar.var r = $.GetRecordsByLength(exchange, testPeriod, 8000)Les données de longue ligne K renvoyées par l'interface sont testées en diagramme:

    // 使用画图测试,方便观察
    $.PlotRecords(r, "k")

Les données de cette longue ligne K sont ensuite analysées:

    // 检测数据
    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("检测通过")

1, pour vérifier s'il y a une répétition dans la ligne KBar. 2, vérifier la cohérence de la ligne KBar (s'il y a une différence de décalage horaire égale entre les lignes voisines de Bar)

Une fois ces contrôles passés, l'interface utilisée pour mettre à jour la ligne K est vérifiée.$.UpdataRecords(exchange, r, testPeriod)Est-ce que cela est normal:

    // 更新数据
    while (true) {
        $.UpdataRecords(exchange, r, testPeriod)
        LogStatus(_D(), "r.length:", r.length)
        $.PlotRecords(r, "k")
        Sleep(5000)
    }

Ce code continue de produire des lignes K sur le graphique stratégique pendant que le disque fonctionne, afin de vérifier si les données de la ligne KBar sont mises à jour ou ajoutées correctement.

img

img

En utilisant la ligne K pour accéder à la journée, vous pouvez configurer l'accès à 8000 racines (sachant clairement qu'il n'y a pas de données sur le marché avant 8000 jours) pour tester la violence comme suit:

img

Les chiffres de l'échange, qui ne sont que de 1309 lignes, sont comparés à ceux du graphique:

img

img

Vous pouvez voir que les données correspondent également.

Résultats

L'adresse du modèle:"Page partagé JavaScript pour consulter le modèle d'historique de ligne K"L'adresse du modèle:"La bibliothèque des lignes de dessin"

Les modèles ci-dessus, le code de stratégie sont uniquement à des fins pédagogiques, d'apprentissage et d'utilisation.


Plus de