Pour les débutants, vérifiez Je vous emmène à la négociation quantitative de crypto-monnaie (6)

Auteur:Je suis désolée., Créé: 2021-04-21 18:13:03, Mis à jour: 2021-04-22 12:00:05

Pour les débutants, vérifiez Je vous emmène à la négociation quantitative de crypto-monnaie (6)

Dans l'article précédent, nous avons fait une stratégie de grille simple ensemble. Dans cet article, nous avons mis à niveau et élargi cette stratégie en une stratégie de grille de points à symboles multiples, et laissons cette stratégie être testée en pratique. Le but n'est pas de trouver un " saint graal ", mais de discuter de divers problèmes et solutions dans le processus de conception de stratégies. Cet article expliquera une partie de mon expérience dans la conception de la stratégie. Le contenu de cet article est légèrement compliqué et nécessite une certaine base en programmation.

Réflexion sur la conception basée sur l'exigence de stratégie

Dans cet article, comme dans le précédent, nous discutons de la conception basée sur la plateforme de trading quantique FMZ (FMZ.COM).

  • Symbole multiple Pour être honnête, je veux que la stratégie du réseau ne fasse pas seulementBTC_USDT, mais aussiLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTQuoi qu'il en soit, pour les paires de trading au comptant, opérez le trading de grille de tous les symboles que vous voulez échanger en même temps.

    Oui, c'est bien de capturer les cotations vibrantes de plusieurs symboles.
    Bien que l'exigence semble simple, elle devient difficile lorsque vous commencez à concevoir.

      1. Tout d'abord, obtenir les cotations de marché de plusieurs symboles. C'est le premier problème à résoudre. Après avoir lu la documentation de l'API de la plateforme, j'ai constaté que les plateformes fournissent généralement les interfaces agrégées. D'accord, nous utilisons l'interface de données de marché agrégées pour obtenir les données.
      1. Le deuxième problème est les actifs du compte. Pour que nous puissions exécuter des stratégies multi-symbole, nous devrions envisager de gérer séparément les actifs de chaque paire de trading, et obtenir toutes les données d'actifs et les enregistrer une fois. Pourquoi devrions-nous obtenir les données des actifs du compte? Et pourquoi enregistrer également chaque paire de trading séparément?

    Étant donné que vous devez évaluer les actifs disponibles lors de la passation des commandes, n'est-il pas nécessaire d'obtenir les données avant le jugement? En outre, le rendement doit être calculé.Doit-on enregistrer d'abord les données d'actifs de la première comptabilité et ensuite obtenir les données d'actifs de la balance courante et calculer le résultat en comparant avec la première? Heureusement, l'interface de compte d'actifs d'une plateforme renvoie généralement toutes les données d'actifs en devises, nous n'avons donc qu'à les obtenir une fois, puis à les traiter.

      1. Conception de paramètres de stratégie. La conception de paramètres de la stratégie multi-symbole est assez différente de la conception de paramètres de la stratégie à symbole unique, car même la logique de trading de chaque symbole de la stratégie multi-symbole est la même, il est possible que les paramètres de chaque symbole soient différents pendant le trading. Par exemple, dans la stratégie de grille, vous pouvez vouloir échanger 0,01 BTC à chaque fois lorsque vous faites la paire de trading BTC_USDT, mais il est évidemment inapproprié d'utiliser ce paramètre (trading 0,01 devise) lorsque vous faites DOGE_USDT. Bien sûr, vous pouvez également traiter par le montant de USDT. Mais il y aura toujours des problèmes. Et si vous voulez échanger 1000U par BTC_USDT et 10U par DOGE_USDT? La demande ne peut jamais être satisfaite. Probablement, certains étudiants pourraient penser à la question, et proposer que, Je peux définir plus de groupes de paramètres, et séparément contrôler les paramètres de différentes paires de trading à exploiter. Ce qui ne peut toujours pas répondre de manière flexible au besoin, pour combien de groupes de paramètres doivent être définis? nous définissons 3 groupes; et si nous voulons opérer 4 symboles? avons-nous besoin de modifier la stratégie et d'augmenter les paramètres? Par conséquent, réfléchissez pleinement à la nécessité de la différenciation lors de la conception de paramètres de stratégie multi-symbole.
        Par exemple:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      est utilisé pour diviser les données de chaque symbole, indiquant que:ETHUSDT:100:0.002contrôle la paire de négociation ETH_USDT, etLTCUSDT:20:0.1La fonction de segmentation est assurée par l'indicateur . DansETHUSDT:100:0.002, ETHUSDT représente la paire de négociation que vous souhaitez opérer; 100 est l'espacement de la grille; 0,002 est le montant d'ETH négocié de chaque grille; : est utilisé pour diviser les données mentionnées ci-dessus (bien sûr, les règles des paramètres sont établies par le concepteur de la stratégie; vous pouvez concevoir ce que vous voulez en fonction de vos besoins).
      Ces chaînes contiennent déjà les informations de paramètres de chaque symbole que vous devez utiliser. Vous pouvez analyser les chaînes et attribuer des valeurs aux variables de la stratégie, pour contrôler la logique de trading de chaque symbole. Comment analyser?

      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

      Bien sûr, vous pouvez utiliser directement des chaînes JSON, ce qui est plus facile.

      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. Persistance des données Il y a une grande différence entre une stratégie pratique et une stratégie d'enseignement. La stratégie d'enseignement dans le dernier article est juste pour le test initial de la logique et de la désignation de la stratégie. Il y a plus de problèmes à se soucier lors de l'exécution réelle d'une stratégie dans un bot. Lors de l'exécution d'un bot, le bot peut être démarré et arrêté. À ce moment-là, toutes les données pendant l'exécution du bot seront perdues. Alors, comment poursuivre l'état précédent lors du redémarrage du bot, après l'arrêt du bot? Ici, il est nécessaire d'enregistrer de manière persistante les données clés lorsque le bot est en cours d'exécution, de sorte que les données puissent être lues et que le bot continue à fonctionner lorsque le bot est redémarré. Vous pouvez utiliser le_G()fonction sur FMZ Quant, ou utiliser la fonction opérationDBExec()dans la base de données, et vous pouvez consulter la documentation FMZ API pour plus de détails.

      Par exemple, nous voulons concevoir une fonction de nettoyage en utilisant la fonction_G(), pour sauvegarder les données du réseau.

      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. Limites sur la précision du montant de la commande, la précision du prix de la commande, le volume minimum de la commande, le montant minimum de la commande, etc.

      Le système de backtest n'a pas de limites aussi strictes sur le volume des ordres et la précision des ordres; mais dans un bot, chaque plate-forme a des normes strictes pour le prix des ordres et le volume des ordres, et différentes paires de trading ont des limites différentes.

      Pour les cas à plusieurs symboles, l'exigence est plus compliquée. Pour une stratégie à symbole unique, vous pouvez concevoir un paramètre pour spécifier des informations telles que la précision. Cependant, lorsque vous concevez une stratégie à plusieurs symboles, il est évident que l'écriture des informations dans un paramètre rendra le paramètre très fastidieux.

      À ce stade, vous devez vérifier la documentation API de la plate-forme pour voir s'il existe des interfaces pour les informations relatives aux paires de trading dans la documentation. S'il existe ces interfaces, 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 dans les informations relatives aux paires de trading dans le commerce (en bref, la précision est automatiquement obtenue à partir de la plate-forme, puis adaptée à la variable liée au paramètre de stratégie).

      1. Adaptation d'une plateforme différente Pourquoi le problème est-il mentionné à la fin? Le traitement de tous les problèmes que nous avons mentionnés ci-dessus conduira au dernier problème: nous prévoyons d'utiliser l'interface de marché agrégée dans la stratégie, l'adaptation de l'accès Étant donné que notre stratégie prévoit d'utiliser l'interface de marché agrégée, des solutions telles que l'accès à la précision de la paire de négociation de la plateforme et d'autres adaptations de données, ainsi que l'accès aux informations du compte pour traiter chaque paire de négociation séparément, etc., apporteront de grandes différences en raison de différentes plateformes. Il existe des différences dans l'appel d'interface et des différences dans le mécanisme. Pour les plateformes spot, la différence est relativement faible si la stratégie de grille est étendue à la version à terme. Les différences dans le mécanisme de chaque plate-forme sont encore plus grandes. Une solution consiste à concevoir une bibliothèque de modèles FMZ; écrire la conception de la mise en œuvre de la différenciation dans la bibliothèque pour réduire le couplage entre la stratégie elle-même et la plate-forme. L'inconvénient est que vous devez écrire une bibliothèque de modèles, et dans ce modèle, implémenter spécifiquement la différenciation basée sur chaque plate-forme.

Concevoir une bibliothèque de modèles

Sur la base de l'analyse ci-dessus, nous avons conçu une bibliothèque de modèles pour réduire le couplage entre stratégie, mécanisme de plateforme et interface.
Nous pouvons concevoir la bibliothèque de modèles comme ceci (une partie du code est omise):

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
}

Dans le modèle, implémenter la rédaction de code visant une forme de jeu spécifique; prenez le bot simulé FMZ WexApp à titre d'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) {
        // 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
            })
        })        
    }
}

Il sera très facile d'utiliser le modèle dans la stratégie:

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 stratégie

Il est très simple de concevoir et d'écrire la stratégie basée sur le modèle ci-dessus.

img

img

En ce moment, il a des pertes.T_T, donc le code source ne sera pas fourni.

Il existe plusieurs codes d'enregistrement; si vous êtes intéressé, vous pouvez les essayer dans wexApp:

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

Il n'y avait que plus de 200 USD, et quand le bot a été lancé, il a rencontré un grand marché unilatéral. Il a besoin de temps pour couvrir la perte. Le plus grand avantage de la stratégie de la grille spot est: se sentir en sécurité pour dormir! La stabilité de la stratégie est bonne, et je ne l'ai pas modifiée depuis le 27 mai.


Plus de