4
tập trung vào
1076
Người theo dõi

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Được tạo ra trong: 2024-11-12 22:10:49, cập nhật trên: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Trong giao dịch định lượng và phát triển chiến lược tự động, đôi khi dịch vụ http được sử dụng. Nền tảng định lượng Inventor gần đây đã được bổ sung_Serve() Chức năng cung cấp cho người dùng khả năng tạo dịch vụ HTTP, HTTPS và TCP linh hoạt. Với tính năng này, các nhà phát triển có thể đơn giản hóa quy trình cấu hình dịch vụ và triển khai nhiều dịch vụ tùy chỉnh hơn trong môi trường định lượng, giúp việc thiết kế chiến lược trở nên dễ dàng và thuận tiện hơn. Bài viết này sẽ giới thiệu_Serve() Các tình huống sử dụng và thao tác cơ bản của chức năng giúp bạn nhanh chóng bắt đầu sử dụng chức năng mới này của Inventor Quant.

Về_Serve()Đã cập nhật trong tài liệu API của Nền tảng:

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


nhu cầu

Nền tảng đã được nâng cấp_Serve()chức năng (vì ngôn ngữ JavaScript trước đây không có chức năng tạo dịch vụ nên chức năng này chỉ hỗ trợ các chính sách ngôn ngữ JavaScript). Nói một cách đơn giản, nó cho phép các chính sách có khả năng tạo dịch vụ mạng. Dựa trên chức năng này, chúng ta có thể phát triển nhiều chức năng và giải quyết nhiều nhu cầu. Ví dụ, chiến lược có thể có giao diện bên ngoài, chuyển tiếp dữ liệu và hợp tác với các chức năng giao thức chung của nền tảng để đóng gói liền mạch các trao đổi không được nền tảng FMZ hỗ trợ.

Trong bài viết này, chúng tôi sẽ lấy yêu cầu “hợp tác với chức năng giao thức chung của nền tảng để đóng gói liền mạch các trao đổi không được nền tảng FMZ hỗ trợ” làm ví dụ. Trong các bài viết trước「Hướng dẫn giao thức chung」Trong bài viết này, chúng tôi sử dụng ngôn ngữ Python để đóng gói API của sàn giao dịch OKX ở chế độ giao ngay (vì bản thân FMZ hỗ trợ OKX nên OKX được sử dụng ở đây chỉ là ví dụ và có thể áp dụng cho các sàn giao dịch khác mà nền tảng FMZ không kết nối tới). Chương trình giao thức chung Python trong bài viết này cần phải được chạy riêng. Khi ngôn ngữ JavaScript hỗ trợ_Serve()Sau hàm này, việc truy cập vào giao thức chung của chiến lược ngôn ngữ JavaScript trở nên dễ dàng hơn.

Chúng tôi đóng gói giao thức chung của giao diện trao đổi để được đóng gói vào “thư viện mẫu” và tích hợp trực tiếp vào chiến lược, để chiến lược có thể truy cập liền mạch vào các trao đổi không được hỗ trợ trên FMZ. Tôi sẽ không đi sâu vào cách cấu hình đối tượng trao đổi “General Protocol” ở đây, bạn có thể tham khảo bài viết:

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

  • Cấu hình trao đổi giao thức chung trên nền tảng như sau:

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Khi thiết kế mẫu, bạn có thể/OKXXác định đối tượng trao đổi giao thức chung được cấu hình thuộc về sàn giao dịch nào.


Triển khai mẫu giao thức chung

Đầu tiên, hãy tạo một chiến lược mới trên Nền tảng giao dịch định lượng Inventor, đặt loại chiến lược thành thư viện mẫu và ngôn ngữ chiến lược thành JavaScript.

Thiết kế tham số mẫu

Thêm ba tham số vào mẫu chính sách đã tạo:

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Sau đó, bạn có thể bắt đầu thiết kế và viết mã cho mẫu giao thức chung.

Triển khai mã

Mã được viết theo phong cách TS.$.startService()Hàm này là hàm giao diện mẫu được sử dụng để khởi động dịch vụ giao thức chung.

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

Do không gian hạn chế, không phải tất cả các giao diện đều được triển khai ở đây, chỉ cóTruy vấn thị trườngTruy vấn tài sản、*IO gọi*Sinh viên quan tâm có thể triển khai tất cả các giao diện; sau khi thiết kế hoàn tất, hãy lưu mã mẫu và lưu tên mẫu dưới dạng: “Ví dụ về giao thức chung của phiên bản TypeScript”.


Chiến lược thử nghiệm

Sau khi cấu hình apikey, secretkey, passphrase, v.v. của sàn giao dịch OKX, chúng ta có thể viết chiến lược thử nghiệm để kiểm tra.

Chiến lược Kiểm tra thư viện mẫu được thiết kế của chúng tôi:

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Mã chiến lược thử nghiệm:

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

Chạy thử nghiệm

Các nhà phát minh định lượng các tính năng mới: sử dụng_Chức năng Serve dễ dàng tạo ra các dịch vụ HTTP

Như bạn có thể thấy, chiến lược này chỉ cần kiểm tra một mẫu để đạt được quyền truy cập liền mạch vào sàn giao dịch OKX (mặc dù sàn giao dịch OKX đã hỗ trợ, ví dụ, OKX được thay thế ở đây bằng một sàn giao dịch mà FMZ vẫn chưa kết nối tới).