Projeto de estratégia Martingale para futuros de criptomoedas

Autora:Lydia., Criado: 2022-08-04 15:41:45, Atualizado: 2023-09-21 21:10:49

img

Projeto de estratégia Martingale para futuros de criptomoedas

Recentemente, há muitas estratégias Martingale discutidas no grupo oficial da FMZ, e não há muitas estratégias Martingale de contratos de criptomoedas na plataforma. Portanto, aproveitei esta oportunidade para projetar uma estratégia Martingale simples para futuros de criptomoedas. Por que é chamada de estratégia Martingale? Porque os riscos potenciais da estratégia Martin não são pequenos, ela não é projetada exatamente de acordo com a estratégia Martin. No entanto, esse tipo de estratégia ainda tem muitos riscos, e as configurações de parâmetros da estratégia tipo Martin estão intimamente relacionadas ao risco, e o risco não deve ser ignorado.

Este artigo explica principalmente e aprende com o design de estratégias do tipo Martin.

Obtenção de património total

O equidade total é frequentemente usado ao projetar estratégias de futuros de criptomoedas. Isso ocorre porque os retornos precisam ser calculados, especialmente quando você precisa calcular retornos flutuantes. Como a posição é ocupada com margem, a ordem pendente também é ocupada.exchange.GetAccount()Na verdade, a maioria das exchanges de futuros de criptomoedas fornece os dados do patrimônio total, mas esse atributo não é uniformemente empacotado na FMZ.

Então nós projetamos funções para obter esses dados de acordo com diferentes trocas:

// OKEX V5 obtain total equity
function getTotalEquity_OKEX_V5() {
    var totalEquity = null 
    var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT")
    if (ret) {
        try {
            totalEquity = parseFloat(ret.data[0].details[0].eq)
        } catch(e) {
            Log("failed to obtain the total equity of the account!")
            return null
        }
    }
    return totalEquity
}

// Binance futures
function getTotalEquity_Binance() {
    var totalEquity = null 
    var ret = exchange.GetAccount()
    if (ret) {
        try {
            totalEquity = parseFloat(ret.Info.totalWalletBalance)
        } catch(e) {
            Log("failed to obtain the total equity of the account!")
            return null
        }
    }
    return totalEquity
}

OtotalEquityEntão nós escrevemos uma função como a entrada de chamada, e chamamos a função correspondente de acordo com o nome da troca.

function getTotalEquity() {
    var exName = exchange.GetName()
    if (exName == "Futures_OKCoin") {
        return getTotalEquity_OKEX_V5()
    } else if (exName == "Futures_Binance") {
        return getTotalEquity_Binance()
    } else {
        throw "This exchange is not supported"
    }
}

Projetar algumas funções auxiliares

Antes de projetar a função principal e a lógica principal, precisamos fazer alguns preparativos e projetar algumas funções auxiliares.

  • Cancelar todas as ordens pendentes

    function cancelAll() {
        while (1) {
            var orders = _C(exchange.GetOrders)
            if (orders.length == 0) {
                break
            }
            for (var i = 0 ; i < orders.length ; i++) {
                exchange.CancelOrder(orders[i].Id, orders[i])
                Sleep(500)
            }
            Sleep(500)
        }
    }
    

    Esta função é familiar para aqueles que frequentemente lêem o código de exemplo de estratégia no quadrado de estratégia FMZ, e muitas estratégias usaram projetos semelhantes.

  • Operações de colocação de futuros

    function trade(distance, price, amount) {
        var tradeFunc = null 
        if (distance == "buy") {
            tradeFunc = exchange.Buy
        } else if (distance == "sell") {
            tradeFunc = exchange.Sell
        } else if (distance == "closebuy") {
            tradeFunc = exchange.Sell
        } else {
            tradeFunc = exchange.Buy
        }
        exchange.SetDirection(distance)
        return tradeFunc(price, amount)
    }
    
    function openLong(price, amount) {
        return trade("buy", price, amount)
    }
    
    function openShort(price, amount) {
        return trade("sell", price, amount)
    }
    
    function coverLong(price, amount) {
        return trade("closebuy", price, amount)
    }
    
    function coverShort(price, amount) {
        return trade("closesell", price, amount)
    }
    

    Existem quatro direções para a negociação de futuros: openLong, openShort, coverLong e coverShort. Então, projetamos quatro funções de ordem correspondentes a essas operações. Se você considerar apenas a ordem, então há vários fatores necessários: direção, preço da ordem e volume da ordem. Então nós também projetamos uma função chamada:tradepara lidar com a operação quandodistance, price, amountsão especificados. As chamadas de função para openLong, openShort, coverLong e coverShort são finalmente completadas pelotradefunção, isto é, colocar uma ordem numa bolsa de futuros com base na distância, preço e quantidade estabelecidos.

Função principal

