В процессе загрузки ресурсов... загрузка...

Обзор FMZ-платформы для получения внешних сигналов: расширение API против стратегии встроенного HTTP-сервиса

Автор:Изобретатели количественного измерения - мечты, Создано: 2024-12-12 18:33:26, Обновлено: 2024-12-16 09:15:23

img

Преамбула

В библиотеке платформы есть несколько статей о параллельных веб-хоках Trading View, которые позволяют вести сигнальные сделки с внешними системами, когда платформа еще не поддерживала встроенную функцию HTTP-сервиса языка JavaScript. Используется расширенный интерфейс API платформы:CommandRobotВ общем, это запрос на внешний сигнал http/https, отправляемый на платформу FMZ, которая передает сигнал в качестве уведомления о стратегическом взаимодействии с программой.

По мере развития, обновления и обновления платформы появилось множество новых функций; новые варианты приема внешних сигналов; различные варианты имеют свои преимущества.

Расширенный API с помощью платформы FMZ

При использовании такого способа параллели с внешними системами преимуществами являются: простота, высокая безопасность и высокая стабильность интерфейсов расширяемых API, зависящих от платформы.

Процесс получения внешних сигналов:

Внешняя система ((Trading View webhook) > FMZ расширенный API-сервис > стратегический диск

Внешняя система (Trading View webhook): например, сценарий PINE, работающий на Trading View, может устанавливать сигналы, которые после запуска отправляют HTTP-запрос в URL-адрес установленного веб-хука в качестве сигнала. 2, FMZ расширенный сервис API: после успешного доступа к интерфейсу платформа передает сообщения, которые отправляются на стратегический диск в качестве интерактивных сообщений. 3, Политика диска: в диске политики можно спроектировать функцию GetCommand для прослушивания взаимодействующих сообщений и выполнения установленных операций после обнаружения сообщения.

В сравнении с использованием встроенного HTTP-сервиса для прямого создания сервисного приема сигналов, это еще один шаг в середине ("переход между платформами").

Политики встроенного HTTP-сервиса

После того, как платформа поддерживает встроенную функцию HTTP-сервиса языка JavaScript, можно напрямую создать параллельный сервис для прослушивания внешних сигналов. Преимущества: созданный HTTP-сервис является отдельным потоком, не влияет на логику основных функций, может прослушивать сообщения, как и функция GetCommand, и напрямую прослушивать внешние сигналы, в отличие от использования расширенных API-программ.

Процесс получения внешних сигналов:

Внешняя система (Trading View webhook)

Внешняя система (Trading View webhook): например, сценарий PINE, работающий на Trading View, может устанавливать сигналы, которые после запуска отправляют http-запрос в URL-адрес установленного веб-хока в качестве сигнала. 2, Политика реального диска: Политика одновременно запускает HTTP-сервис, который получает внешние сигналы напрямую.

Это позволяет избежать шага, но для повышения безопасности лучше всего настроить https-сервис, который требует свертывания; это немного сложнее, чем использовать расширенный API.

Испытание кода

Проверяется два варианта, при которых следующая политика будет циркулировать и отправлять по 10 HTTP/HTTPS запросов в один раунд, чтобы имитировать внешние сигналы. Затем стратегия будет прослушивать "интерактивные сообщения" и "сообщения, отправляемые потоком HTTP-сервиса". Затем политика будет соответствовать внешним сигналам и сигналам, полученным, обнаруживать отсутствие потери сигнала и вычислять время.

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

В случае тестирования необходимо заполнить конкретный IP-адрес сервера, расширение API KEY для платформы FMZ.

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

1, функция serverFunc создает параллельный HTTP-сервис для прослушивания внешних сигналов. Для внешних сообщений, полученных интерфейсом расширения API, используется функция GetCommand.

  • Посылки, отправленные на HTTP-сервисе: Отvar msg = threading.mainThread().peekMessage(-1)Он не хочет, чтобы его убили.

  • Интерактивные сообщения, пересылаемые через расширенный API: Отvar cmd = GetCommand()Он не хочет, чтобы его убили.

2., Процесс передачи и приема сигналов является бесблокированным, платформа оптимизировала механизм восстановления ресурсов многоуровневых каналов для обеспечения безопасности передачи и получения сигналов.ThreadИлиexchange.GoОдновременные функции, не требующие очевидного ожидания выполнения одновременных задач (например, функции join, wait и т. д.), и автоматически обрабатываемые в нижней части системы для восстановления ресурсов (поддерживаемые только хостерами, требующими новейшей версии).

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

Теперь давайте посмотрим на этот процесс тестирования, который показывает, что информация прямо комментируется в коде:

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

Результаты испытаний

img

img

Проведенные тесты показывают, что Http использует в среднем немного меньше времени, чем API.

Встроенная HTTP-сервисная стратегия принимает сигналы, такой метод тестирования не очень строгий, запрос должен исходить извне. Для простого понимания, на первый взгляд, этот фактор можно игнорировать. Для двух способов получения сигнала, встроенная HTTP-сервисная стратегия сокращает один узел, и скорость ответа должна быть немного быстрее. Что касается стабильности сигнала, то более важно, что сигнал не может быть потерян или пропущен.

В этой статье мы не проверяем встроенные HTTP-сервисы в текстовом коде, а просто получаем сообщения, а в следующей статье мы полностью реализуем доступный встроенный HTTP-сервис для получения внешних сигналов Trading View.


Больше