No último artigo, fizemos uma estratégia de grade simples juntos. Neste artigo, atualizamos e expandimos essa estratégia em uma estratégia de grade de pontos de várias espécies, e deixamos essa estratégia ser testada na prática. O objetivo não é encontrar um "santo graal", mas discutir vários problemas e soluções ao projetar estratégias. Este artigo explicará algumas de minhas experiências na concepção dessa estratégia. O conteúdo deste artigo é um pouco complicado e requer uma certa base em programação.
Este artigo, tal como o anterior, continua a discutir o projecto baseado no FMZ Quant (FMZ.COM).
Espécies múltiplas
Para ser franco, penso que esta estratégia de rede não só podeBTC_USDT
, mas tambémLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
De qualquer forma, os pares de negociação spot e as variedades que querem correr são todos negociados na grade ao mesmo tempo.
É bom capturar o mercado volátil de várias espécies. O requisito parece muito simples, e o problema surge quando se desenha.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Entre eles, ETHUSDT:100:0.002
controla o par de negociação ETH_USDT, eLTCUSDT:20:0.1
O par de negociação LTC_USDT é controlado pelo par de negociação LTC_USDT. O par de negociação LTC_USDT é controlado pelo par de negociação LTC_USDT.ETHUSDT:100:0.002
, onde ETHUSDT indica qual o par de negociação que você deseja fazer, 100 é o espaçamento da grade, 0,002 é o número de moedas ETH negociadas em cada grade, e o
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)
}
Olhando para isso, os parâmetros são analisados.
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()
função na plataforma de negociação quantitativa FMZ, ou utilizar a função de operação da base de dadosDBExec()
, e você pode verificar a documentação FMZ API para detalhes.Por exemplo, nós projetar uma função de varredura de cauda e usar o_G()
Função de armazenamento de dados de rede.
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()
}
O sistema de backtesting não impõe restrições tão rígidas sobre o valor do pedido e a precisão do pedido, mas cada exchange pode ter padrões rigorosos para o preço e o valor do pedido ao colocar um pedido no bot real, e essas restrições não são as mesmas em diferentes exchanges.
Para casos de várias espécies, este requisito é mais complicado. Para uma estratégia de uma única espécie, você pode projetar um parâmetro para especificar informações como precisão, mas ao projetar uma estratégia de várias espécies, é óbvio que escrever essa informação nos parâmetros fará com que os parâmetros fiquem muito inchados.
Neste momento, você precisa verificar a documentação da API da bolsa para ver se há informações de interface relacionadas a pares de negociação na documentação da bolsa. Se houver, você pode projetar uma interface de acesso automático na estratégia para obter informações como precisão e configurá-la nas informações de pares de negociação envolvidas na negociação (em suma, a precisão ou algo é obtida da bolsa automaticamente e, em seguida, adaptada às variáveis relacionadas aos parâmetros da estratégia).
Com base na análise acima, uma biblioteca de classes modelo foi concebida para reduzir a ligação entre a estratégia e o mecanismo e interface de troca.
Podemos projetar esta biblioteca de classes modelo assim (parte do código é omitido):
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
}
No modelo, ele é escrito para trocas específicas, tome o bot simulado do 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
})
})
}
}
Então usar este modelo em uma estratégia é simples:
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)
})
}
É muito simples projetar e escrever uma estratégia baseada no modelo acima.
Está a perder dinheiro neste momento.T_T
, o código fonte não será divulgado por enquanto.
Aqui estão alguns códigos de registro, se você estiver interessado, você pode usar o wexApp para tentar:
Buy address: https://www.fmz.com/m/s/284507
Registration code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Pouco mais de 200 U, quando comecei a correr, encontrei um grande mercado unilateral, mas recuperei-me lentamente. A estabilidade não é má, não foi modificada desde 27 de Maio, e a rede de futuros não ousou tentar temporariamente.