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

এফএমজেড কোয়ান্টের নতুন বৈশিষ্ট্যঃ সহজেই এইচটিটিপি সার্ভিস তৈরি করতে _সার্ভ ফাংশন ব্যবহার করুন

লেখক:এফএমজেড-লিডিয়া, তৈরিঃ ২০২৪-১১-১৩ ১১ঃ৫৯ঃ৩০, আপডেটঃ ২০২৪-১১-১৫ ৯ঃ৫৭ঃ০৫

img

পরিমাণগত ট্রেডিং এবং স্বয়ংক্রিয় কৌশল উন্নয়ন, http সেবা কখনও কখনও ব্যবহার করা হয়।_Serve()সম্প্রতি, যা ব্যবহারকারীদের নমনীয় এইচটিটিপি, এইচটিটিপিএস এবং টিসিপি পরিষেবা তৈরির ক্ষমতা সরবরাহ করে। এই ফাংশনের সাহায্যে বিকাশকারীরা পরিষেবা কনফিগারেশন প্রক্রিয়াটিকে সহজতর করতে এবং পরিমাণগত পরিবেশে আরও কাস্টমাইজড পরিষেবাগুলি বাস্তবায়ন করতে পারে, কৌশল নকশাটিকে আরও মসৃণ এবং সুবিধাজনক করে তোলে। এই নিবন্ধটি ফাংশনের ব্যবহারের দৃশ্যকল্প এবং মৌলিক ক্রিয়াকলাপগুলি পরিচয় করিয়ে দেবে।_Serve()আপনাকে দ্রুত এই নতুন ফাংশন FMZ Quant দিয়ে শুরু করতে সাহায্য করার জন্য।

প্ল্যাটফর্ম API ডকুমেন্টেশন_Serve()আপডেট করা হয়েছেঃ

https://www.fmz.com/syntax-guide/fun/global/__serve

চাহিদা

প্ল্যাটফর্মটি ফাংশন আপগ্রেড করেছে_Serve()(যেহেতু জাভাস্ক্রিপ্ট ভাষার আগে পরিষেবা তৈরির ফাংশন ছিল না, এই ফাংশনটি কেবল জাভাস্ক্রিপ্ট ভাষায় কৌশলগুলিকে সমর্থন করে) । সহজভাবে বলতে গেলে, এটি কৌশলগুলিকে নেটওয়ার্ক পরিষেবাগুলি তৈরি করার ক্ষমতা দেয়। এই ফাংশনের ভিত্তিতে, আমরা অনেকগুলি ফাংশন বিকাশ করতে এবং অনেকগুলি সমস্যা সমাধান করতে পারি। উদাহরণস্বরূপ, কৌশলগুলির বাহ্যিক ইন্টারফেস থাকতে পারে, ডেটা ফরোয়ার্ডিং, এবং প্ল্যাটফর্মের কাস্টম প্রোটোকল ফাংশনের সাথে সহযোগিতা করতে পারে যাতে এফএমজেড প্ল্যাটফর্ম দ্বারা সমর্থিত নয় এমন এক্সচেঞ্জগুলি নির্বিঘ্নে ক্যাপসুল করতে পারে।

এই প্রবন্ধে, আমরা একটি উদাহরণ হিসাবে FMZ প্ল্যাটফর্ম দ্বারা সমর্থিত নয় যে এক্সচেঞ্জ seamlessly encapsulate করার জন্য প্ল্যাটফর্মের কাস্টম প্রোটোকল ফাংশন সঙ্গে cooperating চাহিদা ব্যবহার করা হবে। পূর্ববর্তী প্রবন্ধেকাস্টম প্রোটোকল গাইড, আমরা পাইথন ভাষা ব্যবহার করেছি স্পট মোডে OKX এক্সচেঞ্জের API ক্যাপসুল করতে (কারণ FMZ নিজেই OKX সমর্থন করে, OKX এখানে কেবল উদাহরণ হিসাবে ব্যবহৃত হয়, এবং এটি অন্যান্য এক্সচেঞ্জের জন্য প্রযোজ্য যা FMZ প্ল্যাটফর্মের সাথে সংযুক্ত নয়) । এই নিবন্ধে পাইথনের কাস্টম প্রোটোকল প্রোগ্রামটি আলাদাভাবে চালানো দরকার। যখন জাভাস্ক্রিপ্ট ভাষা ফাংশন সমর্থন করে_Serve(), জাভাস্ক্রিপ্ট ভাষা কৌশল কাস্টম প্রোটোকল অ্যাক্সেস করার জন্য সহজ।

