avatar of 发明者量化-小小梦 发明者量化-小小梦
tập trung vào tin nhắn riêng tư
4
tập trung vào
1271
Người theo dõi

Thảo luận về việc tiếp nhận tín hiệu bên ngoài của nền tảng FMZ: API mở rộng so với dịch vụ HTTP tích hợp chiến lược

Được tạo ra trong: 2024-12-12 18:33:26, cập nhật trên: 2025-05-16 15:53:52
comments   0
hits   583

Thảo luận về việc tiếp nhận tín hiệu bên ngoài của nền tảng FMZ: API mở rộng so với dịch vụ HTTP tích hợp chiến lược

Lời nói đầu

Có một số bài viết trong thư viện nền tảng về việc kết nối với webhooks Trading View, cho phép các chiến lược thúc đẩy giao dịch dựa trên tín hiệu từ các hệ thống bên ngoài. Vào thời điểm đó, nền tảng không có chức năng dịch vụ http tích hợp hỗ trợ ngôn ngữ JavaScript. Giao diện API mở rộng của nền tảng được sử dụng:CommandRobotNói một cách đơn giản, 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 và nền tảng này chuyển tiếp tín hiệu dưới dạng tin nhắn tương tác chiến lược đến chương trình chiến lược.

Khi nền tảng phát triển và lặp lại, nhiều tính năng mới đã được nâng cấp và cập nhật. Ngoài ra còn có những giải pháp mới để nhận tín hiệu bên ngoài. Mỗi giải pháp đều có ưu điểm riêng. Trong bài viết này, chúng ta sẽ cùng nhau thảo luận về chủ đề này.

Sử dụng nền tảng FMZ để mở rộng giao diện API

Ưu điểm của việc sử dụng phương pháp này để kết nối với các hệ thống bên ngoài là tương đối đơn giản, có độ bảo mật cao và dựa vào tính ổn định của giao diện API mở rộng của nền tảng.

Quá trình tiếp nhận tín hiệu bên ngoài:

Hệ thống bên ngoài (webhook Trading View) –> Dịch vụ API mở rộng FMZ –> Chiến lược thị trường thực

  1. Hệ thống bên ngoài (webhook Trading View): Ví dụ, tập lệnh PINE chạy trên Trading View có thể thiết lập cảnh báo, cảnh báo này sẽ gửi yêu cầu http đến địa chỉ url webhook đã thiết lập dưới dạng tín hiệu khi được kích hoạt.
  2. Dịch vụ API mở rộng FMZ: Sau khi truy cập thành công vào giao diện, nền tảng sẽ chuyển tiếp thông tin và gửi đến thị trường chiến lược thực tế dưới dạng tin nhắn tương tác.
  3. Triển khai chiến lược: Trong triển khai chiến lược, bạn có thể thiết kế hàm GetCommand để lắng nghe các thông điệp tương tác và thực hiện các hoạt động được chỉ định sau khi phát hiện các thông điệp.

So với việc sử dụng dịch vụ Http tích hợp để trực tiếp tạo ra dịch vụ nhận tín hiệu thì có thêm một bước ở giữa (chuyển nền tảng).

Chiến lược tích hợp dịch vụ Http

Sau khi nền tảng hỗ trợ chức năng dịch vụ Http tích hợp của ngôn ngữ JavaScript, bạn có thể trực tiếp tạo dịch vụ đồng thời để lắng nghe các tín hiệu bên ngoài. Ưu điểm là: dịch vụ Http được tạo ra là một luồng riêng biệt và không ảnh hưởng đến logic chức năng chính. Nó có thể giám sát các thông báo như hàm GetCommand và giám sát trực tiếp các tín hiệu bên ngoài. So với việc sử dụng giải pháp API mở rộng, nó loại bỏ liên kết chuyển giao.

Quá trình tiếp nhận tín hiệu bên ngoài:

Hệ thống bên ngoài (webhook Trading View) –> Chiến lược giao dịch trực tiếp

  1. Hệ thống bên ngoài (webhook Trading View): Ví dụ, tập lệnh PINE chạy trên Trading View có thể thiết lập báo động. Khi được kích hoạt, nó sẽ gửi yêu cầu http đến địa chỉ url webhook đã thiết lập dưới dạng tín hiệu.
  2. Triển khai chiến lược: Chiến lược đồng thời chạy dịch vụ Http để trực tiếp nhận tín hiệu bên ngoài.

