
Lors de la rédaction de stratégies de trading programmatique, utilisant des données K-line, il est souvent nécessaire d’utiliser des données K-line sur une période non standard, telles que des données K-line sur une période de 12 minutes, des données K-line sur une période de 4 heures, généralement ce type de période non standard ne peut pas être obtenu directement. Alors comment répondre à de telles demandes ? La réponse est qu’il existe certainement un moyen. Les cycles non standard peuvent être obtenus en fusionnant et en synthétisant des données provenant de cycles plus petits. Vous pouvez imaginer que le prix le plus élevé dans plusieurs cycles est compté comme le prix le plus élevé après la synthèse, et le prix le plus bas est compté comme le prix le plus bas après la synthèse. L’ouverture le prix ne changera pas. , le premier prix d’ouverture des données de matière première de la ligne K synthétisée est utilisé, le prix de clôture correspond au dernier prix de clôture des données de matière première de la ligne K synthétisée, l’heure est l’heure du prix d’ouverture et le volume des transactions est calculé en additionnant les volumes de transactions des données sur les matières premières. Comme le montre l’image :
Nous prenons le marché des actifs blockchain BTC_USDT comme exemple et synthétisons 1 heure en 4 heures.




|Heure|Haut|Ouverture|Bas|Fermeture| |- |- |- |- |-| |2019.8.12 00:00|11447.07|11382.57|11367.2|11406.92| |2019.8.12 01:00|11420|11405.65|11366.6|11373.83| |2019.8.12 02:00|11419.24|11374.68|11365.51|11398.19| |2019.8.12 03:00|11407.88|11398.59|11369.7|11384.71|
Les données de ces quatre cycles d’une heure sont combinées en données de cycle de 4 heures. Le prix d’ouverture est le prix d’ouverture de la première heure 00:00 : 11 382,57 Le cours de clôture est le dernier, c’est-à-dire le cours de clôture à 03h00 : 11384,71 Le prix le plus élevé est le prix le plus élevé ici : 11447,07 Le prix le plus bas est ici : 11365,51 L’heure de début du cycle de 4 heures est 00h00, l’heure de début de la ligne K d’une heure, soit 2019.8.12 00h00 Le volume des transactions peut être résumé toutes les heures (principalement pour observer la manière dont le prix est synthétisé, ce qui n’est pas indiqué dans les données de volume des transactions). Je n’entrerai pas dans les détails ici.
La ligne K synthétisée sur 4 heures est : Haut : 11447,07 Ouvert: 11382.57 Bas : 11365,51 Reçu: 11384.71 Heure : 2019.8.12 00:00

Vous pouvez voir que les données sont cohérentes.
Après avoir vérifié l’idée initiale, vous pouvez commencer à écrire du code pour implémenter initialement cette exigence.
Publiez le code directement, le code est fourni à titre de référence uniquement :
function GetNewCycleRecords (sourceRecords, targetCycle) { // K线合成函数
var ret = []
// 首先获取源K线数据的周期
if (!sourceRecords || sourceRecords.length < 2) {
return null
}
var sourceLen = sourceRecords.length
var sourceCycle = sourceRecords[sourceLen - 1].Time - sourceRecords[sourceLen - 2].Time
if (targetCycle % sourceCycle != 0) {
Log("targetCycle:", targetCycle)
Log("sourceCycle:", sourceCycle)
throw "targetCycle is not an integral multiple of sourceCycle."
}
if ((1000 * 60 * 60) % targetCycle != 0 && (1000 * 60 * 60 * 24) % targetCycle != 0) {
Log("targetCycle:", targetCycle)
Log("sourceCycle:", sourceCycle)
Log((1000 * 60 * 60) % targetCycle, (1000 * 60 * 60 * 24) % targetCycle)
throw "targetCycle cannot complete the cycle."
}
var multiple = targetCycle / sourceCycle
var isBegin = false
var count = 0
var high = 0
var low = 0
var open = 0
var close = 0
var time = 0
var vol = 0
for (var i = 0 ; i < sourceLen ; i++) {
// 获取 时区偏移数值
var d = new Date()
var n = d.getTimezoneOffset()
if (((1000 * 60 * 60 * 24) - sourceRecords[i].Time % (1000 * 60 * 60 * 24) + (n * 1000 * 60)) % targetCycle == 0) {
isBegin = true
}
if (isBegin) {
if (count == 0) {
high = sourceRecords[i].High
low = sourceRecords[i].Low
open = sourceRecords[i].Open
close = sourceRecords[i].Close
time = sourceRecords[i].Time
vol = sourceRecords[i].Volume
count++
} else if (count < multiple) {
high = Math.max(high, sourceRecords[i].High)
low = Math.min(low, sourceRecords[i].Low)
close = sourceRecords[i].Close
vol += sourceRecords[i].Volume
count++
}
if (count == multiple || i == sourceLen - 1) {
ret.push({
High : high,
Low : low,
Open : open,
Close : close,
Time : time,
Volume : vol,
})
count = 0
}
}
}
return ret
}
// 测试
function main () {
while (true) {
var r = exchange.GetRecords() // 原始数据,作为合成K线的基础K线数据,例如要合成4小时K线,可以用1小时K线作为原始数据。
var r2 = GetNewCycleRecords(r, 1000 * 60 * 60 * 4) // 通过 GetNewCycleRecords 函数 传入 原始K线数据 r , 和目标周期, 1000 * 60 * 60 * 4 即 目标合成的周期 是4小时K线数据。
$.PlotRecords(r2, "r2") // 策略类库栏 可以勾选画线类库,调用 $.PlotRecords 画线类库 导出函数 画图。
Sleep(1000) // 每次循环间隔 1000 毫秒,防止访问K线接口获取数据过于频繁,导致交易所限制。
}
}
En fait, pour synthétiser la ligne K, deux choses sont nécessaires. La première est des données sur les matières premières, c’est-à-dire des données K sur une petite période.var r = exchange.GetRecords()
Les données de la ligne K sur une petite période obtenues. La deuxième consiste à définir clairement la période de synthèse, c’est-à-dire la période cible pour la synthèse des données K-line.
Ensuite, grâce à l’algorithme de la fonction GetNewCycleRecords, les données d’une structure de tableau K-line synthétisée peuvent être finalement renvoyées.
Il convient de noter que :
00:00:00 ~ 00:12:00, le deuxième cycle est00:12:00 ~ 00:24:00, le troisième cycle est00:24:00 ~ 00:36:00, le quatrième cycle est00:36:00 ~ 00:48:00, le cinquième cycle est00:48:00 ~ 01:00:00 , ce qui représente une heure complète.S’il s’agit d’un cycle de 13 minutes, il s’agit d’un cycle ouvert et les données calculées dans un tel cycle ne sont pas uniques, car les données synthétisées différeront en fonction du point de départ des données synthétisées.
Le disque réel a été exécuté :

