Die Ressourcen sind geladen. Beförderung...

Kryptowährungsquantitativer Handel für Anfänger - Sie näher an Kryptowährungsquantitativen (6)

Schriftsteller:FMZ~Lydia, Erstellt: 2022-08-05 17:13:26, Aktualisiert: 2023-09-21 21:02:17

img

In dem letzten Artikel haben wir gemeinsam eine einfache Gitterstrategie erstellt. In diesem Artikel haben wir diese Strategie in eine Multi-Species-Spot-Gitterstrategie aktualisiert und erweitert und diese Strategie in der Praxis testen lassen. Der Zweck ist es nicht, einen "heiligen Gral" zu finden, sondern verschiedene Probleme und Lösungen bei der Gestaltung von Strategien zu diskutieren. In diesem Artikel werde ich einige meiner Erfahrungen bei der Gestaltung dieser Strategie erklären. Der Inhalt dieses Artikels ist etwas kompliziert und erfordert eine gewisse Grundlage in der Programmierung.

Designdenken auf der Grundlage strategischer Bedürfnisse

In diesem Artikel wird, wie im vorherigen, immer noch das Design auf der Grundlage des FMZ Quant (FMZ.COM).

  • Mehrfacharten Ich glaube, daß diese Netzstrategie nicht nurBTC_USDT, aber auchLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTWie auch immer, die Spot-Trading-Paare und die Varianten, die laufen wollen, werden alle gleichzeitig im Netz gehandelt.

    Es fühlt sich gut an, den volatilen Markt vieler Arten zu erobern. Die Anforderung klingt sehr einfach, und das Problem kommt beim Entwerfen.

      1. Zunächst werden die Marktnoten mehrerer Varianten ermittelt. Dies ist das erste zu lösende Problem. Nach der Konsultation der API-Dokumentation der Börse stellte ich fest, dass die meisten Börsen aggregierte Marktschnittstellen bereitstellen. OK, nutzen Sie die aggregierte Markt-Schnittstelle, um Daten zu erhalten.
      1. Das zweite Problem, das auftaucht, sind Kontovermögen. Da es sich um eine Multi-Species-Strategie handelt, ist es notwendig, die Verwaltung jedes Handelspaarvermögens separat zu betrachten. Und wir müssen Daten für alle Vermögenswerte auf einmal erhalten und sie aufzeichnen. Warum müssen wir die Kontovermögensdaten erhalten? Warum müssen wir die Aufzeichnungen jedes Paares trennen? Weil man bei der Bestellung die verfügbaren Vermögenswerte beurteilen muss. Und Sie müssen den Profit berechnen, ist es auch notwendig, zuerst eine ursprüngliche Konto-Vermögensdaten zu erfassen, dann die Leistungskonto-Vermögensdaten zu erhalten und sie mit der ursprünglichen zu vergleichen, um den Gewinn und Verlust zu berechnen? Glücklicherweise gibt die Asset-Account-Schnittstelle der Börse normalerweise alle Währungs-Asset-Daten zurück, wir müssen sie nur einmal erhalten und dann die Daten verarbeiten.
      1. Strategieparameter-Design. Das Parameter-Design von Multi-Species unterscheidet sich stark vom Parameter-Design von Single-Variety, obwohl die Handelslogik jeder Variante von Multi-Variety dieselbe ist, es ist möglich, dass die Parameter während des Handels unterschiedlich sind. Zum Beispiel möchten Sie in der Gitterstrategie möglicherweise jedes Mal 0,01 BTC handeln, wenn Sie BTC_USDT-Trading-Paar machen, aber es ist offensichtlich unangemessen, diesen Parameter (Handel mit 0,01 Münzen) zu verwenden, wenn Sie DOGE_USDT machen. Natürlich können Sie auch mit der USDT-Menge umgehen. Aber es wird immer noch Probleme geben. Was ist, wenn Sie 1000U für BTC_USDT und 10U für DOGE_USDT handeln wollen? Die Nachfrage kann nie erfüllt werden. Es kann jemand sein, der über dieses Problem nachdenkt und dann fragt: Ich kann mehrere Parameter-Sätze festlegen, um die Parameter verschiedener Handelspare zu steuern, die separat durchgeführt werden sollen. Dies ist immer noch nicht flexibel genug, um den Bedürfnissen gerecht zu werden, wie viele Parameter-Sätze sind gut zu setzen? Drei Parameter-Sätze sind festgelegt, was ist, wenn ich 4 Sorten machen möchte? Daher ist es bei der Gestaltung der Parameter der Multi-Species-Strategie notwendig, die Bedürfnisse solcher differenzierten Parameter vollständig zu berücksichtigen. Zum Beispiel:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      Unter ihnen unterteilt gaman die Daten für jede Art, was bedeutet, dassETHUSDT:100:0.002das Handelspaar ETH_USDT steuert undLTCUSDT:20:0.1Der mittlere wird zur Teilung verwendet.ETHUSDT:100:0.002, wo ETHUSDT anzeigt, welches Handelspaar Sie tun möchten, 100 ist der Rasterabstand, 0,002 ist die Anzahl der in jedem Raster gehandelten ETH-Münzen, und das : ist, um diese Daten zu teilen (natürlich werden diese Parameterregeln vom Strategieentwickler erstellt, Sie können alles nach Ihren Bedürfnissen entwerfen). Diese Strings enthalten die Parameterinformationen jeder Art, die Sie tun möchten. Parsieren Sie diese Strings in der Strategie und weisen Sie den Variablen der Strategie Werte zu, um die Handelslogik jeder Art zu steuern. Wie werden sie parsiert? Verwenden Sie immer noch das obige Beispiel.

      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

      Natürlich können Sie auch JSON-Strings direkt verwenden, was einfacher ist.

      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. Dauerhaftigkeit der Daten Es gibt auch einen großen Unterschied zwischen den Strategien, die in der Praxis angewendet werden können, und den Tutorial-Strategien. Die Tutorial-Strategien im vorherigen Artikel sind nur ein vorläufiger Test der Strategie-Logik und des Designs, und es gibt mehr Probleme, die zu berücksichtigen sind, wenn es um die reale Welt geht. Im echten Bot ist es möglich, den echten Handel zu starten und zu stoppen. Zu diesem Zeitpunkt werden alle Daten während des realen Bot-Betriebs verloren gehen. Wie lässt man den echten Bot dann neu starten, um im vorherigen Status weiter zu laufen, nachdem er gestoppt wurde? Hier ist es notwendig, die Schlüsseldaten dauerhaft zu speichern, wenn der echte Bot läuft, damit die Daten gelesen und weiter ausgeführt werden können, wenn er neu gestartet wird. Sie können die_G()Funktion auf der quantitativen Handelsplattform FMZ, oder die Datenbankbetriebsfunktion verwendenDBExec(), und Sie können die FMZ-API-Dokumentation für Details überprüfen.

      Zum Beispiel entwerfen wir eine Tail Sweep-Funktion und verwenden_G()Funktion zum Speichern von Netzdaten.

      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. Grenzen wie die Präzision der Auftragsmenge, die Präzision des Auftragspreises, die Mindestbestellmenge und der Mindestbestellbetrag usw.

      Das Backtesting-System legt keine so strengen Einschränkungen für den Auftragsbetrag und die Auftragsgenauigkeit auf, aber jeder Austausch kann strenge Standards für den Preis und den Auftragsbetrag haben, wenn er einen Auftrag in den echten Bot platziert, und diese Einschränkungen sind nicht gleich in verschiedenen Börsen. Daher gibt es Anfänger, die problemlos im Backtesting-System testen. Sobald der echte Bot gestartet wurde, gibt es verschiedene Probleme, wenn der Handel ausgelöst wird, und dann wird der Inhalt der Fehlermeldung nicht gelesen, und verschiedene verrückte Phänomene erscheinen.

      Für Multi-Species-Fälle ist diese Anforderung komplizierter. Für eine Single-Species-Strategie können Sie einen Parameter entwerfen, um Informationen wie Genauigkeit anzugeben, aber wenn Sie eine Multi-Species-Strategie entwerfen, ist es offensichtlich, dass das Schreiben dieser Informationen in die Parameter die Parameter sehr aufgebläht macht.

      Zu diesem Zeitpunkt müssen Sie die API-Dokumentation der Börse überprüfen, um zu sehen, ob in der Börsendokumentation eine Schnittstelleninformation zum Handel mit Paaren vorhanden ist. Wenn dies der Fall ist, können Sie eine automatische Zugriffsoberfläche in der Strategie entwerfen, um Informationen wie Genauigkeit zu erhalten, und sie in die im Handel beteiligten Handelspaarinformationen einrichten (kurz gesagt, die Genauigkeit oder etwas wird automatisch von der Börse erhalten und dann an die Variablen angepasst, die mit den Strategieparametern zusammenhängen).

      1. Anpassung an verschiedene Börsen Warum diese Frage am Ende? Da die Lösungen für diese Probleme, über die wir oben gesprochen haben, das letzte Problem verursachen werden, weil unsere Strategie die aggregierte Marktoberfläche, den Zugang zur Genauigkeit des Börsenhandelspaares und andere Datenadaptiv nutzen soll, Zugang zu Kontoinformationen, um jedes Handelspaar separat zu behandeln, können diese Lösungen von Börse zu Börse stark variieren. Es gibt Unterschiede in den Schnittstellenanrufen und Mechanismen. Für Spot-Börsen ist der Unterschied kleiner, wenn die Gitterstrategie auf die Futures-Version erweitert wird. Die Unterschiede im Mechanismus verschiedener Börsen sind noch größer. Eine Lösung besteht darin, eine FMZ-Vorlage-Klassenbibliothek zu entwerfen. Schreiben Sie das Design in der Klassenbibliothek, um diese Unterschiede umzusetzen. Reduzieren Sie die Kopplung zwischen der Strategie selbst und der Börse. Der Nachteil ist, dass Sie eine Template-Klassenbibliothek schreiben und sie speziell für jede Exchange-Differenz in dieser Vorlage implementieren müssen.

