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

Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

Creado el: 2021-06-04 10:08:48, Actualizado el: 2024-12-04 21:14:15
comments   6
hits   2596

Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

En el artículo anterior, trabajamos juntos para crear una estrategia de cuadrícula simple. En este artículo, mejoraremos y ampliaremos esta estrategia para convertirla en una estrategia de cuadrícula de puntos de múltiples variedades y la pondremos a prueba en combate real. El objetivo no es encontrar un “santo grial”, sino explorar diversos problemas y soluciones a la hora de diseñar estrategias. En este artículo se explicarán algunas de mis experiencias en el diseño de esta estrategia. El contenido de este artículo es un poco complicado y requiere cierta base en programación.

Design thinking basado en necesidades estratégicas

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

  • Variedades múltiples Para decirlo sin rodeos, creo que esta estrategia de red no sóloBTC_USDT, también puede hacerloLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. De todos modos, para los pares de negociación al contado, todos los productos que desea negociar pueden negociarse en la red al mismo tiempo.

Mmm~~Se siente bien capturar el mercado volátil de múltiples variedades. Los requisitos parecen simples, pero surgen problemas durante el diseño.

    1. En primer lugar, obtenga la información del mercado de múltiples variedades. Éste es el primer problema a resolver. Después de verificar la documentación de la API del exchange, descubrí que la mayoría de los exchanges proporcionan una interfaz de información de mercado agregada. Bien, utilice la interfaz de datos de mercado agregados para obtener datos.
    1. El segundo problema encontrado son los activos de la cuenta. Dado que queremos implementar una estrategia multivariante, debemos considerar la gestión separada de los activos para cada transacción. Y obtener datos y registros de todos los activos a la vez. ¿Por qué necesitamos obtener datos de los activos de la cuenta? ¿Necesitamos mantener registros separados para cada par de transacciones? Dado que es necesario evaluar los activos disponibles al realizar un pedido, ¿es necesario obtenerlos antes de emitir un juicio? Y es necesario calcular los ingresos. ¿También es necesario registrar primero los datos de los activos de la cuenta inicial y luego obtener los datos de los activos de la cuenta corriente y compararlos con los iniciales para calcular las ganancias y las pérdidas? Afortunadamente, la interfaz de cuenta de activos del exchange suele devolver los datos de activos de todas las monedas. Solo tenemos que obtenerlos una vez y luego procesarlos.
    1. Diseño de parámetros de estrategia. El diseño de parámetros de variedades múltiples es bastante diferente al de variedades individuales, porque si bien la lógica comercial de cada variedad es la misma, los parámetros durante el comercio pueden ser diferentes. Por ejemplo, en una estrategia de cuadrícula, es posible que desees negociar 0,01 BTC cada vez que hagas operaciones con pares BTC_USDT. Sin embargo, si aún usas este parámetro (negociando 0,01 monedas) cuando haces operaciones con DOGE_USDT, obviamente es inapropiado. Por supuesto, puedes También lo manejamos de acuerdo al monto de USDT. Pero aún habrá problemas. ¿Qué pasa si solo quieres negociar 1000U con BTC_USDT y 10U con DOGE_USDT? La demanda nunca podrá ser satisfecha. Algunos estudiantes pueden pensar en esta pregunta y luego decir: “Puedo configurar varios grupos de parámetros para controlar por separado los parámetros de diferentes pares comerciales”. Esto todavía no puede satisfacer las necesidades de manera flexible. ¿Cuántos grupos de parámetros se deben configurar? Se establecen tres conjuntos de parámetros. ¿Qué sucede si quiero comercializar 4 variedades? ¿Es posible modificar la estrategia y agregar parámetros? Por lo tanto, al diseñar los parámetros de una estrategia multivariante, debemos tener plenamente en cuenta la demanda de parámetros diferenciados. Una solución es diseñar los parámetros como cadenas ordinarias o cadenas JSON. Por ejemplo:
    ETHUSDT:100:0.002|LTCUSDT:20:0.1
    

    El “|” separa los datos de cada variedad, lo que significaETHUSDT:100:0.002Controla el par comercial ETH_USDT.LTCUSDT:20:0.1Controla el par comercial LTC_USDT. El “|” en el medio sirve como separador. ETHUSDT:100:0.002, donde ETHUSDT indica el par comercial que desea negociar, 100 es el espaciado de la cuadrícula, 0.002 es la cantidad de monedas ETH negociadas en cada cuadrícula y el signo “:” se usa para separar estos datos (por supuesto, estas reglas de parámetros son establecido por el diseñador de la estrategia). Puedes diseñarlo según tus necesidades). Estas cadenas contienen la información de los parámetros de cada producto que desea comercializar. Analice estas cadenas en la estrategia y asigne valores específicos a las variables de la estrategia para controlar la lógica de negociación de cada producto. Entonces, ¿cómo analizarlo? Utilicemos nuevamente el ejemplo anterior.

    function main() {
        var net = []  // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据
        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]              // 交易对名称
            var diff = parseFloat(arr[1])    // 网格间距
            var amount = parseFloat(arr[2])  // 网格下单量
            net.push({symbol : symbol, diff : diff, amount : amount})
        })
        Log("网格参数数据:", net)
    }
    

    Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

    Puedes ver que los parámetros se analizan de esta manera. Por supuesto, también puedes usar cadenas JSON directamente, lo cual es más sencillo.

    function main() {        
        var params = '[{"symbol":"ETHUSDT","diff":100,"amount":0.002},{"symbol":"LTCUSDT","diff":20,"amount":0.1}]'
        var net = JSON.parse(params)  // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据        
        _.each(net, function(pair) {
            Log("交易对:", pair.symbol, pair)
        })
    }
    

    Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

    1. Persistencia de datos También existe una gran diferencia entre las estrategias que se pueden poner en práctica y las estrategias de enseñanza. La estrategia de enseñanza del artículo anterior es solo una prueba preliminar de la lógica y el diseño de la estrategia. Hay más cuestiones que considerar en la práctica real. Durante el trading real, puedes iniciar y detener el trading real. En este momento, se perderán todos los datos durante la operación en tiempo real. Entonces, ¿cómo podemos hacer que el disco real se detenga y luego se reinicie para continuar ejecutándose en el estado anterior? Aquí es necesario conservar los datos clave durante la operación en tiempo real para que se puedan leer y continuar cuando se reinicie el sistema. Se puede utilizar en la plataforma de comercio cuantitativo de Inventor_G()Función, o utilizar la función de operación de base de datosDBExec()Para obtener más detalles, consulte la documentación de la API de FMZ.

    Por ejemplo, diseñamos una función de barrido, utilizando_G()Función, guardar los datos de la cuadrícula.

    var net = null 
    function main() {  // 策略主函数
        // 首先读取储存的net
        net = _G("net")
    
    
        // ...
    }
    
    
    function onExit() {
        _G("net", net)
        Log("执行扫尾处理,保存数据", "#FF0000")
    }
    
    
    function onexit() {    // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
        onExit()
    }
    
    
    function onerror() {   // 平台系统定义的异常退出函数,在程序发生异常时触发执行
        onExit()
    }
    
    1. Restricciones en la precisión de la cantidad del pedido, precisión del precio del pedido, cantidad mínima del pedido, importe mínimo del pedido, etc.

    El sistema de backtest no impone restricciones tan estrictas sobre la cantidad y la precisión de las órdenes, pero en el comercio real, cada bolsa puede tener estándares estrictos para el precio y la cantidad de las órdenes, y estos estándares para cada par de operaciones también son muy estrictos. Las restricciones no son lo mismo. Por lo tanto, los novatos a menudo prueban el sistema de backtesting y ven todo tipo de problemas al activar transacciones en el mercado real. Entonces ni siquiera leen el mensaje de error y experimentan todo tipo de problemas locos [cabeza de perro].

    En el caso de variedades múltiples, este requisito es más complicado. En el caso de una estrategia de un solo producto, se puede diseñar un parámetro para especificar información como la precisión. Sin embargo, al diseñar una estrategia de varios productos, es obvio que escribir esta información en el parámetro hará que este parezca muy inflado.

    En este momento, debe consultar la documentación de la API de intercambio para ver si hay una interfaz con información relacionada con el par comercial en la documentación del intercambio. Si estas interfaces están disponibles, puede diseñar una interfaz de acceso automático en la estrategia para obtener información como la precisión y configurarla para la información del par comercial involucrado en la transacción (en términos simples, la precisión se solicita automáticamente al intercambio y luego adaptado a los parámetros de la estrategia). variables).

    1. Adaptación a diferentes tipos de intercambios ¿Por qué poner esta pregunta al final? Porque las soluciones a los problemas de los que hablamos anteriormente traerán consigo este último problema, porque nuestra estrategia planea utilizar la interfaz de mercado agregada, acceder a la precisión del par comercial de intercambio y a la adaptación de otros datos, acceder a la información de la cuenta y procesar cada par comercial por separado, etc. Estas soluciones serán Hay enormes diferencias entre los intercambios. Existen diferencias en las llamadas de interfaz y diferencias en los mecanismos. En el caso de los intercambios al contado, la diferencia es menor si esta estrategia de cuadrícula se amplía a una versión de futuros. Las diferencias en los mecanismos de los distintos intercambios son aún mayores. Una solución es diseñar una biblioteca de plantillas FMZ. Escriba y diseñe estas implementaciones diferenciadas en la biblioteca de clases. Reducir el acoplamiento entre la propia estrategia y el intercambio. La desventaja de hacer esto es que necesitas escribir una biblioteca de plantillas e implementarla específicamente para cada intercambio en esta plantilla.

