avatar of 发明者量化-小小梦 发明者量化-小小梦
konzentrieren Sie sich auf Private Nachricht
4
konzentrieren Sie sich auf
1271
Anhänger

Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

Erstellt in: 2021-06-04 10:08:48, aktualisiert am: 2024-12-04 21:14:15
comments   6
hits   2596

Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

Im vorherigen Artikel haben wir gemeinsam eine einfache Rasterstrategie entwickelt. In diesem Artikel werden wir diese Strategie zu einer vielseitigen Spot-Rasterstrategie erweitern und im tatsächlichen Kampf auf die Probe stellen. Das Ziel besteht nicht darin, einen „Heiligen Gral“ zu finden, sondern bei der Entwicklung von Strategien verschiedene Probleme und Lösungen zu erkunden. In diesem Artikel werde ich einige meiner Erfahrungen beim Entwerfen dieser Strategie erläutern. Der Inhalt dieses Artikels ist etwas kompliziert und erfordert gewisse Programmierkenntnisse.

Design Thinking basierend auf strategischen Bedürfnissen

In diesem Artikel wird, wie auch im vorherigen, noch der Entwurf auf Basis der Inventor-Quantisierung (FMZ.COM) besprochen.

  • Mehrere Sorten Um es ganz deutlich zu sagen: Ich denke, diese Netzstrategie wird nicht nurBTC_USDT, kann auchLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. Bei Spot-Handelspaaren können jedoch alle Produkte, mit denen Sie handeln möchten, gleichzeitig im Grid gehandelt werden.

