In dem letzten Artikel haben wir gemeinsam eine einfache Gitterstrategie erstellt. In diesem Artikel haben wir diese Strategie in eine Multi-Symbol-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 im Prozess der Strategieentwicklung zu diskutieren. Dieser Artikel wird einige meiner Erfahrungen bei der Gestaltung der Strategie erklären. Der Inhalt dieses Artikels ist etwas kompliziert und erfordert eine gewisse Grundlagenbildung in der Programmierung.
In diesem Artikel, wie im vorherigen, diskutieren wir über das Design auf Basis der FMZ Quant Trading Platform (FMZ.COM).
Mehrfaches Symbol
Ich möchte Ihnen sagen, daß ich die Grid-StrategieBTC_USDT
, aber auchLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
Wie auch immer, für die Spot-Handel Paare, betreiben Sie die Gitter Handel aller Symbole, die Sie gleichzeitig handeln möchten.
Ja, es fühlt sich gut an, die vibrierenden Marktkurse von mehreren Symbolen einzufangen.
Obwohl die Anforderung einfach klingt, wird sie schwierig, wenn man anfängt zu entwerfen.
Da Sie bei der Auftragserteilung die verfügbaren Vermögenswerte beurteilen müssen, ist es nicht notwendig, die Daten vor der Beurteilung zu erhalten? Wird die erste Aktiva-Bilanz erst erfasst und dann die Aktiva-Bilanz der Leistungsbilanz ermittelt und der Gewinn und Verlust durch Vergleich mit der ersten Berechnung berechnet? Glücklicherweise gibt die Asset-Account-Schnittstelle einer Plattform normalerweise alle Währungs-Asset-Daten zurück, also müssen wir sie nur einmal erhalten und dann die Daten verarbeiten.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Die Angabe der Daten für jedes Symbol wird mit ETHUSDT:100:0.002
das Handelspaar ETH_USDT steuert undLTCUSDT:20:0.1
Der ETHUSDT:100:0.002
,
Diese Zeichenfolgen enthalten bereits die Parameterinformationen jedes Symbols, das Sie bedienen müssen. Sie können die Zeichenfolgen analysieren und den Variablen in der Strategie Werte zuweisen, um die Handelslogik jedes Symbols zu steuern. Wie analysieren? Lassen Sie uns das oben erwähnte Beispiel verwenden.
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)
}
Siehst du, wir haben die Parameter analysiert. Sicher, du kannst direkt JSON-Strings 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; when specifically running the grid trading logic, use the data from here
_.each(net, function(pair) {
Log("Trading pair:", pair.symbol, pair)
})
}
_G()
Funktion auf FMZ Quant, oder die Funktion Operation verwendenDBExec()
in der Datenbank, und Sie können die FMZ API Dokumentation für Details abfragen.Zum Beispiel wollen wir eine Reinigungsfunktion mit der Funktion_G()
, um die Netzdaten zu speichern.
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()
}
Das Backtest-System hat keine so strengen Grenzen für das Auftragsvolumen und die Auftragspräzision; aber in einem Bot hat jede Plattform strenge Standards für den Auftragspreis und das Auftragsvolumen, und verschiedene Handelspare haben unterschiedliche Grenzen. Daher testen Anfänger OKEX oft im Backtest-System. Sobald die Strategie auf einem Bot ausgeführt wurde, gibt es verschiedene Probleme, wenn ein Handel ausgelöst wird, und dann wird der Inhalt der Fehlermeldung nicht gelesen und verschiedene verrückte Phänomene erscheinen.
Für Multi-Symbol-Fälle ist die Anforderung komplizierter. Für eine Single-Symbol-Strategie können Sie einen Parameter entwerfen, um Informationen wie Präzision anzugeben. Wenn Sie jedoch eine Multi-Symbol-Strategie entwerfen, ist es offensichtlich, dass das Schreiben der Informationen in einen Parameter den Parameter sehr mühsam macht.
Wenn diese Schnittstellen vorhanden sind, können Sie eine automatische Zugriffsoberfläche in der Strategie entwerfen, um Informationen wie Präzision zu erhalten, und sie in die Handelspaarinformationen im Handel konfigurieren (kurz gesagt, die Präzision wird automatisch von der Plattform erhalten und dann an die Variable angepasst, die mit dem Strategieparameter zusammenhängt).
Auf der Grundlage der obigen Analyse entwerfen wir eine Template-Bibliothek, um die Verbindung zwischen Strategie, Plattformmechanismus und Schnittstelle zu reduzieren.
Wir können die Vorlagebibliothek 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()
// 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
}
In der Vorlage implementieren Sie das Schreiben von Codes, die auf eine bestimmte Spielform abzielen; nehmen Sie den FMZ-Simulationsbot WexApp 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) {
// 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
})
})
}
}
Es ist sehr einfach, die Vorlage in der Strategie zu verwenden:
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)
})
}
Es ist sehr einfach, die Strategie basierend auf der obigen Vorlage zu entwerfen und zu schreiben. Die gesamte Strategie hat etwa über 300 Zeilen Code. Das implementiert eine Kryptowährungs-Spot-Multi-Symbol-Gitterstrategie.
Im Moment hat es Verluste.T_T
, so dass der Quellcode nicht bereitgestellt wird.
Es gibt mehrere Registrierungscodes; wenn Sie interessiert sind, können Sie sie in wexApp ausprobieren:
Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Es gab nur über 200 USD, und als der Bot gerade gestartet wurde, stieß er auf einen großen einseitigen Markt. Er braucht Zeit, um den Verlust zu decken. Der größte Vorteil der Spot-Grid-Strategie ist: