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 varios símbolos, 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 en el proceso de diseño de estrategias. Este artículo explicará parte de mi experiencia en el diseño de la estrategia. El contenido de este artículo es ligeramente complicado y requiere una cierta base en programación.
En este artículo, al igual que en el anterior, discutimos el diseño basado en la plataforma de negociación cuántica FMZ (FMZ.COM).
Símbolo múltiple
Para ser honesto, quiero que la estrategia de red no sóloBTC_USDT
, pero tambiénLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
De todos modos, para los pares de comercio al contado, operar la red de comercio de todos los símbolos que desea comerciar al mismo tiempo.
Sí, se siente bien capturar las cotizaciones vibrante del mercado de múltiples símbolos.
Aunque el requisito suena simple, se vuelve difícil cuando empiezas a diseñar.
Debido a que es necesario evaluar los activos disponibles al realizar pedidos, ¿no es necesario obtener los datos antes del juicio? Además, hay que calcular el rendimiento. ¿Deberíamos registrar primero los datos iniciales del activo de la cuenta corriente y luego obtener los datos del activo de la cuenta corriente y calcular el beneficio y la pérdida comparándolos con el inicial? Afortunadamente, la interfaz de cuenta de activos de una plataforma generalmente devuelve todos los datos de activos de divisas, por lo que solo necesitamos obtenerlos una vez y luego procesar los datos.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
ETHUSDT:100:0.002
controla el par de operaciones ETH_USDT, yLTCUSDT:20:0.1
El símbolo ETHUSDT:100:0.002
,
Estas cadenas ya contienen la información de parámetros de cada símbolo que necesita para operar. Puede analizar las cadenas y asignar valores a las variables en la estrategia, para controlar la lógica de negociación de cada símbolo. ¿Cómo analizar? Usemos el ejemplo mencionado anteriormente.
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)
}
Ver, hemos analizado los parámetros. Claro, usted puede utilizar directamente cadenas JSON, que es más fácil.
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()
función en FMZ Quant, o utilizar la función de operaciónDBExec()
en la base de datos, y puede consultar la documentación de la API FMZ para obtener detalles.Por ejemplo, queremos diseñar una función de limpieza utilizando la función_G()
, para guardar los datos de la red.
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()
}
El sistema de backtest no tiene límites tan estrictos en el volumen de orden y precisión de orden; pero en un bot, cada plataforma tiene estándares estrictos para el precio de la orden y el volumen de orden, y diferentes pares de operaciones tienen límites diferentes. Por lo tanto, los principiantes a menudo prueban OKEX en el sistema de backtest. Una vez que la estrategia se ejecuta en un bot, hay varios problemas cuando se activa un comercio, y luego no se lee el contenido del mensaje de error, y aparecen varios fenómenos locos.
Para los casos de múltiples símbolos, el requisito es más complicado. Para una estrategia de un solo símbolo, puede diseñar un parámetro para especificar información como precisión. Sin embargo, cuando diseña una estrategia de múltiples símbolos, es obvio que escribir la información en un parámetro hará que el parámetro sea muy tedioso.
En este momento, debe verificar la documentación de la API de la plataforma para ver si hay interfaces para la información relacionada con el par de operaciones en la documentación. Si existen estas interfaces, puede diseñar una interfaz de acceso automático en la estrategia para obtener información como precisión y configurarla en la información del par de operaciones en el comercio (en resumen, la precisión se obtiene automáticamente de la plataforma y luego se adapta a la variable relacionada con el parámetro de estrategia).
Basándonos en el análisis anterior, diseñamos una biblioteca de plantillas para reducir el acoplamiento entre estrategia, mecanismo de plataforma e interfaz.
Podemos diseñar la biblioteca de plantillas 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()
// 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
}
En la plantilla, implemente la escritura de código dirigida a una forma de juego específica; tome el bot simulado FMZ WexApp como ejemplo:
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
})
})
}
}
Será muy fácil utilizar la plantilla en la estrategia:
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)
})
}
Es muy simple diseñar y escribir la estrategia basada en la plantilla anterior. La estrategia completa tiene alrededor de más de 300 líneas de código.
Ahora mismo, tiene pérdidas.T_T
, por lo que el código fuente no se proporcionará.
Hay varios códigos de registro; si está interesado, puede probarlos en wexApp:
Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Solo había más de 200 USD, y cuando el bot recién comenzó, se encontró con un gran mercado unilateral. Necesita tiempo para cubrir la pérdida. La estabilidad de la estrategia está bien, y no la he modificado desde el 27 de mayo.