Dalam artikel sebelumnya, kami membuat strategi grid sederhana bersama-sama. Dalam artikel ini, kami memperbarui dan memperluas strategi ini menjadi strategi grid spot multi-simbol, dan biarkan strategi ini diuji dalam praktik. Tujuannya bukan untuk menemukan
Dalam artikel ini, sama seperti yang sebelumnya, kita membahas tentang desain berdasarkan FMZ Quant Trading Platform (FMZ.COM).
Simbol ganda
Sejujurnya, saya ingin strategi grid tidak hanyaBTC_USDT
, tapi jugaLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
Bagaimanapun, untuk pasangan perdagangan spot, mengoperasikan perdagangan grid dari semua simbol yang ingin Anda perdagangan pada saat yang sama.
Ya, rasanya enak untuk menangkap penawaran pasar bergetar dari beberapa simbol.
Meskipun persyaratan ini terdengar sederhana, menjadi sulit ketika Anda mulai merancang.
Karena Anda perlu menilai aset yang tersedia saat menempatkan pesanan, bukankah perlu untuk mendapatkan data sebelum penilaian? Selain itu, pengembalian harus dihitung. Haruskah kita mencatat data aset akuntansi awal terlebih dahulu? dan kemudian mendapatkan data aset dari rekening arus dan menghitung laba rugi dengan membandingkan dengan yang awal? Untungnya, antarmuka akun aset dari platform biasanya mengembalikan semua data aset mata uang, jadi kita hanya perlu mendapatkannya sekali, dan kemudian memproses data.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
ETHUSDT:100:0.002
mengontrol pasangan perdagangan ETH_USDT, danLTCUSDT:20:0.1
Mengontrol pasangan perdagangan LTC_USDT. ETHUSDT:100:0.002
,
String ini sudah berisi informasi parameter dari setiap simbol yang perlu Anda operasikan. Anda dapat menganalisis string, dan menetapkan nilai untuk variabel dalam strategi, untuk mengontrol logika perdagangan dari setiap simbol. Bagaimana untuk menganalisis? Mari kita gunakan contoh yang disebutkan di atas.
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)
}
Lihat, kita telah menganalisis parameter. tentu saja, Anda dapat langsung menggunakan string JSON, yang lebih mudah.
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()
Fungsi pada FMZ Quant, atau gunakan fungsi operasiDBExec()
dalam database, dan Anda dapat menanyakan dokumentasi FMZ API untuk rincian.Sebagai contoh, kita ingin merancang fungsi pembersihan dengan menggunakan fungsi_G()
, untuk menyimpan data grid.
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()
}
Sistem backtest tidak memiliki batasan yang ketat pada volume order dan presisi order; tetapi dalam bot, setiap platform memiliki standar yang ketat untuk harga order dan volume order, dan pasangan perdagangan yang berbeda memiliki batas yang berbeda. Oleh karena itu, pemula sering menguji OKEX dalam sistem backtest. Setelah strategi dijalankan pada bot, ada berbagai masalah ketika perdagangan dipicu, dan kemudian isi pesan kesalahan tidak dibaca, dan berbagai fenomena gila muncul.
Untuk kasus multi-simbol, persyaratan lebih rumit. Untuk strategi simbol tunggal, Anda dapat merancang parameter untuk menentukan informasi seperti presisi. Namun, ketika Anda merancang strategi multi-simbol, jelas bahwa menulis informasi ke dalam parameter akan membuat parameter sangat membosankan.
Pada saat ini, Anda perlu memeriksa dokumentasi API platform untuk melihat apakah ada antarmuka untuk informasi terkait pasangan perdagangan dalam dokumentasi. Jika ada antarmuka ini, Anda dapat merancang antarmuka akses otomatis dalam strategi untuk mendapatkan informasi seperti presisi, dan mengkonfigurasinya ke dalam informasi pasangan perdagangan dalam perdagangan (singkatnya, presisi secara otomatis diperoleh dari platform, dan kemudian disesuaikan dengan variabel yang terkait dengan parameter strategi).
Berdasarkan analisis di atas, kami merancang perpustakaan template untuk mengurangi kopling antara strategi, mekanisme platform dan antarmuka.
Kita dapat merancang perpustakaan template seperti ini (bagian dari kode dihilangkan):
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
}
Dalam templat, terapkan penulisan kode yang ditujukan untuk bentuk permainan tertentu; ambil FMZ simulasi bot WexApp sebagai contoh:
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
})
})
}
}
Ini akan sangat mudah untuk menggunakan template dalam strategi:
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)
})
}
Ini sangat sederhana untuk merancang dan menulis strategi berdasarkan template di atas. Seluruh strategi memiliki sekitar lebih dari 300 baris kode. Itu menerapkan strategi grid multi-simbol spot cryptocurrency.
Saat ini, ia memiliki kerugianT_T
, jadi kode sumber tidak akan diberikan.
Ada beberapa kode pendaftaran; jika Anda tertarik, Anda dapat mencobanya di wexApp:
Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Ada hanya lebih dari 200 USD, dan ketika bot baru saja dimulai, ia menemukan pasar satu sisi yang besar. Stabilitas strategi ini baik, dan saya tidak memodifikasinya sejak 27 Mei.