En la carga de los recursos... Cargando...

Exploración de la recepción de señales externas de la plataforma FMZ: API de expansión vs estrategia de servicio HTTP incorporado

El autor:Los inventores cuantifican - sueños pequeños, Creado: 2024-12-12 18:33:26, Actualizado: 2024-12-16 09:15:23

img

Prólogo

Hay varios artículos en la biblioteca de la plataforma sobre el enlace al Trading View webhook, que permite que las transacciones sean impulsadas por señales de sistemas externos a la estrategia, cuando la plataforma aún no soportaba la función de servicio HTTP incorporada en el lenguaje JavaScript.CommandRobotEn pocas palabras, es una solicitud de señal externa de http/https enviada a la plataforma FMZ, que transmite la señal como una notificación de mensajes de interacción estratégica al programa estratégico.

A medida que la plataforma evoluciona, se repite y se actualiza, muchas nuevas funciones se actualizan. También hay nuevos sistemas para recibir señales externas. Cada sistema tiene sus propias ventajas.

API ampliada con la plataforma FMZ

Las ventajas de usar este método para vincular sistemas externos son la simplicidad, la seguridad y la alta estabilidad de las interfaces API de extensión dependientes de la plataforma.

El proceso de recepción de señales externas:

Sistema externo (Trading View webhook) > Servicio de API de extensión FMZ > Plataforma de estrategia

Sistemas externos (Trading View webhook): por ejemplo, el script PINE que se ejecuta en Trading View puede establecer una alarma que, cuando se activa, envía una solicitud http a la URL del webhook configurado como una señal. 2, FMZ extender API service: una vez que el acceso a la interfaz es exitoso, la plataforma transmite la información, que se envía como un mensaje interactivo al disco real de la estrategia. 3. Disco real de políticas: en el disco real de políticas, se puede diseñar la función GetCommand para escuchar mensajes de interacción y ejecutar operaciones establecidas después de detectar el mensaje.

En comparación con el uso de servicios HTTP integrados para crear señales de recepción de servicios directamente, hay un paso más en el medio (cambio de plataforma).

Política de servicio HTTP incorporado

Después de que la plataforma admite la función de servicio HTTP incorporada en el lenguaje JavaScript, se puede crear directamente un servicio para escuchar señales externas simultáneas. Las ventajas son: el servicio HTTP creado es un hilo separado y no afecta a la lógica de la función principal, puede escuchar mensajes similares a la función GetCommand, escuchando directamente las señales externas, en comparación con el uso de programas de API de extensión, eliminando los lazos de desvío intermedios.

El proceso de recepción de señales externas:

El sistema externo (Trading View webhook)

Sistemas externos (Trading View webhook): por ejemplo, los scripts PINE que se ejecutan en Trading View pueden establecer alarmas que, cuando se activan, envían una solicitud http a la URL del webhook configurado como señal. 2, la política de disco real: la política de simultáneo para ejecutar un servicio HTTP y recibir señales externas directamente.

Este programa ahorra un paso, pero para mejorar la seguridad, es mejor configurar el servicio https, que requiere un poco de flexibilidad.

Prueba de código

Para probar dos opciones, la siguiente estrategia va a circular y enviar 10 solicitudes HTTP/Https por ronda, para simular señales externas. La estrategia es escuchar "mensajes de interacción" y "mensajes enviados por el hilo del servicio HTTP". La estrategia va a combinar señales externas con señales recibidas, detectar si hay pérdida de señal, y calcular el tiempo.

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 se prueba, se requiere que se complete una dirección IP específica del servidor, la API KEY de la extensión de la plataforma FMZ.

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

La función serverFunc crea un servicio HTTP paralelo para escuchar las señales externas. Para los mensajes externos recibidos por la interfaz de API de extensión, se escucha con la función GetCommand.

  • El mensaje fue enviado a través de un servicio HTTP: Por elvar msg = threading.mainThread().peekMessage(-1)¿Qué es lo que está pasando?

  • Los mensajes interactivos que se envían a través de la API de extensión: Por elvar cmd = GetCommand()¿Qué es lo que está pasando?

2, el proceso de transmisión y recepción de señales es libre de bloqueos, y la plataforma optimiza el mecanismo de recuperación de recursos multithread de nivel inferior, para que los usuarios puedan utilizar la red de transmisión de señales de transmisión de señales.Thread¿Qué es esto?exchange.GoFunciones de concurrencia, sin necesidad de esperar expresamente para completar tareas de concurrencia (por ejemplo, funciones de unión, funciones de espera, etc.), el sistema de base se encarga automáticamente de la recuperación de recursos (que requiere el soporte de un administrador de última versión).

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

A continuación vemos este proceso de prueba, donde la información se anota directamente en el código:

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

Resultados de las pruebas

img

img

La prueba de tiempo muestra que el HTTP es un poco más lento que el API.

La estrategia de recepción de señales de servicio HTTP incorporado, este método de prueba no es muy estricto, las solicitudes deben provenir del exterior. Para una comprensión simple, se puede ignorar este factor. Para la captura de señales de ambos modos, la estrategia de recepción de señales HTTP incorporado debe ser un poco más rápido. Para la estabilidad de la señal, lo más importante es que la señal no se pierda o se pierda.

En este artículo, el servicio HTTP incorporado en el código del texto no se verifica, y sólo recibe datos de mensajes sencillos, en el siguiente artículo completamos la implementación de un servicio HTTP incorporado disponible para recibir señales externas de Trading View.


Más.