Diseñar una biblioteca de plantillas

Con base en el análisis anterior, se diseña una biblioteca de plantillas para reducir el acoplamiento entre estrategias y mecanismos de intercambio e interfaces.

Podemos diseñar esta biblioteca de clases de plantilla de la siguiente manera (se omiten algunos códigos):

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()
    
    // 需要实现的接口
    self.interfaceGetTickers = null   // 创建异步获取聚合行情数据线程的函数
    self.interfaceGetAcc = null       // 创建异步获取账户数据线程的函数
    self.interfaceGetPos = null       // 获取持仓
    self.interfaceTrade = null        // 创建并发下单
    self.waitTickers = null           // 等待并发行情数据 
    self.waitAcc = null               // 等待账户并发数据
    self.waitTrade = null             // 等待下单并发数据
    self.calcAmount = null            // 根据交易对精度等数据计算下单量
    self.init = null                  // 初始化工作,获取精度等数据
    
    // 执行配置函数,给对象配置
    funcConfigure(self)

    // 检测configList约定的接口是否都实现
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "接口" + funcName + "未实现"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    // OK期货的实现
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // wexApp的实现
    }
    return dicRegister
}

En la plantilla, escríbalo para la implementación de intercambio específica, por ejemplo, tome el disco de simulación WexApp de FMZ 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) {
        // 获取交易对信息
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = null 
        var equalAmount = null  // 记录币数
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min / price) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min / price)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {   // 自动处理精度等条件的函数
        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 utilizar esta plantilla en la estrategia es sencillo:

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()
    
    // 测试获取行情
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // 测试获取账户信息
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // 打印行情数据
                Log(symbol, ticker)
            }
        })

        // 打印资产数据
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)
    })
}

Estrategia de mercado real

Es muy sencillo diseñar y escribir una estrategia basada en la plantilla anterior. La estrategia completa consta de más de 300 líneas, que implementan una estrategia de cuadrícula de múltiples variedades de moneda digital al contado.

Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

Si eres un principiante en el trading cuantitativo en el ámbito de las criptomonedas, echa un vistazo a este artículo: Te acercamos al trading cuantitativo en el ámbito de las criptomonedas (VI)

Actualmente perdiendo dineroT_T, el código fuente no se publicará por el momento.

A continuación se muestran algunos códigos de registro. Si te interesa, puedes probarlo en wexApp:

购买地址: https://www.fmz.com/m/s/284507
注册码: 
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

Éramos un poco más de 200 y justo cuando empezó a funcionar se topó con un gran mercado unilateral y se recuperó lentamente. La mayor ventaja de la red de puntos es: “¡Puedes dormir bien!” La estabilidad está bien. No la he tocado desde el 27 de mayo. No me atrevo a probar la red de futuros por el momento.