Äh~~Es ist ein gutes Gefühl, den volatilen Markt mit seinen zahlreichen Sorten zu erobern. Die Anforderungen klingen einfach, doch bei der Konzeption treten Probleme auf.

    1. Besorgen Sie sich zunächst Marktinformationen zu mehreren Sorten. Dies ist das erste zu lösende Problem. Nachdem ich die API-Dokumentation der Börse überprüft hatte, stellte ich fest, dass die meisten Börsen eine Schnittstelle für aggregierte Marktinformationen bereitstellen. OK, verwenden Sie die Schnittstelle für aggregierte Marktdaten, um Daten abzurufen.
    1. Das zweite aufgetretene Problem betrifft das Kontovermögen. Da wir eine Multi-Sorten-Strategie umsetzen möchten, müssen wir für jede Transaktion eine separate Vermögensverwaltung in Betracht ziehen. Und um Daten und Aufzeichnungen aller Vermögenswerte auf einmal zu erhalten. Warum müssen wir Daten zum Kontovermögen erheben? Müssen wir für jedes Transaktionspaar separate Aufzeichnungen führen? Da Sie bei der Auftragserteilung die verfügbaren Vermögenswerte beurteilen müssen, müssen Sie diese vor der Urteilsbildung beschaffen? Und Sie müssen das Einkommen berechnen. Müssen Sie auch zuerst die anfänglichen Kontovermögensdaten aufzeichnen und dann die aktuellen Kontovermögensdaten abrufen und sie mit den anfänglichen vergleichen, um Gewinn und Verlust zu berechnen? Glücklicherweise gibt die Vermögenskontoschnittstelle der Börse normalerweise die Vermögensdaten aller Währungen zurück. Wir müssen sie nur einmal abrufen und dann die Daten verarbeiten.
    1. Gestaltung der Strategieparameter. Die Parametergestaltung mehrerer Sorten unterscheidet sich erheblich von der einzelner Sorten, denn obwohl die Handelslogik jeder Sorte gleich ist, können die Parameter während des Handels unterschiedlich sein. Beispielsweise möchten Sie bei einer Grid-Strategie möglicherweise jedes Mal 0,01 BTC handeln, wenn Sie BTC_USDT-Handelspaare durchführen. Wenn Sie diesen Parameter jedoch immer noch verwenden (Handel mit 0,01 Münzen), wenn Sie DOGE_USDT durchführen, ist dies offensichtlich unangemessen. Natürlich können Sie Behandeln Sie es auch entsprechend dem USDT-Betrag. Aber es wird trotzdem Probleme geben. Was ist, wenn Sie nur 1000U mit BTC_USDT und 10U mit DOGE_USDT handeln möchten? Die Nachfrage kann nie gedeckt werden. Einige Studenten denken vielleicht über diese Frage nach und sagen dann: „Ich kann mehrere Parametergruppen festlegen, um die Parameter verschiedener Handelspaare separat zu steuern.“ Dies kann den Anforderungen immer noch nicht flexibel gerecht werden. Wie viele Parametergruppen sollen eingestellt werden? Es sind drei Parametersätze festgelegt. Was ist, wenn ich 4 Sorten handeln möchte? Ist es möglich, die Strategie zu ändern und Parameter hinzuzufügen … Daher müssen wir beim Entwurf der Parameter einer Multivariate-Strategie den Bedarf an differenzierten Parametern umfassend berücksichtigen. Eine Lösung besteht darin, die Parameter als normale Zeichenfolgen oder JSON-Zeichenfolgen zu entwerfen. Zum Beispiel:
    ETHUSDT:100:0.002|LTCUSDT:20:0.1
    

    Das „|“ trennt die Daten der einzelnen Sorten, das heißtETHUSDT:100:0.002Es steuert das Handelspaar ETH_USDT.LTCUSDT:20:0.1Es steuert das Handelspaar LTC_USDT. Das „|“ in der Mitte dient als Trennzeichen. ETHUSDT:100:0.002, wobei ETHUSDT das Handelspaar angibt, mit dem Sie handeln möchten, 100 der Rasterabstand ist, 0,002 die Anzahl der in jedem Raster gehandelten ETH-Münzen ist und das “:”-Zeichen verwendet wird, um diese Daten zu trennen (natürlich sind diese Parameterregeln vom Strategiedesigner festgelegt). Sie können es entsprechend Ihren Anforderungen gestalten). Diese Zeichenfolgen enthalten die Parameterinformationen jedes Produkts, mit dem Sie handeln möchten. Analysieren Sie diese Zeichenfolgen in der Strategie und weisen Sie den Variablen der Strategie bestimmte Werte zu, um die Handelslogik jedes Produkts zu steuern. Wie also analysiert man es? Lassen Sie uns das obige Beispiel noch einmal verwenden.

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

    Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

    Sie können sehen, dass die Parameter auf diese Weise analysiert werden. Natürlich können Sie auch direkt JSON-Zeichenfolgen 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)  // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据        
        _.each(net, function(pair) {
            Log("交易对:", pair.symbol, pair)
        })
    }
    

    Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

    1. Datenpersistenz Es besteht auch ein großer Unterschied zwischen Strategien, die in der Praxis angewendet werden können, und Lehrstrategien. Die Lehrstrategie im vorherigen Artikel ist nur ein vorläufiger Test der Strategielogik und des Strategiedesigns. In der Praxis müssen noch weitere Aspekte berücksichtigt werden. Während des realen Handels können Sie den realen Handel starten und stoppen. Dabei gehen sämtliche Daten des Echtzeitvorgangs verloren. Wie können wir also die echte Festplatte stoppen und dann neu starten, damit sie im vorherigen Zustand weiterläuft? Hierbei ist es erforderlich, die wesentlichen Daten im Echtzeitbetrieb zu persistieren, damit beim Neustart des Systems die Daten ausgelesen und fortgeführt werden können. Es kann auf der Inventor Quantitative Trading Platform verwendet werden_G()Funktion oder Datenbankoperationsfunktion verwendenDBExec()Einzelheiten entnehmen Sie bitte der FMZ-API-Dokumentation.

    Als Beispiel entwerfen wir eine Sweep-Funktion mit_G()Funktion zum Speichern der Rasterdaten.

    var net = null 
    function main() {  // 策略主函数
        // 首先读取储存的net
        net = _G("net")
    
    
        // ...
    }
    
    
    function onExit() {
        _G("net", net)
        Log("执行扫尾处理,保存数据", "#FF0000")
    }
    
    
    function onexit() {    // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
        onExit()
    }
    
    
    function onerror() {   // 平台系统定义的异常退出函数,在程序发生异常时触发执行
        onExit()
    }
    
    1. Einschränkungen hinsichtlich der Genauigkeit der Bestellmenge, der Genauigkeit des Bestellpreises, der Mindestbestellmenge, des Mindestbestellbetrags usw.

    Das Backtest-System legt keine derart strengen Beschränkungen für Auftragsmenge und Auftragsgenauigkeit fest, aber im realen Handel kann jede Börse strenge Standards für Auftragspreis und Auftragsmenge haben, und diese Standards für jedes Handelspaar sind ebenfalls sehr streng. Die Beschränkungen sind nicht das gleiche. Daher testen Neulinge oft das Backtest-System und sehen alle möglichen Probleme, wenn sie Transaktionen auf dem realen Markt auslösen. Sie lesen dann nicht einmal die Fehlermeldung und erleben alle möglichen verrückten Probleme [Doghead].

    Bei mehreren Sorten ist diese Anforderung komplizierter. Bei einer Einzelproduktstrategie können Sie einen Parameter entwerfen, um Informationen wie Genauigkeit anzugeben. Bei der Entwicklung einer Mehrproduktstrategie ist es jedoch offensichtlich, dass das Schreiben dieser Informationen in den Parameter den Parameter sehr aufgebläht erscheinen lässt.

    Zu diesem Zeitpunkt müssen Sie die API-Dokumentation der Börse prüfen, um festzustellen, ob darin eine Schnittstelle mit Informationen zu Handelspaaren vorhanden ist. Wenn diese Schnittstellen verfügbar sind, können Sie in der Strategie eine automatische Zugriffsschnittstelle entwerfen, um Informationen wie Genauigkeit abzurufen, und diese auf die an der Transaktion beteiligten Handelspaarinformationen konfigurieren (vereinfacht ausgedrückt wird die Genauigkeit automatisch von der Börse angefordert und dann an die Strategieparameter angepasst). Variablen).

    1. Anpassung an verschiedene Börsen Warum diese Frage am Ende stellen? Denn die Lösungen für die oben besprochenen Probleme werden dieses letzte Problem mit sich bringen, weil unsere Strategie vorsieht, die aggregierte Marktschnittstelle zu verwenden, auf die Genauigkeit der Börsenhandelspaare und andere Datenanpassungen zuzugreifen, auf Kontoinformationen zuzugreifen und jedes Handelspaar separat zu verarbeiten usw. . Diese Lösungen werden sein. Es gibt große Unterschiede zwischen den Börsen. Es gibt Unterschiede bei den Schnittstellenaufrufen und Unterschiede bei den Mechanismen. Für Spotbörsen ist der Unterschied geringer, wenn diese Netzstrategie zu einer Futures-Version erweitert wird. Noch größer sind die Unterschiede in den Mechanismen der verschiedenen Börsen. Eine Lösung besteht darin, eine FMZ-Vorlagenbibliothek zu entwerfen. Schreiben und entwerfen Sie diese differenzierten Implementierungen in der Klassenbibliothek. Reduzieren Sie die Kopplung zwischen der Strategie selbst und dem Austausch. Der Nachteil hierbei besteht darin, dass Sie eine Vorlagenbibliothek schreiben und diese speziell für jeden Austausch in dieser Vorlage implementieren müssen.

