플랫폼 CommandRobot
간단히 말해서, 외부 신호의 http/https 요청은 FMZ 플랫폼에 전송되고, 플랫폼은 전략 상호 작용 메시지로서 신호를 전략 프로그램에 전달합니다.
플랫폼이 개발되고 반복됨에 따라 많은 새로운 기능이 업데이트되고 업그레이드되었습니다. 또한 외부 신호를 수신하기위한 새로운 솔루션이 있습니다. 각 솔루션에는 자신의 장점이 있습니다. 이 기사에서는이 주제에 대해 함께 논의 할 것입니다.
이러한 방법을 사용하여 외부 시스템과 연결하는 장점은 상대적으로 간단하고 매우 안전하며 플랫폼의 확장 된 API 인터페이스의 안정성에 의존한다는 것입니다.
외부 신호를 수신하는 과정:
외부 시스템 (Trading View webhook)
> FMZ 확장 API 서비스 > 전략 라이브 거래
내부 HTTP 서비스를 사용하여 신호를 직접 수신하는 서비스를 만드는 것과 비교하면 중간 (플랫폼 전송) 에 추가 단계가 있습니다.
플랫폼이 자바스크립트 언어의 내장된 Http 서비스 기능을 지원 한 후, 외부 신호를 직접 듣기 위해 동시 서비스를 만들 수 있습니다. 이점은: 생성 된 Http 서비스는 별도의 스레드이며 주요 기능 논리에 영향을 미치지 않습니다. GetCommand 기능과 같은 메시지를 듣고 외부 신호를 직접 들을 수 있습니다. 확장 된 API 솔루션 사용에 비해 트랜시트의 필요성을 제거합니다.
외부 신호를 수신하는 과정:
외부 시스템 (Trading View webhook)
> 전략 실시간 거래
이 솔루션은 단계를 절약하지만 보안을 향상시키기 위해 https 서비스를 구성하는 것이 좋습니다. 이것은 약간의 노력을 필요로합니다. 확장 API 솔루션을 사용하는 것보다 조금 더 번거롭습니다.
두 가지 솔루션을 테스트하십시오. 다음 전략은 외부 신호를 시뮬레이션하기 위해 각 사이클에 10 개의 Http/Https 요청을 병렬로 보낼 것입니다. 그 다음 전략은 Http 서비스 스레드에서
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 서비스를 가지고 있다. 이 테스트 방법은 매우 엄격하지 않으며, 요청은 외부에서 오아야 한다. 간단한 이해를 위해, 이 요인은 무시될 수 있다. 신호 획득의 두 가지 방법의 경우, 전략
이 기사 는 시작점 일 뿐 입니다. 토론 할 수 있도록 환영 합니다. 읽어주셔서 감사합니다.