Es gibt mehrere Artikel in der Plattform CommandRobot
, in einfachen Worten, wird die http/https-Anfrage des externen Signals an die FMZ-Plattform gesendet, und die Plattform leitet das Signal als Strategie-Interaktionsbotschaft an das Strategieprogramm weiter.
Da sich die Plattform weiterentwickelt und iteratiert, wurden viele neue Funktionen aktualisiert und verbessert. Es gibt auch neue Lösungen zum Empfangen von externen Signalen. Jede Lösung hat ihre eigenen Vorteile. In diesem Artikel werden wir dieses Thema gemeinsam diskutieren.
Die Vorteile der Verwendung dieser Methode zur Verbindung mit externen Systemen liegen darin, dass sie relativ einfach, sehr sicher und auf die Stabilität der erweiterten API-Schnittstelle der Plattform beruht.
Der Prozess des Empfangs externer Signale:
Externes System (Trading View webhook)
> FMZ erweiterter API-Dienst > Strategie Live Trading
Im Vergleich zur Nutzung des integrierten Http-Dienstes, um einen Dienst zum direkten Empfang von Signalen zu erstellen, gibt es einen zusätzlichen Schritt in der Mitte (Plattformübertragung).
Nachdem die Plattform die integrierte Http-Service-Funktion der JavaScript-Sprache unterstützt hat, können Sie einen gleichzeitigen Dienst erstellen, um externe Signale direkt anzuhören. Die Vorteile sind: Der erstellte Http-Service ist ein separater Thread und beeinflusst nicht die Hauptfunktionslogik. Er kann Nachrichten wie die GetCommand-Funktion hören und externe Signale direkt hören. Im Vergleich zur Verwendung der erweiterten API-Lösung eliminiert er die Notwendigkeit von Transit.
Der Prozess des Empfangs externer Signale:
Externes System (Trading View webhook)
> Strategie Live-Handel
Diese Lösung spart einen Schritt, aber um die Sicherheit zu verbessern, ist es besser, den https-Dienst zu konfigurieren, der einige Anstrengungen erfordert.
Test zwei Lösungen. Die folgende Strategie sendet 10 Http/Https-Anfragen parallel in jedem Zyklus, um externe Signale zu simulieren. Dann überwacht die Strategie
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)
}
}
Bei Tests müssen Sie die spezifische Server-IP-Adresse und den erweiterten API-Key der FMZ-Plattform eingeben.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
Nachrichten, die vom Http-Dienst-Thread verschoben wurden:
vonvar msg = threading.mainThread().peekMessage(-1)
.
Interaktionsmeldungen, die über die erweiterte API-Schnittstelle weitergeleitet werden:
vonvar cmd = GetCommand()
.
Thread
oderexchange.Go
Das zugrunde liegende System wird die Ressourcenwiederherstellung automatisch verarbeiten (erfordert die neueste Version des Dockers, um dies zu unterstützen). // 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
Als nächstes betrachten wir den Testprozess, bei dem die Informationen direkt im Code kommentiert werden:
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)
}
}
Nach einer Testphase kann festgestellt werden, dass die HTTP-Methode im Durchschnitt etwas weniger Zeit benötigt als die API-Methode.
Die Strategie verfügt über einen integrierten Http-Service zum Empfangen von Signalen. Diese Testmethode ist nicht sehr streng, und die Anfrage sollte von außen kommen. Um ein einfaches Verständnis zu gewährleisten, kann dieser Faktor ignoriert werden. Für die beiden Methoden der Signalgewinnung reduziert die Strategie
Dieser Artikel ist nur ein Ausgangspunkt, Sie sind herzlich eingeladen, darüber zu diskutieren.