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

Design de estratégia de Martingale para futuros de criptomoedas

Criado em: 2021-07-02 14:35:34, atualizado em: 2023-09-21 21:09:56
comments   22
hits   6170

Design de estratégia de Martingale para futuros de criptomoedas

Design de estratégia de Martingale para futuros de criptomoedas

Ultimamente, tem havido muitas discussões sobre estratégias de Martingale no grupo oficial da FMZ, mas não há muitas estratégias de Martingale para contratos de moeda digital na plataforma. Por isso, aproveitei a oportunidade para criar uma estratégia simples de Martingale para futuros de criptomoedas. Por que é chamada de estratégia Martingale? Porque os riscos potenciais da estratégia Martingale não são realmente pequenos, então ela não é projetada inteiramente de acordo com a estratégia Martingale. No entanto, esse tipo de estratégia ainda apresenta riscos consideráveis, e as configurações dos parâmetros da estratégia Martingale estão intimamente relacionadas aos riscos, portanto, os riscos não devem ser ignorados.

Este artigo explica e estuda principalmente o design de estratégias do tipo Martin. A ideia da estratégia em si é muito clara. Como usuários do FMZ, consideramos mais o design da estratégia.

Obtenha patrimônio total

Ao projetar estratégias de futuros de moeda digital, os dados de patrimônio líquido total são frequentemente usados. Porque é necessário calcular os retornos, especialmente quando é necessário calcular retornos flutuantes. Como as posições abertas ocupam margem, as ordens pendentes também ocupam margem. Neste momento, chame a interface API da plataforma FMZexchange.GetAccount()O que é obtido são os ativos disponíveis e os ativos congelados por ordens pendentes. De fato, a maioria das bolsas de futuros de moeda digital fornece dados sobre o patrimônio total, mas a FMZ não encapsula esse atributo de maneira uniforme.

Portanto, projetamos funções para obter esses dados de acordo com diferentes trocas:

// OKEX V5 获取总权益
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("获取账户总权益失败!")
            return null
        }
    }
    return totalEquity
}

// 币安期货
function getTotalEquity_Binance() {
    var totalEquity = null 
    var ret = exchange.GetAccount()
    if (ret) {
        try {
            totalEquity = parseFloat(ret.Info.totalWalletBalance)
        } catch(e) {
            Log("获取账户总权益失败!")
            return null
        }
    }
    return totalEquity
}

No códigototalEquityEsse é o patrimônio total que precisamos. Em seguida, escrevemos uma função como 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 "不支持该交易所"
    }
}

Projete algumas funções auxiliares

Antes de projetar a função principal e a lógica principal. Ainda precisamos fazer alguma preparação e projetar algumas funções auxiliares.

  • Cancelar todos os pedidos pendentes atuais
  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)
      }
  }

Acredito que aqueles que frequentemente olham para os códigos de exemplo de estratégia no FMZ Strategy Square estão muito familiarizados com essa função. Muitas estratégias usaram designs semelhantes. Sua função é obter a lista atual de pedidos pendentes e então cancelá-los um por um.

  • Operação de ordem 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 na negociação de futuros: abrir uma posição longa (openLong), abrir uma posição curta (openShort), fechar uma posição longa (coverLong) e fechar uma posição curta (coverShort). Então, projetamos quatro funções de ordem para corresponder a essas operações. Se você estiver pensando apenas em fazer um pedido, há vários fatores necessários: direção, preço do pedido e quantidade do pedido. Então também criamos um programa chamado:tradeA função para manipular方向(distance)下单价格(price)下单量(amount)Todas as operações são claras. As chamadas de função de abertura de uma posição longa (openLong), abertura de uma posição curta (openShort), fechamento de uma posição longa (coverLong) e fechamento de uma posição curta (coverShort) são executadas portradeA função executa a função real, que é colocar uma ordem na bolsa de futuros de acordo com a direção, preço e quantidade estabelecidos.

Função principal

A estratégia é muito simples. Use o preço atual como base e coloque ordens de venda (curta) e ordens de compra (longa) a uma certa distância acima e abaixo do preço atual. Uma vez que um lado é executado, todas as ordens restantes serão canceladas e, em seguida, uma nova ordem de fechamento será colocada a uma certa distância com base no preço da posição, e uma ordem de aumento será colocada no preço atual atualizado, mas a ordem de aumento será não dobrar a quantidade do pedido.

  • Trabalho inicial Como precisamos fazer pedidos, precisamos de duas variáveis ​​globais para registrar os IDs dos pedidos.
  var buyOrderId = null
  var sellOrderId = null