Entwerfen einer Vorlagenbibliothek

Basierend auf der obigen Analyse wird eine Vorlagenbibliothek entwickelt, um die Kopplung zwischen Strategien und Austauschmechanismen und Schnittstellen zu reduzieren.

Wir können diese Vorlagenklassenbibliothek wie folgt gestalten (einige Codes werden 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()
    
    // 需要实现的接口
    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
}

Schreiben Sie es in die Vorlage für die spezifische Austauschimplementierung. Nehmen Sie beispielsweise die Simulationsdiskette WexApp von FMZ als Beispiel:

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

Dann ist die Verwendung dieser Vorlage in der Strategie ganz 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()
    
    // 测试获取行情
    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)
    })
}

Strategie Realmarkt

Es ist sehr einfach, eine Strategie basierend auf der obigen Vorlage zu entwerfen und zu schreiben. Die gesamte Strategie besteht aus über 300 Zeilen, die eine Multi-Varietät-Rasterstrategie für digitale Spotwährungen implementieren.

Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

Neulinge im quantitativen Handel in Kryptowährungskreisen, schauen Sie sich bitte dies an - So kommen Sie dem quantitativen Handel in Kryptowährungskreisen näher (VI)

Verliert derzeit GeldT_T, der Source-Code wird vorerst nicht veröffentlicht.

Hier sind einige Registrierungscodes. Wenn Sie interessiert sind, können Sie es auf wexApp ausprobieren:

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

Es gab etwas mehr als 200 US-Unternehmen, und gleich zu Beginn traf es auf einen großen einseitigen Markt und erholte sich langsam. Der größte Vorteil des Punktgitters ist: „Man kann gut schlafen!“ Die Stabilität ist ok. Ich habe es seit dem 27. Mai nicht mehr angefasst. An das Futures-Grid traue ich mich vorerst nicht heran.