Giải pháp này tiết kiệm được một bước, nhưng để cải thiện bảo mật, tốt nhất là nên cấu hình dịch vụ https, điều này đòi hỏi một chút công sức. Việc này rắc rối hơn một chút so với việc sử dụng API mở rộng.

Mã kiểm tra

Kiểm tra hai giải pháp. Chiến lược sau sẽ gửi 10 yêu cầu HTTP/HTTPS đồng thời trong mỗi chu kỳ để mô phỏng các tín hiệu bên ngoài. Sau đó, chiến lược này sẽ theo dõi “tin nhắn tương tác” và “tin nhắn được đẩy bởi luồng dịch vụ Http”. Sau đó, chương trình chiến lược sẽ so sánh từng tín hiệu bên ngoài với tín hiệu nhận được, phát hiện xem có mất tín hiệu hay không và tính toán thời gian tiêu thụ.

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 đang thử nghiệm, bạn cần điền địa chỉ IP máy chủ cụ thể và API KEY mở rộng của nền tảng FMZ.

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
  1. Hàm serverFunc tạo ra một dịch vụ Http đồng thời để giám sát các tín hiệu bên ngoài. Đối với các tin nhắn bên ngoài được nhận bởi giao diện API mở rộng, hàm GetCommand được sử dụng để giám sát.
  • Các thông điệp được đẩy bởi luồng dịch vụ Http: Phụ thuộc vàovar msg = threading.mainThread().peekMessage(-1)màn hình.

  • Tin nhắn tương tác được chuyển tiếp bởi giao diện API mở rộng: Phụ thuộc vàovar cmd = GetCommand()màn hình.

  1. Cả quá trình gửi và nhận tín hiệu đều không chặn. Nền tảng tối ưu hóa cơ chế tái chế tài nguyên đa luồng cơ bản.Threadhoặcexchange.GoCác hàm đồng thời không còn cần phải chờ các tác vụ đồng thời hoàn tất (chẳng hạn như hàm tham gia, hàm chờ, v.v.) và hệ thống cơ bản sẽ tự động xử lý việc tái chế tài nguyên (yêu cầu phiên bản mới nhất của máy chủ hỗ trợ tính năng này).
    // 摘录代码片段,发送信号
    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 hãy xem xét quy trình thử nghiệm này, trong đó 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)
    }
}

Kết quả kiểm tra

Thảo luận về việc tiếp nhận tín hiệu bên ngoài của nền tảng FMZ: API mở rộng so với dịch vụ HTTP tích hợp chiến lược

Thảo luận về việc tiếp nhận tín hiệu bên ngoài của nền tảng FMZ: API mở rộng so với dịch vụ HTTP tích hợp chiến lược

Sau một thời gian thử nghiệm, có thể thấy rằng phương pháp Http mất ít thời gian trung bình hơn so với phương pháp API.

Chiến lược này có dịch vụ Http tích hợp để nhận tín hiệu. Phương pháp thử nghiệm này không quá nghiêm ngặt và yêu cầu phải đến từ bên ngoài. Để đơn giản, yếu tố này có thể bị bỏ qua. Đối với cả hai phương pháp thu tín hiệu, dịch vụ Http tích hợp của chiến lược này đều giảm một liên kết và có tốc độ phản hồi nhanh hơn. Để tín hiệu ổn định, điều quan trọng hơn là tín hiệu không bị mất hoặc bị bỏ sót. 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 quá trình thử nghiệm, nhưng không loại trừ khả năng các yếu tố khác nhau như mạng có thể gây ra sự cố 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 cách tốt hơn. kế hoạch.

Bài viết này chỉ là điểm khởi đầu. Dịch vụ Http tích hợp trong mã không được xác minh và chỉ nhận dữ liệu tin nhắn. Trong bài viết tiếp theo, chúng tôi sẽ triển khai đầy đủ mẫu dịch vụ Http tích hợp có thể sử dụng để nhận tín hiệu Trading View bên ngoài. Chào mừng bạn đến thảo luận. Cảm ơn bạn đã đọc.