avatar of 发明者量化-小小梦 发明者量化-小小梦
Suivre Messages privés
4
Suivre
1271
Abonnés

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

Créé le: 2021-06-04 10:08:48, Mis à jour le: 2024-12-04 21:14:15
comments   6
hits   2596

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

Dans l’article précédent, nous avons travaillé ensemble pour créer une stratégie de grille simple. Dans cet article, nous allons mettre à niveau et étendre cette stratégie en une stratégie de grille de points multi-variétés et mettre cette stratégie à l’épreuve en combat réel. Le but n’est pas de trouver un « Saint Graal », mais d’explorer divers problèmes et solutions lors de la conception de stratégies. Cet article explique certaines de mes expériences dans la conception de cette stratégie. Le contenu de cet article est légèrement compliqué et nécessite une certaine base en programmation.

Design thinking basé sur les besoins stratégiques

Cet article, comme le précédent, traite toujours de la conception basée sur Inventor Quantization (FMZ.COM).

  • Plusieurs variétés Pour le dire franchement, je pense que cette stratégie de grille non seulementBTC_USDT, peut aussi faireLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. Quoi qu’il en soit, pour les paires de trading spot, tous les produits que vous souhaitez trader peuvent être négociés en grille en même temps.

Euh~~C’est agréable de capturer le marché volatil de multiples variétés. Les exigences semblent simples, mais des problèmes surviennent lors de la conception.

    1. Tout d’abord, obtenez des informations sur le marché de plusieurs variétés. C’est le premier problème à résoudre. Après avoir vérifié la documentation API de l’échange, j’ai constaté que la plupart des échanges fournissent une interface d’informations de marché agrégées. OK, utilisez l’interface de données de marché agrégées pour obtenir des données.
    1. Le deuxième problème rencontré concerne les actifs du compte. Parce que nous souhaitons mettre en œuvre une stratégie multi-variété, nous devons envisager la gestion séparée des actifs pour chaque transaction. Et d’obtenir des données et des enregistrements de tous les actifs à la fois. Pourquoi avons-nous besoin d’obtenir des données sur les actifs du compte ? Devons-nous conserver des enregistrements distincts pour chaque paire de transactions ? Parce que vous devez évaluer les actifs disponibles lors de la passation d’une commande, devez-vous les obtenir avant de porter un jugement ? Et vous devez calculer le revenu. Devez-vous également enregistrer d’abord les données d’actif du compte initial, puis obtenir les données d’actif du compte courant et les comparer avec celles initiales pour calculer le bénéfice et la perte ? Heureusement, l’interface de compte d’actifs de la bourse renvoie généralement les données d’actifs de toutes les devises. Nous n’avons besoin de les obtenir qu’une seule fois, puis de traiter les données.
    1. Conception des paramètres de stratégie. La conception des paramètres de plusieurs variétés est assez différente de celle des variétés uniques, car bien que la logique de négociation de chaque variété soit la même, les paramètres lors de la négociation peuvent être différents. Par exemple, dans une stratégie de grille, vous souhaiterez peut-être négocier 0,01 BTC à chaque fois lorsque vous effectuez des transactions sur des paires BTC_USDT. Cependant, si vous utilisez toujours ce paramètre (négociation de 0,01 pièces) lorsque vous effectuez des transactions DOGE_USDT, cela est évidemment inapproprié. Bien sûr, vous pouvez gérez-le également en fonction du montant USDT. Mais il y aura toujours des problèmes. Que se passe-t-il si vous souhaitez simplement échanger 1000U avec BTC_USDT et 10U avec DOGE_USDT ? La demande ne pourra jamais être satisfaite. Certains étudiants peuvent réfléchir à cette question et dire ensuite : « Je peux définir plusieurs groupes de paramètres pour contrôler séparément les paramètres de différentes paires de trading. » Cela ne permet toujours pas de répondre de manière flexible aux besoins. Combien de groupes de paramètres doivent être définis ? Trois ensembles de paramètres sont définis. Que faire si je souhaite échanger 4 variétés ? Est-il possible de modifier la stratégie et d’ajouter des paramètres ? Par conséquent, lors de la conception des paramètres d’une stratégie multivariée, nous devons pleinement tenir compte de la demande de paramètres différenciés. Une solution consiste à concevoir les paramètres sous forme de chaînes ordinaires ou de chaînes JSON. Par exemple:
    ETHUSDT:100:0.002|LTCUSDT:20:0.1
    

    Le « | » sépare les données de chaque variété, ce qui signifieETHUSDT:100:0.002Il contrôle la paire de trading ETH_USDT.LTCUSDT:20:0.1Il contrôle la paire de trading LTC_USDT. Le « | » au milieu sert de séparateur. ETHUSDT:100:0.002, où ETHUSDT indique la paire de trading que vous souhaitez trader, 100 est l’espacement de la grille, 0,002 est le nombre de pièces ETH échangées dans chaque grille et le signe “:” est utilisé pour séparer ces données (bien entendu, ces règles de paramètres sont défini par le concepteur de la stratégie). Vous pouvez le concevoir en fonction de vos besoins). Ces chaînes contiennent les informations sur les paramètres de chaque produit que vous souhaitez négocier. Analysez ces chaînes dans la stratégie et attribuez des valeurs spécifiques aux variables de la stratégie pour contrôler la logique de négociation de chaque produit. Alors comment l’analyser ? Reprenons l’exemple ci-dessus.

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

    Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

    Vous pouvez voir que les paramètres sont analysés de cette manière. Bien entendu, vous pouvez également utiliser directement des chaînes JSON, ce qui est plus simple.

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

    Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

    1. Persistance des données Il existe également une grande différence entre les stratégies qui peuvent être mises en pratique et les stratégies d’enseignement. La stratégie d’enseignement présentée dans l’article précédent n’est qu’un test préliminaire de la logique et de la conception de la stratégie. Il y a d’autres questions à prendre en compte dans la pratique. Pendant le trading réel, vous pouvez démarrer et arrêter le trading réel. À ce stade, toutes les données pendant l’opération en temps réel seront perdues. Alors, comment pouvons-nous faire en sorte que le disque réel s’arrête puis redémarre pour continuer à fonctionner dans l’état précédent ? Ici, il est nécessaire de conserver les données clés pendant le fonctionnement en temps réel afin que les données puissent être lues et poursuivies lorsque le système est redémarré. Il peut être utilisé sur la plateforme de trading quantitative Inventor_G()Fonction ou utilisation d’une fonction d’opération de base de donnéesDBExec()Pour plus de détails, veuillez vous référer à la documentation de l’API FMZ.

    Par exemple, nous concevons une fonction de balayage, en utilisant_G()Fonction, enregistrer les données de la grille.

    var net = null 
    function main() {  // 策略主函数
        // 首先读取储存的net
        net = _G("net")
    
    
        // ...
    }
    
    
    function onExit() {
        _G("net", net)
        Log("执行扫尾处理,保存数据", "#FF0000")
    }
    
    
    function onexit() {    // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
        onExit()
    }
    
    
    function onerror() {   // 平台系统定义的异常退出函数,在程序发生异常时触发执行
        onExit()
    }
    
    1. Restrictions sur l’exactitude de la quantité commandée, l’exactitude du prix de la commande, la quantité minimale de commande, le montant minimum de commande, etc.

    Le système de backtest n’impose pas de restrictions aussi strictes sur la quantité et la précision des commandes, mais dans le trading réel, chaque bourse peut avoir des normes strictes pour le prix et la quantité des commandes, et ces normes pour chaque paire de trading sont également très strictes. Les restrictions ne sont pas le même. C’est pourquoi les débutants testent souvent le système de backtest et constatent toutes sortes de problèmes lors du déclenchement de transactions sur le marché réel. Ils ne lisent alors même pas le message d’erreur et rencontrent toutes sortes de problèmes fous [tête de chien].

    Pour plusieurs variétés, cette exigence est plus compliquée. Pour une stratégie à produit unique, vous pouvez concevoir un paramètre pour spécifier des informations telles que la précision. Cependant, lors de la conception d’une stratégie multi-produits, il est évident que l’écriture de ces informations dans le paramètre rendra le paramètre très volumineux.

    À ce stade, vous devez vérifier la documentation de l’API de l’échange pour voir s’il existe une interface avec des informations relatives aux paires de trading dans la documentation de l’échange. Si ces interfaces sont disponibles, vous pouvez concevoir une interface d’accès automatique dans la stratégie pour obtenir des informations telles que la précision et la configurer en fonction des informations de la paire de négociation impliquée dans la transaction (en termes simples, la précision est automatiquement demandée à la bourse et puis adaptés aux paramètres de la stratégie). variables).

    1. Adaptation aux différents échanges Pourquoi mettre cette question à la fin ? Parce que les solutions aux problèmes dont nous avons parlé ci-dessus entraîneront ce dernier problème, car notre stratégie prévoit d’utiliser l’interface de marché agrégée, d’accéder à la précision des paires de négociation en bourse et à d’autres adaptations de données, d’accéder aux informations de compte et de traiter chaque paire de négociation séparément, etc. Ces solutions seront Il existe d’énormes différences entre les échanges. Il existe des différences dans les appels d’interface et des différences dans les mécanismes. Pour les échanges au comptant, la différence est plus faible si cette stratégie de grille est étendue à une version à terme. Les différences dans les mécanismes des différents échanges sont encore plus grandes. Une solution consiste à concevoir une bibliothèque de modèles FMZ. Écrivez et concevez ces implémentations différenciées dans la bibliothèque de classes. Réduire le couplage entre la stratégie elle-même et l’échange. L’inconvénient de cette méthode est que vous devez écrire une bibliothèque de modèles et l’implémenter spécifiquement pour chaque échange dans ce modèle.

