Uma breve discussão sobre o processamento de dados da linha K em negociação programada
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:
-
Ideias
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:00Você pode ver que os dados são consistentes.
-
Escreva código para implementar
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:-
- O ciclo de destino não pode ser menor que o ciclo da linha K que você passa para a função GetNewCycleRecords como matéria-prima de dados.
Isso ocorre porque é impossível usar um ciclo pequeno para sintetizar dados de um ciclo menor.
- O ciclo de destino não pode ser menor que o ciclo da linha K que você passa para a função GetNewCycleRecords como matéria-prima de dados.
-
- O período alvo definido deve ser um período fechado.
O que é fechamento de ciclo?
Simplificando, dentro de uma hora ou um dia, os intervalos de tempo do ciclo alvo são combinados para formar um ciclo fechado.
Exemplo:
Por exemplo, a linha K de um ciclo de 12 minutos começa em 0:00 de cada hora (tome 0:00 como exemplo), e o primeiro ciclo é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 período alvo definido deve ser um período fechado.
-
-
Use dados da linha K para construir a estrutura de dados necessária
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ão
11466.9289O código acima pode ser copiado para a estratégia para executar o teste. Lembre-se de verificar "Line Drawing Library" e salvá-lo!
-
Como obter dados K-line no mercado de moeda digital
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=btcusdtparâ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.

if (((1000 * 60 * 60 * 24) - sourceRecords[i].Time % (1000 * 60 * 60 * 24) + (n * 1000 * 60)) % targetCycle == 0) {
isBegin = true
}
这一句有问题,无法用3小时或6小时k合成日k,只能用1小时,2小时,4小时的k线合成日k
- 1











