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 multi-espèces, 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 lors de la conception de stratégies. Cet article expliquera une partie de mon expérience dans la conception de cette stratégie. Le contenu de cet article est légèrement compliqué et nécessite une certaine base en programmation.
Cet article, comme le précédent, traite encore de la conception basée sur le FMZ Quant (FMZ.COM).
Multifonctions
Pour être franc, je pense que cette stratégie de réseau ne peut pas seulementBTC_USDT
, mais aussiLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
Quoi qu'il en soit, les paires de négociation au comptant et les variétés qui veulent courir sont toutes négociées sur la grille en même temps.
Ça fait du bien de capturer le marché volatil de plusieurs espèces. L'exigence semble très simple, et le problème vient lors de la conception.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Parmi eux, ETHUSDT:100:0.002
contrôle la paire de négociation ETH_USDT, etLTCUSDT:20:0.1
La valeur de l'échange est calculée en fonction de la valeur de l'échange.ETHUSDT:100:0.002
, où ETHUSDT indique quelle paire de trading vous voulez faire, 100 est l'espacement de la grille, 0,002 est le nombre de pièces ETH négociées dans chaque grille, et le
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)
}
En regardant cela, les paramètres sont analysés. Bien sûr, vous pouvez également utiliser des chaînes JSON directement, ce qui est plus simple.
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()
fonction sur la plateforme de négociation quantitative FMZ, ou utiliser la fonction d'exploitation de base de donnéesDBExec()
, et vous pouvez consulter la documentation FMZ API pour plus de détails.Par exemple, nous concevons une fonction de balayage de queue et utilisons le_G()
fonction de sauvegarde des données de réseau.
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()
}
Le système de backtesting n'impose pas de restrictions aussi strictes sur le montant de l'ordre et l'exactitude de l'ordre, mais chaque échange peut avoir des normes strictes pour le prix et le montant de l'ordre lors de la passation d'un ordre dans le vrai bot, et ces restrictions ne sont pas les mêmes dans les différents échanges.
Pour les cas multi-espèces, cette exigence est plus compliquée. Pour une stratégie d'une seule espèce, vous pouvez concevoir un paramètre pour spécifier des informations telles que l'exactitude, mais lors de la conception d'une stratégie multi-espèces, il est évident que l'écriture de ces informations dans les paramètres rendra les paramètres très gonflés.
À ce stade, vous devez vérifier la documentation API de l'échange pour voir s'il existe des informations d'interface liées aux paires de trading dans la documentation de l'échange. Si elles existent, vous pouvez concevoir une interface d'accès automatique dans la stratégie pour obtenir des informations telles que l'exactitude, et la configurer dans les informations de paire de trading impliquées dans le trading (en bref, l'exactitude ou quelque chose est obtenue de l'échange automatiquement, puis adaptée aux variables liées aux paramètres de la stratégie).
Sur la base de l'analyse ci-dessus, une bibliothèque de classes de modèle est conçue pour réduire le couplage entre la stratégie et le mécanisme d'échange et l'interface.
Nous pouvons concevoir cette bibliothèque de classes de modèle 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()
// 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
}
Dans le modèle, il est écrit pour des échanges spécifiques, prenons par exemple le bot simulé de 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
})
})
}
}
L'utilisation de ce modèle dans une stratégie est simple:
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)
})
}
Il est très simple de concevoir et d'écrire une stratégie basée sur le modèle ci-dessus.
Il perd de l'argent actuellement.T_T
, le code source ne sera pas publié pour le moment.
Voici quelques codes d'enregistrement, si vous êtes intéressé, vous pouvez utiliser la wexApp pour essayer:
Buy address: https://www.fmz.com/m/s/284507
Registration code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Juste après 200 U, quand j'ai commencé à courir, j'ai rencontré un grand marché unilatéral, mais je me suis rétabli lentement. La stabilité n'est pas mauvaise, elle n'a pas été modifiée depuis le 27 mai, et le réseau des contrats à terme n'a pas osé tenter temporairement.