Concevoir une bibliothèque de modèles

Sur la base de l’analyse ci-dessus, une bibliothèque de modèles est conçue pour réduire le couplage entre les stratégies et les mécanismes d’échange et les interfaces.

Nous pouvons concevoir cette bibliothèque de classes de modèles comme ceci (certains codes sont omis) :

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
}

Dans le modèle, écrivez-le pour l’implémentation d’échange spécifique, par exemple, prenez le disque de simulation WexApp de FMZ comme exemple :

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

Ensuite, utiliser ce modèle dans la stratégie est 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()
    
    // 测试获取行情
    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)
    })
}

Stratégie marché réel

Il est très simple de concevoir et d’écrire une stratégie basée sur le modèle ci-dessus. L’ensemble de la stratégie comporte plus de 300 lignes, qui mettent en œuvre une stratégie de grille multi-variétés de devises numériques.

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VI)

Je perds actuellement de l’argentT_T, le code source ne sera pas publié pour le moment.

Voici quelques codes d’inscription. Si vous êtes intéressé, vous pouvez l’essayer sur wexApp :

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

Il y en avait un peu plus de 200, et juste au moment où il a commencé à fonctionner, il a rencontré un grand marché unilatéral et s’est lentement rétabli. Le plus grand avantage du spot grid est : « Vous pouvez bien dormir ! » La stabilité est correcte. Je n’y ai pas touché depuis le 27 mai. Je n’ose pas essayer la grille des futures pour le moment.