Sumber daya yang dimuat... Pemuatan...

Fitur Baru FMZ Quant: Gunakan Fungsi _Serve untuk Membuat Layanan HTTP dengan Mudah

Penulis:FMZ~Lydia, Dibuat: 2024-11-13 11:59:30, Diperbarui: 2024-11-15 09:57:05

img

Dalam perdagangan kuantitatif dan pengembangan strategi otomatis, layanan http kadang-kadang digunakan._Serve()dengan fungsi ini, pengembang dapat menyederhanakan proses konfigurasi layanan dan menerapkan layanan yang lebih disesuaikan dalam lingkungan kuantitatif, membuat desain strategi lebih mulus dan lebih nyaman._Serve()untuk membantu Anda dengan cepat memulai dengan fungsi baru FMZ Quant.

Dokumentasi API platform untuk_Serve()telah diperbarui:

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

Permintaan

Platform telah meningkatkan fungsi_Serve()(karena bahasa JavaScript tidak memiliki fungsi untuk membuat layanan sebelumnya, fungsi ini hanya mendukung strategi dalam bahasa JavaScript). Sederhananya, ini memungkinkan strategi untuk memiliki kemampuan untuk membuat layanan jaringan. Berdasarkan fungsi ini, kita dapat mengembangkan banyak fungsi dan memecahkan banyak masalah.

Dalam artikel ini, kita akan menggunakan permintaan bekerja sama dengan fungsi protokol kustom platform untuk mengkapsulkan pertukaran yang tidak didukung oleh platform FMZ sebagai contoh.Panduan Protokol Khusus, kami menggunakan bahasa Python untuk mengkapsulkan API pertukaran OKX dalam mode spot (karena FMZ sendiri mendukung OKX, OKX digunakan di sini hanya sebagai contoh, dan berlaku untuk pertukaran lain yang tidak terhubung ke platform FMZ)._Serve(), lebih mudah bagi strategi bahasa JavaScript untuk mengakses protokol kustom.

Kami mengkapsulkan protokol kustom dari antarmuka pertukaran untuk dikapsulkan sebagai template library dan mengintegrasikannya ke dalam strategi secara langsung, sehingga strategi dapat mengakses pertukaran yang tidak didukung di FMZ.

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

  • Konfigurasi pertukaran protokol khusus pada platform adalah sebagai berikut:

img

Saat merancang template, kita bisa menggunakan/OKXuntuk mengidentifikasi pertukaran mana objek pertukaran protokol kustom yang dikonfigurasi miliknya.

Implementasi Templat Protokol Khusus

Pertama, buat strategi baru di Platform Perdagangan Kuantum FMZ, atur tipe strategi ke perpustakaan template, dan bahasa strategi ke JavaScript.

Desain Parameter Template

Tambahkan 3 parameter ke template strategi yang dibuat:

img

Kemudian kita bisa mulai merancang dan menulis kode untuk template protokol kustom.

Implementasi Kode

Kode ditulis dalam gaya TS.$.startService()fungsi adalah fungsi antarmuka templat yang digunakan untuk memulai layanan protokol kustom.

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

Karena ruang terbatas, tidak semua antarmuka diimplementasikan di sini.pertanyaan pasar , Pertanyaan aset, danPanggilan IOPengguna yang tertarik dapat menerapkan semua antarmuka. Setelah desain selesai, simpan kode template dan simpan nama template sebagai: TypeScript versi contoh protokol kustom.

Strategi pengujian

Setelah mengkonfigurasi OKX exchanges apikey, secretkey, passphrase, dll, kita dapat menulis strategi uji untuk menguji.

Strategi memeriksa perpustakaan template yang dirancang:

img

Kode strategi uji:

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

Tes Lari

img

Seperti yang dapat kita lihat, strategi hanya perlu memeriksa template untuk mencapai akses mulus ke pertukaran OKX (meskipun pertukaran OKX sudah mendukungnya, misalnya, OKX diganti di sini dengan pertukaran yang belum terhubung FMZ).


Lebih banyak