Estratégia de cobertura spot de criptomoedas (1)

Autora:Ninabadass, Criado: 2022-04-14 11:57:46, Atualizado: 2022-04-14 16:32:56

Estratégia de cobertura spot de criptomoedas (1)

Para iniciantes no projeto de estratégia, a estratégia de hedge é muito boa para prática.

Funções e parâmetros de projeto por requisito de estratégia

Em primeiro lugar, precisamos garantir que a estratégia a ser projetada seja uma estratégia de hedge spot de criptomoeda. Projetamos a hedge mais simples. Nós apenas vendemos na plataforma com o preço mais alto entre as duas plataformas spot e compramos na plataforma com o preço mais baixo para ganhar o spread de preço. Quando a plataforma com o preço mais alto está cheia de símbolos de moeda de cotação (porque o preço é alto, todos os símbolos de moeda são vendidos), ou quando a plataforma com o preço mais baixo está cheia de símbolos de moeda (porque o preço é baixo, símbolos de moeda são comprados por todos os ativos), ela não pode ser hedged. Neste momento, você só pode esperar que o preço se inverta para hedge.

Para o preço da ordem e o valor durante a cobertura, existem limites de precisão em todas as plataformas, e também há um limite no valor mínimo da ordem. Além do limite mínimo, a estratégia também precisa considerar o valor máximo da ordem para uma cobertura. Se o valor da ordem for muito grande, o mercado não terá um volume de ordem suficiente para isso. Também é necessário considerar como converter a taxa de câmbio se as duas plataformas tiverem moedas de cotação diferentes. A taxa de manipulação durante a cobertura e o deslizamento do tomador da ordem são todos custos de negociação. A cobertura nem sempre acontece enquanto há uma diferença de preço. Portanto, o spread do preço da cobertura também tem um valor de gatilho. Se for menor que um certo spread de preço, a cobertura fará um prejuízo.

Com base nisso, a estratégia deve ser concebida com vários parâmetros:

  • Previsão de risco:hedgeDiffPriceSe o diferencial exceder o valor, será activada uma cobertura.
  • Valor mínimo da cobertura:minHedgeAmount, o montante mínimo de ordem (valor do símbolo) disponível para uma cobertura.
  • Valor máximo da cobertura:maxHedgeAmount, o montante máximo da ordem (valor do símbolo) disponível para uma cobertura.
  • Precisão do preço A:pricePrecisionA, a precisão do preço da ordem (números decimais) da plataforma A.
  • Precisão do montante do pedido A:amountPrecisionA, a precisão do montante da ordem (números decimais) da plataforma A.
  • Precisão do preço B:pricePrecisionB, a precisão do preço da ordem (números decimais) da plataforma B.
  • Precisão do montante do pedido B:amountPrecisionB, a precisão do montante da ordem (números decimais) da plataforma B.
  • Taxa de câmbio A:rateA, a conversão da taxa de câmbio do primeiro objecto de câmbio adicionado; o valor por defeito é 1, indicando não conversão.
  • Taxa de câmbio B:rateB, a conversão da taxa de câmbio do segundo objecto de câmbio adicionado; o valor por defeito é 1, indicando não conversão.

A estratégia de hedge precisa manter o valor do símbolo de moeda das duas contas inalterado (ou seja, não manter posições direcionais e manter neutro), por isso precisa haver uma lógica de equilíbrio na estratégia para sempre detectar o equilíbrio.

  • atualizaçãoAccs
    function updateAccs(arrEx) {
        var ret = []
        for (var i = 0 ; i < arrEx.length ; i++) {
            var acc = arrEx[i].GetAccount()
            if (!acc) {
                return null
            }
            ret.push(acc)
        }
        return ret 
    }
    

