Novato, comprueba Te llevará al comercio cuantitativo de criptomonedas (6)

El autor:No lo sé., Creado: 2022-04-21 18:13:03, Actualizado: 2022-04-22 12:00:05

Novato, comprueba Te llevará al comercio cuantitativo de criptomonedas (6)

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 varios símbolos, 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 en el proceso de diseño de estrategias. Este artículo explicará parte de mi experiencia en el diseño de la estrategia. El contenido de este artículo es ligeramente complicado y requiere una cierta base en programación.

Pensamiento del diseño basado en el requisito de la estrategia

En este artículo, al igual que en el anterior, discutimos el diseño basado en la plataforma de negociación cuántica FMZ (FMZ.COM).

  • Símbolo múltiple Para ser honesto, quiero que la estrategia de red no sóloBTC_USDT, pero tambiénLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTDe todos modos, para los pares de comercio al contado, operar la red de comercio de todos los símbolos que desea comerciar al mismo tiempo.

    Sí, se siente bien capturar las cotizaciones vibrante del mercado de múltiples símbolos.
    Aunque el requisito suena simple, se vuelve difícil cuando empiezas a diseñar.

      1. Primero, obtener las cotizaciones de mercado de múltiples símbolos. Es el primer problema que se debe resolver. Después de leer la documentación de la API de la plataforma, descubrí que las plataformas generalmente proporcionan las interfaces agregadas. Vale, usamos la interfaz de datos de mercado agregados para obtener los datos.
      1. El segundo problema es los activos de la cuenta. Para que queramos realizar estrategias de símbolos múltiples, debemos considerar administrar los activos de cada par de operaciones por separado, y obtener todos los datos de activos y registrarlos una vez. ¿Por qué debemos obtener los datos de activos de la cuenta? Y por qué también registrar cada par de operaciones por separado?

    Debido a que es necesario evaluar los activos disponibles al realizar pedidos, ¿no es necesario obtener los datos antes del juicio? Además, hay que calcular el rendimiento. ¿Deberíamos registrar primero los datos iniciales del activo de la cuenta corriente y luego obtener los datos del activo de la cuenta corriente y calcular el beneficio y la pérdida comparándolos con el inicial? Afortunadamente, la interfaz de cuenta de activos de una plataforma generalmente devuelve todos los datos de activos de divisas, por lo que solo necesitamos obtenerlos una vez y luego procesar los datos.

      1. Diseño de parámetros de estrategia. El diseño de parámetros de la estrategia de símbolos múltiples es bastante diferente del diseño de parámetros de uno de símbolos únicos, porque incluso la lógica de negociación de cada símbolo en la estrategia de símbolos múltiples es la misma, es posible que los parámetros de cada símbolo durante la negociación sean diferentes. Por ejemplo, en la estrategia de cuadrícula, 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 moneda) cuando haga DOGE_USDT. Por supuesto, también puede procesar por la cantidad de USDT. Pero aún habrá problemas. ¿Qué pasa si desea negociar 1000U por BTC_USDT y 10U por DOGE_USDT? Probablemente, algunos estudiantes podrían pensar en la pregunta, y proponer que, Puedo establecer más grupos de parámetros, y controlar por separado los parámetros de los diferentes pares de negociación a operar. ¿Qué tal si queremos operar 4 símbolos? ¿Necesitamos modificar la estrategia y aumentar los parámetros? Por lo tanto, piense plenamente en la necesidad de diferenciación al diseñar parámetros de estrategia de símbolos múltiples.
        Por ejemplo:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      se utiliza para dividir los datos de cada símbolo, lo que indica queETHUSDT:100:0.002controla el par de operaciones ETH_USDT, yLTCUSDT:20:0.1El símbolo en el centro desempeña un papel de segmentación. En elETHUSDT:100:0.002, ETHUSDT representa el par de operaciones que desea operar; 100 es el espaciamiento de la cuadrícula; 0.002 es la cantidad de ETH negociada de cada cuadrícula; : se utiliza para dividir los datos mencionados anteriormente (seguramente, las reglas de parámetros son hechas por el diseñador de la estrategia; puede diseñar lo que quiera en función de sus necesidades).
      Estas cadenas ya contienen la información de parámetros de cada símbolo que necesita para operar. Puede analizar las cadenas y asignar valores a las variables en la estrategia, para controlar la lógica de negociación de cada símbolo. ¿Cómo analizar? Usemos el ejemplo mencionado anteriormente.

      function main() {
          var net = []  // the recorded grid parameters; when specifically running the grid trading logic, use the data from here 
          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 amount 
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("Grid parameter data:", net)
      }
      

      img

      Ver, hemos analizado los parámetros. Claro, usted puede utilizar directamente cadenas JSON, que es más fácil.

      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; when specifically running the grid trading logic, use the data from here         
          _.each(net, function(pair) {
              Log("Trading pair:", pair.symbol, pair)
          })
      }
      

      img

      1. Persistencia de los datos Hay una gran diferencia entre una estrategia práctica y una estrategia de enseñanza. La estrategia de enseñanza en el último artículo es sólo para la prueba inicial de la lógica de la estrategia y el diseño. Hay más problemas de los que preocuparse cuando realmente ejecutar una estrategia en un bot. Cuando se ejecuta un bot, el bot se puede iniciar y detener. En ese momento, todos los datos durante la ejecución del bot se perderán. Entonces, ¿cómo continuar el estado anterior al reiniciar el bot, después de que se detenga el bot? Aquí, es necesario guardar persistentemente los datos clave cuando el bot se está ejecutando, para que los datos puedan ser leídos y el bot continúe ejecutándose cuando el bot se reinicie. Puede usar el_G()función en FMZ Quant, o utilizar la función de operaciónDBExec()en la base de datos, y puede consultar la documentación de la API FMZ para obtener detalles.

      Por ejemplo, queremos diseñar una función de limpieza utilizando la función_G(), para guardar los datos de la red.

      var net = null 
      function main() {  // strategy main function 
          // first read the stored net 
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Execute the clean-up processing, and save the data", "#FF0000")
      }
      
      function onexit() {    // the onexit function defined by the platform system, which will be triggered when clicking the bot to stop 
          onExit()
      }
      
      function onerror() {   // the onerror function defined by the platform system, which will be triggered when the program exception occurs 
          onExit()
      }
      
      1. Los límites de la precisión del importe de la orden, la precisión del precio de la orden, el volumen mínimo de la orden, el importe mínimo de la orden, etc.

      El sistema de backtest no tiene límites tan estrictos en el volumen de orden y precisión de orden; pero en un bot, cada plataforma tiene estándares estrictos para el precio de la orden y el volumen de orden, y diferentes pares de operaciones tienen límites diferentes. Por lo tanto, los principiantes a menudo prueban OKEX en el sistema de backtest. Una vez que la estrategia se ejecuta en un bot, hay varios problemas cuando se activa un comercio, y luego no se lee el contenido del mensaje de error, y aparecen varios fenómenos locos.

      Para los casos de múltiples símbolos, el requisito es más complicado. Para una estrategia de un solo símbolo, puede diseñar un parámetro para especificar información como precisión. Sin embargo, cuando diseña una estrategia de múltiples símbolos, es obvio que escribir la información en un parámetro hará que el parámetro sea muy tedioso.

      En este momento, debe verificar la documentación de la API de la plataforma para ver si hay interfaces para la información relacionada con el par de operaciones en la documentación. Si existen estas interfaces, puede diseñar una interfaz de acceso automático en la estrategia para obtener información como precisión y configurarla en la información del par de operaciones en el comercio (en resumen, la precisión se obtiene automáticamente de la plataforma y luego se adapta a la variable relacionada con el parámetro de estrategia).

      1. Adaptación de una plataforma diferente ¿Por qué se menciona el problema al final? El tratamiento de todos los problemas que hemos mencionado anteriormente conducirá al último problema. Debido a que nuestra estrategia planea utilizar la interfaz de mercado agregada, soluciones como acceder a la precisión del par de operaciones de la plataforma y otra adaptación de datos, así como acceder a la información de la cuenta para procesar cada par de operaciones por separado, etc., traerá grandes diferencias debido a las diferentes plataformas. Hay diferencias en la llamada de la interfaz y diferencias en el mecanismo. Para las plataformas spot, la diferencia es comparativamente pequeña si la estrategia de red se extiende a la versión de futuros. Las diferencias en el mecanismo de cada plataforma son aún mayores. Una solución es diseñar una biblioteca de plantillas FMZ; escribir el diseño de implementación de la diferenciación en la biblioteca para reducir el acoplamiento entre la estrategia misma y la plataforma. La desventaja de eso es que usted necesita escribir una biblioteca de plantillas, y en esta plantilla, específicamente implementar la diferenciación basada en cada plataforma.

