Quando se escreve uma estratégia de negociação programada usando dados de linha K, muitas vezes há uma necessidade de usar alguns dados de linha K de ciclo não padrão, como dados de ciclo de linha K de 12 minutos, dados de ciclo de linha K de 4 horas, que geralmente não podem ser obtidos diretamente. Então, como lidar com essa necessidade? A resposta é que há uma solução. Os ciclos não padrão podem ser obtidos por meio de dados de ciclos menores, combinados e sintetizados. Imagine que o preço mais alto em vários ciclos seja o preço mais alto após a síntese, o preço mais baixo seja o preço mais baixo após a síntese, o preço de abertura não mude. O primeiro preço de abertura é o primeiro preço de abertura dos dados de matéria-prima da linha K, o preço de fechamento corresponde ao último preço de fechamento dos dados de matéria-prima da linha K, o tempo é o tempo do preço de abertura, o volume de transações é calculado e obtido com os dados de matéria-prima. O que é que ele está a fazer?
O mercado de ativos da blockchain BTC_USDT, por exemplo, se tornou 4 horas em 1 hora.
Tempo | Alto | Desligue | Baixo | Recolha |
---|---|---|---|---|
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 |
Estes quatro ciclos de uma hora, combinados com um ciclo de quatro horas, o preço de abertura é o primeiro preço de abertura 00:00:11382.57 O preço de fechamento é o último, que é o preço de fechamento às 03:00: 11384.71. Preço mais alto para encontrar o preço mais alto aqui: 11447.07 Preço mais baixo para encontrar o preço mais baixo aqui: 11365.51 O ciclo de 4 horas começa às 00:00 e a linha K de 1 hora começa em 2019.8.12 00:00 A soma de pedidos de transações por hora (a principal observação de como os preços são sintetizados não é mostrada nos dados de transações) não é descrita aqui.
A linha K de quatro horas é composta por: Altura: 11447.07 Abre: 11382.57 Baixo: 11365.51 Coleção:11384.71 Hora: 19 de agosto de 2019
A partir de agora, o número de casos confirmados no país aumentou.
Depois de validar a ideia inicial, é possível escrever manualmente um código para realizar essa necessidade inicialmente.
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 linhas K, são necessárias duas coisas: primeiro, precisamos de dados de matérias-primas, ou seja, dados de linhas K de pequenos ciclos.var r = exchange.GetRecords()
Os dados de linha K de pequenos ciclos obtidos. Em segundo lugar, é necessário definir o quão grande é o ciclo, ou seja, o ciclo alvo para a síntese de dados de linha K.
O algoritmo da função GetNewCycleRecords é usado para retornar dados de uma estrutura de arquivos de linha K sintetizada.
O que é importante lembrar:
1, o ciclo de destino não pode ser menor do que o ciclo da linha K da função GetNewCycleRecords como matéria-prima de dados. A partir daí, a empresa começou a trabalhar com os seus clientes, porque não era possível sintetizar dados de períodos menores com períodos menores.
2, os ciclos de alvo definidos devem ser de ciclo fechado.
O que é o fechamento de ciclo?
Simplificando, é uma combinação de intervalos de tempo de ciclos de objetivos dentro de uma hora ou dentro de um dia, formando um ciclo fechado.
Exemplos:
Por exemplo, a linha K de um ciclo de 12 minutos, que começa a partir de 0 minutos e 0 segundos de cada hora (por exemplo, 0 horas), 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
A partir daí, o programa foi lançado no Brasil, onde foi exibido em todo o mundo.
Se for um ciclo de 13 minutos, ou um ciclo não fechado, os dados calculados para esse ciclo não são únicos, pois os dados sintetizados variam de acordo com o ponto de partida dos dados sintetizados.
O disco real começou a funcionar:
Gráfico de troca
Muitas vezes os amigos perguntam: "Quero calcular a média do preço mais alto de cada linha K, e como?"
Normalmente, a linha média é calculada como a média do preço de fechamento calculado, que compõe a linha média, mas às vezes é necessário calcular o preço máximo, o preço mínimo, o preço de abertura, etc.
Não podemos fazer isso diretamente.exchange.GetRecords()
Os dados da linha K que a função retorna são transmitidos diretamente para a função de cálculo do indicador.
Por exemplo:talib.MAA função de cálculo de indicadores homogêneos tem dois parâmetros, o primeiro parâmetro é o parâmetro de dados a serem transmitidos e o segundo parâmetro é o parâmetro de ciclo do indicador. Por exemplo, vamos calcular o indicador do diagrama abaixo.
O ciclo da linha K é de 4 horas. No gráfico da bolsa, uma linha média com um parâmetro de ciclo de linha média de 9 é definida. A fonte de dados que é calculada é o preço mais alto por Bar.Ou seja, esta linha média é a média calculada do preço máximo de 9 ciclos de 4 horas K-LineBar, que é composta por uma linha média de indicadores.
Nós construímos um cálculo de dados para ver se ele é o mesmo que o gráfico da bolsa.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
Uma vez que o valor médio do preço mais alto de cada Bar deve ser calculado, obtém-se um indicador linear. Em seguida, é necessário construir uma matriz em que cada elemento de dados é o valor máximo correspondente a cada Bar. Podemos ver que as variáveis highs começam como uma matriz vazia, e então vamos percorrer a variável de dados da linha K, r2 (não se lembram de r2? Vejam o código na função principal acima para a síntese da linha K de 4 horas). Leia o valor máximo de r2 por Bar (ou seja, r2[i].High, i vale o intervalo de 0 a r2.length - 1), e empurre para highs.
Neste momento, os highs podem ser transmitidos para o talib.
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)
}
}
Re-teste em execução:
Como você pode ver no gráfico, o valor do indicador da linha média da posição do rato é11466.9289
O código acima pode ser copiado para executar testes na política, e lembre-se de salvar depois de selecionar "Draw Line Library"!
Os inventores da plataforma de negociação quantitativa já possuem uma interface embalada, a função exchange.GetRecords, para obter dados de linha K. A seguir, o foco é o acesso direto à interface de dados da linha K do exchange para obter dados, pois às vezes é necessário especificar parâmetros para obter mais linhas K, com a interface GetRecords embalada. Normalmente, o retorno é de 100 bits. Se a estratégia inicialmente requer mais de 100 K-strings, é necessário coletar e esperar. Para que a política funcione o mais rápido possível, é possível envelopar uma função por conta própria, acessar diretamente a interface da linha K da bolsa e especificar parâmetros para obter mais dados da linha K.
Com o BTC_USDT como exemplo de par de transações, nós realizamos esta necessidade:
Para encontrar a documentação da API da bolsa, veja a descrição da interface da linha K:
https://api.huobi.pro/market/history/kline?period=1day&size=200&symbol=btcusdt
Parâmetros:
Nome do parâmetro | Tipo | É necessário? | Descrição | Valorização |
---|---|---|---|---|
símbolo | cordel | verdade | Negociação | Não, não, não, não. |
período | cordel | verdade | Retorna o tempo dos grânulos de dados, ou seja, o intervalo de tempo de cada linha. | 1 minuto, 5 minutos, 15 minutos, 30 minutos, 60 minutos, 1 dia, 1 mês, 1 semana, 1 ano |
tamanho | Número inteiro | Falso | Retorna o número de linhas de dados 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")
}
A versão em Python, exemplo de como acessar a interface do 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.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画线类库
A versão em Python, exemplo de interface K-line para acessar o Bitcoin 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画线类库
Como pode ver no log, imprima records.length para 300, ou seja, registros K linha de barras de dados com 300 caracteres.
BamsmenNão é possível sintetizar k dias em 3 horas ou 6 horas.
Bamsmenif (((1000 * 60 * 60 * 24) - sourceRecords[i].Time % (1000 * 60 * 60 * 24) + (n * 1000 * 60)) % targetCycle == 0) { isBegin = verdadeiro Não. A frase tem um problema, não é possível sintetizar k dias em 3 horas ou 6 horas, só é possível sintetizar k dias em linha de 1 hora, 2 horas e 4 horas.
xis2004Se você quiser escalar toda a história de uma espécie, você pode escalar?
WillzhangObrigado pela resposta.
WillzhangPor favor, como é melhor se você quiser mais de 300 bits? Por exemplo, 1000 K-lines de dados.
Inventor quantificado - sonho pequenoO que você está fazendo aqui é muito ruim.
Inventor quantificado - sonho pequenoÉ o número de dados que você recebe para acessar a interface da bolsa.
Inventor quantificado - sonho pequenoSe exceder o número máximo de retornos suportados pela interface da bolsa, só será possível coletar dados, por exemplo, uma quantidade suficiente de dados da linha K.