Após a colocação de uma ordem, se não houver uma ordem executada, precisamos cancelá-la a tempo, e a ordem não pode ser mantida pendente.

  • cancelar tudo
    function cancelAll() {
        _.each(exchanges, function(ex) {
            while (true) {
                var orders = _C(ex.GetOrders)
                if (orders.length == 0) {
                    break
                }
                for (var i = 0 ; i < orders.length ; i++) {
                    ex.CancelOrder(orders[i].Id, orders[i])
                    Sleep(500)
                }
            }
        })
    }
    

Ao equilibrar a quantidade de símbolos de moeda, precisamos de encontrar o preço com uma certa quantidade em um certo nível de dados, então precisamos de uma função como esta para lidar com isso.

  • GetDepthPrice
    function getDepthPrice(depth, side, amount) {
        var arr = depth[side]
        var sum = 0
        var price = null
        for (var i = 0 ; i < arr.length ; i++) {
            var ele = arr[i]
            sum += ele.Amount
            if (sum >= amount) {
                price = ele.Price
                break
            }
        }
        return price
    }
    

Em seguida, precisamos projetar e escrever a operação de ordem de cobertura específica, que precisa ser projetada para colocar ordens simultaneamente:

  • coberta
    function hedge(buyEx, sellEx, price, amount) {
        var buyRoutine = buyEx.Go("Buy", price, amount)
        var sellRoutine = sellEx.Go("Sell", price, amount)
        Sleep(500)
        buyRoutine.wait()
        sellRoutine.wait()
    }
    

Finalmente, vamos completar o projeto da função de equilíbrio, que é um pouco mais complicado.

  • manter o equilíbrio
    function keepBalance(initAccs, nowAccs, depths) {
        var initSumStocks = 0
        var nowSumStocks = 0 
        _.each(initAccs, function(acc) {
            initSumStocks += acc.Stocks + acc.FrozenStocks
        })
        _.each(nowAccs, function(acc) {
            nowSumStocks += acc.Stocks + acc.FrozenStocks
        })
      
        var diff = nowSumStocks - initSumStocks
        // calculate currency spread 
        if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
            var index = -1
            var available = []
            var side = diff > 0 ? "Bids" : "Asks"
            for (var i = 0 ; i < nowAccs.length ; i++) {
                var price = getDepthPrice(depths[i], side, Math.abs(diff))
                if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
                    available.push(i)
                } else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
                    available.push(i)
                }
            }
            for (var i = 0 ; i < available.length ; i++) {
                if (index == -1) {
                    index = available[i]
                } else {
                    var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
                    var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
                    if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
                        index = available[i]
                    } else if (priceIndex && priceI && priceI < priceIndex) {
                        index = available[i]
                    }
                }
            }
            if (index == -1) {
                Log("cannot balance")            
            } else {
                // balanced ordering 
                var price = getDepthPrice(depths[index], side, Math.abs(diff))
                if (price) {
                    var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
                    tradeFunc(price, Math.abs(diff))
                } else {
                    Log("invalid price", price)
                }
            }        
            return false
        } else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
            Log("error:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
            return true 
        } else {
            return true 
        }
    }
    

Estas funções foram concebidas de acordo com os requisitos da estratégia, e podemos começar a conceber a função principal da estratégia.

Estratégia Projeto de função principal