Comparer les tableaux d’échange

Les membres du groupe posent souvent des questions : je veux calculer la moyenne mobile du prix le plus élevé de chaque ligne K, que dois-je faire ?
Habituellement, nous calculons la moyenne mobile en calculant la moyenne des cours de clôture pour former la moyenne mobile, mais il est parfois nécessaire de calculer le prix le plus élevé, le prix le plus bas, le prix d’ouverture, etc.
À ce stade, vous ne pouvez pas simplementexchange.GetRecords() Les données K-line renvoyées par la fonction sont directement transmises à la fonction de calcul de l’indicateur.
Par exemple:
La fonction de calcul de l’indicateur de moyenne mobile talib.MA comporte deux paramètres. Le premier paramètre correspond aux données à transmettre et le deuxième paramètre correspond au paramètre de période de l’indicateur.
Par exemple, nous voulons calculer les indicateurs suivants

Le cycle de la ligne K dure 4 heures.
Sur le graphique d’échange, une moyenne mobile a été définie avec un paramètre de période de moyenne mobile de 9.
Et la source de données pour le calcul est définie sur le prix le plus élevé de chaque barre.
Autrement dit, cette moyenne mobile est la moyenne des prix les plus élevés de 9 barres K-line de 4 heures, qui constituent la moyenne mobile de l’indicateur.
Construisons nous-mêmes quelques données et voyons si elles sont identiques à celles calculées par le graphique de la bourse.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
Puisque nous devons calculer la moyenne des prix les plus élevés de chaque barre pour dériver l’indicateur de moyenne mobile. Ensuite, vous devez d’abord construire un tableau, dans lequel chaque élément de données correspond au prix le plus élevé de chaque barre. Vous pouvez voir que la variable highs est initialement un tableau vide, puis nous parcourons la variable de données du chandelier r2 (vous ne vous souvenez pas de r2 ? Regardez le code dans la fonction principale de synthèse du chandelier de 4 heures ci-dessus). Lisez le prix le plus élevé de chaque barre de r2 (c’est-à-dire r2[i].High , i varie de 0 à r2.length - 1 ), puis le pousse dans les hauts . De cette manière, une structure de données est construite qui correspond exactement aux données de la ligne K Bar.
À ce stade, les sommets peuvent être transmis à la fonction talib.MA pour calculer la moyenne mobile.
Exemple complet :
function main () {
while (true) {
var r = exchange.GetRecords()
var r2 = GetNewCycleRecords(r, 1000 * 60 * 60 * 4)
if (!r2) {
continue
}
$.PlotRecords(r2, "r2") // 画出K线
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
var ma = talib.MA(highs, 9) // 用均线指标函数 talib.MA 计算 均线指标
$.PlotLine("high_MA9", ma[ma.length - 2], r2[r2.length - 2].Time) // 使用画线类库把均线指标画在图表上
Sleep(1000)
}
}
Exécution du backtest :

