Sumber dimuat naik... memuat...

Perbincangan mengenai penerimaan isyarat luaran Platform FMZ: API Terpanjang VS Strategi Perkhidmatan HTTP Terbina dalam

Penulis:FMZ~Lydia, Dicipta: 2024-12-13 13:12:31, Dikemas kini: 2024-12-16 11:32:35

img

Pengantar

Terdapat beberapa artikel di platform Digest mengenai menyambung ke webhook Trading View, yang membolehkan strategi untuk memandu perdagangan dengan isyarat dari sistem luaran. Pada masa itu platform tidak menyokong fungsi perkhidmatan http terbina dalam bahasa JavaScript. Antara muka API platform diperluaskan digunakan:CommandRobot, dalam istilah yang mudah, permintaan http/https isyarat luaran dihantar ke platform FMZ, dan platform meneruskan isyarat sebagai mesej interaksi strategi ke program strategi.

Sebagai platform berkembang dan berulang, banyak ciri baru telah dikemas kini dan dinaik taraf. Terdapat juga penyelesaian baru untuk menerima isyarat luaran. Setiap penyelesaian mempunyai kelebihan sendiri. Dalam artikel ini, kita akan membincangkan topik ini bersama-sama.

Gunakan Platform FMZ untuk meluaskan Antara muka API

Kelebihan menggunakan kaedah ini untuk menyambung ke sistem luaran adalah bahawa ia agak mudah, sangat selamat, dan bergantung pada kestabilan antara muka API platform yang diperluaskan.

Proses menerima isyarat luaran:

Sistem luaran (Trading View webhook) > FMZ perkhidmatan API lanjutan > Strategi perdagangan langsung

  1. Sistem luaran (Trading View webhook): Sebagai contoh, skrip PINE yang berjalan di Trading View boleh menetapkan penggera, dan apabila dicetuskan, ia akan menghantar permintaan http ke alamat url webhook yang ditetapkan sebagai isyarat.
  2. Perkhidmatan API FMZ yang diperluaskan: Selepas mengakses antara muka dengan berjaya, platform meneruskan maklumat dan menghantarnya ke strategi perdagangan langsung sebagai mesej interaktif.
  3. Strategi perdagangan langsung: Dalam strategi perdagangan langsung, anda boleh merancang fungsi GetCommand untuk mendengar mesej interaktif, dan melaksanakan operasi yang ditentukan selepas mengesan mesej.

Berbanding dengan menggunakan perkhidmatan Http terbina dalam untuk membuat perkhidmatan untuk menerima isyarat secara langsung, terdapat langkah tambahan di tengah-tengah (pindahan platform).

Strategi Perkhidmatan Http terbina dalam

Selepas platform menyokong fungsi perkhidmatan Http terbina dalam bahasa JavaScript, anda boleh membuat perkhidmatan serentak untuk mendengar isyarat luaran secara langsung. Kelebihannya ialah: perkhidmatan Http yang dicipta adalah benang yang berasingan dan tidak mempengaruhi logik fungsi utama. Ia boleh mendengar mesej seperti fungsi GetCommand dan mendengar isyarat luaran secara langsung. Berbanding dengan penggunaan penyelesaian API lanjutan, ia menghilangkan keperluan transit.

Proses menerima isyarat luaran:

Sistem luaran (Trading View webhook) > Strategi perdagangan langsung

  1. Sistem luaran (Trading View webhook): Sebagai contoh, skrip PINE yang berjalan di Trading View boleh menetapkan penggera, yang akan menghantar permintaan http ke alamat url webhook yang ditetapkan sebagai isyarat apabila dicetuskan.
  2. Strategi perdagangan langsung: Strategi menjalankan perkhidmatan HTTP secara serentak untuk menerima isyarat luaran secara langsung.

Penyelesaian ini menjimatkan langkah, tetapi untuk meningkatkan keselamatan, lebih baik untuk mengkonfigurasi perkhidmatan https, yang memerlukan beberapa usaha.

Kod ujian

Uji dua penyelesaian. Strategi berikut akan menghantar 10 permintaan Http / Https secara selari dalam setiap kitaran untuk mensimulasikan isyarat luaran. Kemudian strategi memantau mesej interaksi dan mesej yang didorong oleh benang perkhidmatan Http. Kemudian program strategi mencocokkan mesej isyarat luaran dan isyarat yang diterima satu demi satu, mengesan sama ada terdapat kehilangan isyarat, dan mengira penggunaan masa.

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("Test start...", "#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("Waiting is over...", "#FF0000")
                
        var tbls = t.deal()
        LogStatus(_D(), "\n`" + JSON.stringify(tbls) + "`")
        Sleep(20000)
    }
}