Diseñar una biblioteca de plantillas

Basándonos en el análisis anterior, diseñamos una biblioteca de plantillas para reducir el acoplamiento entre estrategia, mecanismo de plataforma e interfaz.
Podemos diseñar la biblioteca de plantillas 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()
    
    // the interfaces that need to be implemented 
    self.interfaceGetTickers = null   // create a function that asynchronously obtains the aggregated market quote threads
    self.interfaceGetAcc = null       // create a function that asynchronously obtains the account data threads 
    self.interfaceGetPos = null       // obtain positions 
    self.interfaceTrade = null        // create concurrent orders 
    self.waitTickers = null           // wait for the concurrent market quote data  
    self.waitAcc = null               // wait for the account concurrent data 
    self.waitTrade = null             // wait for order concurrent data
    self.calcAmount = null            // calculate the order amount according to the trading pair precision and other data 
    self.init = null                  // initialization; obtain the precision and other data 
    
    // execute the configuration function, to configure objects 
    funcConfigure(self)

    // detect whether all the interfaces arranged by configList can be implemented 
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "not implemented"
        }
    })
    
    return self
}

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

En la plantilla, implemente la escritura de código dirigida a una forma de juego específica; tome el bot simulado 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 the trading pair information 
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",trading pair information not found"
        }
        var tradeAmount = null 
        var equalAmount = null  // record the symbol amount  
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // detect the minimum trading amount 
            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))
            // detect the minimum trading amount 
            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() {   // the function that automatically processes conditions like precision, etc.  
        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
            })
        })        
    }
}

Será muy fácil utilizar la plantilla en la estrategia:

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 obtain the market quotes 
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // test to obtain the account information 
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print the market quote data 
                Log(symbol, ticker)
            }
        })

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

Bot de estrategia

Es muy simple diseñar y escribir la estrategia basada en la plantilla anterior. La estrategia completa tiene alrededor de más de 300 líneas de código.

img

img

Ahora mismo, tiene pérdidas.T_T, por lo que el código fuente no se proporcionará.

Hay varios códigos de registro; si está interesado, puede probarlos en wexApp:

Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

Solo había más de 200 USD, y cuando el bot recién comenzó, se encontró con un gran mercado unilateral. Necesita tiempo para cubrir la pérdida. La estabilidad de la estrategia está bien, y no la he modificado desde el 27 de mayo.


Más.