Sumber daya yang dimuat... Pemuatan...

Diskusi tentang Penerimaan Sinyal Eksternal dari Platform FMZ: API Terluas VS Strategi Layanan HTTP Terintegrasi

Penulis:FMZ~Lydia, Dibuat: 2024-12-13 13:12:31, Diperbarui: 2024-12-16 11:32:35

img

Pengantar

Ada beberapa artikel di platform Digest tentang menghubungkan ke webhook Trading View, yang memungkinkan strategi untuk mendorong perdagangan dengan sinyal dari sistem eksternal.CommandRobot, dengan kata sederhana, permintaan http/https dari sinyal eksternal dikirim ke platform FMZ, dan platform meneruskan sinyal sebagai pesan interaksi strategi ke program strategi.

Saat platform berkembang dan diulang, banyak fitur baru telah diperbarui dan ditingkatkan. Ada juga solusi baru untuk menerima sinyal eksternal. Setiap solusi memiliki keuntungannya sendiri.

Gunakan FMZ Platform untuk memperluas API Interface

Keuntungan menggunakan metode ini untuk terhubung ke sistem eksternal adalah bahwa hal itu relatif sederhana, sangat aman, dan bergantung pada stabilitas platforms diperluas antarmuka API.

Proses penerimaan sinyal eksternal:

Sistem eksternal (Trading View webhook) > FMZ layanan API diperluas > Strategi perdagangan langsung

  1. Sistem eksternal (Trading View webhook): Misalnya, skrip PINE yang berjalan di Trading View dapat mengatur alarm, dan ketika dipicu, akan mengirim permintaan http ke alamat url webhook yang ditetapkan sebagai sinyal.
  2. Layanan API FMZ diperluas: Setelah mengakses antarmuka dengan sukses, platform meneruskan informasi dan mengirimkannya ke strategi perdagangan langsung sebagai pesan interaktif.
  3. Strategi perdagangan langsung: Dalam strategi perdagangan langsung, Anda dapat merancang fungsi GetCommand untuk mendengarkan pesan interaktif, dan mengeksekusi operasi yang ditentukan setelah mendeteksi pesan.

Dibandingkan dengan menggunakan layanan Http built-in untuk membuat layanan untuk menerima sinyal secara langsung, ada langkah tambahan di tengah (transfer platform).

Strategi Layanan Http Terbentuk

Setelah platform mendukung fungsi layanan Http built-in dari bahasa JavaScript, Anda dapat membuat layanan serentak untuk mendengarkan sinyal eksternal secara langsung. Keuntungannya adalah: layanan Http yang dibuat adalah thread terpisah dan tidak mempengaruhi logika fungsi utama. Ini dapat mendengarkan pesan seperti fungsi GetCommand dan mendengarkan sinyal eksternal secara langsung. Dibandingkan dengan penggunaan solusi API yang diperluas, ini menghilangkan kebutuhan untuk transit.

Proses penerimaan sinyal eksternal:

Sistem eksternal (Trading View webhook) > Strategi perdagangan langsung

  1. Sistem eksternal (Trading View webhook): Misalnya, skrip PINE yang berjalan di Trading View dapat mengatur alarm, yang akan mengirim permintaan http ke alamat url webhook yang ditetapkan sebagai sinyal ketika dipicu.
  2. Strategi perdagangan langsung: Strategi menjalankan layanan HTTP secara bersamaan untuk menerima sinyal eksternal secara langsung.

Solusi ini menghemat langkah, tetapi untuk meningkatkan keamanan, lebih baik untuk mengkonfigurasi layanan https, yang membutuhkan beberapa usaha.

Kode pengujian

Uji dua solusi. Strategi berikut akan mengirim 10 permintaan Http/Https secara paralel dalam setiap siklus untuk mensimulasikan sinyal eksternal. Kemudian strategi memantau pesan interaksi dan pesan yang didorong oleh thread layanan Http. Kemudian program strategi mencocokkan pesan sinyal eksternal dan sinyal yang diterima satu per satu, mendeteksi apakah ada hilangnya sinyal, dan menghitung konsumsi waktu.

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 pengujian, Anda perlu mengisi alamat IP server tertentu dan KEY API diperpanjang dari platform FMZ.

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
  1. Fungsi serverFunc membuat layanan HTTP bersamaan untuk memantau sinyal eksternal. Untuk pesan eksternal yang diterima oleh antarmuka API yang diperluas, fungsi GetCommand digunakan untuk memantau.
  • Pesan yang didorong oleh thread layanan HTTP: dipantau olehvar msg = threading.mainThread().peekMessage(-1).

  • Pesan interaksi yang diteruskan oleh antarmuka API yang diperluas: dipantau olehvar cmd = GetCommand().

  1. Proses pengiriman dan penerimaan sinyal tidak memblokir. Platform mengoptimalkan mekanisme pemulihan sumber daya multi-threaded yang mendasari.Threadatauexchange.Go, tidak perlu secara eksplisit menunggu tugas serentak untuk diselesaikan (seperti fungsi bergabung, fungsi menunggu, dll).
    // 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

Selanjutnya, mari kita lihat proses pengujian, di mana informasi dicatat langsung dalam kode:

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 Tes

img

img

Setelah periode pengujian, dapat diamati bahwa metode Http membutuhkan sedikit lebih sedikit waktu rata-rata daripada metode API.

Strategi ini memiliki layanan HTTP built-in untuk menerima sinyal. Metode pengujian ini tidak sangat ketat, dan permintaan harus berasal dari luar. Demi pemahaman yang sederhana, faktor ini dapat diabaikan. Untuk kedua metode akuisisi sinyal, strategis built-in layanan HTTP mengurangi satu tautan setelah semua, dan harus merespons lebih cepat. Untuk stabilitas sinyal, lebih penting bahwa sinyal tidak bisa hilang atau terlewatkan. Hasil pengujian menunjukkan bahwa API diperpanjang dari platform FMZ juga stabil, dan tidak ada kehilangan sinyal yang diamati dalam pengujian, tetapi tidak dapat dikecualikan bahwa faktor-faktor seperti jaringan dapat menyebabkan masalah sinyal. Menggunakan layanan HTTP built-in untuk menerima sinyal eksternal secara langsung juga merupakan solusi yang lebih baik.

Artikel ini hanya titik awal, selamat datang untuk membahas, terima kasih telah membaca.


Lebih banyak