রিসোর্স লোড হচ্ছে... লোডিং...

এফএমজেড প্ল্যাটফর্মের বাহ্যিক সংকেত গ্রহণের বিষয়ে আলোচনাঃ বর্ধিত এপিআই বনাম কৌশল অন্তর্নির্মিত এইচটিটিপি পরিষেবা

লেখক:এফএমজেড-লিডিয়া, তৈরিঃ ২০২৪-১২-১৩ 13:12:31, আপডেটঃ ২০২৪-১২-১৬ 11:32:35

img

প্রিফেস

ট্রেডিং ভিউ ওয়েবহুকের সাথে সংযোগ স্থাপনের বিষয়ে প্ল্যাটফর্ম ডাইজেস্টে বেশ কয়েকটি নিবন্ধ রয়েছে, যা কৌশলগুলিকে বাহ্যিক সিস্টেম থেকে সংকেত দিয়ে ট্রেডিং চালানোর অনুমতি দেয়। সেই সময়ে প্ল্যাটফর্মটি জাভাস্ক্রিপ্ট ভাষার অন্তর্নির্মিত http পরিষেবা ফাংশন সমর্থন করেনি। প্ল্যাটফর্মের বর্ধিত এপিআই ইন্টারফেস ব্যবহার করা হয়েছিলঃCommandRobotসহজ কথায় বলতে গেলে, বাহ্যিক সংকেতটির http/https অনুরোধটি FMZ প্ল্যাটফর্মে পাঠানো হয় এবং প্ল্যাটফর্মটি একটি কৌশল ইন্টারঅ্যাকশন বার্তা হিসাবে কৌশল প্রোগ্রামে সংকেতটি ফরোয়ার্ড করে।

প্ল্যাটফর্মটি বিকাশ এবং পুনরাবৃত্তি করার সাথে সাথে অনেকগুলি নতুন বৈশিষ্ট্য আপডেট এবং আপগ্রেড করা হয়েছে। বাহ্যিক সংকেত গ্রহণের জন্যও নতুন সমাধান রয়েছে। প্রতিটি সমাধানের নিজস্ব সুবিধা রয়েছে। এই নিবন্ধে, আমরা এই বিষয়টি একসাথে আলোচনা করব।

এপিআই ইন্টারফেস প্রসারিত করতে এফএমজেড প্ল্যাটফর্ম ব্যবহার করুন

বাহ্যিক সিস্টেমের সাথে সংযোগ স্থাপনের জন্য এই পদ্ধতি ব্যবহারের সুবিধা হ'ল এটি তুলনামূলকভাবে সহজ, অত্যন্ত সুরক্ষিত এবং প্ল্যাটফর্মের বর্ধিত এপিআই ইন্টারফেসের স্থিতিশীলতার উপর নির্ভর করে।

বাহ্যিক সংকেত গ্রহণের প্রক্রিয়াঃ

বাহ্যিক সিস্টেম (ট্রেডিং ভিউ ওয়েবহুক) > এফএমজেড এক্সটেন্ডেড এপিআই পরিষেবা > কৌশল লাইভ ট্রেডিং

  1. বাহ্যিক সিস্টেম (ট্রেডিং ভিউ ওয়েবহুক): উদাহরণস্বরূপ, ট্রেডিং ভিউতে চলমান পাইন স্ক্রিপ্ট একটি অ্যালার্ম সেট করতে পারে এবং যখন এটি ট্রিগার হয়, এটি একটি সংকেত হিসাবে সেট ওয়েবহুক ইউআরএল ঠিকানায় একটি HTTP অনুরোধ পাঠাবে।
  2. এফএমজেড এক্সটেন্ডেড এপিআই পরিষেবাঃ ইন্টারফেসটি সফলভাবে অ্যাক্সেস করার পরে, প্ল্যাটফর্মটি তথ্যটি ফরোয়ার্ড করে এবং এটি একটি ইন্টারেক্টিভ বার্তা হিসাবে কৌশল লাইভ ট্রেডিংয়ে প্রেরণ করে।
  3. কৌশল লাইভ ট্রেডিংঃ কৌশল লাইভ ট্রেডিংয়ে, আপনি ইন্টারেক্টিভ বার্তা শুনতে এবং বার্তা সনাক্ত করার পরে নির্দিষ্ট অপারেশনটি সম্পাদন করতে GetCommand ফাংশনটি ডিজাইন করতে পারেন।

সরাসরি সংকেত গ্রহণের জন্য একটি পরিষেবা তৈরি করতে অন্তর্নির্মিত এইচটিটিপি পরিষেবা ব্যবহারের তুলনায়, মাঝখানে একটি অতিরিক্ত পদক্ষেপ রয়েছে (প্ল্যাটফর্ম স্থানান্তর) ।

কৌশল অন্তর্নির্মিত এইচটিটিপি পরিষেবা

