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.
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_USDT
Wie 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.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Unter ihnen unterteilt ETHUSDT:100:0.002
das Handelspaar ETH_USDT steuert undLTCUSDT:20:0.1
Der mittlere 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
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)
}
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)
})
}
_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()
}
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).
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 FMZ
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)
})
}
Es ist sehr einfach, eine Strategie basierend auf der obigen Vorlage zu entwerfen und zu schreiben.
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.