Có một vài bài viết trong thư viện nền tảng về việc kết nối Trading View webhook, cho phép giao dịch được điều khiển tín hiệu của các hệ thống bên ngoài chính sách, khi nền tảng chưa hỗ trợ chức năng dịch vụ HTTP tích hợp của ngôn ngữ JavaScript.CommandRobot
Đơn giản là một yêu cầu HTTP/https của tín hiệu bên ngoài được gửi đến nền tảng FMZ, nền tảng chuyển tín hiệu, thông báo thông điệp tương tác chiến lược cho chương trình chiến lược.
Khi nền tảng phát triển, lặp lại và nâng cấp, nhiều tính năng mới được cập nhật; nhận tín hiệu bên ngoài cũng có các giải pháp mới; các giải pháp khác nhau có những ưu điểm riêng biệt, và chúng tôi sẽ cùng nhau khám phá chủ đề này.
Sử dụng cách này để kết nối hệ thống bên ngoài, những lợi thế là tương đối đơn giản, an ninh mạnh mẽ, và nền tảng dựa trên API mở rộng giao diện ổn định cao.
Cách nhận tín hiệu bên ngoài:
Hệ thống bên ngoài (trading view webhook)
> FMZ mở rộng dịch vụ API > chiến lược thực tế
Một hệ thống bên ngoài (trading view webhook): ví dụ như một kịch bản PINE chạy trên Trading View, có thể thiết lập báo động, sau khi được kích hoạt, sẽ gửi yêu cầu http đến địa chỉ url của webhook được thiết lập, như một tín hiệu. 2, FMZ mở rộng dịch vụ API: sau khi truy cập giao diện thành công, nền tảng chuyển tiếp thông tin, được gửi đến ổ đĩa chính sách như một thông điệp tương tác. 3, Policy Real Disk: Trong Policy Real Disk, bạn có thể thiết kế chức năng GetCommand để nghe tin nhắn tương tác và thực hiện các hoạt động đã thiết lập sau khi phát hiện tin nhắn.
Một bước nữa ở giữa (chuyển qua nền tảng) so với việc sử dụng dịch vụ HTTP tích hợp để tạo tín hiệu nhận dịch vụ trực tiếp.
Sau khi nền tảng hỗ trợ chức năng dịch vụ HTTP tích hợp trong ngôn ngữ JavaScript, bạn có thể trực tiếp tạo ra một dịch vụ nghe lén tín hiệu bên ngoài song song. Ưu điểm là: dịch vụ HTTP được tạo ra là một chuỗi riêng biệt, không ảnh hưởng đến logic của hàm chính, có thể nghe lén tin nhắn giống như chức năng GetCommand, nghe lén tín hiệu bên ngoài trực tiếp, so với việc sử dụng chương trình API mở rộng, bỏ qua vòng lặp chuyển trung.
Cách nhận tín hiệu bên ngoài:
Hệ thống bên ngoài (trading view webhook)
Các hệ thống bên ngoài (trading view webhook): ví dụ như kịch bản PINE chạy trên Trading View, có thể thiết lập báo động, sau khi được kích hoạt sẽ gửi yêu cầu http đến URL webhook được thiết lập, như một tín hiệu. 2, chiến lược thực: chiến lược đồng thời chạy một dịch vụ HTTP, trực tiếp nhận tín hiệu bên ngoài.
Khả năng này tiết kiệm một bước, nhưng để tăng cường an ninh, tốt nhất là cấu hình dịch vụ https, cần phải gấp đôi.
Để thử nghiệm hai phương án, các chính sách sau đây sẽ lặp lại và gửi 10 yêu cầu HTTP/Https mỗi vòng để mô phỏng tín hiệu bên ngoài. Sau đó, các chính sách sẽ nghe "tin nhắn tương tác" và "tin nhắn được đẩy bởi đường dẫn dịch vụ HTTP". Sau đó, các chính sách sẽ so sánh tín hiệu bên ngoài với tín hiệu nhận được, phát hiện không có mất tín hiệu, tính toán thời gian.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = ""
var secretKey = ""
function serverFunc(ctx) {
var path = ctx.path()
if (path == "/CommandRobot") {
var body = ctx.body()
threading.mainThread().postMessage(body)
ctx.write("OK")
// 200
} else {
ctx.setStatus(404)
}
}
function createMsgTester(accessKey, secretKey, httpUrl) {
var tester = {}
tester.currentRobotId = _G()
tester.arrSendMsgByAPI = []
tester.arrSendMsgByHttp = []
tester.arrEchoMsgByAPI = []
tester.arrEchoMsgByHttp = []
tester.idByAPI = 0
tester.idByHttp = 0
var sendMsgByAPI = function(msgByAPI, robotId, accessKey, secretKey) {
var headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
"Content-Type": "application/json"
}
HttpQuery(`https://www.fmz.com/api/v1?access_key=${accessKey}&secret_key=${secretKey}&method=CommandRobot&args=[${robotId},+""]`, {"method": "POST", "body": JSON.stringify(msgByAPI), "headers": headers})
}
var sendMsgByHttp = function(msgByHttp, httpUrl) {
HttpQuery(httpUrl, {"method": "POST", "body": JSON.stringify(msgByHttp)})
}
tester.run = function() {
var robotId = tester.currentRobotId
for (var i = 0; i < 10; i++) {
var msgByAPI = {"ts": new Date().getTime(), "id": tester.idByAPI, "way": "ByAPI"}
tester.arrSendMsgByAPI.push(msgByAPI)
tester.idByAPI++
threading.Thread(sendMsgByAPI, msgByAPI, robotId, accessKey, secretKey)
var msgByHttp = {"ts": new Date().getTime(), "id": tester.idByHttp, "way": "ByHttp"}
tester.arrSendMsgByHttp.push(msgByHttp)
tester.idByHttp++
threading.Thread(sendMsgByHttp, msgByHttp, httpUrl)
}
}
tester.getEcho =function(msg) {
if (msg["way"] == "ByAPI") {
tester.arrEchoMsgByAPI.push(msg)
} else {
tester.arrEchoMsgByHttp.push(msg)
}
}
tester.deal = function() {
var tbls = []
for (var pair of [[tester.arrEchoMsgByHttp, tester.arrSendMsgByHttp, "ByHttp"], [tester.arrEchoMsgByAPI, tester.arrSendMsgByAPI, "ByAPI"]]) {
var receivedMessages = pair[0]
var sentMessages = pair[1]
var testType = pair[2]
var receivedMap = new Map()
receivedMessages.forEach(message => {
receivedMap.set(message["id"], message)
})
var matchedPairs = []
var timeDifferences = []
for (var sentMessage of sentMessages) {
var receivedMessage = receivedMap.get(sentMessage["id"])
if (receivedMessage) {
matchedPairs.push([JSON.stringify(sentMessage), JSON.stringify(receivedMessage), receivedMessage["ts"] - sentMessage["ts"]])
timeDifferences.push(receivedMessage["ts"] - sentMessage["ts"])
} else {
Log("no matched sentMessage:", sentMessage, "#FF0000")
}
}
var averageTimeDifference = timeDifferences.reduce((sum, diff) => sum + diff, 0) / timeDifferences.length
var tbl = {
"type": "table",
"title": testType + " / averageTimeDifference:" + averageTimeDifference,
"cols": ["send", "received", "ts diff"],
"rows": []
}
for (var pair of matchedPairs) {
tbl["rows"].push(pair)
}
tbls.push(tbl)
Log(testType, ", averageTimeDifference:", averageTimeDifference, "ms")
}
tester.arrSendMsgByAPI = []
tester.arrSendMsgByHttp = []
tester.arrEchoMsgByAPI = []
tester.arrEchoMsgByHttp = []
return tbls
}
return tester
}
function main() {
__Serve("http://0.0.0.0:8088", serverFunc)
var t = createMsgTester(accessKey, secretKey, httpUrl)
while (true) {
Log("测试开始...", "#FF0000")
t.run()
var beginTS = new Date().getTime()
while (new Date().getTime() - beginTS < 60 * 1000) {
var cmd = GetCommand()
if (cmd) {
try {
var obj = JSON.parse(cmd)
obj["ts"] = new Date().getTime()
t.getEcho(obj)
} catch (e) {
Log(e)
}
}
var msg = threading.mainThread().peekMessage(-1)
if (msg) {
try {
var obj = JSON.parse(msg)
obj["ts"] = new Date().getTime()
t.getEcho(obj)
} catch (e) {
Log(e)
}
}
}
Log("等待结束...", "#FF0000")
var tbls = t.deal()
LogStatus(_D(), "\n`" + JSON.stringify(tbls) + "`")
Sleep(20000)
}
}
Nếu thử nghiệm, bạn cần phải điền vào địa chỉ IP máy chủ cụ thể, mở rộng API KEY của nền tảng FMZ.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
1, chức năng serverFunc tạo ra một dịch vụ HTTP song song để nghe tín hiệu bên ngoài. Đối với các thông điệp bên ngoài được nhận bởi giao diện API mở rộng, sử dụng chức năng GetCommand nghe.
Một tin nhắn được gửi bởi một chủ đề của dịch vụ HTTP:
Dovar msg = threading.mainThread().peekMessage(-1)
Nghe đây nhé.
Thông điệp tương tác từ API mở rộng:
Dovar cmd = GetCommand()
Nghe đây nhé.
2, cả quá trình gửi tín hiệu và nhận tín hiệu đều không bị chặn, nền tảng đã tối ưu hóa cơ chế thu hồi tài nguyên đa dây chuyền dưới cùng, giúp cho việc chuyển đổi tín hiệu không bị chặn.Thread
Hoặcexchange.Go
Chức năng đồng bộ, không cần phải chờ đợi nhiệm vụ đồng bộ hoàn thành (ví dụ: join, wait, vv) và hệ thống dưới cùng sẽ tự động xử lý việc phục hồi tài nguyên (chỉ cần hỗ trợ phiên bản mới nhất của người quản lý).
// 摘录代码片段,发送信号
tester.run = function() {
var robotId = tester.currentRobotId
for (var i = 0; i < 10; i++) {
var msgByAPI = {"ts": new Date().getTime(), "id": tester.idByAPI, "way": "ByAPI"}
tester.arrSendMsgByAPI.push(msgByAPI)
tester.idByAPI++
threading.Thread(sendMsgByAPI, msgByAPI, robotId, accessKey, secretKey) // 并发调用,非阻塞
var msgByHttp = {"ts": new Date().getTime(), "id": tester.idByHttp, "way": "ByHttp"}
tester.arrSendMsgByHttp.push(msgByHttp)
tester.idByHttp++
threading.Thread(sendMsgByHttp, msgByHttp, httpUrl) // 并发调用,非阻塞
}
}
// 摘录代码片段,接收信号
var cmd = GetCommand() // 监听来自扩展API的消息,非阻塞
var msg = threading.mainThread().peekMessage(-1) // 监听来自自建Http服务的消息,使用了参数-1,非阻塞
Tiếp theo, chúng ta sẽ xem quá trình kiểm tra này, thông tin được chú thích trực tiếp trong mã:
function main() {
__Serve("http://0.0.0.0:8088", serverFunc) // 在当前策略实例中,创建一个并发的http服务
var t = createMsgTester(accessKey, secretKey, httpUrl) // 创建一个用于测试管理的对象
while (true) { // 策略主循环开始
Log("测试开始...", "#FF0000")
t.run() // 每次循环开始,调用测试管理对象的run函数,使用两种方式(1、通过扩展API发送信号,2、直接向当前策略创建的Http服务发送信号),每种方式并发发送10个请求
var beginTS = new Date().getTime()
while (new Date().getTime() - beginTS < 60 * 1000) { // 循环检测来自扩展API的交互消息,循环检测来自自建Http服务的消息
var cmd = GetCommand()
if (cmd) {
try {
var obj = JSON.parse(cmd)
obj["ts"] = new Date().getTime() // 检测到交互消息,记录消息,更新时间为收到时间
t.getEcho(obj) // 记录到对应数组
} catch (e) {
Log(e)
}
}
var msg = threading.mainThread().peekMessage(-1)
if (msg) {
try {
var obj = JSON.parse(msg)
obj["ts"] = new Date().getTime() // 检测到自建的Http服务收到的消息,更新时间为收到时间
t.getEcho(obj) // ...
} catch (e) {
Log(e)
}
}
}
Log("等待结束...", "#FF0000")
var tbls = t.deal() // 根据记录的消息,配对,检查是否有未配对的消息,如果有说明有信号丢失
LogStatus(_D(), "\n`" + JSON.stringify(tbls) + "`")
Sleep(20000)
}
}
Trong một số trường hợp, việc sử dụng HTTP chỉ mất một chút thời gian so với API.
Chiến lược tích hợp dịch vụ HTTP để nhận tín hiệu, phương pháp thử nghiệm này không nghiêm ngặt, yêu cầu nên đến từ bên ngoài. Để hiểu rõ hơn, bạn có thể bỏ qua yếu tố này. Đối với hai phương thức thu thập tín hiệu, chiến lược tích hợp dịch vụ HTTP sau khi tất cả đã giảm một vòng lặp, nên tốc độ phản hồi nhanh hơn một chút. Đối với sự ổn định tín hiệu, quan trọng hơn là tín hiệu không bị mất hoặc bỏ qua. Kết quả thử nghiệm cho thấy, API mở rộng của nền tảng FMZ cũng ổn định, không thấy mất tín hiệu trong thử nghiệm, nhưng không loại trừ các yếu tố khác nhau về mạng như các yếu tố gây ra vấn đề tín hiệu, sử dụng dịch vụ HTTP tích hợp trực tiếp để nhận tín hiệu bên ngoài cũng là một giải pháp tốt hơn.
Trong bài viết này, các dịch vụ HTTP tích hợp trong mã văn bản không được kiểm tra và chỉ đơn giản là nhận dữ liệu tin nhắn, bài viết tiếp theo chúng tôi đã hoàn thành một mẫu dịch vụ HTTP tích hợp sẵn dùng để nhận tín hiệu Trading View bên ngoài, chào mừng thảo luận, cảm ơn bạn đã đọc.