プラットフォームCommandRobot
単純に言うと,外部信号の http/https リクエストは FMZ プラットフォームに送られ,プラットフォームは戦略プログラムへの戦略インタラクションメッセージとして信号を転送します.
プラットフォームが開発され,繰り返されるにつれて,多くの新しい機能が更新され,アップグレードされています.外部信号を受信するための新しいソリューションもあります.各ソリューションには独自の利点があります.この記事では,このトピックを一緒に議論します.
外部システムに接続するこの方法の使用の利点は,比較的シンプルで,高度に安全であり,プラットフォームの拡張APIインターフェースの安定性に依存することです.
外部信号を受信するプロセス:
外部システム (Trading View webhook)
> FMZ拡張APIサービス > 戦略ライブ取引
信号を直接受信するためのサービスを作成するために内蔵Httpサービスを使用すると比較して,中間 (プラットフォーム転送) に追加ステップがあります.
プラットフォームがJavaScript言語の内蔵Httpサービス機能をサポートした後,外部信号を直接聞くために並行サービスを作成できます.利点としては:作成されたHttpサービスは別々のスレッドであり,メイン機能論理に影響しません.GetCommand機能のようなメッセージを聴き,外部信号を直接聞くことができます.拡張APIソリューションの使用と比較して,トランジットを必要としません.
外部信号を受信するプロセス:
外部システム (Trading View webhook)
> ストラテジーライブ取引
このソリューションはステップを節約しますが,セキュリティを向上させるために,いくつかの努力を要する https サービスを設定するのが良いです.拡張 API ソリューションを使用するよりも少し面倒です.
2つのソリューションをテストする.次の戦略は,外部信号をシミュレートするために,各サイクルで並列に10つのHttp/Httpsリクエストを送信する.その後,戦略は,Httpサービススレッドによって推された"インタラクションメッセージ"と"メッセージ"を監視する.その後,戦略プログラムは,外部信号メッセージと受信された信号を1つずつマッチし,信号損失があるかどうかを検出し,時間の消費を計算する.
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)
}
}
テストの場合は,特定のサーバーの IP アドレスと FMZ プラットフォームの拡張 API キーを入力する必要があります.
var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
Http サービススレッドで押されたメッセージ:
監視されるvar msg = threading.mainThread().peekMessage(-1)
.
拡張されたAPIインターフェースで転送されるインタラクションメッセージ:
監視されるvar cmd = GetCommand()
.
Thread
またはexchange.Go
, 同期タスクの完了を明示的に待つ必要がない (関数結合,等待関数など). 基盤システムは自動的にリソース復元を処理します (これをサポートするためにドッカー最新バージョンが必要です). // 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
次に,テストプロセスを見てみましょう. 情報はコードに直接注記されています.
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)
}
}
テスト期間を経て,HttpメソッドはAPIメソッドよりも平均的に少し時間がかかることが観察できます.
この戦略には信号を受信するための内蔵Httpサービスがあります.このテスト方法は非常に厳格ではなく,要求は外部から来るべきです.簡単な理解のために,この要因を無視することができます. 信号取得の2つの方法では,戦略
この記事 は 始まり の 点 だけ で あり,議論 に 歓迎 さ れ ます.読め た こと に ありがとう.