Trong bài viết trước đây, chúng tôi đã cùng nhau tạo ra một chiến lược lưới đơn giản. Trong bài viết này, chúng tôi đã nâng cấp và mở rộng chiến lược này thành một chiến lược lưới điểm đa biểu tượng, và để cho chiến lược này được thử nghiệm trong thực tế. Mục đích không phải là để tìm ra một
Trong bài viết này, giống như bài trước, chúng tôi thảo luận về thiết kế dựa trên nền tảng FMZ Quant Trading (FMZ.COM).
Biểu tượng đa
Thành thật mà nói, tôi muốn chiến lược lưới điện không chỉBTC_USDT
, nhưng cũngLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
Dù sao, đối với các cặp giao dịch giao ngay, vận hành giao dịch lưới của tất cả các biểu tượng mà bạn muốn giao dịch cùng một lúc.
Ừ, cảm giác tốt khi nắm bắt được sự rung động của thị trường với nhiều biểu tượng.
Mặc dù yêu cầu nghe có vẻ đơn giản, nhưng nó trở nên khó khăn khi bạn bắt đầu thiết kế.
Bởi vì bạn cần đánh giá các tài sản có sẵn khi đặt hàng, không cần thiết để có được dữ liệu trước khi đánh giá? Hơn nữa, lợi nhuận cần phải được tính toán. Chúng ta có nên ghi lại dữ liệu tài sản tài khoản ban đầu trước? và sau đó lấy dữ liệu tài sản của tài khoản vãng lai và tính toán lợi nhuận và lỗ bằng cách so sánh với ban đầu? May mắn thay, giao diện tài khoản tài sản của một nền tảng thường trả về tất cả dữ liệu tài sản tiền tệ, vì vậy chúng ta chỉ cần lấy nó một lần, và sau đó xử lý dữ liệu.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
ETHUSDT:100:0.002
kiểm soát cặp giao dịch ETH_USDT, vàLTCUSDT:20:0.1
điều khiển cặp giao dịch LTC_USDT.
TrongETHUSDT:100:0.002
,
Các chuỗi này đã chứa thông tin tham số của mỗi biểu tượng bạn cần vận hành. Bạn có thể phân tích các chuỗi và gán giá trị cho các biến trong chiến lược, để kiểm soát logic giao dịch của mỗi biểu tượng. Làm thế nào để phân tích? Hãy sử dụng ví dụ được đề cập ở trên.
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)
}
Chắc chắn, bạn có thể trực tiếp sử dụng các chuỗi JSON, dễ dàng hơn.
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()
chức năng trên FMZ Quant, hoặc sử dụng chức năng hoạt độngDBExec()
trong cơ sở dữ liệu, và bạn có thể truy vấn các tài liệu FMZ API cho các chi tiết.Ví dụ, chúng ta muốn thiết kế một chức năng dọn dẹp bằng cách sử dụng chức năng_G()
, để lưu dữ liệu lưới.
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()
}
Hệ thống backtest không có giới hạn nghiêm ngặt về khối lượng lệnh và độ chính xác lệnh; nhưng trong bot, mỗi nền tảng có các tiêu chuẩn nghiêm ngặt về giá lệnh và khối lượng lệnh, và các cặp giao dịch khác nhau có giới hạn khác nhau. Do đó, người mới bắt đầu thường kiểm tra OKEX trong hệ thống backtest. Một khi chiến lược được chạy trên bot, có nhiều vấn đề khác nhau khi giao dịch được kích hoạt, và sau đó nội dung của thông báo lỗi không được đọc, và nhiều hiện tượng điên rồ xuất hiện.
Đối với các trường hợp nhiều ký hiệu, yêu cầu phức tạp hơn. Đối với một chiến lược một ký hiệu, bạn có thể thiết kế một tham số để chỉ định thông tin như độ chính xác. Tuy nhiên, khi bạn thiết kế một chiến lược nhiều ký hiệu, rõ ràng là viết thông tin vào một tham số sẽ làm cho tham số rất tẻ nhạt.
Tại thời điểm này, bạn cần kiểm tra tài liệu API của nền tảng để xem liệu có giao diện cho thông tin liên quan đến cặp giao dịch trong tài liệu không. Nếu có giao diện này, bạn có thể thiết kế giao diện truy cập tự động trong chiến lược để có được thông tin như độ chính xác, và cấu hình nó vào thông tin cặp giao dịch trong giao dịch (tóm lại, độ chính xác được tự động lấy từ nền tảng và sau đó điều chỉnh theo biến liên quan đến tham số chiến lược).
Dựa trên phân tích trên, chúng tôi thiết kế một thư viện mẫu để giảm sự kết nối giữa chiến lược, cơ chế nền tảng và giao diện.
Chúng ta có thể thiết kế thư viện mẫu như thế này (một phần của mã bị bỏ qua):
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
}
Trong mẫu, thực hiện viết mã nhằm vào một hình thức chơi cụ thể; lấy bot mô phỏng FMZ WexApp làm ví dụ:
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
})
})
}
}
Nó sẽ rất dễ dàng để sử dụng mẫu trong chiến lược:
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)
})
}
Nó rất đơn giản để thiết kế và viết chiến lược dựa trên mẫu trên. Toàn bộ chiến lược có khoảng hơn 300 dòng mã.
Ngay bây giờ, nó có những tổn thấtT_T
, vì vậy mã nguồn sẽ không được cung cấp.
Có một số mã đăng ký; nếu bạn quan tâm, bạn có thể thử chúng trong wexApp:
Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Chỉ có hơn 200 USD, và khi bot mới được khởi động, nó đã gặp một thị trường đơn phương tuyệt vời. Nó cần thời gian để bù đắp tổn thất. Lợi thế lớn nhất của chiến lược lưới điểm là: