avatar of 发明者量化-小小梦 发明者量化-小小梦
focar em Mensagem privada
4
focar em
1274
Seguidores

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Criado em: 2019-08-13 11:11:38, atualizado em: 2023-10-20 20:06:13
comments   8
hits   4638

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

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.

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

|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

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Você 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:

    1. 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.
    1. 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 disco real foi executado: Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Comparar gráficos de câmbio Uma breve discussão sobre o processamento de dados da linha K em negociação programada

  • ## 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 Uma breve discussão sobre o processamento de dados da linha K em negociação programada

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. Uma breve discussão sobre o processamento de dados da linha K em negociação programada 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:

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

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!

  • ## 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: Uma breve discussão sobre o processamento de dados da linha K em negociação programada

  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画线类库


Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Uma breve discussão sobre o processamento de dados da linha K em negociação programada

Podemos ver no log que records.length é 300, o que significa que há 300 barras de dados de registros K-line. Uma breve discussão sobre o processamento de dados da linha K em negociação programada