avatar of 发明者量化-小小梦 发明者量化-小小梦
Seguir Mensajes Privados
4
Seguir
1271
Seguidores

Diseño de estrategia Martingala para futuros de criptomonedas

Creado el: 2021-07-02 14:35:34, Actualizado el: 2023-09-21 21:09:56
comments   22
hits   6170

Diseño de estrategia Martingala para futuros de criptomonedas

Diseño de estrategia Martingala para futuros de criptomonedas

Recientemente, ha habido muchas discusiones sobre estrategias Martingala en el grupo oficial de FMZ, pero no hay muchas estrategias Martingala para contratos de moneda digital en la plataforma. Por lo tanto, aproveché esta oportunidad para diseñar una estrategia Martingala simple para futuros de criptomonedas. ¿Por qué se denomina estrategia tipo Martin? Porque los riesgos potenciales de la estrategia Martin no son pequeños, por lo que no está diseñada completamente de acuerdo con ella. Sin embargo, este tipo de estrategia aún conlleva riesgos considerables, y los parámetros de configuración de la estrategia Martingala están estrechamente relacionados con los riesgos, que no deben ignorarse.

Este artículo explica y aprende principalmente sobre el diseño de estrategias de tipo Martin. La idea de la estrategia en sí es muy clara. Como usuarios de FMZ, consideramos más el diseño de estrategias.

Obtenga el capital total

Al diseñar estrategias de futuros de monedas digitales, a menudo se utilizan los datos de capital total. Porque es necesario calcular los rendimientos, especialmente cuando se necesitan calcular rendimientos flotantes. Dado que las posiciones abiertas ocupan margen, las órdenes pendientes también ocupan margen. En este momento, llame a la interfaz API de la plataforma FMZexchange.GetAccount()Lo que se obtiene son los activos disponibles y los activos congelados por órdenes pendientes. De hecho, la mayoría de los mercados de futuros de monedas digitales proporcionan datos sobre el capital total, pero FMZ no encapsula uniformemente este atributo.

Por lo tanto, diseñamos funciones para obtener estos datos según diferentes intercambios:

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

En el códigototalEquityÉste es el capital total que necesitamos. Luego escribimos una función como entrada de llamada y llamamos a la función correspondiente según el nombre de intercambio.

function getTotalEquity() {
    var exName = exchange.GetName()
    if (exName == "Futures_OKCoin") {
        return getTotalEquity_OKEX_V5()
    } else if (exName == "Futures_Binance") {
        return getTotalEquity_Binance()
    } else {
        throw "不支持该交易所"
    }
}

Diseñar algunas funciones auxiliares

Antes de diseñar la función principal y la lógica principal. Todavía tenemos que hacer algunos preparativos y diseñar algunas funciones auxiliares.

  • Cancelar todos los pedidos pendientes actuales
  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)
      }
  }

Creo que quienes consultan con frecuencia los códigos de ejemplo de estrategia en el FMZ Strategy Square están muy familiarizados con esta función. Muchas estrategias han utilizado diseños similares. Su función es obtener la lista actual de órdenes pendientes y luego cancelarlas una por una.

  • Operación de órdenes 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)
  }

Hay cuatro direcciones en el comercio de futuros: abrir una posición larga (openLong), abrir una posición corta (openShort), cerrar una posición larga (coverLong) y cerrar una posición corta (coverShort). Así que diseñamos cuatro funciones de orden para corresponder a estas operaciones. Si solo está considerando realizar un pedido, hay varios factores necesarios: dirección, precio del pedido y cantidad del pedido. Así que también diseñamos un programa llamado:tradeLa función a manejar方向(distance)下单价格(price)下单量(amount)Todas las operaciones están claras. Las llamadas de función de apertura de una posición larga (openLong), apertura de una posición corta (openShort), cierre de una posición larga (coverLong) y cierre de una posición corta (coverShort) se ejecutan en última instancia mediantetradeLa función realiza la función real, que es colocar una orden en la bolsa de futuros de acuerdo con la dirección, el precio y la cantidad establecidos.

Función principal

La estrategia es muy sencilla: se utiliza el precio actual como base y se colocan órdenes de venta (posición corta) y órdenes de compra (posición larga) a una cierta distancia por encima y por debajo del precio actual. Una vez que se ejecuta un lado, todas las órdenes restantes se cancelarán y luego se colocará una nueva orden de cierre a una cierta distancia en función del precio de la posición, y se colocará una orden de aumento al precio actual actualizado, pero la orden de aumento se No duplicar la cantidad del pedido.

  • Trabajo inicial Como necesitamos realizar pedidos, necesitamos dos variables globales para registrar los identificadores de los pedidos.
  var buyOrderId = null
  var sellOrderId = null

Luego, la opción de usar el disco de simulación OKEX_V5 está diseñada en los parámetros de la interfaz de estrategia, por lo que es necesario realizar algún procesamiento en el código:

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

Los parámetros de la interfaz también incluyen una opción para restablecer toda la información, por lo que el código también debe tener el procesamiento correspondiente:

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

Solo ejecutamos contratos perpetuos, por lo que está codificado aquí y configurado solo para contratos perpetuos.

  exchange.SetContractType("swap")

Luego también debemos considerar la precisión de los precios y las cantidades de los pedidos. Si la precisión no se establece correctamente, se perderá durante el proceso de cálculo de la estrategia. Si los datos tienen muchos decimales, es fácil que el pedido se desvíe. rechazado por la interfaz de intercambio.

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

Función de recuperación de datos sencilla

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

Si desea especificar el capital total inicial de la cuenta cuando se ejecuta la estrategia, puede configurar el parámetrototalEqSi este parámetro se establece en -1, la estrategia leerá los datos de capital total almacenados. Si no hay datos de capital total almacenados, el capital total leído actualmente se utilizará como el capital total inicial del progreso de ejecución de la estrategia. Si el total el patrimonio aumenta, es decir si ganas dinero pero tu patrimonio total es menor, significa que has perdido dinero. Si se leen los datos de capital total, continúe ejecutando usando estos datos.

  • Lógica principal Después de realizar el trabajo inicial, finalmente llegamos a la lógica principal de la estrategia. Para facilitar la explicación, escribí la explicación directamente en los comentarios del 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)
    }

Se ha explicado toda la lógica y el diseño.

Prueba retrospectiva

Dejemos que la estrategia experimente la situación del mercado el 19 de mayo.

Diseño de estrategia Martingala para futuros de criptomonedas

Diseño de estrategia Martingala para futuros de criptomonedas

Se puede observar que la estrategia Martingala todavía tiene ciertos riesgos.

Puedes probar la demostración de OKEX V5 para realizar operaciones reales.

Diseño de estrategia Martingala para futuros de criptomonedas

Dirección de estrategia: https://www.fmz.com/strategy/294957

Las estrategias se utilizan principalmente para aprender, así que utilice dinero real con precaución.