Entwerfen einer Template-Klassenbibliothek

Auf der Grundlage der obigen Analyse wurde eine Vorlage-Klassenbibliothek entwickelt, um die Kopplung zwischen der Strategie und dem Austauschmechanismus und der Schnittstelle zu reduzieren.

Wir können diese Vorlage-Klassenbibliothek so gestalten (ein Teil des Codes wird weggelassen):

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
}

In der Vorlage ist es für bestimmte Börsen geschrieben, nehmen wir zum Beispiel FMZs simulierten Bot WexApp:

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

Dann ist die Verwendung dieser Vorlage in einer Strategie einfach:

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

Strategie realer Bot

Es ist sehr einfach, eine Strategie basierend auf der obigen Vorlage zu entwerfen und zu schreiben.

img

img

Sie verliert derzeit Geld.T_T, wird der Quellcode vorerst nicht veröffentlicht.

Hier sind einige Registrierungscodes, wenn Sie interessiert sind, können Sie die wexApp verwenden, um es zu versuchen:

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

Als ich mit dem Laufen begann, stieß ich auf einen großen einseitigen Markt, aber ich erholte mich langsam. Die Stabilität ist nicht schlecht, sie wurde seit dem 27. Mai nicht verändert, und das Futures-Grid hat es vorübergehend nicht gewagt, es zu versuchen.


Verwandt

Mehr