Diseño de estrategias de cobertura de efectivo de la moneda digital (1)

El autor:Un sueño pequeño., Creado: 2021-07-19 17:38:24, Actualizado: 2023-09-20 10:35:16

img

Diseño de estrategias de cobertura de efectivo de la moneda digital (1)

Para los principiantes en el diseño de estrategias, las estrategias de cobertura son muy buenas estrategias de práctica. Este artículo implementa una estrategia de cobertura de efectivo de moneda digital simple pero práctica, con la esperanza de que los principiantes aprendan algo de experiencia en diseño.

Diseñar algunas funciones, parámetros de interfaz de la política según las necesidades de la política

Para empezar, hay que aclarar que la estrategia que se está diseñando es una estrategia de cobertura de divisas digitales, y hemos diseñado la cobertura más simple, que consiste en que sólo los intercambios con un precio más alto se venden entre dos intercambios de divisas, y los intercambios con un precio más bajo se compran para obtener el diferencial. No se puede hacer una cobertura cuando los intercambios con un precio más alto son todos monedas (porque los precios más altos se han vendido) y los intercambios con un precio más bajo son todos monedas (porque los precios más bajos se han comprado en monedas).

El precio, la cantidad y la precisión de los intercambios tienen límites, y también hay un límite mínimo de cantidades. Además de la estrategia de límite mínimo, también se debe tener en cuenta la cantidad máxima de pedido de un solo pedido de cobertura, y no habrá un volumen de pedido suficiente si el pedido es demasiado grande. También se debe tener en cuenta cómo se convierte el tipo de cambio si las dos monedas cotizadas son diferentes.

En base a estas consideraciones, la estrategia necesita diseñar varios parámetros:

  • El precio de cobertura:hedgeDiffPrice, cuando el diferencial excede este valor, se activa la operación de cobertura.
  • El riesgo mínimo es:minHedgeAmountEl número mínimo de monedas que se pueden cubrir.
  • El riesgo máximo es:maxHedgeAmountEl número de monedas es el número máximo de monedas que se pueden cobrar en un hedge.
  • A Precisión del precio:pricePrecisionALa precisión de los precios de los pedidos de los intercambios A.
  • A: Precisión de las unidades:amountPrecisionALa precisión de la unidad de A en el intercambio (número de dígitos menores).
  • Precisión del precio B:pricePrecisionBLa precisión de los precios de los pedidos en el intercambio B (número de dígitos)
  • Precisión de las unidades B:amountPrecisionBLa precisión de la unidad más baja del intercambio B (número de dígitos menores)
  • A cambio de divisas:rateA, la conversión de la tasa de cambio del primer objeto de intercambio añadido, por defecto 1 no se convierte.
  • El intercambio B:rateB, la conversión de la tasa de cambio del segundo objeto de intercambio añadido, por defecto 1 no se convierte.

La estrategia de cobertura requiere mantener el número de monedas de las dos cuentas invariables (es decir, no tener posiciones en ninguna dirección, mantenerse neutro), por lo que se requiere una lógica de equilibrio en la estrategia: siempre detectar el equilibrio. Cuando se detecta el equilibrio, no se puede evitar obtener datos de activos de los dos intercambios. Necesitamos escribir una función para usarla.

  • Actualización de los Accs
    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 
    }
    

Si no se realiza una orden después de la orden actual, no podemos dejar la orden colgada. Esta operación debe ser procesada tanto en el módulo de equilibrio como en la lógica de cobertura, por lo que también se necesita diseñar una función de cancelación total de la orden.

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

En el equilibrio del número de monedas, necesitamos encontrar el precio acumulado hasta cierto número de monedas en un dato de profundidad, por lo que necesitamos una función como esta para procesarla.

  • Obtener el DepthPrice
    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
    }
    

Y luego tenemos que diseñar y escribir operaciones de subordinación específicas para el arriesgamiento, y tenemos que diseñarlas para subordinación simultánea:

  • el seto
    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()
    }
    

