Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

Creado el: 2024-11-12 22:10:49, Actualizado el: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

En el comercio cuantitativo y el desarrollo de estrategias automatizadas, a veces se utilizan servicios http. La plataforma cuantitativa Inventor ha añadido recientemente_Serve() Función que proporciona a los usuarios capacidades flexibles de creación de servicios HTTP, HTTPS y TCP. Con esta función, los desarrolladores pueden simplificar el proceso de configuración del servicio e implementar servicios más personalizados en un entorno cuantitativo, lo que hace que el diseño de la estrategia sea más fluido y conveniente. Este artículo presentará_Serve() Los escenarios de uso y las operaciones básicas de la función le ayudarán a comenzar rápidamente con esta nueva función de Inventor Quant.

acerca de_Serve()Actualizado en la documentación de la API de la plataforma:

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


necesidad

La plataforma ha sido actualizada_Serve()Función (dado que el lenguaje JavaScript no tenía antes la función de crear servicios, esta función solo admite políticas del lenguaje JavaScript). En términos simples, permite que las políticas tengan la capacidad de crear servicios de red. Basándonos en esta función podemos desarrollar muchas funciones y solucionar muchas necesidades. Por ejemplo, la estrategia puede tener interfaces externas, reenvío de datos y cooperar con las funciones de protocolo general de la plataforma para encapsular sin problemas los intercambios que no son compatibles con la plataforma FMZ.

En este artículo, tomaremos como ejemplo el requisito de “cooperar con la función de protocolo general de la plataforma para encapsular sin problemas los intercambios que no son compatibles con la plataforma FMZ”. En artículos anteriores「Guía general de protocolo」En este artículo, usamos el lenguaje Python para encapsular la API del intercambio OKX en modo spot (dado que FMZ en sí mismo admite OKX, OKX se usa aquí solo como ejemplo y es aplicable a otros intercambios a los que la plataforma FMZ no está conectada). El programa de protocolo general de Python de este artículo debe ejecutarse por separado. Cuando el lenguaje JavaScript lo admita_Serve()Después de la función, es más fácil acceder al protocolo común de la estrategia del lenguaje JavaScript.

Encapsulamos el protocolo general de la interfaz de intercambio que se va a encapsular en una “biblioteca de plantillas” y lo integramos directamente en la estrategia, de modo que la estrategia pueda acceder sin problemas a los intercambios que no son compatibles con FMZ. No entraré en detalles sobre cómo configurar el objeto de intercambio “Protocolo general” aquí, puedes consultar el artículo:

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

  • La configuración general del protocolo de intercambio en la plataforma es la siguiente:

Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

Al diseñar la plantilla, puedes/OKXIdentifica a qué intercambio pertenece el objeto de intercambio de protocolo genérico configurado.


Implementación de plantilla de protocolo genérico

Primero, cree una nueva estrategia en la Plataforma de Comercio Cuantitativo de Inventor, configure el tipo de estrategia en biblioteca de plantillas y el lenguaje de la estrategia en JavaScript.

Diseño de parámetros de plantilla

Agregue tres parámetros a la plantilla de política creada:

Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

Luego puede comenzar a diseñar y escribir código para la plantilla de protocolo general.

Implementación de código

El código está escrito en estilo TS.$.startService()La función es una función de interfaz de plantilla que se utiliza para iniciar el servicio de protocolo general.

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

Debido al espacio limitado, no todas las interfaces se implementan aquí, soloConsulta de mercadoConsulta de activos、*Llamadas IO*Los estudiantes interesados ​​pueden implementar todas las interfaces; una vez completado el diseño, guarde el código de la plantilla y guarde el nombre de la plantilla como: “Ejemplo de protocolo general de la versión TypeScript”.


Estrategia de prueba

Después de configurar la clave API, la clave secreta, la frase de contraseña, etc. del intercambio OKX, podemos escribir una estrategia de prueba para probar.

Estrategia Consulte nuestra biblioteca de plantillas diseñadas:

Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

Código de estrategia de prueba:

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

Ejecución de pruebas

Los inventores cuantifican nuevas características: utilizando_La función Serve crea fácilmente servicios HTTP

Como puede ver, la estrategia solo necesita verificar una plantilla para lograr un acceso sin problemas al intercambio OKX (aunque el intercambio OKX ya lo admite, por ejemplo, OKX se reemplaza aquí con un intercambio al que FMZ aún no se ha conectado).