Sumber dimuat naik... memuat...

Ciri baru FMZ Quant: Gunakan fungsi _Serve untuk membuat perkhidmatan HTTP dengan mudah

Penulis:FMZ~Lydia, Dicipta: 2024-11-13 11:59:30, Dikemas kini: 2024-11-15 09:57:05

img

Dalam perdagangan kuantitatif dan pembangunan strategi automatik, perkhidmatan http kadang-kadang digunakan._Serve()Baru-baru ini, yang menyediakan pengguna dengan kemampuan penciptaan perkhidmatan HTTP, HTTPS dan TCP yang fleksibel. Dengan fungsi ini, pemaju boleh mempermudah proses konfigurasi perkhidmatan dan melaksanakan perkhidmatan yang lebih disesuaikan dalam persekitaran kuantitatif, menjadikan reka bentuk strategi lebih lancar dan lebih mudah._Serve()untuk membantu anda dengan cepat memulakan dengan fungsi baru FMZ Quant.

Dokumentasi API platform untuk_Serve()telah dikemas kini:

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

Permintaan

Platform telah menaik taraf fungsi_Serve()(kerana bahasa JavaScript tidak mempunyai fungsi untuk membuat perkhidmatan sebelum ini, fungsi ini hanya menyokong strategi dalam bahasa JavaScript). Sederhananya, ia membolehkan strategi untuk mempunyai keupayaan untuk membuat perkhidmatan rangkaian. Berdasarkan fungsi ini, kita boleh membangunkan banyak fungsi dan menyelesaikan banyak masalah. Sebagai contoh, strategi boleh mempunyai antara muka luaran, penyampaian data, dan bekerjasama dengan fungsi protokol tersuai platform untuk menyusun pertukaran yang tidak disokong oleh platform FMZ.

Dalam artikel ini, kita akan menggunakan permintaan bekerjasama dengan fungsi protokol tersuai platform untuk menyusun pertukaran yang tidak disokong oleh platform FMZ sebagai contoh.Panduan Protokol Sesuai, kami menggunakan bahasa Python untuk merangkumi API pertukaran OKX dalam mod spot (kerana FMZ sendiri menyokong OKX, OKX digunakan di sini hanya sebagai contoh, dan ia boleh digunakan untuk pertukaran lain yang tidak disambungkan ke platform FMZ). Program protokol tersuai Python dalam artikel ini perlu dijalankan secara berasingan._Serve(), ia adalah lebih mudah untuk strategi bahasa JavaScript untuk mengakses protokol tersuai.

Kami merangkumi protokol tersuai antara muka pertukaran untuk dikemas dalam perpustakaan templat dan mengintegrasikannya ke dalam strategi secara langsung, supaya strategi dapat mengakses pertukaran yang tidak disokong di FMZ. Saya tidak akan masuk ke dalam butiran mengenai cara mengkonfigurasi objek pertukaran Protokol tersuai di sini, anda boleh merujuk artikel:

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

  • Konfigurasi pertukaran protokol tersuai di platform adalah seperti berikut:

img

Apabila merancang templat, kita boleh menggunakan/OKXuntuk mengenal pasti pertukaran mana objek pertukaran protokol tersuai yang dikonfigurasikan.

Pelaksanaan Templat Protokol Sesuai

Pertama, buat strategi baru di Platform Dagangan Kuantum FMZ, tetapkan jenis strategi ke perpustakaan templat, dan bahasa strategi ke JavaScript.

Reka bentuk Parameter Templat

Tambah 3 parameter kepada templat strategi yang dicipta:

img

Kemudian kita boleh mula merancang dan menulis kod untuk templat protokol tersuai.

Pelaksanaan Kod

Kod ini ditulis dalam gaya TS.$.startService()fungsi adalah fungsi antara muka templat yang digunakan untuk memulakan perkhidmatan protokol tersuai.

// @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")
    }
}

Oleh kerana ruang yang terhad, tidak semua antara muka dilaksanakan di sini.pertanyaan pasaran , pertanyaan aset, danPanggilan IOPengguna yang berminat boleh melaksanakan semua antara muka. Selepas reka bentuk selesai, simpan kod templat dan simpan nama templat sebagai: TypeScript versi contoh protokol tersuai

Strategi Ujian

Selepas mengkonfigurasi pertukaran OKXs apikey, kunci rahsia, kata laluan, dan lain-lain, kita boleh menulis strategi ujian untuk menguji.

Strategi memeriksa perpustakaan templat kami yang direka:

img

Kod strategi ujian:

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())
}

Ujian Berlari

img

Seperti yang kita lihat, strategi hanya perlu memeriksa templat untuk mencapai akses lancar ke pertukaran OKX (walaupun pertukaran OKX sudah menyokongnya, sebagai contoh, OKX digantikan di sini dengan pertukaran yang belum disambungkan FMZ).


Lebih lanjut