En el último artículo, hicimos una estrategia de cuadrícula simple juntos. En este artículo, actualizamos y ampliamos esta estrategia en una estrategia de cuadrícula de puntos de múltiples especies, y dejamos que esta estrategia sea probada en la práctica. El propósito no es encontrar un "santo grial", sino discutir varios problemas y soluciones al diseñar estrategias. Este artículo explicará parte de mi experiencia en el diseño de esta estrategia. El contenido de este artículo es ligeramente complicado y requiere una cierta base en programación.
Este artículo, al igual que el anterior, sigue discutiendo el diseño basado en el FMZ Quant (FMZ.COM).
Especies múltiples
Para decirlo sin rodeos, creo que esta estrategia de red no sólo puedeBTC_USDT
, pero tambiénLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
De todos modos, los pares de negociación al contado y las variedades que quieren correr se negocian en la red al mismo tiempo.
Se siente bien capturar el mercado volátil de múltiples especies. El requisito suena muy simple, y el problema surge cuando se diseña.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Entre ellos, ETHUSDT:100:0.002
controla el par de operaciones ETH_USDT, yLTCUSDT:20:0.1
El par de operaciones LTC_USDT está controlado por el par de operaciones LTC_USDT. El par de operaciones LTC_USDT se controla por el par de operaciones LTC_USDT.ETHUSDT:100:0.002
, donde ETHUSDT indica cuál es el par de operaciones que desea hacer, 100 es el espaciamiento de la cuadrícula, 0.002 es el número de monedas ETH negociadas en cada cuadrícula, y el
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)
}
Mirando esto, los parámetros se analizan. por supuesto, también se puede utilizar las cadenas JSON directamente, que es más 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()
la función de la plataforma de negociación cuantitativa FMZ, o utilizar la función de operación de la base de datosDBExec()
, y puede consultar la documentación de la API de FMZ para obtener detalles.Por ejemplo, diseñamos una función de barrido de cola y utilizamos el_G()
función para guardar datos de la red.
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()
}
El sistema de backtesting no impone restricciones tan estrictas sobre el monto del pedido y la precisión del pedido, pero cada intercambio puede tener estándares estrictos para el precio y el monto del pedido al realizar un pedido en el bot real, y estas restricciones no son las mismas en diferentes intercambios. Por lo tanto, hay principiantes que prueban en el sistema de backtesting sin problemas. Una vez que se lanza el bot real, hay varios problemas cuando se activa el comercio, y luego no se lee el contenido del mensaje de error, y aparecen varios fenómenos locos.
Para casos de múltiples especies, este requisito es más complicado. Para una estrategia de una sola especie, puede diseñar un parámetro para especificar información como la precisión, pero al diseñar una estrategia de múltiples especies, es obvio que escribir esta información en los parámetros hará que los parámetros se hinchen mucho.
En este momento, debe verificar la documentación de la API del intercambio para ver si hay una información de interfaz relacionada con los pares de negociación en la documentación del intercambio. Si lo hay, puede diseñar una interfaz de acceso automático en la estrategia para obtener información como la precisión y configurarla en la información del par de negociación involucrada en la negociación (en resumen, la precisión o algo se obtiene del intercambio automáticamente, y luego se adapta a las variables relacionadas con los parámetros de la estrategia).
Sobre la base del análisis anterior, una biblioteca de clases de plantilla está diseñada para reducir el acoplamiento entre la estrategia y el mecanismo e interfaz de intercambio.
Podemos diseñar esta biblioteca de clases de plantilla así (se omite parte del código):
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
}
En la plantilla, se escribe para intercambios específicos, tome el bot simulado 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
})
})
}
}
Entonces usar esta plantilla en una estrategia es 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)
})
}
Es muy simple diseñar y escribir una estrategia basada en la plantilla anterior.
Está perdiendo dinero actualmente.T_T
, el código fuente no será publicado por el momento.
Aquí hay algunos códigos de registro, si estás interesado, puedes usar la wexApp para intentarlo:
Buy address: https://www.fmz.com/m/s/284507
Registration code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Poco más de 200 U, cuando empecé a correr, me encontré con un gran mercado unilateral, pero me recuperé lentamente. La estabilidad no es mala, no ha sido modificada desde el 27 de mayo y la red de futuros no se ha atrevido a intentarlo temporalmente.