Comercio cuantitativo de criptomonedas para principiantes - acercándote a la criptomoneda cuantitativa (6)

El autor:- ¿ Por qué?, Creado: 2022-08-05 17:13:26, Actualizado: 2023-09-21 21:02:17

img

En el último artículo, hicimos una estrategia de cuadrícula simple juntos. En este artículo, actualizamos y ampliamos esta estrategia en una estrategia de cuadrícula de puntos de múltiples especies, y dejamos que esta estrategia sea probada en la práctica. El propósito no es encontrar un "santo grial", sino discutir varios problemas y soluciones al diseñar estrategias. Este artículo explicará parte de mi experiencia en el diseño de esta estrategia. El contenido de este artículo es ligeramente complicado y requiere una cierta base en programación.

Pensamiento de diseño basado en necesidades estratégicas

Este artículo, al igual que el anterior, sigue discutiendo el diseño basado en el FMZ Quant (FMZ.COM).

  • Especies múltiples Para decirlo sin rodeos, creo que esta estrategia de red no sólo puedeBTC_USDT, pero tambiénLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTDe todos modos, los pares de negociación al contado y las variedades que quieren correr se negocian en la red al mismo tiempo.

    Se siente bien capturar el mercado volátil de múltiples especies. El requisito suena muy simple, y el problema surge cuando se diseña.

      1. Primero, se obtienen las cotizaciones de mercado de varias variedades. Este es el primer problema que se debe resolver. Después de consultar la documentación de la API de la bolsa, descubrí que la mayoría de las bolsas proporcionan interfaces de mercado agregadas. OK, usa la interfaz de mercado agregado para obtener datos.
      1. El segundo problema que se encuentra es los activos de la cuenta. Debido a que es una estrategia de múltiples especies, es necesario considerar la gestión de cada par de activos de negociación por separado. Y necesitamos obtener datos para todos los activos a la vez, y registrarlos. ¿Por qué necesitamos obtener los datos de los activos de la cuenta? ¿Por qué necesitamos separar los registros de cada par? Debido a que usted necesita juzgar los activos disponibles al hacer un pedido. ¿Es necesario obtenerlo antes de juzgarlo? Y usted necesita para calcular los beneficios, ¿es también necesario registrar una cuenta inicial de los datos de activos primero, a continuación, obtener los datos de activos de la cuenta corriente y compararlo con el inicial para calcular el beneficio y la pérdida? Afortunadamente, la interfaz de cuenta de activos de la bolsa normalmente devuelve todos los datos de activos de moneda, sólo necesitamos obtener una vez, y luego procesar los datos.
      1. Diseño de parámetros de estrategia. El diseño de parámetros de múltiples especies es bastante diferente del diseño de parámetros de variedad única, aunque la lógica de negociación de cada variedad de variedad múltiple es la misma, es posible que los parámetros durante el comercio sean diferentes. Por ejemplo, en la estrategia de la red, es posible que desee negociar 0.01 BTC cada vez que haga el par de operaciones BTC_USDT, pero obviamente es inapropiado usar este parámetro (negociar 0.01 monedas) cuando haga DOGE_USDT. Por supuesto, también puede lidiar con la cantidad de USDT. Pero todavía habrá problemas. ¿Qué pasa si desea negociar 1000U para BTC_USDT y 10U para DOGE_USDT? Puede haber alguien que piense en este problema y luego pregunte: Puedo establecer varios conjuntos de parámetros para controlar los parámetros de diferentes pares comerciales que se deben hacer por separado. Esto todavía no es lo suficientemente flexible como para satisfacer las necesidades, ¿cuántos conjuntos de parámetros son buenos para establecer? Por lo tanto, al diseñar los parámetros de la estrategia multiespecies, es necesario considerar plenamente las necesidades de dichos parámetros diferenciados. Por ejemplo:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      Entre ellos, gaman divide los datos de cada especie, lo que significa queETHUSDT:100:0.002controla el par de operaciones ETH_USDT, yLTCUSDT:20:0.1El par de operaciones LTC_USDT está controlado por el par de operaciones LTC_USDT. El par de operaciones LTC_USDT se controla por el par de operaciones LTC_USDT.ETHUSDT:100:0.002, donde ETHUSDT indica cuál es el par de operaciones que desea hacer, 100 es el espaciamiento de la cuadrícula, 0.002 es el número de monedas ETH negociadas en cada cuadrícula, y el : es para dividir estos datos (por supuesto, estas reglas de parámetros son hechas por el diseñador de estrategias, puede diseñar cualquier cosa de acuerdo a sus necesidades). Estas cadenas contienen la información de parámetros de cada especie que desea hacer. Parse estas cadenas en la estrategia, y asignar valores a las variables de la estrategia para controlar la lógica de negociación de cada especie.

      function main() {
          var net = []  // The recorded grid parameters, use the data when running to the grid trading logic
          var params = "ETHUSDT:100:0.002|LTCUSDT:20:0.1"
          var arrPair = params.split("|")
          _.each(arrPair, function(pair) {
              var arr = pair.split(":")
              var symbol = arr[0]              // Trading pair name
              var diff = parseFloat(arr[1])    // Grid spacing
              var amount = parseFloat(arr[2])  // Grid order volume
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("Grid parameter data:", net)
      }
      

      img

      Mirando esto, los parámetros se analizan. por supuesto, también se puede utilizar las cadenas JSON directamente, que es más simple.

      function main() {        
          var params = '[{"symbol":"ETHUSDT","diff":100,"amount":0.002},{"symbol":"LTCUSDT","diff":20,"amount":0.1}]'
          var net = JSON.parse(params)  // The recorded grid parameters, use the data when running to the grid trading logic        
          _.each(net, function(pair) {
              Log("Trading pairs:", pair.symbol, pair)
          })
      }
      

      img

      1. Durabilidad de los datos También hay una gran diferencia entre las estrategias que se pueden aplicar en la práctica y las estrategias tutoriales. Las estrategias tutoriales en el artículo anterior son solo una prueba preliminar de la lógica y el diseño de la estrategia, y hay más problemas a considerar cuando se trata del mundo real. En el bot real, es posible iniciar y detener el comercio real. En este momento, todos los datos durante la operación del bot real se perderán. Entonces, ¿cómo hacer que el bot real se reinicie para continuar funcionando en el estado anterior después de que se detuviera? Aquí, es necesario guardar los datos clave de forma persistente cuando el bot real está ejecutando, para que los datos puedan ser leídos y continuar ejecutándose cuando se reinicie. Puede usar el_G()la función de la plataforma de negociación cuantitativa FMZ, o utilizar la función de operación de la base de datosDBExec(), y puede consultar la documentación de la API de FMZ para obtener detalles.

      Por ejemplo, diseñamos una función de barrido de cola y utilizamos el_G()función para guardar datos de la red.

      var net = null 
      function main() {  // Strategy main functions
          // Read the stored net first
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Perform tail-sweeping processing and save data", "#FF0000")
      }
      
      function onexit() {    // The exit sweep function defined by the platform system, triggered the execution when the real bot is clicked to stop
          onExit()
      }
      
      function onerror() {   // The abnormal exit function defined by the platform system, triggered the execution when the program is abnormal
          onExit()
      }
      
      1. Limitaciones como la precisión de la cantidad de pedido, la precisión del precio del pedido, la cantidad mínima de pedido y el importe mínimo del pedido, etc.

      El sistema de backtesting no impone restricciones tan estrictas sobre el monto del pedido y la precisión del pedido, pero cada intercambio puede tener estándares estrictos para el precio y el monto del pedido al realizar un pedido en el bot real, y estas restricciones no son las mismas en diferentes intercambios. Por lo tanto, hay principiantes que prueban en el sistema de backtesting sin problemas. Una vez que se lanza el bot real, hay varios problemas cuando se activa el comercio, y luego no se lee el contenido del mensaje de error, y aparecen varios fenómenos locos.

      Para casos de múltiples especies, este requisito es más complicado. Para una estrategia de una sola especie, puede diseñar un parámetro para especificar información como la precisión, pero al diseñar una estrategia de múltiples especies, es obvio que escribir esta información en los parámetros hará que los parámetros se hinchen mucho.

      En este momento, debe verificar la documentación de la API del intercambio para ver si hay una información de interfaz relacionada con los pares de negociación en la documentación del intercambio. Si lo hay, puede diseñar una interfaz de acceso automático en la estrategia para obtener información como la precisión y configurarla en la información del par de negociación involucrada en la negociación (en resumen, la precisión o algo se obtiene del intercambio automáticamente, y luego se adapta a las variables relacionadas con los parámetros de la estrategia).

      1. Adaptación a las distintas bolsas ¿Por qué poner esta pregunta al final? Debido a que las soluciones a estos problemas que hemos hablado anteriormente traerán el último problema, porque nuestra estrategia planea utilizar la interfaz de mercado agregada, el acceso a la precisión del par de intercambio de operaciones y otros datos adaptativos, el acceso a la información de la cuenta para tratar con cada par de operaciones por separado, estas soluciones pueden variar mucho de intercambio a intercambio. Hay diferencias en las llamadas y mecanismos de la interfaz. Para los intercambios al contado, la diferencia es menor si la estrategia de red se extiende a la versión de futuros. Las diferencias en el mecanismo de varios intercambios son aún mayores. Una solución es diseñar una biblioteca de clases de plantillas FMZ. Escriba el diseño en la biblioteca de clases para implementar estas diferencias. Reduzca el acoplamiento entre la estrategia misma y el intercambio. La desventaja de esto es que usted necesita escribir una biblioteca de clases de plantilla, e implementarlo específicamente para cada diferencia de intercambio en esta plantilla.

Diseñar una biblioteca de clases de plantilla

Sobre la base del análisis anterior, una biblioteca de clases de plantilla está diseñada para reducir el acoplamiento entre la estrategia y el mecanismo e interfaz de intercambio.

Podemos diseñar esta biblioteca de clases de plantilla así (se omite parte del código):

function createBaseEx(e, funcConfigure) {
    var self = {}
    self.e = e 
    
    self.funcConfigure = funcConfigure
    self.name = e.GetName()
    self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
    self.label = e.GetLabel()
    
    // Interfaces to be implemented
    self.interfaceGetTickers = null   // Create a function to asynchronously obtain a thread of aggregated market data
    self.interfaceGetAcc = null       // Create a function that asynchronously obtains account data thread
    self.interfaceGetPos = null       // Get a position
    self.interfaceTrade = null        // Create concurrent orders
    self.waitTickers = null           // Waiting for concurrent market data 
    self.waitAcc = null               // Waiting for account concurrent data
    self.waitTrade = null             // Waiting for order concurrent data
    self.calcAmount = null            // Calculate the order volume based on data such as trading pair accuracy
    self.init = null                  // Initialization work, obtaining data such as accuracy
    
    // Execute the configuration function to configure the object
    funcConfigure(self)

    // Check whether the interfaces agreed by configList are implemented
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "unimplemented"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    // Implementation of OK futures
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // Implementation of wexApp
    }
    return dicRegister
}

En la plantilla, se escribe para intercambios específicos, tome el bot simulado de FMZ WexApp como ejemplo:

function funcConfigure_WexApp(self) {
    var formatSymbol = function(originalSymbol) {
        // BTC_USDT
        var arr = originalSymbol.split("_")
        var baseCurrency = arr[0]
        var quoteCurrency = arr[1]
        return [originalSymbol, baseCurrency, quoteCurrency]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://api.wex.app/api/v1/public/tickers")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait()).data
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.buy), 
                bid1Vol: parseFloat(-1),
                ask1: parseFloat(ele.sell), 
                ask1Vol: parseFloat(-1),
                symbol: formatSymbol(ele.market)[0],
                type: "Spot", 
                originalSymbol: ele.market
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {
            self.routineGetAcc = self.e.Go("GetAccount")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var arr = formatSymbol(symbol)
        var ret = null 
        if (self.updateAccsTS != updateTS) {
            ret = self.routineGetAcc.wait().Info
            self.bufferGetAccRet = ret 
        } else {
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }        
        var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        _.each(ret.exchange, function(ele) {
            if (ele.currency == arr[1]) {
                // baseCurrency
                acc.Stocks = parseFloat(ele.free)
                acc.FrozenStocks = parseFloat(ele.frozen)
            } else if (ele.currency == arr[2]) {
                // quoteCurrency
                acc.Balance = parseFloat(ele.free)
                acc.FrozenBalance = parseFloat(ele.frozen)
            }
        })
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
        var symbolInfo = self.getSymbolInfo(symbol)
        var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
        var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
        var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
        if (Math.abs(diffStocks) < symbolInfo.min / price) {
            return []
        }
        return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        var tradeType = ""
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeType = "bid"
        } else {
            tradeType = "ask"
        }
        var params = {
            "market": symbol,
            "side": tradeType,
            "amount": String(amount),
            "price" : String(-1),
            "type" : "market"
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v1/private/order", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // Obtain trading pair information
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ", the trading pair information cannot be checked"
        }
        var tradeAmount = null 
        var equalAmount = null  // Number of coins recorded
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // Check the minimum trading volume
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
            // Check the minimum trading volume
            if (tradeAmount < symbolInfo.min / price) {
                Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min / price)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {   // Functions that deal with conditions such as accuracy automatically
        var ret = JSON.parse(HttpQuery("https://api.wex.app/api/v1/public/markets"))
        _.each(ret.data, function(symbolInfo) {
            self.symbolsInfo.push({
                symbol: symbolInfo.pair,
                amountPrecision: parseFloat(symbolInfo.basePrecision),
                pricePrecision: parseFloat(symbolInfo.quotePrecision),
                multiplier: 1,
                min: parseFloat(symbolInfo.minQty),
                originalInfo: symbolInfo
            })
        })        
    }
}

Entonces usar esta plantilla en una estrategia es simple:

function main() {
    var fuExName = exchange.GetName()
    var fuConfigureFunc = $.getConfigureFunc()[fuExName]
    var ex = $.createBaseEx(exchange, fuConfigureFunc)

    var arrTestSymbol = ["LTC_USDT", "ETH_USDT", "EOS_USDT"]
    var ts = new Date().getTime()
    
    // Test to get tickers
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // Test to obtain account information
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print ticker data
                Log(symbol, ticker)
            }
        })

        // print asset data
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)
    })
}

Estrategia bot real

Es muy simple diseñar y escribir una estrategia basada en la plantilla anterior.

img

img

Está perdiendo dinero actualmente.T_T, el código fuente no será publicado por el momento.

Aquí hay algunos códigos de registro, si estás interesado, puedes usar la wexApp para intentarlo:

Buy address: https://www.fmz.com/m/s/284507
Registration code: 
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

Poco más de 200 U, cuando empecé a correr, me encontré con un gran mercado unilateral, pero me recuperé lentamente. La estabilidad no es mala, no ha sido modificada desde el 27 de mayo y la red de futuros no se ha atrevido a intentarlo temporalmente.


Relacionados

Más.