Por último, vamos a terminar el diseño de la función de equilibrio, que es un poco más compleja.

  • mantener el equilibrio
    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 
        }
    }
    

Estas funciones se han diseñado de acuerdo con las necesidades de la estrategia, y a continuación se puede comenzar a diseñar las funciones principales de la estrategia.

Diseño de la función principal de la estrategia

La estrategia en FMZ es desdemainLa función comenzó a ejecutarse enmainEn la parte inicial de la función, vamos a hacer algunos trabajos de inicialización de la política.

  • Nombre del objeto del intercambio Debido a que muchas de las operaciones de la estrategia se utilizan para objetos de intercambio, como obtener mercados, hacer pedidos, etc. Por lo tanto, usar un nombre más largo cada vez puede ser un problema, el truco es usar un nombre simple en su lugar, por ejemplo:

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

    En la actualidad, la mayoría de las aplicaciones de código abierto están disponibles en la web, pero la mayoría de las aplicaciones de código abierto están disponibles en la web.

  • Desarrollo relacionado con el tipo de cambio y la precisión

      // 精度,汇率设置
      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)
    

    Si el parámetro de cambiorateArateBSi el valor es 1 (default es 1), es decir,rateA != 1¿Qué es esto?rateB != 1No se activará, por lo que no se establecerá la conversión de tipos de cambio.

  • Restablecer todos los datos

    img

    Algunas veces se necesita eliminar todos los registros y registros en blanco cuando se inicia una política. Se puede diseñar un parámetro de interfaz de política.isReset, y luego diseña el código de reubicación de la parte inicializada en la política, por ejemplo:

      if (isReset) {   // 当isReset为真时重置数据
          _G(null)
          LogReset(1)
          LogProfitReset()
          LogVacuum()
          Log("重置所有数据", "#FF0000")
      }
    
  • Restaurar los datos de la cuenta inicial, actualizar los datos de la cuenta actual Para determinar el equilibrio, la estrategia requiere registrar continuamente el estado de los activos de la cuenta inicial en comparación con el actual.nowAccsEsta es la variable que se usa para registrar los datos de la cuenta actual usando una función que acabamos de diseñar.updateAccsObtener datos de cuentas de los intercambios actuales.initAccsSe utiliza para registrar el estado inicial de las cuentas (datos como el número de monedas de los intercambios A y B, el número de monedas cuantificadas).initAccsEl primer uso_G()Función de recuperación ((_G) La función _G registra datos permanentemente y puede volver a registrar los datos registrados, consulte la documentación de la API:EnlacesEn caso de que la consulta no sea posible, asignar y utilizar la información de la cuenta actual._GEl registro de funciones.

    Por ejemplo, el siguiente código:

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

Lógica de transacciones, el ciclo principal de las funciones principales

El código en el bucle principal es el proceso que se ejecuta en cada ronda de la lógica de la política, y la ejecución continua y repetida constituye el bucle principal de la política. Veamos a continuación el proceso que se ejecuta cada vez que un programa en el bucle principal.

  • Obtener datos de mercado para evaluar su eficacia

          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 
          }
    

    Aquí se pueden ver las funciones de concurrencia que utilizan la plataforma FMZ.exchange.GoCreado el llamadoGetDepth()Objetos simultáneos de la interfazdepthARoutinedepthBRoutine│ Cuando se crean estos dos objetos simultáneos, se llamaGetDepth()La interfaz también se produjo de inmediato, cuando ambas solicitudes para obtener datos profundos se enviaron al intercambio en el pasado. Entonces llamamosdepthARoutinedepthBRoutineObjetowait()Los métodos para obtener datos de profundidad.
    Después de obtener los datos de profundidad, se necesita examinar los datos de profundidad para determinar su eficacia.continueLas frases vuelven a ejecutar el ciclo principal.

  • Uso价差值¿Cuál es el parámetro?差价比例¿Cuáles son los 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
          }
    

    En los parámetros hemos hecho este diseño. Los parámetros de FMZ pueden basarse en algún parámetro.Muestra¿Qué es esto?EscondidoAsí que podemos hacer un parámetro para decidir si se usa.价格差¿Qué es esto?差价比例

    img

    Añadir un parámetro en el parámetro de la interfaz de la estrategiadiffAsPercentageLos otros dos parámetros para mostrar o ocultar basados en este parámetro son:hedgeDiffPrice@!diffAsPercentageCuandodiffAsPercentagePara mostrar este parámetro por defecto.hedgeDiffPercentage@diffAsPercentageCuandodiffAsPercentage¿Por qué mostrar el parámetro como verdad? Después de diseñarlo, seleccionamos:diffAsPercentageParámetros, es decir, en proporción a la diferencia de precios como condición para activar el arriesgamiento.diffAsPercentageLos parámetros son la diferencia de precio como condición para activar el arriesgamiento.

  • Determinación de las condiciones que desencadenan la cobertura

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

    En la actualidad, el sector de los servicios de telecomunicaciones tiene una importante presencia en los mercados de capitales. 1, primero se satisface el diferencial de cobertura y sólo se puede cubrir cuando el diferencial de la transacción satisface el parámetro de diferencia establecido. 2, el valor de cobertura de transacción debe cumplir con el valor mínimo de cobertura establecido en los parámetros, ya que los valores mínimos que pueden limitarse en diferentes mercados son diferentes, por lo que se debe tomar el menor de los dos. 3. Los activos de los intercambios que venden las operaciones son suficientes para vender y los activos de los intercambios que compran las operaciones son suficientes para comprar. Cuando se cumplen estas condiciones, se ejecuta una función de cobertura. Antes de la función principal, declaramos una variable.isTradePara marcar si se produjo un riesgo, aquí si se produce un riesgo, se establece la variable comotrueY también restablecer las variables globales.lastKeepBalanceTSPara 0 ((lastKeepBalanceTS se utiliza para marcar el tiempo de la última operación de equilibrio, que se establece en 0 para activar la operación de equilibrio inmediatamente) y luego cancelar todas las listas colgantes).

  • Operaciones de equilibrio

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

    Se puede ver que las funciones de equilibrio se ejecutan regularmente, pero si se activa una operación de cobertura después de que se haya activado una operación de equilibrio, la función de equilibrio se ejecuta regularmente.lastKeepBalanceTSSi se restablece a 0, la operación de equilibrio se activará inmediatamente. Se calculará el beneficio después del éxito del equilibrio.

  • Información de la 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", 
              "当前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)
    

    La barra de estado no es de un diseño especialmente complejo, muestra la hora actual, muestra la diferencia de los intercambios de A a B y de B a A. Muestra la diferencia de los objetivos de cobertura actuales. Muestra los datos de activos de las cuentas de A y B.

Tratamiento de las transacciones en pares de divisas diferentes

En los parámetros hemos diseñado los parámetros de conversión de valores de los tipos de cambio.mainTambién hemos diseñado la conversión de tipos de cambio. Hay que tener en cuenta queSetRateLa función de conversión de los tipos de cambio debe ejecutarse primero. La función tiene dos dimensiones:

  • Los precios en todos los datos de mercado, datos de pedidos y datos de almacenamiento.
  • El cambio de divisas en los activos de la cuenta. Por ejemplo, el par de transacciones en cursoBTC_USDTEl precio es el mismo.USDTEn el caso de los activos de la cuenta, también se puede utilizar moneda.USDTSi quiero cambiar el valor a CNY, configurarlo en el códigoexchange.SetRate(6.8)¿Qué es esto?exchangeTodos los datos obtenidos por las funciones bajo este objeto de intercambio se cambian a CNY. En cambio, ¿por qué el precio de las monedas?SetRateTransmisión de funcionesTales de cambio de la moneda actual a la moneda de destino

La estrategia completa es:Estrategias de cobertura de divisas en el mercado de divisas (enseñanza)


Relacionados

Más.

Es una especie de escarabajo de Ucrania.Qué bueno.