
Trong bài viết trước, chúng ta đã 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 ta sẽ nâng cấp và mở rộng chiến lược này thành một chiến lược lưới đa dạng và đưa chiến lược này vào thử nghiệm trong chiến đấu thực tế. Mục đích không phải là tìm ra “chén thánh” mà là khám phá các vấn đề và giải pháp khác nhau khi thiết kế chiến lược. Bài viết này sẽ giải thích một số kinh nghiệm của tôi trong việc thiết kế chiến lược này. Nội dung của bài viết này hơi phức tạp và đòi hỏi một nền tảng nhất định về lập trình.
Bài viết này, giống như bài viết trước, vẫn thảo luận về thiết kế dựa trên Inventor Quantization (FMZ.COM).
BTC_USDT, cũng có thể làmLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT. Dù sao đi nữa, đối với các cặp giao dịch giao ngay, tất cả sản phẩm bạn muốn giao dịch đều có thể được giao dịch theo lưới cùng một lúc.Ừm~~Thật tuyệt khi nắm bắt được thị trường biến động của nhiều loại sản phẩm khác nhau. Các yêu cầu nghe có vẻ đơn giản, nhưng lại nảy sinh vấn đề trong quá trình thiết kế.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
Dấu “|” phân tách dữ liệu của từng loại, có nghĩa làETHUSDT:100:0.002Nó kiểm soát cặp giao dịch ETH_USDT.LTCUSDT:20:0.1Nó kiểm soát cặp giao dịch LTC_USDT. Dấu “|” ở giữa có tác dụng phân tách.
ETHUSDT:100:0.002, trong đó ETHUSDT biểu thị cặp giao dịch bạn muốn giao dịch, 100 là khoảng cách lưới, 0,002 là số lượng đồng ETH được giao dịch trong mỗi lưới và dấu “:” được sử dụng để phân tách các dữ liệu này (tất nhiên, các quy tắc tham số này là được thiết lập bởi nhà thiết kế chiến lược). Bạn có thể thiết kế nó theo nhu cầu của mình).
Các chuỗi này chứa thông tin tham số của từng sản phẩm bạn muốn giao dịch. Phân tích các chuỗi này trong chiến lược và gán các giá trị cụ thể cho các biến của chiến lược để kiểm soát logic giao dịch của từng sản phẩm. Vậy làm sao để phân tích nó? Chúng ta hãy sử dụng lại ví dụ trên.
function main() {
var net = [] // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据
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] // 交易对名称
var diff = parseFloat(arr[1]) // 网格间距
var amount = parseFloat(arr[2]) // 网格下单量
net.push({symbol : symbol, diff : diff, amount : amount})
})
Log("网格参数数据:", net)
}

Bạn có thể thấy các tham số được phân tích theo cách này. Tất nhiên, bạn cũng có thể sử dụng trực tiếp chuỗi JSON, đơn giản 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) // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据
_.each(net, function(pair) {
Log("交易对:", pair.symbol, pair)
})
}