No FMZ, a estratégia é executada a partir domainNo início do período de transiçãomainFunção, precisamos fazer alguma inicialização da estratégia.

  • Nome do objeto de troca Para muitas operações na estratégia usar objetos de troca, tais como obter cotações de mercado, a colocação de ordens, e assim por diante, por isso seria inconveniente usar um nome mais longo a cada vez, meu pequeno truque é usar um simples nome curto em vez disso, por exemplo:

    var exA = exchanges[0]
    var exB = exchanges[1]
    

    Então, será mais confortável escrever o código mais tarde.

  • Taxa de câmbio e precisão

      // settings of precision and exchange rate
      if (rateA != 1) {
          // set exchange rate A 
          exA.SetRate(rateA)
          Log("Platform A sets exchange rate:", rateA, "#FF0000")
      }
      if (rateB != 1) {
          // set exchange rate B
          exB.SetRate(rateB)
          Log("Platform B sets exchange rate:", rateB, "#FF0000")
      }
      exA.SetPrecision(pricePrecisionA, amountPrecisionA)
      exB.SetPrecision(pricePrecisionB, amountPrecisionB)
    

    Se um dos parâmetros da taxa de câmbio, a saber:rateAerateB, é definido como 1 (o padrão é 1), ou seja,rateA != 1ourateB != 1A taxa de câmbio não pode ser convertida.

  • Reinicie todas as datas

    img

    Às vezes, é necessário excluir todos os registros e aspirar os registros de dados quando a estratégia é iniciada.isReset, e, em seguida, projetar o código de reinicialização na parte de inicialização da estratégia, por exemplo:

      if (isReset) {   // when "isReset" is true, reset the data 
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("Reset all data", "#FF0000")
      }
    
  • Recuperar os dados da conta inicial e atualizar os dados da conta corrente Para avaliar o saldo, a estratégia deve registar continuamente a condição do activo inicial da conta para comparação com a corrente.nowAccsO sistema de registo das contas correntes é utilizado para registar os dados das contas correntes.updateAccsfunção que acabamos de projetar para obter os dados da conta da plataforma atual.initAccsÉ utilizado para registar o estado inicial da conta (dados como o valor do símbolo de moeda de A e B, o valor da moeda de cotação, etc.).initAccs, primeiro use o_G()função para restaurar (a função _G gravará dados de forma persistente e pode retornar os dados gravados novamente; leia a documentação da API para mais detalhes:ligação).
    Se você não pode consultar os dados, use as informações da conta corrente para atribuir e usar_G()função para registar.

    Por exemplo, o seguinte código:

      var nowAccs = _C(updateAccs, exchanges)
      var initAccs = _G("initAccs")
      if (!initAccs) {
          initAccs = nowAccs
          _G("initAccs", initAccs)
      }
    

Lógica de negociação, Loop principal na função principal

