Die Ressourcen sind geladen. Beförderung...

FMZ-Plattform-External Signal Reception: Erweiterung der API vs. Strategien für den eingebauten HTTP-Dienst

Schriftsteller:Die Erfinder quantifizieren - Kleine Träume, Erstellt: 2024-12-12 18:33:26, Aktualisiert: 2024-12-16 09:15:23

img

Vorwort

Es gibt mehrere Artikel in der Plattform-Bibliothek über die Verbindung von Trading View-Webhooks, die Signal-getriebene Transaktionen von Systemen außerhalb der Strategie ermöglichen, bevor die Plattform die eingebaute HTTP-Dienstfunktion der JavaScript-Sprache unterstützt.CommandRobotEinfach ausgedrückt, ist es eine http/https-Anfrage für ein externes Signal, das an die FMZ-Plattform gesendet wird, die das Signal als Strategie-Interaktionsbotschaft an den Strategie-Prozessor weiterleitet.

Die Plattform entwickelt sich, wird aktualisiert und viele neue Funktionen werden aktualisiert. Es gibt auch neue Möglichkeiten, um externe Signale zu empfangen.

Erweiterte API mit der FMZ-Plattform

Die Vorteile dieser Art der Verknüpfung von externen Systemen sind: einfache, sichere und stabilere API-Interfaces.

Der Prozess der Empfangung externer Signale:

Externe Systeme (Trading View webhook) > FMZ Erweiterung API Dienst > Strategie Festplatte

1. Externe Systeme (Trading View webhook): Zum Beispiel kann ein PINE-Skript, das auf Trading View ausgeführt wird, einen Alarm einstellen, der nach dem Auslösen eine HTTP-Anfrage an die eingestellte Webhook-URL sendet, als Signal. 2. FMZ Extension API Service: Nach erfolgreichem Zugriff auf die Schnittstelle leitet die Plattform eine Nachricht weiter, die als Interaktionsbotschaft an die Strategie-Rechner gesendet wird. 3. Strategie-Realdisk: In der Strategie-Realdisk kann die GetCommand-Funktion entworfen werden, um Interaktionsnachrichten abzuhören und nach dem Nachrichtenerkennen vorgegebene Aktionen auszuführen.

Es ist ein weiterer Schritt in der Mitte ("Plattformwechsel") als mit dem eingebauten Http-Dienst, um das Empfangssignal des Dienstes direkt zu erstellen.

Strategie für den eingebauten HTTP-Dienst

Die Plattform unterstützt die eingebaute Http-Dienstfunktion der JavaScript-Sprache und ermöglicht die direkte Erstellung eines parallel erstellten Dienstes zur Überwachung externer Signale. Die Vorteile sind: Die erstellte Http-Dienstleistung ist ein separater Thread und beeinflusst nicht die Logik der Hauptfunktion.

Der Prozess der Empfangung externer Signale:

Externe Systeme (Trading View webhook)

1. Externe Systeme (Trading View webhook): Zum Beispiel kann ein PINE-Skript, das auf Trading View ausgeführt wird, einen Alarm einstellen, der nach dem Auslösen eine HTTP-Anfrage an die eingestellte Webhook-URL sendet, als Signal. 2. Strategische Festplatte: Strategie, die gleichzeitig einen HTTP-Dienst ausführt, der direkt externe Signale empfängt.

Dies erspart einen Schritt, aber um die Sicherheit zu erhöhen, ist es am besten, den https-Dienst zu konfigurieren, der ein wenig gekrümmt werden muss. Im Vergleich zu einem Programm, das eine erweiterte API verwendet, ist es ein bisschen problematisch.

Testcode

Beide Szenarien wurden getestet. Die folgende Strategie schaltet 10 HTTP/HTTPS-Anfragen pro Runde ein, um externe Signale zu simulieren. Die Strategie überwacht dann "Interaktionsnachrichten" und "Nachrichten, die vom HTTP-Dienst-Thread weitergeleitet werden".

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

Wenn Sie testen möchten, müssen Sie die spezifische IP-Adresse des Servers, die API KEY für die FMZ-Plattform, ausfüllen.

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"

1. Die ServerFunc-Funktion erstellt einen parallel laufenden HTTP-Dienst, um externe Signale abzuhören. Für externe Nachrichten, die von der erweiterten API-Schnittstelle empfangen werden, wird mit der GetCommand-Funktion abgehört.

  • Die Nachricht, die von einem HTTP-Thread übertragen wurde: Vonvar msg = threading.mainThread().peekMessage(-1)Das ist ein großes Problem.

  • Interaktive Nachrichten, die über die erweiterte API weitergeleitet werden: Vonvar cmd = GetCommand()Das ist ein großes Problem.

2. Die Send- und Empfangssignalprozesse sind nicht blockiert.ThreadOderexchange.GoParallelfunktionen, die nicht mehr explizit warten müssen, bis die Parallelfunktionen abgeschlossen sind (z. B. Join- oder Wait-Funktionen usw.), und die Systemunterseite verarbeitet die Ressourcenwiederbeschaffung automatisch (für die Unterstützung der neuesten Versionen benötigt wird).

    // 摘录代码片段,发送信号
    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,非阻塞

Im Folgenden sehen wir den Testprozess, in dem die Informationen direkt in den Code kommentiert werden:

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

Ergebnisse der Tests

img

img

Über einen Zeitraum von Tests kann man beobachten, dass der HTTP-Modus im Durchschnitt etwas weniger Zeit braucht als der API-Modus.

Die Strategie ist ein integrierter HTTP-Dienst, der Signale empfängt. Diese Testmethode ist nicht sehr streng, die Anfragen sollten von außen kommen. Für eine einfache Auffassung kann man diesen Faktor ignorieren. Für die beiden Arten der Signalgewinnung sollte der integrierte HTTP-Dienst schließlich eine Schleife reduzieren und die Reaktionsgeschwindigkeit etwas schneller sein.

In diesem Artikel haben wir die integrierte HTTP-Dienstleistung für den Code nicht überprüft und nur einfache Nachrichtendaten empfangen. In unserem nächsten Beitrag haben wir gemeinsam eine vollständige Implementierung einer verfügbaren integrierten HTTP-Dienstleistung für die Empfangung von externen Trading View-Signalen realisiert.


Mehr