_G()Chức năng, hoặc sử dụng chức năng hoạt động cơ sở dữ liệuDBExec()Để biết chi tiết, vui lòng tham khảo tài liệu API FMZ.Ví dụ, chúng tôi thiết kế một hàm quét, sử dụng_G()Chức năng lưu dữ liệu lưới.
var net = null
function main() { // 策略主函数
// 首先读取储存的net
net = _G("net")
// ...
}
function onExit() {
_G("net", net)
Log("执行扫尾处理,保存数据", "#FF0000")
}
function onexit() { // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
onExit()
}
function onerror() { // 平台系统定义的异常退出函数,在程序发生异常时触发执行
onExit()
}
Hệ thống kiểm tra ngược không áp đặt những hạn chế nghiêm ngặt như vậy đối với số lượng lệnh và độ chính xác của lệnh, nhưng trong giao dịch thực tế, mỗi sàn giao dịch có thể có những tiêu chuẩn nghiêm ngặt về giá lệnh và số lượng lệnh, và những tiêu chuẩn này đối với mỗi cặp giao dịch cũng rất nghiêm ngặt. Những hạn chế không giống nhau. Do đó, người mới thường kiểm tra hệ thống kiểm tra ngược và thấy đủ loại vấn đề khi kích hoạt giao dịch trên thị trường thực. Sau đó, họ thậm chí không đọc thông báo lỗi và gặp phải đủ loại vấn đề điên rồ [dog head].
Đối với nhiều loại, yêu cầu này phức tạp hơn. Đối với chiến lược sản phẩm đơn lẻ, 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 thiết kế chiến lược nhiều sản phẩm, rõ ràng là việc ghi thông tin này vào tham số sẽ khiến tham số trông rất cồng kềnh.
Lúc này, bạn cần kiểm tra tài liệu API của sàn giao dịch để xem có giao diện nào chứa thông tin liên quan đến cặp giao dịch trong tài liệu của sàn giao dịch hay không. Nếu các giao diện này khả dụng, bạn có thể thiết kế một giao diện truy cập tự động trong chiến lược để lấy thông tin như độ chính xác và cấu hình nó cho thông tin cặp giao dịch liên quan đến giao dịch (nói một cách đơn giản, độ chính xác được tự động yêu cầu từ sàn giao dịch và sau đó điều chỉnh theo các tham số chiến lược).
Dựa trên phân tích trên, một thư viện mẫu được thiết kế để giảm sự kết hợp giữa các chiến lược và cơ chế trao đổi cũng như giao diện.
Chúng ta có thể thiết kế thư viện lớp mẫu này như thế này (một số mã bị lược bỏ):
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()
// 需要实现的接口
self.interfaceGetTickers = null // 创建异步获取聚合行情数据线程的函数
self.interfaceGetAcc = null // 创建异步获取账户数据线程的函数
self.interfaceGetPos = null // 获取持仓
self.interfaceTrade = null // 创建并发下单
self.waitTickers = null // 等待并发行情数据
self.waitAcc = null // 等待账户并发数据
self.waitTrade = null // 等待下单并发数据
self.calcAmount = null // 根据交易对精度等数据计算下单量
self.init = null // 初始化工作,获取精度等数据
// 执行配置函数,给对象配置
funcConfigure(self)
// 检测configList约定的接口是否都实现
_.each(configList, function(funcName) {
if (!self[funcName]) {
throw "接口" + funcName + "未实现"
}
})
return self
}
$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
dicRegister = {
"Futures_OKCoin" : funcConfigure_Futures_OKCoin, // OK期货的实现
"Huobi" : funcConfigure_Huobi,
"Futures_Binance" : funcConfigure_Futures_Binance,
"Binance" : funcConfigure_Binance,
"WexApp" : funcConfigure_WexApp, // wexApp的实现
}
return dicRegister
}
Trong mẫu, hãy viết cho mục đích triển khai trao đổi cụ thể, ví dụ, lấy đĩa mô phỏng WexApp của FMZ 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) {
// 获取交易对信息
var symbolInfo = self.getSymbolInfo(symbol)
if (!symbol) {
throw symbol + ",交易对信息查询不到"
}
var tradeAmount = null
var equalAmount = null // 记录币数
if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
// 检查最小交易量
if (tradeAmount < symbolInfo.min) {
Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
return false
}
equalAmount = tradeAmount / price
} else {
tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
// 检查最小交易量
if (tradeAmount < symbolInfo.min / price) {
Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min / price)
return false
}
equalAmount = tradeAmount
}
return [tradeAmount, equalAmount]
}
self.init = function init() { // 自动处理精度等条件的函数
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
})
})
}
}
Sau đó, sử dụng mẫu này trong chiến lược rất đơn giản:
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()
// 测试获取行情
ex.goGetTickers()
var tickers = ex.getTickers()
Log("tickers:", tickers)
// 测试获取账户信息
ex.goGetAcc(symbol, ts)
_.each(arrTestSymbol, function(symbol) {
_.each(tickers, function(ticker) {
if (symbol == ticker.originalSymbol) {
// 打印行情数据
Log(symbol, ticker)
}
})
// 打印资产数据
var acc = ex.getAcc(symbol, ts)
Log("acc:", acc.symbol, acc)
})
}
Rất đơn giản để thiết kế và viết một chiến lược dựa trên mẫu trên. Toàn bộ chiến lược dài khoảng 300+ dòng, thực hiện chiến lược lưới đa dạng giao ngay tiền kỹ thuật số.


Hiện đang mất tiềnT_T, mã nguồn sẽ không được phát hành trong thời điểm hiện tại.
Sau đây là một số mã đăng ký. Nếu bạn quan tâm, bạn có thể dùng thử trên wexApp:
购买地址: https://www.fmz.com/m/s/284507
注册码:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
Chỉ có hơn 200 Us, và ngay khi bắt đầu hoạt động, nó đã gặp phải một thị trường một chiều lớn và dần phục hồi. Ưu điểm lớn nhất của lưới điểm là: “Bạn có thể ngủ ngon!” Độ ổn định thì ổn. Tôi chưa đụng đến nó kể từ ngày 27 tháng 5. Tôi không dám thử lưới tương lai vào lúc này.