
Dans l’article précédent, nous avons travaillé ensemble pour créer une stratégie de grille simple. Dans cet article, nous allons mettre à niveau et étendre cette stratégie en une stratégie de grille de points multi-variétés et mettre cette stratégie à l’épreuve en combat réel. Le but n’est pas de trouver un « Saint Graal », mais d’explorer divers problèmes et solutions lors de la conception de stratégies. Cet article explique certaines de mes expériences 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 toujours de la conception basée sur Inventor Quantization (FMZ.COM).
BTC_USDT, peut aussi faireLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. Quoi qu’il en soit, pour les paires de trading spot, tous les produits que vous souhaitez trader peuvent être négociés en grille en même temps.Euh~~C’est agréable de capturer le marché volatil de multiples variétés. Les exigences semblent simples, mais des problèmes surviennent lors de la conception.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Le « | » sépare les données de chaque variété, ce qui signifieETHUSDT:100:0.002Il contrôle la paire de trading ETH_USDT.LTCUSDT:20:0.1Il contrôle la paire de trading LTC_USDT. Le « | » au milieu sert de séparateur.
ETHUSDT:100:0.002, où ETHUSDT indique la paire de trading que vous souhaitez trader, 100 est l’espacement de la grille, 0,002 est le nombre de pièces ETH échangées dans chaque grille et le signe “:” est utilisé pour séparer ces données (bien entendu, ces règles de paramètres sont défini par le concepteur de la stratégie). Vous pouvez le concevoir en fonction de vos besoins).
Ces chaînes contiennent les informations sur les paramètres de chaque produit que vous souhaitez négocier. Analysez ces chaînes dans la stratégie et attribuez des valeurs spécifiques aux variables de la stratégie pour contrôler la logique de négociation de chaque produit. Alors comment l’analyser ? Reprenons l’exemple ci-dessus.
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)
}

Vous pouvez voir que les paramètres sont analysés de cette manière. Bien entendu, vous pouvez également utiliser directement des chaînes JSON, 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) // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据
_.each(net, function(pair) {
Log("交易对:", pair.symbol, pair)
})
}

_G()Fonction ou utilisation d’une fonction d’opération de base de donnéesDBExec()Pour plus de détails, veuillez vous référer à la documentation de l’API FMZ.Par exemple, nous concevons une fonction de balayage, en utilisant_G()Fonction, enregistrer les données de la grille.
var net = null
function main() { // 策略主函数
// 首先读取储存的net
net = _G("net")
// ...
}
function onExit() {
_G("net", net)
Log("执行扫尾处理,保存数据", "#FF0000")
}
function onexit() { // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
onExit()
}
function onerror() { // 平台系统定义的异常退出函数,在程序发生异常时触发执行
onExit()
}
Le système de backtest n’impose pas de restrictions aussi strictes sur la quantité et la précision des commandes, mais dans le trading réel, chaque bourse peut avoir des normes strictes pour le prix et la quantité des commandes, et ces normes pour chaque paire de trading sont également très strictes. Les restrictions ne sont pas le même. C’est pourquoi les débutants testent souvent le système de backtest et constatent toutes sortes de problèmes lors du déclenchement de transactions sur le marché réel. Ils ne lisent alors même pas le message d’erreur et rencontrent toutes sortes de problèmes fous [tête de chien].
Pour plusieurs variétés, cette exigence est plus compliquée. Pour une stratégie à produit unique, vous pouvez concevoir un paramètre pour spécifier des informations telles que la précision. Cependant, lors de la conception d’une stratégie multi-produits, il est évident que l’écriture de ces informations dans le paramètre rendra le paramètre très volumineux.
À ce stade, vous devez vérifier la documentation de l’API de l’échange pour voir s’il existe une interface avec des informations relatives aux paires de trading dans la documentation de l’échange. Si ces interfaces sont disponibles, 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 en fonction des informations de la paire de négociation impliquée dans la transaction (en termes simples, la précision est automatiquement demandée à la bourse et puis adaptés aux paramètres de la stratégie). variables).
Sur la base de l’analyse ci-dessus, une bibliothèque de modèles est conçue pour réduire le couplage entre les stratégies et les mécanismes d’échange et les interfaces.
Nous pouvons concevoir cette bibliothèque de classes de modèles comme ceci (certains codes sont omis) :
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
}
Dans le modèle, écrivez-le pour l’implémentation d’échange spécifique, par exemple, prenez le disque de simulation WexApp de FMZ comme 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) {
// 获取交易对信息
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
})
})
}
}
Ensuite, utiliser ce modèle dans la 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()
// 测试获取行情
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)
})
}
Il est très simple de concevoir et d’écrire une stratégie basée sur le modèle ci-dessus. L’ensemble de la stratégie comporte plus de 300 lignes, qui mettent en œuvre une stratégie de grille multi-variétés de devises numériques.


Je perds actuellement de l’argentT_T, le code source ne sera pas publié pour le moment.
Voici quelques codes d’inscription. Si vous êtes intéressé, vous pouvez l’essayer sur wexApp :
购买地址: https://www.fmz.com/m/s/284507
注册码:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Il y en avait un peu plus de 200, et juste au moment où il a commencé à fonctionner, il a rencontré un grand marché unilatéral et s’est lentement rétabli. Le plus grand avantage du spot grid est : « Vous pouvez bien dormir ! » La stabilité est correcte. Je n’y ai pas touché depuis le 27 mai. Je n’ose pas essayer la grille des futures pour le moment.