O recurso está a ser carregado... Carregamento...

Desenvolvimento de estratégias de hedge de caixa para moedas digitais (1)

Autora:Inventor quantificado - sonho pequeno, Criado: 2021-07-19 17:38:24, Atualizado: 2023-09-20 10:35:16

img

Desenvolvimento de estratégias de hedge de caixa para moedas digitais (1)

Para os iniciantes no design de estratégias, a estratégia de hedge é uma ótima estratégia de prática. Este artigo implementa uma estratégia de hedge de caixa de moeda digital simples, mas prática, com a esperança de que os iniciantes aprendam alguma experiência de design.

Desenhar funções, parâmetros de interface de estratégia de acordo com as necessidades da política

Para começar, é preciso esclarecer que a estratégia que estamos a planear é uma estratégia de hedge de moeda digital, que é a mais simples, onde só se vende uma moeda com um preço mais alto entre duas moedas, e a moeda mais baixa é comprada e ganha o diferencial. Quando a moeda mais alta é comprada e a moeda mais baixa é comprada, não se pode hedgear.

O preço, a quantidade e a precisão dos pedidos são limitados no momento do hedge, e há um limite de quantidade mínima de pedidos. Além do limite mínimo, a estratégia fora do limite mínimo também deve considerar o máximo de pedidos de um hedge, o que significa que não haverá quantidade suficiente de pedidos. Também é necessário considerar como converter os preços de câmbio se as duas moedas negociadas forem diferentes.

Com base nessas considerações, a estratégia precisa projetar vários parâmetros:

  • A diferença de cobertura:hedgeDiffPriceQuando a diferença excede esse valor, a operação de hedge é desencadeada.
  • O risco mínimo é:minHedgeAmountO número mínimo de moedas cobertas é o valor mínimo de moedas cobertas.
  • Maximo de cobertura:maxHedgeAmountO número máximo de moedas cobertas por um hedge.
  • A Precision:pricePrecisionAA precisão do preço de compra da bolsa A (número de dígitos menores)
  • A precisão da unidade é:amountPrecisionAA precisão da quantidade de unidades (número de dígitos menores) no mercado A.
  • Preço B:pricePrecisionBA exibição de preços é feita por uma plataforma de negociação chamada B.
  • A precisão da unidade B é:amountPrecisionBA precisão da quantidade da bolsa B é menor que a quantidade da bolsa B.
  • A taxa de câmbio:rateA, a conversão de taxa de câmbio do primeiro objeto de troca adicionado, o 1 não é convertido por defeito.
  • Taxa de câmbio da Bolsa B:rateB, a conversão de taxa de câmbio do segundo objeto de troca adicionado, o 1 não é convertido por defeito.

A estratégia de hedge requer manter o número de moedas de ambas as contas inalterado (isto é, não manter posições direcionais, mantendo-se neutro), portanto, é necessário que a estratégia tenha uma lógica de equilíbrio para sempre detectar o equilíbrio. Ao detectar o equilíbrio, não é necessário obter dados de ativos de ambas as bolsas. Precisamos escrever uma função para usar.

  • 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 
    }
    

Se a ordem não for executada, precisamos cancelar a ordem em tempo hábil e não podemos manter a ordem pendente. Esta operação precisa ser tratada tanto no módulo de equilíbrio quanto na lógica de hedge, então é necessário projetar uma função de cancelamento total de ordem.

  • 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)
                }
            }
        })
    }
    

Quando estamos a equilibrar o número de moedas, precisamos de encontrar um preço que se acumule a um determinado número de moedas em algum tipo de dados de profundidade, por isso 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
    }
    

E então nós precisamos de criar e escrever uma operação de sub-ordem para um hedge específico, que precisa ser projetado como uma sub-ordem em simultâneo:

  • 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 terminar o design da função de equilíbrio, que é um pouco mais complexa.

  • 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
        // 计算币差
        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("无法平衡")            
            } else {
                // 平衡下单
                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("价格无效", price)
                }
            }        
            return false
        } else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
            Log("错误:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
            return true 
        } else {
            return true 
        }
    }
    