Jika ujian, anda perlu mengisi alamat IP pelayan tertentu dan KEY API lanjutan platform FMZ.

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
  1. Fungsi serverFunc mencipta perkhidmatan Http serentak untuk memantau isyarat luaran. Untuk mesej luaran yang diterima oleh antara muka API yang dilanjutkan, fungsi GetCommand digunakan untuk memantau.
  • Mesej yang didorong oleh benang perkhidmatan Http: dipantau olehvar msg = threading.mainThread().peekMessage(-1).

  • Mesej interaksi yang dihantar melalui antara muka API yang diperluaskan: dipantau olehvar cmd = GetCommand().

  1. Proses penghantaran dan penerimaan isyarat adalah tidak menyekat. Platform mengoptimumkan mekanisme pemulihan sumber berbilang benang yang mendasari.Threadatauexchange.Go, tidak perlu secara eksplisit menunggu tugas serentak selesai (seperti fungsi bergabung, fungsi menunggu, dll.). Sistem asas akan mengendalikan pemulihan sumber secara automatik (membutuhkan versi terbaru docker untuk menyokong ini).
    // Extract code snippet, send signal
    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)   // Concurrent calls, non-blocking

            var msgByHttp = {"ts": new Date().getTime(), "id": tester.idByHttp, "way": "ByHttp"}
            tester.arrSendMsgByHttp.push(msgByHttp)
            tester.idByHttp++
            threading.Thread(sendMsgByHttp, msgByHttp, httpUrl)                       // Concurrent calls, non-blocking
        }
    }

    // Extract code snippet, receiving signal
    var cmd = GetCommand()                              // Listen for messages from the extension API, non-blocking
    var msg = threading.mainThread().peekMessage(-1)    // Listen for messages from self-built Http service, using parameter -1, non-blocking

Seterusnya, mari kita lihat proses ujian, di mana maklumat dicatatkan secara langsung dalam kod:

function main() {
    __Serve("http://0.0.0.0:8088", serverFunc)      // Create a concurrent http service in the current strategy instance

    var t = createMsgTester(accessKey, secretKey, httpUrl)   // Create an object for test management
    while (true) {                                           // The strategy main loop starts
        Log("Test start...", "#FF0000")
        t.run()                                              // At the beginning of each loop, the run function of the test management object is called in two ways (1. Sending signals through the extended API, 2. Sending signals directly to the Http service created by the current strategy), each way sends 10 requests concurrently

        var beginTS = new Date().getTime()
        while (new Date().getTime() - beginTS < 60 * 1000) {   // Loop detection of interactive messages from extended APIs and messages from self-built Http services
            var cmd = GetCommand()
            if (cmd) {
                try {
                    var obj = JSON.parse(cmd)
                    obj["ts"] = new Date().getTime()        // Detect interactive messages, record messages, and update time to the time received
                    t.getEcho(obj)                          // Record to the corresponding array
                } catch (e) {
                    Log(e)
                }
            }
            
            var msg = threading.mainThread().peekMessage(-1)
            if (msg) {
                try {
                    var obj = JSON.parse(msg)
                    obj["ts"] = new Date().getTime()        // Detects the message received by the self-built Http service, and the update time is the receiving time
                    t.getEcho(obj)                          // ...
                } catch (e) {
                    Log(e)
                }
            }
        }
        Log("Waiting is over...", "#FF0000")
                
        var tbls = t.deal()                                  // Pair according to the recorded messages and check if there is any unpaired message. If there is, it means the signal is lost.
        LogStatus(_D(), "\n`" + JSON.stringify(tbls) + "`")
        Sleep(20000)
    }
}

Hasil Ujian

img

img

Selepas tempoh ujian, ia dapat diperhatikan bahawa kaedah HTTP mengambil sedikit lebih sedikit masa purata daripada kaedah API.

Strategi ini mempunyai perkhidmatan Http terbina dalam untuk menerima isyarat. Kaedah ujian ini tidak sangat ketat, dan permintaan harus datang dari luar. Demi pemahaman yang mudah, faktor ini boleh diabaikan. Untuk kedua-dua kaedah pemerolehan isyarat, strategis perkhidmatan Http terbina dalam mengurangkan satu pautan selepas semua, dan harus bertindak balas lebih cepat. Untuk kestabilan isyarat, lebih penting bahawa isyarat tidak boleh hilang atau terlepas. Hasil ujian menunjukkan bahawa API lanjutan platform FMZ juga stabil, dan tidak ada kehilangan isyarat diperhatikan dalam ujian, tetapi tidak dapat dikecualikan bahawa faktor seperti rangkaian boleh menyebabkan masalah isyarat. Menggunakan perkhidmatan Http terbina dalam untuk menerima isyarat luaran secara langsung juga merupakan penyelesaian yang lebih baik.

Artikel ini hanyalah titik permulaan, selamat datang untuk membincangkan, terima kasih kerana membaca.


Lebih lanjut