Tài nguyên đang được tải lên... tải...

Các nhà phát minh định lượng tính năng mới: dễ dàng tạo dịch vụ HTTP bằng chức năng _Serve

Tác giả:Những nhà phát minh định lượng - những giấc mơ nhỏ, Tạo: 2024-11-12 22:10:49, Cập nhật: 2024-11-15 09:27:20

[TOC]

img

Dịch vụ HTTP đôi khi được sử dụng trong việc phát triển giao dịch định lượng và chiến lược tự động hóa._Serve()Các chức năng này cung cấp cho người dùng khả năng tạo các dịch vụ HTTP, HTTPS và TCP linh hoạt. Thông qua chức năng này, các nhà phát triển có thể đơn giản hóa quá trình cấu hình dịch vụ và thực hiện nhiều dịch vụ tùy chỉnh hơn trong môi trường định lượng, làm cho việc thiết kế chính sách dễ dàng hơn._Serve()Các kịch bản sử dụng và các hoạt động cơ bản của các hàm giúp bạn nhanh chóng có thể định lượng các tính năng mới này.

Về_Serve()Các tài liệu API đã được cập nhật trên nền tảng:

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


Nhu cầu

Nâng cấp nền tảng_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ụ, chỉ hỗ trợ các chính sách của ngôn ngữ JavaScript) đơn giản là cho các chính sách có khả năng tạo các 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 để giải quyết nhiều nhu cầu. Ví dụ như cho các chính sách có giao diện bên ngoài, chuyển dữ liệu, hỗ trợ các nền tảng giao thức chung không hỗ trợ các sàn giao dịch FMZ.

Trong bài viết này, chúng tôi đã đưa ra ví dụ về nhu cầu: "các sàn giao dịch không được hỗ trợ bởi nền tảng FMZ được đóng gói một cách liền mạch với các chức năng giao thức chung của nền tảng"."Hướng dẫn giao thức chung"Trong đó, chúng tôi sử dụng ngôn ngữ Python để giao dịch OKX vì vậy mô hình hiện tại được gói gọn API ((vì FMZ tự hỗ trợ OKX, sử dụng OKX ở đây chỉ là một ví dụ, áp dụng cho các sàn giao dịch khác mà không có quyền truy cập nền tảng FMZ)); Trong văn bản, các chương trình giao thức chung của Python cần phải chạy riêng biệt, khi ngôn ngữ JavaScript được hỗ trợ._Serve()Sau các hàm, các chiến lược của ngôn ngữ JavaScript để truy cập vào giao thức chung sẽ đơn giản hơn.

Chúng tôi sẽ gói giao thức chung của giao diện giao dịch để được đóng gói như là "thư viện mẫu" trực tiếp tích hợp vào chính sách, để có thể truy cập một cách liền mạch vào các giao dịch không được hỗ trợ trên FMZ.

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

  • Các giao dịch thông thường trên nền tảng được cấu hình như sau:

    img

    Bạn có thể sử dụng nó để thiết kế mẫu./OKXĐịnh dạng giao dịch giao dịch thông thường được xác định là đối tượng của sàn giao dịch nào.


Thực hiện mẫu giao thức chung

Đầu tiên là tạo ra một chính sách mới trên nền tảng giao dịch định lượng của nhà phát minh, đặt loại chính sách là thư viện mẫu và ngôn ngữ chính sách là ngôn ngữ JavaScript.

Thiết kế tham số mẫu

Các công cụ này được sử dụng để tạo ra các mô hình chính sách tốt.

img

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

Thực hiện mã

Có thể sử dụng các mã khác nhau.$.startService()Các hàm là các hàm giao diện của mẫu, được sử dụng để khởi động các 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")
    }
}

Một phần của nó là một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của một phần của mộtCác cuộc điều traTìm kiếm tài sảnGọi IOCác học sinh quan tâm có thể thực hiện tất cả các giao diện; thiết kế hoàn tất, lưu mã mẫu, tên mẫu được lưu như: TypeScript phiên bản giao thức chung ví dụ .


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

Sau khi cấu hình OKX cho các giao dịch như apkey, secretkey, passphrase, chúng ta có thể viết một chính sách thử nghiệm để kiểm tra.

Các chiến lược chọn thư viện các mẫu chúng tôi thiết kế:

img

Có thể sử dụng các công cụ như:

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

Thử nghiệm chạy

img

Như bạn có thể thấy, chỉ cần chọn một mẫu, chính sách thực hiệ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 đã được hỗ trợ, ví dụ như OKX thay thế một sàn giao dịch FMZ chưa được truy cập).


Thêm nữa