As funções foram projetadas de acordo com as necessidades da política, e abaixo você pode começar a projetar as principais funções da política.

Design de funções estratégicas

A estratégia no FMZ é de:mainA função é executada em.mainA parte inicial da função é onde nós vamos fazer algumas estratégias de inicialização.

  • Nome do objeto da troca Uma vez que muitas das operações da estratégia são usadas para objetos de câmbio, como obter transações, fazer pedidos, etc. Por isso, usar um nome mais longo a cada vez pode ser um problema.

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

    A partir daí, é mais confortável escrever código para trás.

  • Desenhos relacionados a taxas de câmbio e precisão

      // 精度,汇率设置
      if (rateA != 1) {
          // 设置汇率A
          exA.SetRate(rateA)
          Log("交易所A设置汇率:", rateA, "#FF0000")
      }
      if (rateB != 1) {
          // 设置汇率B
          exB.SetRate(rateB)
          Log("交易所B设置汇率:", rateB, "#FF0000")
      }
      exA.SetPrecision(pricePrecisionA, amountPrecisionA)
      exB.SetPrecision(pricePrecisionB, amountPrecisionB)
    

    Se o parâmetro de câmbiorateArateBA definição de 1 (default é 1), ou seja,rateA != 1OurateB != 1A conversão de taxa de câmbio não é iniciada, portanto não é configurada.

  • Reinicie todos os dados

    img

    Às vezes, quando uma política é iniciada, é necessário excluir todos os registros, dados de registros vazios.isResetA partir daí, a parte de código redesenhada que foi initializada na política é projetada, por exemplo:

      if (isReset) {   // 当isReset为真时重置数据
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("重置所有数据", "#FF0000")
      }
    
  • Restaurar dados de conta inicial, atualizar dados de conta atual Para determinar o equilíbrio, a estratégia requer um registro contínuo da situação dos ativos da conta inicial em comparação com a atual.nowAccsEsta variável é usada para registrar os dados da conta atual usando uma função que acabamos de criar.updateAccsPara obter os dados da conta da bolsa atual.initAccsPara registrar o estado inicial da conta (datos como número de moedas, quantidade de moedas de câmbio A e B);initAccsUsar primeiro_G()Função de recuperação ((_G função que registra dados permanentemente e pode retornar os dados registrados, veja a documentação API:LinksSe você não consegue fazer a consulta, atribui e usa as informações da conta atual._GA função é registrada.

    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 transação, ciclo principal da função principal

O código no ciclo principal é o processo executado a cada rodada da lógica da estratégia, e a execução repetida e contínua constitui o ciclo principal da estratégia. Vejamos abaixo o processo executado a cada vez pelo programa no ciclo principal.

  • Obter dados do mercado para avaliar a eficácia dos dados do mercado

          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 uma função paralela usando a plataforma FMZ.exchange.GoA partir daí, o Google criou um chamado.GetDepth()Objetos em simultâneo da interfacedepthARoutinedepthBRoutine│ Quando os dois objetos são criados em simultâneo, é chamadoGetDepth()A interface também ocorreu de imediato, quando as duas solicitações para obter dados de profundidade foram enviadas para a troca. E depois chamou.depthARoutinedepthBRoutineObjetowait()Como obter dados em profundidade?
    Após a obtenção de dados de profundidade, é necessário examinar os dados de profundidade para determinar sua eficácia.continueA frase re-executou o ciclo principal.

  • Utilização价差值Os parâmetros são差价比例Parâmetros?

          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 esse design. Os parâmetros do FMZ podem ser baseados em algum parâmetro.DisponívelOuEscondidoEntão podemos fazer um parâmetro para decidir se usar.价格差Ou差价比例

    img

    Adicionar um parâmetro no parâmetro da interface da estratégiadiffAsPercentageOs outros dois parâmetros para exibição ou ocultação baseados neste parâmetro são:hedgeDiffPrice@!diffAsPercentageQuandodiffAsPercentagePara mostrar este parâmetro.hedgeDiffPercentage@diffAsPercentageQuandodiffAsPercentagePor que mostrar o parâmetro como verdadeiro? Depois de ter feito esse projeto, escolhemos:diffAsPercentageParâmetros, ou seja, proporção de diferença de preços como condição de ação de hedge; não selecionadodiffAsPercentageO parâmetro é a diferença de preço como condição para o desencadeamento do hedge.

  • Determinação de condições de desencadeamento do hedge

          if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) {          // A -> B 盘口条件满足            
              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("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks)  // 提示信息
                  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 盘口条件满足
              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("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks)  // 提示信息
                  hedge(exA, exB, price, amount)
                  cancelAll()
                  lastKeepBalanceTS = 0
                  isTrade = true 
              }            
          }
    

    A partir daí, a empresa começou a vender seus produtos para a China. 1, o diferencial de hedge é satisfeito primeiro e só pode ser hedgeado quando o diferencial de transação atende aos parâmetros de diferencial definidos. 2, o volume de hedge disponível deve satisfazer o mínimo de hedge definido no parâmetro, pois o limite mínimo de unidades que diferentes exchanges podem limitar é diferente, portanto, o menor dos dois deve ser escolhido. 3. Os ativos da bolsa que vendeu a operação são suficientes para vender e os ativos da bolsa que comprou a operação são suficientes para comprar. Quando essas condições são atendidas, executar a função de hedge é executado. Antes da função principal, declaramos uma variável antecipadamente.isTradePara marcar se houve ou não um hedge, aqui, se o hedge for desencadeado, a variável é definida comotrueE reinicie as variáveis globais.lastKeepBalanceTSPara 0 ((lastKeepBalanceTS é usado para marcar a barra de tempo da operação de balanço mais recente, que é definida como 0 para iniciar a operação de balanço imediatamente) e desativar todas as listas pendentes.

  • Operação de equilíbrio

          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 
                  }                
              }            
          }
    

    A função de equilíbrio pode ser executada regularmente, mas se a operação de hedge for desencadeada, a função de equilíbrio pode ser executada regularmente, mas a função de equilíbrio não pode ser executada regularmente.lastKeepBalanceTSA operação de equilíbrio é imediatamente acionada se for reiniciada para 0; o lucro é calculado após o sucesso do equilíbrio.

  • Informações de barra de status

          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", 
              "当前A,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n", 
              "当前B,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n", 
              "初始A,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n", 
              "初始B,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
    

    A barra de status não é de design especialmente complicado, mostra o tempo atual, mostra o diferencial de A para B e de B para A. Mostra o diferencial atual do alvo de hedge. Mostra os dados de ativos da conta de A e os dados de ativos da conta de B.

Tratamento de pares de transações em diferentes moedas

No parâmetro, nós projetamos o parâmetro de conversão de valores de câmbio, que é o início da estratégia.mainA parte inicial da operação da função que nós também projetamos é a conversão de taxas.SetRateA função de conversão de câmbio precisa ser executada primeiro. A função é uma função que se aplica a dois níveis:

  • Os preços são convertidos em todos os dados de mercado, dados de pedidos e dados de estoque.
  • A troca de moedas no ativo da conta. Por exemplo, o par de transações atual éBTC_USDTOs preços são:USDTA moeda disponível para a conta é também a moeda.USDTSe eu quiser converter em CNY, configure no código:exchange.SetRate(6.8)E então,exchangeTodos os dados obtidos por funções sob este objeto de troca são convertidos em CNY. Em troca do preço da moeda.SetRateFunção de entradaTaxa de câmbio da moeda atual para a moeda alvo

A estratégia completa:Estratégias de hedge de curto prazo para diferentes moedas


Relacionados

Mais.

Espinhaço-de-cabeça-largo _ Espada Santa da UcrâniaMuito bem.