Il y a plusieurs articles dans la plateforme CommandRobot
, en termes simples, la demande http/https du signal externe est envoyée à la plateforme FMZ, et la plateforme transmet le signal en tant que message d'interaction stratégique au programme stratégique.
Au fur et à mesure que la plateforme se développe et s'itère, de nombreuses nouvelles fonctionnalités ont été mises à jour et améliorées. Il existe également de nouvelles solutions pour recevoir des signaux externes. Chaque solution a ses propres avantages. Dans cet article, nous discuterons de ce sujet ensemble.
Les avantages de l'utilisation de cette méthode pour se connecter à des systèmes externes sont qu'elle est relativement simple, hautement sécurisée et repose sur la stabilité de l'interface API étendue de la plateforme.
Le processus de réception des signaux externes:
Système externe (Trading View webhook)
> FMZ service étendu d'API > Stratégie de négociation en direct
Par rapport à l'utilisation du service Http intégré pour créer un service pour recevoir des signaux directement, il y a une étape supplémentaire au milieu (transfert de plate-forme).
Une fois que la plateforme prend en charge la fonction de service Http intégrée du langage JavaScript, vous pouvez créer un service concurrent pour écouter directement les signaux externes. Les avantages sont les suivants: le service Http créé est un fil distinct et n'affecte pas la logique de la fonction principale. Il peut écouter des messages comme la fonction GetCommand et écouter directement les signaux externes. Par rapport à l'utilisation de la solution API étendue, il élimine le besoin de transit.
Le processus de réception des signaux externes:
Système externe (Trading View webhook)
> Stratégie négociation en direct
Cette solution permet d'économiser une étape, mais pour améliorer la sécurité, il est préférable de configurer le service https, ce qui nécessite un certain effort.
Testez deux solutions. La stratégie suivante enverra 10 requêtes Http/Https en parallèle dans chaque cycle pour simuler des signaux externes. Ensuite, la stratégie surveille
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 vous testez, vous devez remplir l'adresse IP spécifique du serveur et la clé API étendue de la plateforme FMZ.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
Messages envoyés par le fil de service Http:
surveillés parvar msg = threading.mainThread().peekMessage(-1)
.
Messages d'interaction transmis par l'interface API étendue:
surveillés parvar cmd = GetCommand()
.
Thread
ouexchange.Go
, il n'est pas nécessaire d'attendre explicitement l'achèvement des tâches simultanées (telles que les fonctions de jointure, les fonctions d'attente, etc.). Le système sous-jacent gérera automatiquement la récupération des ressources (exige la dernière version du docker pour le prendre en charge). // 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
Ensuite, regardons le processus de test, où l'information est annotée directement dans le code:
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)
}
}
Après une période de test, on peut observer que la méthode Http prend en moyenne un peu moins de temps que la méthode API.
La stratégie a un service HTTP intégré pour recevoir des signaux. Cette méthode de test n'est pas très rigoureuse, et la demande doit venir de l'extérieur. Pour des raisons de compréhension simple, ce facteur peut être ignoré. Pour les deux méthodes d'acquisition de signal, le service HTTP intégré de la stratégie réduit une liaison après tout et devrait répondre plus rapidement. Pour la stabilité du signal, il est plus important que le signal ne puisse pas être perdu ou manqué. Les résultats des tests montrent que l'API étendue de la plate-forme FMZ est également stable et aucune perte de signal n'a été observée dans le test, mais il ne peut pas être exclu que des facteurs tels que le réseau puissent causer des problèmes de signal.
Cet article n'est qu'un point de départ, bienvenue à discuter, merci de lire.