Trong các bài viết trước của Thư viện FMZ, chúng tôi đã thiết kế một số chiến lược đồng bộ hóa thứ tự và vị trí.
Những điều này nhằm đưa tài khoản tham chiếu và tài khoản đồng bộ hóa vào một chiến lược để quản lý và đồng bộ hóa các lệnh và vị thế. Hôm nay chúng ta sẽ thử một thiết kế khác. Dựa trên giao diện API mở rộng mạnh mẽ của nền tảng giao dịch định lượng FMZ, chúng ta sẽ thiết kế một hệ thống quản lý đồng bộ hóa lệnh.
Trước hết chúng tôi cần một số gợi ý và nhu cầu tốt. Hai chiến lược đồng bộ hóa thứ tự và vị trí trước đó ở trên có một số điểm khó khăn rõ ràng. Chúng ta hãy cùng thảo luận về chúng:
Giải pháp:
Sử dụng giao diện API mở rộng của FMZ, chủ sở hữu tài khoản đồng bộ hóa (người theo dõi) chỉ cần đăng ký nền tảng giao dịch định lượng FMZ và sau đó chạy chiến lược (trong hệ thống được thiết kế trong bài viết này:订单同步管理系统(Synchronous Server)
Chiến lược thị trường thực tế). Sau đó cung cấp API KEY mở rộng FMZ (lưu ý, không phải API KEY của tài khoản trao đổi) và ID thời gian thực của hệ thống quản lý đồng bộ hóa lệnh (Synchronous Server) cho chủ sở hữu tài khoản tham chiếu (người mang lệnh đến) .
Khi tham chiếu đến lệnh thực tế của chủ tài khoản (người có lệnh) (trong hệ thống được thiết kế trong bài viết này)订单同步管理系统类库(Single Server)
) gửi tín hiệu, tài khoản thật của chủ tài khoản được đồng bộ hóa sẽ nhận được tín hiệu giao dịch và tự động đặt lệnh sau đó.
订单同步管理系统类库(Single Server)
Chiến lược), cho phép chủ sở hữu tài khoản tham chiếu (người tiếp nhận lệnh) nhúng trực tiếp thư viện mẫu này vào chiến lược của mình để đạt được chức năng đồng bộ hóa lệnh và vị trí.Vì vậy, hệ thống này bao gồm 2 phần: 1. Thư viện lớp hệ thống quản lý đồng bộ hóa đơn hàng (Single Server) 2. Hệ thống quản lý đơn hàng đồng bộ (Synchronous Server)
Bây giờ các yêu cầu đã rõ ràng, chúng ta hãy bắt đầu thiết kế nhé!
Lưu ý rằng đây không phải là một chiến lược. Đây là thư viện lớp mẫu của FMZ. Khái niệm về thư viện lớp mẫu có thể được tìm kiếm trong tài liệu API của FMZ, vì vậy tôi sẽ không đi sâu vào chi tiết ở đây.
Mã thư viện mẫu:
// 全局变量
var keyName_label = "label"
var keyName_robotId = "robotId"
var keyName_extendAccessKey = "extendAccessKey"
var keyName_extendSecretKey = "extendSecretKey"
var fmzExtendApis = parseConfigs([config1, config2, config3, config4, config5])
var mapInitRefPosAmount = {}
function parseConfigs(configs) {
var arr = []
_.each(configs, function(config) {
if (config == "") {
return
}
var strArr = config.split(",")
if (strArr.length != 4) {
throw "configs error!"
}
var obj = {}
obj[keyName_label] = strArr[0]
obj[keyName_robotId] = strArr[1]
obj[keyName_extendAccessKey] = strArr[2]
obj[keyName_extendSecretKey] = strArr[3]
arr.push(obj)
})
return arr
}
function getPosAmount(pos, ct) {
var longPosAmount = 0
var shortPosAmount = 0
_.each(pos, function(ele) {
if (ele.ContractType == ct && ele.Type == PD_LONG) {
longPosAmount = ele.Amount
} else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
shortPosAmount = ele.Amount
}
})
var timestamp = new Date().getTime()
return {ts: timestamp, long: longPosAmount, short: shortPosAmount}
}
function sendCommandRobotMsg (robotId, accessKey, secretKey, msg) {
// https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
var url = "https://www.fmz.com/api/v1?access_key=" + accessKey + "&secret_key=" + secretKey + "&method=CommandRobot&args=[" + robotId + ',"' + msg + '"]'
Log(url)
var ret = HttpQuery(url)
return ret
}
function follow(nowPosAmount, symbol, ct, type, delta) {
var msg = ""
var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
if (delta > 0) {
// 开仓
var tradeDirection = type == PD_LONG ? "buy" : "sell"
// 发送信号
msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
} else if (delta < 0) {
// 平仓
var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
if (nowAmount <= 0) {
Log("未检测到持仓")
return
}
// 发送信号
msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
} else {
throw "错误"
}
if (msg) {
_.each(fmzExtendApis, function(extendApiConfig) {
var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
Log("调用CommandRobot接口,", "label:", extendApiConfig[keyName_label], ", msg:", msg, ", ret:", ret)
Sleep(1000)
})
}
}
$.PosMonitor = function(exIndex, symbol, ct) {
var ts = new Date().getTime()
var ex = exchanges[exIndex]
// 判断ex类型
var exName = ex.GetName()
var isFutures = exName.includes("Futures_")
var exType = isFutures ? "futures" : "spot"
if (!isFutures) {
throw "仅支持期货跟单"
}
if (exType == "futures") {
// 缓存 symbol ct
var buffSymbol = ex.GetCurrency()
var buffCt = ex.GetContractType()
// 切换到对应的交易对、合约代码
ex.SetCurrency(symbol)
if (!ex.SetContractType(ct)) {
throw "SetContractType failed"
}
// 监控持仓
var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct // refPos-exIndex-symbol-contractType
var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
if (!initRefPosAmount) {
// 没有初始化数据,初始化
mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
}
// 监控
var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
// 计算仓位变动
var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short
// 检测变动
if (!(longPosDelta == 0 && shortPosDelta == 0)) {
// 执行多头动作
if (longPosDelta != 0) {
Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行多头跟单,变动量:", longPosDelta)
follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
}
// 执行空头动作
if (shortPosDelta != 0) {
Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行空头跟单,变动量:", shortPosDelta)
follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
}
// 执行跟单操作后,更新
mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
}
// 恢复 symbol ct
ex.SetCurrency(buffSymbol)
ex.SetContractType(buffCt)
} else if (exType == "spot") {
// 现货
}
}
$.getTbl = function() {
var tbl = {
"type" : "table",
"title" : "同步数据",
"cols" : [],
"rows" : []
}
// 构造表头
tbl.cols.push("监控账户:refPos-exIndex-symbol-contractType")
tbl.cols.push(`监控持仓:{"时间戳":xxx,"多头持仓量":xxx,"空头持仓量":xxx}`)
_.each(fmzExtendApis, function(extendApiData, index) {
tbl.cols.push(keyName_robotId + "-" + index)
})
// 写入数据
_.each(mapInitRefPosAmount, function(initRefPosAmount, key) {
var arr = [key, JSON.stringify(initRefPosAmount)]
_.each(fmzExtendApis, function(extendApiData) {
arr.push(extendApiData[keyName_robotId])
})
tbl.rows.push(arr)
})
return tbl
}
// 引用该模板类库的策略调用范例
function main() {
// 清除所有日志
LogReset(1)
// 切换到OKEX 模拟盘测试
exchanges[0].IO("simulate", true)
// 设置合约
exchanges[0].SetCurrency("ETH_USDT")
exchanges[0].SetContractType("swap")
// 定时交易时间间隔
var tradeInterval = 1000 * 60 * 3 // 三分钟交易一次,用于观察跟单信号
var lastTradeTS = new Date().getTime()
while (true) {
// 策略其它逻辑...
// 用于测试的模拟交易触发
var ts = new Date().getTime()
if (ts - lastTradeTS > tradeInterval) {
Log("模拟带单策略发生交易,持仓变化", "#FF0000")
exchanges[0].SetDirection("buy")
exchanges[0].Buy(-1, 1)
lastTradeTS = ts
}
// 使用模板的接口函数
$.PosMonitor(0, "ETH_USDT", "swap") // 可以设置多个监控,监控带单策略上的不同的exchange对象
var tbl = $.getTbl()
// 显示状态栏
LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
Sleep(1000)
}
}
Thiết kế rất đơn giản, thư viện lớp này có 2 hàm chức năng. Khi một chiến lược giao dịch theo chương trình trên nền tảng FMZ tham chiếu订单同步管理系统类库(Single Server)
Sau thư viện mẫu. Chiến lược này có thể được thực hiện bằng cách sử dụng hàm sau.
$.PosMonitor Chức năng này là theo dõi sự thay đổi vị trí của đối tượng giao dịch trong chiến lược, sau đó gửi tín hiệu giao dịch đến thị trường thực được thiết lập trong các tham số của mẫu: Thư viện lớp hệ thống quản lý đồng bộ hóa lệnh (Máy chủ đơn).
$.getTbl Trả về dữ liệu đồng bộ hóa được giám sát.
Ví dụ sử dụng nằm trong: Mẫu Thư viện lớp Hệ thống quản lý đồng bộ hóa đơn hàng (Máy chủ đơn)main
Trong hàm:
// 引用该模板类库的策略调用范例
function main() {
// 清除所有日志
LogReset(1)
// 切换到OKEX 模拟盘测试
exchanges[0].IO("simulate", true)
// 设置合约
exchanges[0].SetCurrency("ETH_USDT")
exchanges[0].SetContractType("swap")
// 定时交易时间间隔
var tradeInterval = 1000 * 60 * 3 // 三分钟交易一次,用于观察跟单信号
var lastTradeTS = new Date().getTime()
while (true) {
// 策略其它逻辑...
// 用于测试的模拟交易触发
var ts = new Date().getTime()
if (ts - lastTradeTS > tradeInterval) {
Log("模拟带单策略发生交易,持仓变化", "#FF0000")
exchanges[0].SetDirection("buy")
exchanges[0].Buy(-1, 1)
lastTradeTS = ts
}
// 使用模板的接口函数
$.PosMonitor(0, "ETH_USDT", "swap") // 可以设置多个监控,监控带单策略上的不同的exchange对象
var tbl = $.getTbl()
// 显示状态栏
LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
Sleep(1000)
}
}
Bản thân thư viện mẫu cũng có thể tạo ra các chiến lược thực tế, thường được sử dụng để kiểm tra thư viện mẫu. Ví dụ, hãy thử nghiệm mẫu này. Bạn có thể hiểu mẫumain
Chức năng là chiến lược của riêng bạn.main
chức năng.
Mã kiểm tra được viết để sử dụng thử nghiệm đĩa mô phỏng OKEX. Cần phải cấu hình API KEY của đĩa mô phỏng OKEX trên FMZ làm tài khoản tham chiếu (có lệnh) và bắt đầu chuyển sang đĩa mô phỏng trong chức năng chính. Sau đó đặt cặp giao dịch thành ETH_USDT và hợp đồng thành vĩnh viễn (hoán đổi). Sau đó nhập vòng lặp while. Một lệnh được đặt sau mỗi 3 phút trong một chu kỳ để mô phỏng việc kích hoạt giao dịch chiến lược. Vòng lặp while gọi$.PosMonitor(0, "ETH_USDT", "swap")
Tham số đầu tiên của hàm này được truyền vào là 0, cho biết các trao đổi đang được theo dõi.[0] Đối tượng trao đổi này theo dõi cặp giao dịch ETH_USDT và hợp đồng hoán đổi. Sau đó gọi$.getTbl()
Để có thông tin biểu đồ, hãy sử dụngLogStatus(_D(), "\n" + "
” + JSON.stringify(tbl) + “")
Cho phép hiển thị dữ liệu biểu đồ trên thanh trạng thái.
Vì vậy, bạn thấy đấy, miễn là bạn sử dụng nó trong chính sách có tham chiếu đến mẫu này$.PosMonitor(0, "ETH_USDT", "swap")
, chiến lược này có thể được trang bị chức năng theo dõi vị trí của một sản phẩm nhất định và gửi tin nhắn dựa trên sự thay đổi vị trí.
Trước khi thử nghiệm, vui lòng giải thích订单同步管理系统类库(Single Server)
Thiết kế tham số chiến lược:
Tôi vừa nói về cách sử dụng chức năng giao diện mẫu để cho phép nâng cấp chiến lược thành một chức năng duy nhất. Vậy tín hiệu sẽ được gửi đến ai khi vị trí thay đổi?
Câu hỏi này được gửi đến ai được xác định bởi订单同步管理系统类库(Single Server)
Các thông số cần cấu hình.
Bạn có thể thấy có 5 tham số, hỗ trợ tối đa 5 lần đẩy (bạn có thể mở rộng nếu cần tăng) và tham số mặc định là chuỗi rỗng, nghĩa là nó không được xử lý. Định dạng chuỗi cấu hình: nhãn, robotId, accessKey, secretKey
label Nhãn của tài khoản được đồng bộ hóa được sử dụng để đánh dấu một tài khoản nhất định và tên có thể được đặt tùy ý.
robotId
ID thực, được tạo bởi chủ sở hữu tài khoản đồng bộ hóa订单同步管理系统(Synchronous Server)
ID của giao dịch thực tế.
accessKey Khóa truy cập API mở rộng của FMZ
secretKey Khóa bí mật API mở rộng của FMZ
Tiếp theo chúng ta có thể làm một thử nghiệm đơn giản.
Thư viện lớp hệ thống quản lý đồng bộ hóa đơn hàng (Máy chủ đơn) hoạt động đĩa thực:
Hệ thống quản lý đồng bộ đơn hàng (Synchronous Server) nhận được tín hiệu: Chúng tôi vẫn chưa hoàn thành thiết kế hệ thống quản lý đồng bộ hóa đơn hàng (Synchronous Server). Trước tiên hãy triển khai nó bằng một đoạn mã đơn giản không thực hiện giao dịch mà chỉ in ra tín hiệu:
Mã tạm thời của hệ thống quản lý đồng bộ đơn hàng (Synchronous Server):
function main() {
LogReset(1)
while (true) {
var cmd = GetCommand()
if (cmd) {
// cmd: ETH_USDT,swap,buy,1
Log("cmd: ", cmd)
}
Sleep(1000)
}
}
Bạn có thể thấy rằng tài khoản thực của chủ tài khoản đồng bộ hóa đã nhận được thông tin:ETH_USDT,swap,buy,1
。
Theo cách này, bước tiếp theo là tự động thực hiện lệnh dựa trên cặp giao dịch, mã hợp đồng, hướng giao dịch và số lượng trong thông tin.
hiện tại订单同步管理系统(Synchronous Server)
Đây chỉ là mã tạm thời, chúng ta sẽ tiếp tục khám phá thiết kế của nó trong số tiếp theo.