আমরা এক্সচেঞ্জ ইন্টারফেসের কাস্টম প্রোটোকলটি একটি টেমপ্লেট লাইব্রেরি হিসাবে ক্যাপসুল করা হবে এবং এটিকে সরাসরি কৌশলটিতে সংহত করব, যাতে কৌশলটি এফএমজেডে সমর্থিত নয় এমন এক্সচেঞ্জগুলিতে নির্বিঘ্নে অ্যাক্সেস করতে পারে। আমি এখানে কাস্টম প্রোটোকল এক্সচেঞ্জ অবজেক্টটি কীভাবে কনফিগার করব সে সম্পর্কে বিস্তারিত জানাব না, আপনি নিবন্ধটি উল্লেখ করতে পারেনঃ

https://www.fmz.com/bbs-topic/10527

  • প্ল্যাটফর্মে কাস্টম প্রোটোকল এক্সচেঞ্জ কনফিগারেশন নিম্নরূপঃ

img

একটি টেমপ্লেট ডিজাইন করার সময়, আমরা ব্যবহার করতে পারেন/OKXকনফিগার করা কাস্টম প্রোটোকল এক্সচেঞ্জ অবজেক্টটি কোন এক্সচেঞ্জের অন্তর্গত তা সনাক্ত করতে।

কাস্টম প্রোটোকল টেমপ্লেট বাস্তবায়ন

প্রথমে, FMZ Quant Trading Platform এ একটি নতুন কৌশল তৈরি করুন, কৌশল টাইপটি টেমপ্লেট লাইব্রেরিতে এবং কৌশল ভাষাটি জাভাস্ক্রিপ্টে সেট করুন।

টেমপ্লেট প্যারামিটার ডিজাইন

তৈরি করা কৌশল টেমপ্লেটে ৩টি পরামিতি যোগ করুনঃ

img

তারপর আমরা কাস্টম প্রোটোকল টেমপ্লেটের জন্য ডিজাইন এবং কোড লিখতে শুরু করতে পারি।

কোড বাস্তবায়ন

কোডটি TS স্টাইলে লেখা আছে।$.startService()ফাংশন একটি টেমপ্লেট ইন্টারফেস ফাংশন যা কাস্টম প্রোটোকল পরিষেবা শুরু করতে ব্যবহৃত হয়।

// @ts-check

