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.
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_USDT
Quoi 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.
É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.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
ETHUSDT:100:0.002
contrôle la paire de négociation ETH_USDT, etLTCUSDT:20:0.1
La fonction de segmentation est assurée par l'indicateur ETHUSDT:100:0.002
,
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)
}
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)
})
}
_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()
}
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).
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)
})
}
Il est très simple de concevoir et d'écrire la stratégie basée sur le modèle ci-dessus.
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: