
Ao escrever estratégias de negociação programáticas, usando dados da linha K, geralmente é necessário usar alguns dados da linha K de período não padrão, como dados da linha K de período de 12 minutos, dados da linha K de período de 4 horas, geralmente esse tipo de não padrão O período não pode ser obtido diretamente. Então, como respondemos a essas demandas? A resposta é que definitivamente há um jeito. Ciclos não padronizados podem ser obtidos pela fusão e síntese de dados de ciclos menores. Você pode imaginar que o preço mais alto em vários ciclos é contado como o preço mais alto após a síntese, e o preço mais baixo é contado como o preço mais baixo após a síntese. A abertura o preço não mudará. , o primeiro preço de abertura dos dados da matéria-prima da linha K sintetizada é usado, o preço de fechamento corresponde ao último preço de fechamento dos dados da matéria-prima da linha K sintetizada, o tempo é o tempo do preço de abertura e o volume de negociação é calculado pela soma dos volumes de transação dos dados da matéria-prima. Conforme mostrado na imagem:
Tomamos o mercado de ativos de blockchain BTC_USDT como exemplo e sintetizamos 1 hora em 4 horas.




|Hora|Máxima|Abertura|Mínima|Fechamento| |- |- |- |- |-| |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|
Os dados desses quatro ciclos de 1 hora são combinados em dados de ciclo de 4 horas. O preço de abertura é o preço de abertura do primeiro tempo 00:00: 11382,57 O preço de fechamento é o último, ou seja, o preço de fechamento às 03:00: 11384,71 O preço mais alto é o preço mais alto aqui: 11447,07 O menor preço está aqui: 11365.51 O horário de início do ciclo de 4 horas é 00:00, o horário de início da linha K de 1 hora, ou seja, 2019.8.12 00:00 O volume de negociação pode ser somado a cada 1 hora (principalmente para observar como o preço é sintetizado, o que não é mostrado nos dados de volume de negociação). Não entrarei em detalhes aqui.
A linha K sintetizada de 4 horas é: Máxima: 11447,07 Aberto: 11382.57 Baixo: 11365,51 Recebido: 11384.71 Hora: 2019.8.12 00:00

Você pode ver que os dados são consistentes.
Depois de verificar as ideias preliminares, você pode começar a escrever algum código para implementar preliminarmente esse requisito.
Libere o código diretamente, o código é apenas para referência:
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线接口获取数据过于频繁,导致交易所限制。
}
}
Na verdade, para sintetizar K-line, duas coisas são necessárias. A primeira são dados de matéria-prima, ou seja, dados K-line de um pequeno período.var r = exchange.GetRecords()
Dados obtidos da linha K de pequeno período. A segunda é definir claramente o período de síntese, ou seja, o período-alvo para a síntese de dados da linha K.
Então, por meio do algoritmo da função GetNewCycleRecords, os dados de uma estrutura de matriz K-line sintetizada podem ser finalmente retornados.
Deve-se notar que:
00:00:00 ~ 00:12:00, o segundo ciclo é00:12:00 ~ 00:24:00, o terceiro ciclo é00:24:00 ~ 00:36:00, o quarto ciclo é00:36:00 ~ 00:48:00, o quinto ciclo é00:48:00 ~ 01:00:00 , o que perfaz 1 hora completa.Se for um ciclo de 13 minutos, é um ciclo aberto, e os dados calculados em tal ciclo não são únicos, porque os dados sintetizados serão diferentes dependendo do ponto de partida dos dados sintetizados.
O disco real foi executado:

Comparar gráficos de câmbio

Os membros do grupo costumam fazer perguntas: Quero calcular a média móvel do preço mais alto de cada linha K, o que devo fazer?
Normalmente, calculamos a média móvel calculando a média dos preços de fechamento para formar a média móvel, mas às vezes é necessário calcular o preço mais alto, o preço mais baixo, o preço de abertura, etc.
Neste momento, você não pode simplesmenteexchange.GetRecords() Os dados da linha K retornados pela função são passados diretamente para a função de cálculo do indicador.
Por exemplo:
A função de cálculo do indicador de média móvel talib.MA tem dois parâmetros. O primeiro parâmetro são os dados que precisam ser passados, e o segundo parâmetro é o parâmetro do período do indicador.
Por exemplo, queremos calcular os seguintes indicadores

O ciclo da linha K é de 4 horas.
No gráfico de câmbio, uma média móvel foi definida com um parâmetro de período de média móvel de 9.
E a fonte de dados para cálculo é definida como o preço mais alto de cada barra.
Ou seja, essa média móvel é a média dos preços mais altos de 9 barras da linha K de 4 horas, que constituem a média móvel do indicador.
Vamos construir alguns dados nós mesmos e ver se eles são os mesmos que os calculados pelo gráfico da bolsa.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
Precisamos calcular a média dos preços mais altos de cada barra para derivar o indicador de média móvel. Então você precisa primeiro construir uma matriz, na qual cada elemento de dados corresponde ao preço mais alto de cada barra. Você pode ver que a variável highs é inicialmente uma matriz vazia, e então percorremos a variável de dados do candlestick r2 (não se lembra do r2? Veja o código na função principal de sintetizar o candlestick de 4 horas acima). Leia o preço mais alto de cada barra de r2 (ou seja, r2[i].High , i varia de 0 a r2.length - 1 ), e então o empurra para highs . Dessa forma, é construída uma estrutura de dados que corresponde um a um com a barra de dados da linha K.
Neste ponto, os máximos podem ser passados para a função talib.MA para calcular a média móvel.
Exemplo completo:
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)
}
}
Execução de backtest:

Você pode ver que os valores do indicador de média móvel na posição do mouse na figura são11466.9289
O código acima pode ser copiado para a estratégia para executar o teste. Lembre-se de verificar “Line Drawing Library” e salvá-lo!
A plataforma de negociação quantitativa Inventor já possui uma interface empacotada, a função exchange.GetRecords, que pode obter dados da linha K. O seguinte se concentra no acesso direto à interface de dados K-line do Exchange para obter dados, porque às vezes você precisa especificar parâmetros para obter mais K-lines, interface GetRecords encapsulada Geralmente 100 são retornados. Se a estratégia exigir inicialmente mais de 100 K-lines, você precisará coletar e esperar. Para fazer a estratégia ser executada o mais rápido possível, você pode encapsular uma função, acessar diretamente a interface K-line da exchange e especificar parâmetros para obter mais dados K-line.
Tomando o par de negociação BTC_USDT da Huobi como exemplo, implementamos este requisito:
Encontre a documentação da API da exchange e veja a descrição da interface K-line:

https://api.huobi.pro/market/history/kline?period=1day&size=200&symbol=btcusdt
parâmetro: |Nome do parâmetro|Tipo|Obrigatório|Descrição|Valor| |-|-|-|-|-| |símbolo|string|verdadeiro|Par de negociação|btcusdt, ethbtc…| |período|string|verdadeiro|Retorna a granularidade de tempo dos dados, ou seja, o intervalo de tempo de cada vela|1min, 5min, 15min, 30min, 60min, 1dia, 1mês, 1semana, 1ano| |tamanho|inteiro|falso|Retorna o número de dados da linha K|[1, 2000]|
Código de teste:
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")
}
Versão Python, exemplo de acesso à interface de troca 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画线类库
Versão Python, um exemplo de acesso à interface K-line da 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画线类库


Podemos ver no log que records.length é 300, o que significa que há 300 barras de dados de registros K-line.
