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.
Données de ligne K
L'heure d'ouverture de chaque ligne K dans leGET /dapi/v1/klines
le point final peut être considéré comme un identifiant unique.
Le poids de la demande dépend de la valeur du paramètre
Paramètres:
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!
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, legetRecordsForFuturesBinance
Il peut également être étendu pour prendre en charge les interfaces K-line d'autres échanges de crypto-monnaie.
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
La
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_M5
Ensuite, 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")
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.
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).
Étant donné qu'il n'y a que 1309 lignes K quotidiennes, comparez les données des graphiques de change:
Vous pouvez voir que les données correspondent aussi.
Adresse du modèle:
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.