Vous pouvez voir que les valeurs de l’indicateur de moyenne mobile à la position de la souris sur la figure sont11466.9289
Le code ci-dessus peut être copié dans la stratégie pour exécuter le test. N’oubliez pas de cocher la case « Bibliothèque de dessins au trait » et de l’enregistrer !
La plateforme de trading quantitative Inventor dispose déjà d’une interface packagée, à savoir la fonction exchange.GetRecords, qui peut obtenir des données de ligne K. Ce qui suit se concentre sur l’accès direct à l’interface de données de la ligne K d’échange pour obtenir des données, car vous devez parfois spécifier des paramètres pour obtenir plus de lignes K, interface GetRecords encapsulée En général, 100 sont retournés. Si la stratégie nécessite initialement plus de 100 lignes K, vous devez collecter et attendre. Afin que la stratégie s’exécute le plus rapidement possible, vous pouvez encapsuler vous-même une fonction, accéder directement à l’interface K-line de l’échange et spécifier des paramètres pour obtenir plus de données K-line.
En prenant comme exemple la paire de trading BTC_USDT de Huobi, nous mettons en œuvre cette exigence :
Retrouvez le document API de l’échange et consultez la description de l’interface K-line :

https://api.huobi.pro/market/history/kline?period=1day&size=200&symbol=btcusdt
paramètre: |Nom du paramètre|Type|Obligatoire|Description|Valeur| |-|-|-|-|-| |symbole|chaîne|vrai|Paire de trading|btcusdt, ethbtc…| |period|string|true|Renvoie la granularité temporelle des données, c’est-à-dire l’intervalle de temps de chaque bougie|1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year| |size|integer|false|Renvoie le nombre de données K-line|[1, 2000]|
Code de test :
function GetRecords_Huobi (period, size, symbol) {
var url = "https://api.huobi.pro/market/history/kline?" + "period=" + period + "&size=" + size + "&symbol=" + symbol
var ret = HttpQuery(url)
try {
var jsonData = JSON.parse(ret)
var records = []
for (var i = jsonData.data.length - 1; i >= 0 ; i--) {
records.push({
Time : jsonData.data[i].id * 1000,
High : jsonData.data[i].high,
Open : jsonData.data[i].open,
Low : jsonData.data[i].low,
Close : jsonData.data[i].close,
Volume : jsonData.data[i].vol,
})
}
return records
} catch (e) {
Log(e)
}
}
function main() {
var records = GetRecords_Huobi("1day", "300", "btcusdt")
Log(records.length)
$.PlotRecords(records, "K")
}
Version Python, exemple d’accès à l’interface d’échange Huobi :
#!python3
import json
import urllib2
def GetRecords_Huobi(period, size, symbol):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
url = "https://api.huobi.pro/market/history/kline?" + "period=" + period + "&size=" + size + "&symbol=" + symbol
request = urllib2.Request(url)
request.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6')
opener = urllib2.build_opener()
f= opener.open(request)
ret = f.read().decode('utf-8')
try :
jsonData = json.loads(ret)
records = []
for i in range(len(jsonData["data"]) - 1, -1, -1):
records.append({
"Time" : jsonData["data"][i]["id"] * 1000,
"High" : jsonData["data"][i]["high"],
"Open" : jsonData["data"][i]["open"],
"Low" : jsonData["data"][i]["low"],
"Close" : jsonData["data"][i]["close"],
"Volume" : jsonData["data"][i]["vol"],
})
return records
except Exception as e:
Log(e)
def main():
r = GetRecords_Huobi("1day", "300", "btcusdt")
Log(len(r))
ext.PlotRecords(r, "K") # 需要引用Python画线类库
Version Python, un exemple d’accès à l’interface K-line de Binance Exchange :
#!python3
import json
import urllib2
def GetRecords_Huobi(period, size, symbol):
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
url = "https://api.binance.com/api/v3/klines?symbol=" + symbol + "&interval=" + period
request = urllib2.Request(url)
request.add_header('User-Agent','Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6')
opener = urllib2.build_opener()
f= opener.open(request)
ret = f.read().decode('utf-8')
try :
jsonData = json.loads(ret)
records = []
for i in range(len(jsonData)):
records.append({
"Time" : float(jsonData[i][0]),
"High" : float(jsonData[i][2]),
"Open" : float(jsonData[i][1]),
"Low" : float(jsonData[i][3]),
"Close" : float(jsonData[i][4]),
"Volume" : float(jsonData[i][5]),
})
return records
except Exception as e:
Log(e)
def main():
r = GetRecords_Huobi("1m", "300", "BTCUSDT")
Log(len(r))
ext.PlotRecords(r, "K") # 需要引用Python画线类库


Nous pouvons voir dans le journal que records.length est de 300, ce qui signifie qu’il y a 300 barres de données K-line d’enregistrements.