$.startService = function (address, port, proxyConfig) {
    __Serve(`http://${address}:${port}`, function (ctx, proxyConfig) {
        // interface
        interface IData {
            data: object
            raw: object
        }
        
        interface IError {
            error: any
        }
        
        // custom protocol for OKX
        class CustomProtocolOKX {
            apiBase: string = "https://www.okx.com"
            accessKey: string
            secretKey: string
            passphrase: string
            proxyConfig: string = ""
            simulate: boolean = false
            
            constructor(accessKey: string, secretKey: string, passphrase: string, simulate?: boolean, proxyConfig?: string) {
                this.accessKey = accessKey
                this.secretKey = secretKey
                this.passphrase = passphrase
                if (typeof(simulate) == "boolean") {
                    this.simulate = simulate
                }
                this.proxyConfig = proxyConfig
            }
            
            httpReq(method: string, path: string, query: string = "", params: {[key: string]: any} = {}, headers: {key: string, value: string | ArrayBuffer}[] = []): {[key: string]: any} {
                let ret = null
                let options = {
                    method: method,                    
                    headers: {
                        'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6', 
                        'Content-Type': 'application/json; charset=UTF-8',
                        'x-simulated-trading': this.simulate ? "1" : "0"
                    },
                }
                
                // headers
                if (Array.isArray(headers) && headers.length > 0) {
                    for (var pair of headers) {
                        options.headers[pair.key] = pair.value
                    }
                }
                
                let url = ""
                if (method == "GET") {
                    if (typeof(query) == "string" && query.length > 0) {
                        url = `${this.apiBase}${path}?${query}`
                    } else {
                        url = `${this.apiBase}${path}`
                    }
                } else {
                    url = `${this.apiBase}${path}`
                    options.body = JSON.stringify(params)
                }

                // request
                try {
                    if (this.proxyConfig != "") {
                        url = `${this.proxyConfig}${url}`
                    }
                    ret = JSON.parse(HttpQuery(url, options))
                } catch(e) {
                    return null
                }
    
                return ret
            }

            callSignedAPI(method: string, path: string, query: string = "", params: {[key: string]: any} = {}): {[key: string]: any} {
                const strTime = new Date().toISOString().slice(0, -5) + 'Z'
                let jsonStr = ""
                if (method == "GET") {
                    jsonStr = Object.keys(params).length > 0 ? JSON.stringify(params) : ""
                } else {
                    jsonStr = Object.keys(params).length > 0 ? JSON.stringify(params) : "{}"
                }                
                let message = `${strTime}${method}${path}${jsonStr}`
                if (method === "GET" && query !== "") {
                    message = `${strTime}${method}${path}?${query}${jsonStr}`
                }

                const signature = Encode("sha256", "string", "base64", message, "string", this.secretKey)
                let headers = []
                headers.push({key: "OK-ACCESS-KEY", value: this.accessKey})
                headers.push({key: "OK-ACCESS-PASSPHRASE", value: this.passphrase})
                headers.push({key: "OK-ACCESS-TIMESTAMP", value: strTime})
                headers.push({key: "OK-ACCESS-SIGN", value: signature})                

                return this.httpReq(method, path, query, params, headers)
            }
            
            urlEncode(params: {[key: string]: string | number}): string {
                let encodeParams: string[] = []
                for (const [key, value] of Object.entries(params)) {
                    encodeParams.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
                }

                return encodeParams.join("&")
            }

            symbol2Inst(symbol: string): string {
                let arr = symbol.split("_")
                if (arr.length >= 2) {
                    return `${arr[0]}-${arr[1]}`.toUpperCase()
                } else {
                    return `${arr[0]}-USDT`.toUpperCase()
                }
            }

            getSymbol(inst: string): string {
                let arr = inst.split("-")
                if (arr.length >= 2) {
                    return `${arr[0]}_${arr[1]}`.toUpperCase()
                } else {
                    return `${arr[0]}-USDT`.toUpperCase()
                }
            }
            
            // The following code encapsulates OKX's interface
            GetTicker(symbol: string): IData | IError {
                // GET /api/v5/market/ticker , param: instId 

                let inst = this.symbol2Inst(symbol)
                let ret = this.httpReq("GET", "/api/v5/market/ticker", `instId=${inst}`)

                let retData = {}
                for (var ele of ret["data"]) {
                    retData["symbol"] = this.getSymbol(ele["instId"])
                    retData["buy"] = ele["bidPx"]
                    retData["sell"] = ele["askPx"]
                    retData["high"] = ele["high24h"]
                    retData["low"] = ele["low24h"]
                    retData["open"] = ele["open24h"]
                    retData["last"] = ele["last"]
                    retData["vol"] = ele["vol24h"]
                    retData["time"] = ele["ts"]
                }

                return {data: retData, raw: ret}
            }
            
            GetAccount(): IData | IError {
                // GET /api/v5/account/balance
                
                let ret = this.callSignedAPI("GET", "/api/v5/account/balance")

                let retData = []
                for (var ele of ret["data"]) {
                    for (var detail of ele["details"]) {
                        let asset = {"currency": detail["ccy"], "free": detail["availEq"], "frozen": detail["ordFrozen"]}
                        if (detail["availEq"] == "") {
                            asset["free"] = detail["availBal"]
                        }
                        retData.push(asset)
                    }
                }
                
                return {data: retData, raw: ret}
            }

            IO(method: string, path: string, params: {[key: string]: any}): {[key: string]: any} {
                let ret = null 
                if (method == "GET") {
                    ret = this.callSignedAPI(method, path, this.urlEncode(params))
                } else {
                    ret = this.callSignedAPI(method, path, "", params)
                }

                return {data: ret}
            }
        }
        
        // protocol factory
        class ProtocolFactory {
            static createExWrapper(accessKey: string, secretKey: string, exName: string): any {
                let protocol = null
                if (exName == "/OKX") {
                    try {
                        let passphrase = ""
                        let simulate = false
                        let arrSecretKey = secretKey.split(",")

                        if (arrSecretKey.length == 2) {
                            secretKey = arrSecretKey[0]
                            passphrase = arrSecretKey[1]
                        } else if (arrSecretKey.length == 3) {
                            secretKey = arrSecretKey[0]
                            passphrase = arrSecretKey[1]
                            simulate = arrSecretKey[2] == "simulate" ? true : false 
                        } else {
                            return null
                        }
                        protocol = new CustomProtocolOKX(accessKey, secretKey, passphrase, simulate, proxyConfig)
                    } catch(e) {
                        Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
                        return null
                    }
                }
                return protocol
            }
        }
        
        // http service
        let resp = {}
        let reqMethod = ctx.method()
        let reqPath = ctx.path()
        let httpMethod = ctx.header("Http-Method")
        let reqBody = null

        try {
            reqBody = JSON.parse(ctx.body())
        } catch(e) {
            resp = {error: {name: e.name, stack: e.stack, message: e.message, errDesc: "JSON parse error."}}
        }

        // onPost
        if (reqMethod == "POST") {
            if (!["access_key", "secret_key", "method", "params"].every(key=> key in reqBody)) {
                resp = {error: {reqBody: reqBody, errDesc: "reqBody error."}}
            }

            if ("error" in resp) {
                ctx.write(JSON.stringify(resp))
                return 
            }

            let accessKey = reqBody["access_key"]
            let secretKey = reqBody["secret_key"]
            let method = reqBody["method"]
            let params = reqBody["params"]

            let protocol = ProtocolFactory.createExWrapper(accessKey, secretKey, reqPath)
            if (!protocol) {
                ctx.write(JSON.stringify({error: {errDesc: "createExWrapper error."}}))
                return 
            }

            // process GetTicker / GetAccount ...
            if (method == "ticker") {
                if (!["symbol"].every(key=> key in params)) {
                    resp = {error: {params: params, errDesc: "params error."}}
                } else {
                    let symbol = params["symbol"]
                    resp = protocol.GetTicker(symbol)
                }
            } else if (method == "accounts") {
                resp = protocol.GetAccount()
            } else if (method.slice(0, 6) == "__api_") {
                resp = protocol.IO(httpMethod, method.slice(6), params)
            } else {
                ctx.write(JSON.stringify({error: {method: method, errDesc: "method not support."}}))
                return 
            }

            ctx.write(JSON.stringify(resp))
        }
    }, proxyConfig)
}