A ideia da estratégia é muito simples, tomar o preço atual como a linha de base, e colocar ordens de venda (curto) e compra (longo) em uma certa distância para cima ou para baixo.

  • Trabalhos iniciais Por causa da ordem pendente, precisamos de duas variáveis globais para registrar a ID da ordem.

    var buyOrderId = null
    var sellOrderId = null
    

    Em seguida, os parâmetros da interface de estratégia são projetados para usar a opção de bot simulado OKEX_V5, então algum processamento precisa ser feito no código:

    var exName = exchange.GetName()    
    // Switch OKEX V5 simulated bot
    if (isSimulate && exName == "Futures_OKCoin") {
        exchange.IO("simulate", true)
    }
    

    Há também uma opção para redefinir todas as informações nos parâmetros da interface, de modo que deve haver um processamento correspondente no código:

    if (isReset) {
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("reset all data", "#FF0000")
    }
    

    Só temos contratos perpétuos, por isso a escrita está fixa aqui e definida para perpétuo.

    exchange.SetContractType("swap")
    

    Então, também precisamos considerar a precisão do preço da ordem e do valor da ordem. Se a precisão não for definida corretamente, a precisão será perdida durante o processo de cálculo da estratégia. Se os dados tiverem um grande número de casas decimais, é fácil fazer com que a ordem seja rejeitada pela interface de troca.

    exchange.SetPrecision(pricePrecision, amountPrecision)
    Log("set precision", pricePrecision, amountPrecision)
    

    Recuperação de dados simples por projeto

    if (totalEq == -1 && !IsVirtual()) {
        var recoverTotalEq = _G("totalEq")
        if (!recoverTotalEq) {
            var currTotalEq = getTotalEquity()
            if (currTotalEq) {
                totalEq = currTotalEq
                _G("totalEq", currTotalEq)
            } else {
                throw "failed to obtain initial equity"
            }
        } else {
            totalEq = recoverTotalEq
        }
    }
    

    Se você quiser especificar o patrimônio líquido total inicial da conta quando a estratégia está sendo executada, você pode definir o parâmetrototalEqSe este parâmetro for definido em -1, a estratégia lerá os dados de patrimônio total armazenados. Se não houver dados de patrimônio total armazenados, o patrimônio total lido atual é usado como o patrimônio total inicial da estratégia em andamento. Depois disso, um aumento no patrimônio total indica um lucro e uma diminuição no patrimônio total indica uma perda. Se os dados de patrimônio total forem lidos, a estratégia continuará a ser executada com esses dados.

  • lógica principal Após o trabalho inicial ser feito, finalmente chegamos à parte lógica principal da estratégia. Para facilitar a explicação, escrevi as instruções diretamente nos comentários do código.

      while (1) {                                  // The main logic of the strategy is designed as an infinite loop
          var ticker = _C(exchange.GetTicker)      // Read the current market information first, mainly using the latest transaction price
          var pos = _C(exchange.GetPosition)       // Read current position data
          if (pos.length > 1) {                    // Judging the position data, because of the logic of this strategy, it is unlikely that long and short positions will appear at the same time, so if there are long and short positions at the same time, an error will be thrown
              Log(pos)
              throw "Simultaneous long and short positions"                  // Throw an error to stop the strategy
          }
          //Depends on status
          if (pos.length == 0) {                    // Make different operations according to the position status, when there is no position, pos.length == 0 
              // If you have not held a position, count the profit once
              if (!IsVirtual()) {
                  var currTotalEq = getTotalEquity()
                  if (currTotalEq) {
                      LogProfit(currTotalEq - totalEq, "current total equity:", currTotalEq)
                  }
              }
    
              buyOrderId = openLong(ticker.Last - targetProfit, amount)       // Open a buy order for a long position
              sellOrderId = openShort(ticker.Last + targetProfit, amount)     // Open a short sell order
          } else if (pos[0].Type == PD_LONG) {   // For long positions, the position and quantity of pending orders are different
              var n = 1
              var price = ticker.Last
              buyOrderId = openLong(price - targetProfit * n, amount)
              sellOrderId = coverLong(pos[0].Price + targetProfit, pos[0].Amount)
          } else if (pos[0].Type == PD_SHORT) {   // For short positions, the position and quantity of pending orders are different
              var n = 1
              var price = ticker.Last
              buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount)
              sellOrderId = openShort(price + targetProfit * n, amount)
          }
    
          if (!sellOrderId || !buyOrderId) {   // If one side of the pending order fails, cancel all pending orders and start over
              cancelAll()
              buyOrderId = null 
              sellOrderId = null
              continue
          } 
    
          while (1) {  // The pending order is completed, start monitoring the order
              var isFindBuyId = false 
              var isFindSellId = false
              var orders = _C(exchange.GetOrders)
              for (var i = 0 ; i < orders.length ; i++) {
                  if (buyOrderId == orders[i].Id) {
                      isFindBuyId = true 
                  }
                  if (sellOrderId == orders[i].Id) {
                      isFindSellId = true 
                  }               
              }
              if (!isFindSellId && !isFindBuyId) {    // Detected that both buy and sell orders have been filled
                  cancelAll()
                  break
              } else if (!isFindBuyId) {   // Detected buy order closing
                  Log("buy order closing")
                  cancelAll()
                  break
              } else if (!isFindSellId) {  // Detected sell order closing
                  Log("sell order closing")
                  cancelAll()
                  break
              }
              LogStatus(_D())
              Sleep(3000)
          }
          Sleep(500)
      }
    

Toda a lógica e o desenho são explicados.

Testes de retrocesso

Deixe a estratégia passar por um mercado de 19 de Maio.

img

img

Pode-se ver que a estratégia Martingale ainda apresenta certos riscos.

O bot real pode ser executado com o bot de simulação OKEX V5

Endereço estratégico:https://www.fmz.com/strategy/294957

As estratégias são usadas principalmente para aprender, e o dinheiro real deve ser usado com cautela~!


Relacionados

Mais.