Die Ressourcen sind geladen. Beförderung...

Diskussion über den externen Signalempfang der FMZ-Plattform: Erweiterte API VS Strategie eingebauter HTTP-Service

Schriftsteller:FMZ~Lydia, Erstellt: 2024-12-13 13:12:31, Aktualisiert: 2024-12-16 11:32:35

img

Vorwort

Es gibt mehrere Artikel in der Plattform Digest über die Verbindung zu Trading View Webhooks, die Strategien ermöglichen, den Handel mit Signalen von externen Systemen zu steuern.CommandRobot, in einfachen Worten, wird die http/https-Anfrage des externen Signals an die FMZ-Plattform gesendet, und die Plattform leitet das Signal als Strategie-Interaktionsbotschaft an das Strategieprogramm weiter.

Da sich die Plattform weiterentwickelt und iteratiert, wurden viele neue Funktionen aktualisiert und verbessert. Es gibt auch neue Lösungen zum Empfangen von externen Signalen. Jede Lösung hat ihre eigenen Vorteile. In diesem Artikel werden wir dieses Thema gemeinsam diskutieren.

FMZ-Plattform zur Erweiterung der API-Schnittstelle

Die Vorteile der Verwendung dieser Methode zur Verbindung mit externen Systemen liegen darin, dass sie relativ einfach, sehr sicher und auf die Stabilität der erweiterten API-Schnittstelle der Plattform beruht.

Der Prozess des Empfangs externer Signale:

Externes System (Trading View webhook) > FMZ erweiterter API-Dienst > Strategie Live Trading

  1. Externes System (Trading View webhook): Zum Beispiel kann das PINE-Skript, das in Trading View ausgeführt wird, einen Alarm setzen, und wenn es ausgelöst wird, sendet es eine http-Anfrage an die festgelegte Webhook-URL-Adresse als Signal.
  2. FMZ erweiterter API-Dienst: Nach erfolgreichem Zugriff auf die Schnittstelle leitet die Plattform die Informationen weiter und sendet sie als interaktive Nachricht an die Strategie Live Trading.
  3. Strategie Live Trading: Im Strategie Live Trading können Sie die GetCommand-Funktion so gestalten, dass sie der interaktiven Nachricht zuhört und die angegebene Operation nach Erkennung der Nachricht ausführt.

Im Vergleich zur Nutzung des integrierten Http-Dienstes, um einen Dienst zum direkten Empfang von Signalen zu erstellen, gibt es einen zusätzlichen Schritt in der Mitte (Plattformübertragung).

Strategie eingebauter HTTP-Dienst

Nachdem die Plattform die integrierte Http-Service-Funktion der JavaScript-Sprache unterstützt hat, können Sie einen gleichzeitigen Dienst erstellen, um externe Signale direkt anzuhören. Die Vorteile sind: Der erstellte Http-Service ist ein separater Thread und beeinflusst nicht die Hauptfunktionslogik. Er kann Nachrichten wie die GetCommand-Funktion hören und externe Signale direkt hören. Im Vergleich zur Verwendung der erweiterten API-Lösung eliminiert er die Notwendigkeit von Transit.

Der Prozess des Empfangs externer Signale:

Externes System (Trading View webhook) > Strategie Live-Handel

  1. Externes System (Trading View webhook): Zum Beispiel kann das PINE-Skript, das in Trading View ausgeführt wird, einen Alarm setzen, der bei Auslösung eine HTTP-Anfrage an die festgelegte Webhook-URL-Adresse als Signal sendet.
  2. Strategie Live Trading: Die Strategie läuft gleichzeitig einen HTTP-Dienst, um externe Signale direkt zu empfangen.

Diese Lösung spart einen Schritt, aber um die Sicherheit zu verbessern, ist es besser, den https-Dienst zu konfigurieren, der einige Anstrengungen erfordert.

Prüfcode

Test zwei Lösungen. Die folgende Strategie sendet 10 Http/Https-Anfragen parallel in jedem Zyklus, um externe Signale zu simulieren. Dann überwacht die Strategie Interaktionsnachrichten und Nachrichten, die von Http-Service-Threads verschoben werden. Dann passt das Strategieprogramm externe Signalnachrichten und empfangene Signale nacheinander an, erkennt, ob es einen Signalverlust gibt, und berechnet den Zeitverbrauch.

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)
    }
}

Bei Tests müssen Sie die spezifische Server-IP-Adresse und den erweiterten API-Key der FMZ-Plattform eingeben.

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
  1. Die ServerFunc-Funktion erstellt einen gleichzeitigen HTTP-Dienst zur Überwachung externer Signale. Für externe Nachrichten, die von der erweiterten API-Schnittstelle empfangen werden, wird die GetCommand-Funktion zur Überwachung verwendet.
  • Nachrichten, die vom Http-Dienst-Thread verschoben wurden: vonvar msg = threading.mainThread().peekMessage(-1).

  • Interaktionsmeldungen, die über die erweiterte API-Schnittstelle weitergeleitet werden: vonvar cmd = GetCommand().

  1. Die Signal- und Empfangsprozesse sind nicht blockierend. Die Plattform optimiert den zugrunde liegenden Multi-Threaded-Ressource-Recovery-Mechanismus. Für gleichzeitige Funktionen wieThreadoderexchange.GoDas zugrunde liegende System wird die Ressourcenwiederherstellung automatisch verarbeiten (erfordert die neueste Version des Dockers, um dies zu unterstützen).
    // 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

Als nächstes betrachten wir den Testprozess, bei dem die Informationen direkt im Code kommentiert werden:

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)
    }
}

Testergebnisse

img

img

Nach einer Testphase kann festgestellt werden, dass die HTTP-Methode im Durchschnitt etwas weniger Zeit benötigt als die API-Methode.

Die Strategie verfügt über einen integrierten Http-Service zum Empfangen von Signalen. Diese Testmethode ist nicht sehr streng, und die Anfrage sollte von außen kommen. Um ein einfaches Verständnis zu gewährleisten, kann dieser Faktor ignoriert werden. Für die beiden Methoden der Signalgewinnung reduziert die Strategies integrierter Http-Service schließlich einen Link und sollte schneller reagieren. Für die Signalstabilität ist es wichtiger, dass das Signal nicht verloren geht oder verpasst wird. Die Testergebnisse zeigen, dass die erweiterte API der FMZ-Plattform ebenfalls stabil ist und im Test kein Signalverlust beobachtet wurde, aber es kann nicht ausgeschlossen werden, dass Faktoren wie das Netzwerk Signalprobleme verursachen können. Die Verwendung des integrierten Http-Service zum direkten Empfangen externer Signale ist auch eine bessere Lösung.

Dieser Artikel ist nur ein Ausgangspunkt, Sie sind herzlich eingeladen, darüber zu diskutieren.


Mehr