Les ressources ont été chargées... Je charge...

Débat sur la réception de signaux externes sur la plateforme FMZ: API étendue contre stratégie de service HTTP intégré

Auteur:L'inventeur de la quantification - un petit rêve, Créé à partir de: 2024-12-12 18:33:26, mis à jour à partir de: 2024-12-16 09:15:23

img

Préambule

Il existe plusieurs articles dans la bibliothèque de la plate-forme sur le lien entre le Trading View webhook, qui permettait aux systèmes extérieurs à la stratégie d'effectuer des transactions par signal, alors que la plate-forme ne supportait pas encore la fonctionnalité de service HTTP intégrée au langage JavaScript.CommandRobotEn d'autres termes, une requête HTTP/https pour un signal externe est envoyée à la plate-forme FMZ, qui transmet le signal en tant que message d'interaction stratégique pour informer le programme stratégique.

Au fur et à mesure que la plateforme évolue, les mises à niveau et les mises à jour apportent de nombreuses nouvelles fonctionnalités.

Interface d'API étendue avec la plateforme FMZ

L'utilisation de cette méthode pour coupler des systèmes externes présente des avantages en termes de simplicité, de sécurité et de stabilité de l'interface API étendue dépendant de la plate-forme.

Le processus de réception des signaux extérieurs:

Système externe (Trading View webhook) Pan> FMZ étendre le service API Pan> stratégie disque virtuel

Système externe (Trading View webhook): par exemple, un script PINE exécuté sur Trading View peut être configuré pour déclencher une alerte qui envoie une requête http à l'adresse url de votre webhook. 2, FMZ extension API service: une fois que l'accès à l'interface est réussi, la plate-forme renvoie des informations, qui sont envoyées au disque dur stratégique en tant que messages interactifs. 3, disque de stratégie: dans le disque de stratégie, la fonction GetCommand peut être conçue pour écouter les messages d'interaction, détecter les messages et exécuter les opérations établies.

Il y a une étape supplémentaire dans l'intermédiaire (transfert de plate-forme) par rapport à l'utilisation du service HTTP intégré pour créer directement le signal de réception du service.

Politique de service HTTP intégré

La plate-forme prend en charge la fonctionnalité de service HTTP intégrée au langage JavaScript et permet de créer directement un service parallèle pour écouter les signaux externes. L'avantage est que le service HTTP créé est un thread distinct et n'affecte pas la logique de la fonction principale.

Le processus de réception des signaux extérieurs:

Système externe (Trading View webhook) Panneau de configuration

Système externe (Trading View webhook): par exemple, un script PINE exécuté sur Trading View peut être configuré pour déclencher une alerte qui envoie une requête http à l'adresse url du webhook configuré. La stratégie de la plate-forme: la stratégie de la plate-forme fonctionne en parallèle avec un service HTTP qui reçoit directement des signaux externes.

Cette solution économise une étape, mais pour améliorer la sécurité, il est préférable de configurer le service https et de le plier.

Le code de test

Pour tester les deux options, la stratégie suivante est de faire un cycle et d'envoyer 10 requêtes HTTP/HTTPS par tour pour simuler les signaux externes. La stratégie écoute ensuite les "messages interactifs" et les "messages envoyés par le fil de service HTTP". La stratégie correspond ensuite aux messages externes et aux signaux reçus, détecte la perte de signal et prend du temps pour le calcul.

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

Si le test est effectué, vous devez remplir une adresse IP spécifique du serveur, l'API KEY d'extension de la plateforme FMZ.

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

La fonction serverFunc crée un service HTTP parallèle pour écouter les signaux externes. Pour les messages externes reçus par l'API d'extension, elle écoute avec la fonction GetCommand.

  • Les messages sont envoyés par le fil HTTP: Parvar msg = threading.mainThread().peekMessage(-1)Je ne peux pas vous aider.

  • Les messages interactifs sont transférés à partir de l'API étendue: Parvar cmd = GetCommand()Je ne peux pas vous aider.

La plate-forme a optimisé le mécanisme de récupération de ressources multi-threaded au niveau inférieur, ce qui permet d'améliorer les performances de l'émetteur et du récepteur.ThreadOu alorsexchange.GoLes fonctions de synchronisation ne doivent plus attendre explicitement que les tâches de synchronisation soient terminées (par exemple, les fonctions de jointure, de attente, etc.) et le sous-système traite automatiquement la récupération des ressources (qui nécessite une assistance de l'hôte la plus récente).

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

Nous allons voir ce processus de test, qui montre que les informations sont directement annotées dans le code:

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

Résultats des tests

img

img

Les tests ont montré que la méthode HTTP prendrait un peu moins de temps que la méthode API en moyenne.

Pour les deux modes d'acquisition de signaux, la stratégie de service HTTP intégré est de réduire un nœud, ce qui devrait être une vitesse de réponse un peu plus rapide. Pour la stabilité du signal, il est plus important que le signal ne soit pas perdu ou manqué. Les résultats du test montrent que l'API d'extension de la plate-forme FMZ est tout aussi stable, aucune perte de signal n'est vue dans les tests, mais sans exclure les facteurs qui causent des problèmes de signal dans tous les aspects du réseau, l'utilisation d'un service HTTP intégré pour recevoir directement des signaux externes est également une solution plus appropriée.

Dans cet article, le service HTTP intégré dans le code n'est pas vérifié et ne reçoit que des données de messages. Dans l'article suivant, nous avons complété la mise en œuvre d'un modèle d'un service HTTP intégré disponible pour recevoir des signaux externes Trading View.


Plus de