প্ল্যাটফর্মটি জাভাস্ক্রিপ্ট ভাষার অন্তর্নির্মিত এইচটিটিপি পরিষেবা ফাংশন সমর্থন করার পরে, আপনি সরাসরি বাহ্যিক সংকেতগুলি শুনতে একটি সমান্তরাল পরিষেবা তৈরি করতে পারেন। সুবিধাগুলি হ'লঃ তৈরি করা এইচটিটিপি পরিষেবাটি একটি পৃথক থ্রেড এবং মূল ফাংশন লজিককে প্রভাবিত করে না। এটি গেটকম্যান্ড ফাংশনের মতো বার্তাগুলি শুনতে এবং সরাসরি বাহ্যিক সংকেতগুলি শুনতে পারে। বর্ধিত এপিআই সমাধান ব্যবহারের তুলনায় এটি ট্রানজিট প্রয়োজন দূর করে।

বাহ্যিক সংকেত গ্রহণের প্রক্রিয়াঃ

বহিরাগত সিস্টেম (ট্রেডিং ভিউ ওয়েবহুক) > কৌশল লাইভ ট্রেডিং

  1. বাহ্যিক সিস্টেম (ট্রেডিং ভিউ ওয়েবহুক): উদাহরণস্বরূপ, ট্রেডিং ভিউতে চলমান পাইন স্ক্রিপ্ট একটি অ্যালার্ম সেট করতে পারে, যা ট্রিগার হওয়ার পরে একটি সংকেত হিসাবে সেট ওয়েবহুক ইউআরএল ঠিকানায় একটি HTTP অনুরোধ প্রেরণ করবে।
  2. কৌশল লাইভ ট্রেডিংঃ কৌশলটি সরাসরি বহিরাগত সংকেত গ্রহণের জন্য একই সাথে একটি এইচটিটিপি পরিষেবা চালায়।

এই সমাধানটি একটি পদক্ষেপ সংরক্ষণ করে, তবে সুরক্ষা উন্নত করার জন্য, 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)
    }
}

পরীক্ষার ক্ষেত্রে, আপনাকে নির্দিষ্ট সার্ভারের আইপি ঠিকানা এবং FMZ প্ল্যাটফর্মের বর্ধিত API কী পূরণ করতে হবে।

var httpUrl = "http://123.123.123.123:8088/CommandRobot"
var accessKey = "xxx"
var secretKey = "xxx"
  1. সার্ভার ফাংশন বহিরাগত সংকেত নিরীক্ষণের জন্য একটি সমান্তরাল এইচটিটিপি পরিষেবা তৈরি করে। বর্ধিত এপিআই ইন্টারফেসের দ্বারা প্রাপ্ত বহিরাগত বার্তাগুলির জন্য, GetCommand ফাংশনটি নিরীক্ষণের জন্য ব্যবহৃত হয়।
  • এইচটিটিপি সার্ভিস থ্রেড দ্বারা পাঠানো বার্তাঃ দ্বারা পর্যবেক্ষণ করাvar msg = threading.mainThread().peekMessage(-1).

  • এক্সটেন্ডেড এপিআই ইন্টারফেসের মাধ্যমে পাঠানো ইন্টারঅ্যাকশন বার্তাঃ দ্বারা পর্যবেক্ষণ করাvar cmd = GetCommand().

  1. সিগন্যাল প্রেরণ এবং গ্রহণ প্রক্রিয়াগুলি নন-ব্লকিং। প্ল্যাটফর্মটি অন্তর্নিহিত মাল্টি-থ্রেডেড রিসোর্স পুনরুদ্ধার প্রক্রিয়াটি অনুকূল করে তোলে।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)
    }
}

পরীক্ষার ফলাফল

img

img

পরীক্ষার পর দেখা যায় যে, এইচটিটিপি পদ্ধতিতে এপিআই পদ্ধতির তুলনায় কিছুটা কম সময় লাগে।

কৌশলটি সংকেত গ্রহণের জন্য একটি অন্তর্নির্মিত এইচটিটিপি পরিষেবা রয়েছে। এই পরীক্ষার পদ্ধতিটি খুব কঠোর নয়, এবং অনুরোধটি বাইরে থেকে আসা উচিত। সহজ বোঝার স্বার্থে, এই কারণটি উপেক্ষা করা যেতে পারে। সংকেত অধিগ্রহণের দুটি পদ্ধতির জন্য, কৌশল এর অন্তর্নির্মিত এইচটিটিপি পরিষেবাটি সর্বোপরি একটি লিঙ্ক হ্রাস করে এবং দ্রুত প্রতিক্রিয়া জানাতে হবে। সংকেত স্থিতিশীলতার জন্য, সংকেতটি হারাতে বা মিস করা যায় না তা আরও গুরুত্বপূর্ণ। পরীক্ষার ফলাফলগুলি দেখায় যে এফএমজেড প্ল্যাটফর্মের বর্ধিত এপিআইও স্থিতিশীল, এবং পরীক্ষায় কোনও সংকেত ক্ষতি পর্যবেক্ষণ করা হয়নি, তবে নেটওয়ার্কের মতো কারণগুলি সংকেত সমস্যার কারণ হতে পারে তা বাদ দেওয়া যায় না। সরাসরি বাহ্যিক সংকেত গ্রহণের জন্য অন্তর্নির্মিত এইচটিটিপি পরিষেবাটি ব্যবহার করাও আরও ভাল সমাধান।

এই নিবন্ধটি কেবল একটি সূচনা পয়েন্ট, আলোচনা করার জন্য স্বাগতম, পড়ার জন্য ধন্যবাদ।


আরো