Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Dicipta dalam: 2024-11-12 22:10:49, dikemas kini pada: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Dalam perdagangan kuantitatif dan pembangunan strategi automatik, perkhidmatan http kadangkala digunakan. Platform Kuantitatif Inventor telah ditambah baru-baru ini_Serve() Berfungsi, menyediakan pengguna dengan keupayaan penciptaan perkhidmatan HTTP, HTTPS dan TCP yang fleksibel. Dengan ciri ini, pembangun boleh memudahkan proses konfigurasi perkhidmatan dan melaksanakan perkhidmatan yang lebih tersuai dalam persekitaran kuantitatif, menjadikan reka bentuk strategi lebih lancar dan lebih mudah. Artikel ini akan memperkenalkan_Serve() Senario penggunaan dan operasi asas fungsi membantu anda bermula dengan cepat dengan fungsi baharu Inventor Quant ini.

kira-kira_Serve()Dikemas kini dalam dokumentasi API Platform:

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


perlukan

Platform telah dinaik taraf_Serve()fungsi (memandangkan bahasa JavaScript tidak mempunyai fungsi mencipta perkhidmatan sebelum ini, fungsi ini hanya menyokong dasar bahasa JavaScript Secara ringkas, ia membolehkan polisi mempunyai keupayaan untuk mencipta perkhidmatan rangkaian). Berdasarkan fungsi ini, kita boleh membangunkan banyak fungsi dan menyelesaikan banyak keperluan. Sebagai contoh, strategi boleh mempunyai antara muka luaran, pemajuan data dan bekerjasama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak disokong oleh platform FMZ dengan lancar.

Dalam artikel ini, kami akan mengambil keperluan untuk “bekerjasama dengan fungsi protokol umum platform untuk merangkum pertukaran dengan lancar yang tidak disokong oleh platform FMZ” sebagai contoh. Dalam artikel sebelum ini「Panduan Protokol Am」Dalam artikel ini, kami menggunakan bahasa Python untuk merangkum 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 platform FMZ tidak disambungkan). Program protokol umum Python dalam artikel ini perlu dijalankan secara berasingan Apabila bahasa JavaScript menyokong_Serve()Selepas fungsi itu, lebih mudah untuk mengakses protokol umum strategi bahasa JavaScript.

Kami merangkum protokol umum antara muka pertukaran untuk dirangkumkan ke dalam “pustaka templat” dan menyepadukannya secara langsung ke dalam strategi, supaya strategi boleh mengakses pertukaran yang tidak disokong pada FMZ dengan lancar. Saya tidak akan menerangkan secara terperinci tentang cara mengkonfigurasi objek pertukaran “Protokol Umum” di sini, anda boleh merujuk kepada artikel:

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

  • Konfigurasi pertukaran protokol umum pada platform adalah seperti berikut:

Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Semasa mereka bentuk templat, anda boleh/OKXMengenal pasti pertukaran mana yang dimiliki oleh objek pertukaran protokol generik yang dikonfigurasikan.


Pelaksanaan templat protokol generik

Mula-mula, cipta strategi baharu dalam Platform Dagangan Kuantitatif Pencipta, tetapkan jenis strategi kepada perpustakaan templat dan bahasa strategi kepada JavaScript.

Reka bentuk parameter templat

Tambahkan tiga parameter pada templat dasar yang dibuat:

Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Kemudian anda boleh mula mereka bentuk dan menulis kod untuk templat protokol umum.

Pelaksanaan Kod

Kod itu ditulis dalam gaya TS.$.startService()Fungsi ini ialah fungsi antara muka templat yang digunakan untuk memulakan perkhidmatan 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")
    }
}

Oleh kerana ruang yang terhad, tidak semua antara muka dilaksanakan di sini sahajaPertanyaan PasaranPertanyaan Aset、*panggilan IO*Pelajar yang berminat boleh melaksanakan semua antara muka; selepas reka bentuk selesai, simpan kod templat dan simpan nama templat sebagai: “Contoh protokol umum versi TypeScript”.


Strategi Pengujian

Selepas mengkonfigurasi apikey, kunci rahsia, frasa laluan, dsb. pertukaran OKX, kita boleh menulis strategi ujian untuk diuji.

Strategi Semak perpustakaan templat kami yang direka bentuk:

Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Kod strategi ujian:

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 Ujian

Pencipta mengira ciri baharu: menggunakan_Fungsi servis mencipta perkhidmatan HTTP dengan mudah

Seperti yang anda lihat, strategi hanya perlu menyemak templat untuk mencapai akses lancar ke bursa OKX (walaupun bursa OKX sudah menyokongnya, contohnya, OKX digantikan di sini dengan pertukaran yang FMZ belum disambungkan lagi).