Hay varios artículos en la plataforma CommandRobot
, en términos simples, la solicitud http/https de la señal externa se envía a la plataforma FMZ, y la plataforma reenvía la señal como un mensaje de interacción de estrategia al programa de estrategia.
A medida que la plataforma se desarrolla e itera, muchas nuevas características se han actualizado y actualizado. También hay nuevas soluciones para recibir señales externas. Cada solución tiene sus propias ventajas.
Las ventajas de usar este método para conectarse a sistemas externos son que es relativamente simple, altamente seguro y depende de la estabilidad de la interfaz API extendida de la plataforma.
Proceso de recepción de señales externas:
Sistema externo (Trading View webhook)
> Servicio de API ampliado de FMZ > Trading en vivo de la estrategia
En comparación con el uso del servicio HTTP incorporado para crear un servicio para recibir señales directamente, hay un paso adicional en el medio (transferencia de plataforma).
Después de que la plataforma admita la función de servicio HTTP incorporada del lenguaje JavaScript, puede crear un servicio concurrente para escuchar señales externas directamente. Las ventajas son: el servicio HTTP creado es un hilo separado y no afecta la lógica de la función principal. Puede escuchar mensajes como la función GetCommand y escuchar señales externas directamente. En comparación con el uso de la solución de API extendida, elimina la necesidad de tránsito.
Proceso de recepción de señales externas:
Sistema externo (webhook de la vista de operaciones)
> Operación de negociación en vivo de estrategia
Esta solución ahorra un paso, pero con el fin de mejorar la seguridad, es mejor configurar el servicio https, que requiere cierto esfuerzo.
Prueba dos soluciones. La siguiente estrategia enviará 10 solicitudes Http/Https en paralelo en cada ciclo para simular señales externas. Luego la estrategia supervisa
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)
}
}
Si está probando, debe rellenar la dirección IP específica del servidor y la clave API extendida de la plataforma FMZ.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
Mensajes enviados por el hilo del servicio HTTP:
Monitoreado porvar msg = threading.mainThread().peekMessage(-1)
.
Mensajes de interacción enviados por la interfaz API extendida:
Monitoreado porvar cmd = GetCommand()
.
Thread
o bienexchange.Go
, no hay necesidad de esperar explícitamente para que las tareas concurrentes se completen (como funciones de unión, funciones de espera, etc.). // 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
A continuación, vamos a ver el proceso de prueba, donde la información se anota directamente en el código:
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)
}
}
Después de un período de pruebas, se puede observar que el método HTTP tarda un poco menos de tiempo en promedio que el método API.
La estrategia tiene un servicio HTTP incorporado para recibir señales. Este método de prueba no es muy riguroso, y la solicitud debe provenir del exterior. Por el bien de la comprensión simple, este factor puede ignorarse. Para los dos métodos de adquisición de señales, el servicio HTTP incorporado de la estrategia reduce un enlace después de todo, y debe responder más rápido. Para la estabilidad de la señal, es más importante que la señal no se pierda o se pierda. Los resultados de la prueba muestran que la API extendida de la plataforma FMZ también es estable, y no se observó ninguna pérdida de señal en la prueba, pero no se puede descartar que factores como la red puedan causar problemas de señal.
Este artículo es sólo un punto de partida, bienvenido a discutir, gracias por leer.