O código no loop principal é o processo de cada rodada de execução de lógica de estratégia, e a execução repetitiva sem parar constrói o loop principal de estratégia.

  • Obtenha as cotações do mercado e julgue a validade

          var ts = new Date().getTime()
          var depthARoutine = exA.Go("GetDepth")
          var depthBRoutine = exB.Go("GetDepth")
          var depthA = depthARoutine.wait()
          var depthB = depthBRoutine.wait()
          if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
              Sleep(500)
              continue 
          }
    

    Aqui você pode ver que a função concorrenteexchange.Goda plataforma FMZ é usado para criar objetos simultâneosdepthARoutineedepthBRoutineque chamam aGetDepth()Quando estes dois objetos simultâneos são criados, oGetDepth()interface é chamada imediatamente, e ambas as solicitações para os dados de profundidade são enviados para a plataforma. Então, chama owait()Método do objetodepthARoutinee objetodepthBRoutinepara obter os dados de profundidade. Após a obtenção dos dados de profundidade, é necessário verificar os dados de profundidade para avaliar a sua validade.continueA instrução é acionada para re-executar o loop principal.

  • Utilizaçãoprice spreadouspread ratio?

          var targetDiffPrice = hedgeDiffPrice
          if (diffAsPercentage) {
              targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
          }
    

    Em termos de parâmetros, nós fizemos tal projeto.exibiçãoouesconder-sebaseado em um parâmetro, para que possamos fazer um parâmetro para decidir se usarprice spread, ouspread ratio.

    img

    O parâmetrodiffAsPercentageOs outros dois parâmetros, que serão exibidos ou ocultados com base no parâmetro, são definidos como:hedgeDiffPrice@!diffAsPercentage; quandodiffAsPercentageé falso, será mostrado.hedgeDiffPercentage@diffAsPercentage; quandodiffAsPercentageSe for verdade, será exibido.
    Após o desenho, verificámos adiffAsPercentageO valor de cobertura é o valor da taxa de variação de risco.diffAsPercentageSe o parâmetro não for verificado, o spread de preço é utilizado como condição de activação da cobertura.

  • Juiz Hedge Trigger

          if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) {          // A -> B market condition satisfied             
              var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
              var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
              if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
                  amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
                  Log("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks)  // prompt message 
                  hedge(exB, exA, price, amount)
                  cancelAll()
                  lastKeepBalanceTS = 0
                  isTrade = true 
              }            
          } else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) {   // B -> A market condition satisfied 
              var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
              var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
              if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
                  amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
                  Log("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks)  // prompt message
                  hedge(exA, exB, price, amount)
                  cancelAll()
                  lastKeepBalanceTS = 0
                  isTrade = true 
              }            
          }
    

    Existem várias condições de desencadeamento para a cobertura: 1.Em primeiro lugar, satisfazer o diferencial de cobertura; só quando o diferencial de mercado satisfazer o parâmetro de diferencial definido, é possível a cobertura.

    2.O montante de cobertura do mercado deve corresponder ao montante mínimo de cobertura estabelecido nos parâmetros.Dado que o montante mínimo de ordem das diferentes plataformas é diferente, deve ser escolhido o menor dos dois.

    3.Os ativos na plataforma com a operação de venda são suficientes para vender, e os ativos na plataforma com a operação de compra são suficientes para comprar.isTradeAqui, se a cobertura é acionada, a variável é definida comotrueE redefinir a variável globallastKeepBalanceTSpara 0 (o lastKeepBalanceTS é utilizado para marcar a marca de tempo da última operação de saldo, e a sua definição para 0 desencadeará imediatamente a operação de saldo), e depois cancelar todas as ordens pendentes.

  • Operação de balanço

          if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
              nowAccs = _C(updateAccs, exchanges)
              var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
              cancelAll()
              if (isBalance) {
                  lastKeepBalanceTS = ts
                  if (isTrade) {
                      var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
                      var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
                      LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
                      isTrade = false 
                  }                
              }            
          }
    

    Pode-se ver que a função de balanço é executada periodicamente, mas se a função de balanço é executadalastKeepBalanceTSSe a operação de cobertura for reiniciada, a operação de equilíbrio será iniciada imediatamente e o rendimento será calculado após o êxito da operação.

  • Informações da barra de estado

          LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n", 
              "currentA,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n", 
              "currentB,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n", 
              "initialA,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n", 
              "initialB,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
    

    A barra de estado não é projetada para ser particularmente complicada. Ela exibe a hora atual, o spread de preço da plataforma A para a plataforma B, bem como o spread de preço de B para A; também exibe o spread de meta de cobertura atual, os dados do ativo da plataforma A e os dados do ativo da plataforma B.

Processamento de pares de negociação para moedas de cotação diferentes

Em termos de parâmetros, concebemos o parâmetro de conversão do valor da taxa de câmbio, e também concebemos a conversão da taxa de câmbio na operação inicial do sistema de câmbio.mainA estratégia tem por objectivo a melhoria da qualidade de vida dos trabalhadores.SetRateA função de conversão de taxa de câmbio deve ser executada primeiro.

Para a função afectará dois aspectos:

  • Conversão de preços em todos os dados de cotação de mercado, dados de ordens e dados de posição.
  • Conversão das moedas de cotação em activos da conta.

Por exemplo, o par de negociação atual éBTC_USDT, a unidade de preço éUSDT, e a moeda de cotação disponível nos activos da conta é igualmenteUSDTSe eu quiser converter o valor dos ativos em CNY, definirexchange.SetRate(6.8)no código para converter os dados obtidos por todas as funçõesexchangeobjecto, e depois converter em CNY. Para converter para qual moeda de cotação, importarA taxa de câmbio da moeda de cotação corrente para a moeda de cotação-alvopara oSetRate function.

Estratégia completa:Estratégia de cobertura spot de moeda de cotação diferente (Teaching)


Mais.