function init() {
    $.startService(address, port, proxyConfig)
    Log("Start the custom protocol service, address:", address, ",port:", port, "#FF0000")
    if (proxyConfig != "") {
        Log("Setting up the proxy:", proxyConfig, "#FF0000")
    }
}

সীমিত জায়গার কারণে, এখানে সমস্ত ইন্টারফেস বাস্তবায়ন করা হয় না।বাজার অনুসন্ধান , সম্পদ অনুসন্ধান, এবংআইও কলব্যবহারকারীরা যারা আগ্রহী তারা সমস্ত ইন্টারফেস বাস্তবায়ন করতে পারেন। নকশা সম্পন্ন হওয়ার পরে, টেমপ্লেট কোড সংরক্ষণ করুন এবং টেমপ্লেট নামটি সংরক্ষণ করুনঃ TypeScript সংস্করণ কাস্টম প্রোটোকল উদাহরণ

পরীক্ষার কৌশল

OKX এক্সচেঞ্জের APikey, secretkey, পাসফ্রেজ ইত্যাদি কনফিগার করার পর, আমরা পরীক্ষা করার জন্য একটি পরীক্ষা কৌশল লিখতে পারি।

কৌশল আমাদের ডিজাইন করা টেমপ্লেট লাইব্রেরি পরীক্ষা করেঃ

img

পরীক্ষার কৌশল কোডঃ

function main() {
    // Test GetTicker
    Log(`exchange.GetTicker():`, exchange.GetTicker())

    // Test GetAccount
    Log(`exchange.GetAccount():`, exchange.GetAccount())

    // Test exchange.IO
    Log(`exchange.IO("api", "POST", "/api/v5/trade/cancel-all-after", "timeOut=0"):`, exchange.IO("api", "POST", "/api/v5/trade/cancel-all-after", "timeOut=0"))

    // Output the exchange name added by the custom protocol
    Log(`exchange.GetName():`, exchange.GetName())

    // Output exchange tags added by the custom protocol
    Log(`exchange.GetLabel():`, exchange.GetLabel())
}

চলমান পরীক্ষা

img

যেমনটি আমরা দেখতে পাচ্ছি, কৌশলটি কেবলমাত্র একটি টেমপ্লেট পরীক্ষা করতে হবে যাতে OKX এক্সচেঞ্জের সাথে বিরামবিহীন অ্যাক্সেস অর্জন করা যায় (যদিও OKX এক্সচেঞ্জ ইতিমধ্যে এটি সমর্থন করে, উদাহরণস্বরূপ, OKX এখানে একটি এক্সচেঞ্জের সাথে প্রতিস্থাপিত হয় যা FMZ এখনও সংযুক্ত হয়নি) ।


আরো