Quando se escreve uma estratégia de negociação quantitativa, usando os dados da linha K, muitas vezes há casos em que são necessários dados de linha K não padrão. por exemplo, são necessários dados de linha K de ciclo de 12 minutos e dados de ciclo de linha K de 4 horas. Normalmente, esses ciclos não padrão não estão diretamente disponíveis. Então, como lidar com tais necessidades?
Os dados da linha K do ciclo não padrão podem ser obtidos combinando os dados do ciclo menor. Imagine isso, o preço mais alto em vários ciclos é contado como o preço mais alto após a síntese da linha K do ciclo múltiplo, e o preço mais baixo é calculado como o preço mais baixo após a síntese, e o preço de abertura não muda. O primeiro preço de abertura dos dados da matéria-prima da linha K é sintetizado. O preço de fechamento corresponde ao preço de fechamento dos últimos dados da matéria-prima da linha K. O tempo usa o tempo da linha de preço de abertura k. O volume da transação usa os dados da matéria-prima somados e calculados.
Tal como mostra a figura:
Vamos tomar o ativo blockchain BTC_USDT como exemplo e sintetizar 1 hora em 4 horas.
Tempo | Maior | Aberto | Baixo | Fechado |
---|---|---|---|---|
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 de quatro ciclos de uma hora são combinados num único dado de um ciclo de quatro horas.
O preço de abertura é o primeiro preço de abertura da linha K às 00:00 horas: 11382.57 O preço de fechamento é o último preço de fechamento da linha k às 03:00: 11384.71 O preço mais alto é encontrar o preço mais alto entre eles: 11447,07 O preço mais baixo é encontrar o preço mais baixo entre eles: 11365,51
Nota: O mercado de futuros de commodities da China fechou às 15:00 em um dia de negociação normal
A hora de início do ciclo de 4 horas é a hora de início da primeira linha K de 1 hora, ou seja, 2019.8.12 00:00
A soma do volume de todas as linhas k de 1 hora é utilizada como volume da linha k de 4 horas.
Uma linha K de 4 horas é sintetizada:
High: 11447.07
Open: 11382.57
Low: 11365.51
Close: 11384.71
Time: 209.8.12 00:00
Podem ver que os dados são consistentes.
Depois de compreender as ideias iniciais, pode escrever manualmente o código para realizar os requisitos.
Estes códigos são apenas para referência:
function GetNewCycleRecords (sourceRecords, targetCycle) { // K line synthesis function
var ret = []
// First get the source K line data cycle
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++) {
// Get the time zone offset value
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
}
// test
function main () {
while (true) {
var r = exchange.GetRecords() // Raw data, as the basic K-line data of the synthesize K line. for example, to synthesize a 4-hour K-line, you can use the 1-hour K-line as the raw data.
var r2 = GetNewCycleRecords(r, 1000 * 60 * 60 * 4) // Pass the original K-line data r through the GetNewCycleRecords function, and the target cycles, 1000 * 60 * 60 * 4, ie the target synthesis cycle is 4 hours K-line data .
$.PlotRecords(r2, "r2") // The strategy class library bar can be selected by check the line class library, and calling the $.PlotRecords line drawing class library to export the function drawing.
Sleep(1000) // Each cycle is separated by 1000 milliseconds, preventing access to the K-line interface too much, resulting in transaction restrictions.
}
}
Na verdade, para sintetizar a linha K, você precisa de duas coisas. O primeiro são os dados da matéria-prima, ou seja, os dados da linha K de um ciclo menor.var r = exchange.GetRecords()
para obter os dados da linha de ciclo K menor.
O segundo é descobrir o tamanho do ciclo de síntese, nós usamos o algoritmo função GetNewCycleRecords para fazer isso, então você pode finalmente retornar os dados de uma estrutura de matriz K-line sintetizada.
Tenha em conta:
O ciclo de destino não pode ser menor do que o ciclo da linha K que você passou na função GetNewCycleRecords como matéria-prima para os dados.
O ciclo alvo deve ser definido como
Por exemplo:
A linha K do ciclo de 12 minutos começa a partir de 0:0 a cada hora, o primeiro ciclo é 00:00:00 ~ 00:12:00, e 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, que são exatamente uma hora completa.
Se for um ciclo de 13 minutos, será um ciclo que não está fechado. Os dados calculados por tal ciclo não são únicos porque os dados sintetizados diferem dependendo do ponto de partida dos dados sintetizados.
Faça isso no mercado real:
Gráfico de troca de contraste
Quero calcular a média móvel do preço mais alto para todas as linhas K. O que devo fazer?
Normalmente, nós calcular as médias móveis usando a média dos preços de fechamento, mas às vezes há demanda para usar o preço mais alto, o preço mais baixo, o preço de abertura e assim por diante.
Para estas demandas extras, os dados de linha K devolvidos pela função exchange.GetRecords ((() não podem ser transmitidos diretamente para a função de cálculo do indicador.
Por exemplo:
Otalib.MA
A função de cálculo do indicador da média móvel tem dois parâmetros, o primeiro dos quais são os dados a transmitir e o segundo o parâmetro do ciclo do indicador.
Por exemplo, precisamos calcular os indicadores como mostrado abaixo.
O ciclo da linha K é de 4 horas.
No gráfico das cotações de câmbio, foi fixada uma linha média com o parâmetro do ciclo de 9.
A fonte de dados calculada utiliza o preço mais elevado por barra.
Ou seja, esta linha média móvel consiste na média do preço médio mais alto de nove ciclos de 4 horas K-line Bar.
Vamos construir um dado nós mesmos para ver se é o mesmo com os dados da troca.
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
highs.push(r2[i].High)
}
Como precisamos calcular o preço mais alto de cada barra para obter o valor do indicador da média móvel, precisamos construir uma matriz na qual cada elemento de dados tem o preço mais alto para cada barra.
Podem ver que ohighs
A variável é inicialmente uma matriz vazia, então atravessamos a variável de dados de linha r2 k (não lembram da r2?
Leia o preço mais alto de cada Bar de r2 (ou seja, r2 [i].High, i varia de 0 a r2.longitude - 1), em seguida, empurrar parahighs
Desta forma, nós apenas construímos uma estrutura de dados que corresponde um a um com a barra de dados da linha K.
Neste momento,highs
pode passar otalib.MA
Função 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") // Draw the K line
var highs = []
for (var i = 0 ; i < r2.length ; i++) {
Highs.push(r2[i].High)
}
var ma = talib.MA(highs, 9) // use the moving average function "talib.MA" to calculate the moving average indicator
$.PlotLine("high_MA9", ma[ma.length - 2], r2[r2.length - 2].Time) // Use the line drawing library to draw the moving average indicator on the chart
Sleep(1000)
}
}
Teste de retrocesso:
Você pode ver que o valor médio do indicador da posição do ponto do mouse na figura é 11466.9289
O código acima pode ser copiado para a estratégia para executar o teste, lembre-se de verificar a
A plataforma FMZ Quant já dispõe de uma interface
exchange.GetRecords
função, para obter dados da linha K.
O seguinte foca no acesso direto à interface de dados de linha K do exchangeGetRecords
interface geralmente retorna 100 k linhas. se você encontrar uma estratégia que inicialmente requer mais de 100 K-linhas, você precisa esperar o processo de coleta.
Para fazer a estratégia funcionar o mais rápido possível, você pode encapsular uma função, acessar diretamente a interface da linha K da troca e especificar parâmetros para obter mais dados da linha K.
Usando o par de negociação BTC_USDT na troca Huobi como exemplo, implementamos este requisito:
Encontre a documentação da API do exchange e veja a descrição da interface da linha K:
https://huobiapi.github.io/docs/spot/v1/en/#get-klines-candles
Parâmetros:
Nome | Tipo | É necessário? | Descrição | Valor |
---|---|---|---|---|
símbolo | cordel | verdade | Pares de negociação | Não, não, não, não. |
período | cordel | verdade | Retorna a granularidade temporal dos dados, que é o intervalo de tempo de cada linha k | 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 ensaio:
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")
}
Podem ver no registo, impressãorecords.length
é 300, ou seja, o número derecords
A barra de dados da linha K é 300.