Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Dibuat di: 2024-11-12 22:10:49, diperbarui pada: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Dalam perdagangan kuantitatif dan pengembangan strategi otomatis, layanan http terkadang digunakan. Platform Kuantitatif Inventor baru-baru ini menambahkan_Serve() Fungsi yang menyediakan pengguna dengan kemampuan pembuatan layanan HTTP, HTTPS, dan TCP yang fleksibel. Dengan fitur ini, pengembang dapat menyederhanakan proses konfigurasi layanan dan menerapkan layanan yang lebih disesuaikan dalam lingkungan kuantitatif, membuat desain strategi lebih lancar dan nyaman. Artikel ini akan memperkenalkan_Serve() Skenario penggunaan dan operasi dasar fungsi membantu Anda memulai dengan cepat fungsi baru Inventor Quant ini.

tentang_Serve()Diperbarui dalam dokumentasi API Platform:

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


membutuhkan

Platform telah ditingkatkan_Serve()fungsi (karena bahasa JavaScript sebelumnya tidak memiliki fungsi untuk membuat layanan, fungsi ini hanya mendukung kebijakan bahasa JavaScript). Secara sederhana, fungsi ini memungkinkan kebijakan memiliki kemampuan untuk membuat layanan jaringan. Berdasarkan fungsi ini, kita dapat mengembangkan banyak fungsi dan menyelesaikan banyak kebutuhan. Misalnya, strategi tersebut dapat memiliki antarmuka eksternal, penerusan data, dan bekerja sama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak didukung oleh platform FMZ secara mulus.

Dalam artikel ini, kami akan mengambil persyaratan “bekerja sama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak didukung oleh platform FMZ secara mulus” sebagai contoh. Pada artikel sebelumnya「Panduan Protokol Umum」Dalam artikel ini, kami menggunakan bahasa Python untuk merangkum API bursa OKX dalam mode spot (karena FMZ sendiri mendukung OKX, OKX digunakan di sini hanya sebagai contoh, dan ini berlaku untuk bursa lain yang tidak terhubung dengan platform FMZ). Program protokol umum Python dalam artikel ini perlu dijalankan secara terpisah. Ketika bahasa JavaScript mendukung_Serve()Setelah fungsi tersebut, lebih mudah untuk mengakses protokol umum strategi bahasa JavaScript.

Kami merangkum protokol umum antarmuka pertukaran yang akan dienkapsulasi ke dalam “perpustakaan templat” dan langsung mengintegrasikannya ke dalam strategi, sehingga strategi tersebut dapat mengakses pertukaran yang tidak didukung pada FMZ dengan lancar. Saya tidak akan membahas secara detail tentang cara mengonfigurasi objek pertukaran “Protokol Umum” di sini, Anda dapat merujuk ke artikel:

https://www.fmz.com/digest-topic/10518

  • Konfigurasi pertukaran protokol umum pada platform adalah sebagai berikut:

Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Saat mendesain template, Anda dapat/OKXMengidentifikasi bursa tempat objek pertukaran protokol generik yang dikonfigurasi berada.


Implementasi templat protokol generik

Pertama, buat strategi baru di Inventor Quantitative Trading Platform, atur jenis strategi ke pustaka templat, dan bahasa strategi ke JavaScript.

Desain parameter template

Tambahkan tiga parameter ke templat kebijakan yang dibuat:

Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Kemudian Anda dapat mulai merancang dan menulis kode untuk templat protokol umum.

Implementasi Kode

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

// @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("启动通用协议服务,address:", address, ",port:", port, "#FF0000")
    if (proxyConfig != "") {
        Log("设置代理:", proxyConfig, "#FF0000")
    }
}

Karena keterbatasan ruang, tidak semua antarmuka diimplementasikan di sini, hanyaPertanyaan PasarKueri Aset、*Panggilan IO*Siswa yang berminat dapat mengimplementasikan semua antarmuka; setelah desain selesai, simpan kode templat dan simpan nama templat sebagai: “Contoh protokol umum versi TypeScript”.


Strategi Pengujian

Setelah mengonfigurasi apikey, secretkey, passphrase, dll. di bursa OKX, kita dapat menulis strategi pengujian untuk diuji.

Strategi Periksa pustaka templat yang kami desain:

Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Kode strategi pengujian:

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

    // 测试GetAccount
    Log(`exchange.GetAccount():`, exchange.GetAccount())

    // 测试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"))

    // 输出通用协议添加的交易所名称
    Log(`exchange.GetName():`, exchange.GetName())

    // 输出通用协议添加的交易所标签
    Log(`exchange.GetLabel():`, exchange.GetLabel())
}

Menjalankan Tes

Penemu mengukur fitur baru: menggunakan_Fungsi Serve dengan mudah membuat layanan HTTP

Seperti yang bisa Anda lihat, strategi tersebut hanya perlu memeriksa templat untuk mencapai akses lancar ke bursa OKX (walaupun bursa OKX sudah mendukungnya, misalnya, OKX diganti di sini dengan bursa yang belum terhubung dengan FMZ).