Então a opção de usar o disco de simulação OKEX_V5 é projetada nos parâmetros da interface de estratégia, então algum processamento precisa ser feito no código:

  var exName = exchange.GetName()    
  // 切换OKEX V5模拟盘
  if (isSimulate && exName == "Futures_OKCoin") {
      exchange.IO("simulate", true)
  }

Os parâmetros da interface também incluem uma opção para redefinir todas as informações, então o código também deve ter o processamento correspondente:

  if (isReset) {
      _G(null)
      LogReset(1)
      LogProfitReset()
      LogVacuum()
      Log("重置所有数据", "#FF0000")
  }

Executamos apenas contratos perpétuos, por isso está codificado aqui e definido apenas para contratos perpétuos.

  exchange.SetContractType("swap")

Então também precisamos considerar a precisão dos preços dos pedidos e das quantidades dos pedidos. 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 muitas casas decimais, é fácil que o pedido seja rejeitado pela interface de troca.

  exchange.SetPrecision(pricePrecision, amountPrecision)
  Log("设置精度", pricePrecision, amountPrecision)

Função simples de recuperação de dados

  if (totalEq == -1 && !IsVirtual()) {
      var recoverTotalEq = _G("totalEq")
      if (!recoverTotalEq) {
          var currTotalEq = getTotalEquity()
          if (currTotalEq) {
              totalEq = currTotalEq
              _G("totalEq", currTotalEq)
          } else {
              throw "获取初始权益失败"
          }
      } else {
          totalEq = recoverTotalEq
      }
  }

Se você quiser especificar o patrimônio total inicial da conta quando a estratégia for executada, você pode definir o parâmetrototalEqSe este parâmetro for definido como -1, a estratégia lerá os dados de patrimônio líquido total armazenados. Se não houver dados de patrimônio líquido total armazenados, o patrimônio líquido total lido atual será usado como o patrimônio líquido total inicial do progresso da estratégia em execução. Se o total o patrimônio líquido aumenta, significa que se você ganha dinheiro, mas seu patrimônio líquido total é menor, significa que você perdeu dinheiro. Se os dados de patrimônio líquido total forem lidos, continue a execução usando esses dados.

  • Lógica principal Depois que o trabalho inicial foi feito, finalmente chegamos à lógica principal da estratégia. Para a conveniência da explicação, escrevi a explicação diretamente nos comentários do código.
    while (1) {                                  // 策略主要逻辑设计为一个死循环
        var ticker = _C(exchange.GetTicker)      // 首先读取当前行情信息,主要用到最新成交价
        var pos = _C(exchange.GetPosition)       // 读取当前持仓数据
        if (pos.length > 1) {                    // 判断持仓数据,由于这个策略的逻辑,是不太可能同时出现多空持仓的,所以发现同时出现多空持仓就抛出错误
            Log(pos)
            throw "同时有多空持仓"                  // 抛出错误,让策略停止
        }
        // 根据状态而定
        if (pos.length == 0) {                    // 根据持仓状态做出不同操作,pos.length == 0是当没有持仓时
            // 未持仓了,统计一次收益
            if (!IsVirtual()) {
                var currTotalEq = getTotalEquity()
                if (currTotalEq) {
                    LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq)
                }
            }

            buyOrderId = openLong(ticker.Last - targetProfit, amount)       // 挂开多仓的买单
            sellOrderId = openShort(ticker.Last + targetProfit, amount)     // 挂开空仓的卖单
        } else if (pos[0].Type == PD_LONG) {   // 有多头持仓,挂单位置、数量有所不同
            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) {   // 有空头持仓,挂单位置、数量有所不同
            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) {   // 如果有一边挂单失败就取消所有挂单,重来
            cancelAll()
            buyOrderId = null 
            sellOrderId = null
            continue
        } 

        while (1) {  // 挂单完成,开始监控订单
            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) {    // 检测到买卖单都成交了
                cancelAll()
                break
            } else if (!isFindBuyId) {   // 检测到买单成交
                Log("买单成交")
                cancelAll()
                break
            } else if (!isFindSellId) {  // 检测到卖单成交
                Log("卖单成交")
                cancelAll()
                break
            }
            LogStatus(_D())
            Sleep(3000)
        }
        Sleep(500)
    }

Toda a lógica e design foram explicados.

Testes retrospectivos

Deixe a estratégia experimentar a situação do mercado em 19 de maio.

Design de estratégia de Martingale para futuros de criptomoedas

Design de estratégia de Martingale para futuros de criptomoedas

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

Você pode experimentar a demonstração do OKEX V5 para negociações reais.

Design de estratégia de Martingale para futuros de criptomoedas

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

As estratégias são usadas principalmente para aprendizado, então use dinheiro real com cautela ~!