Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Créé le: 2024-11-12 22:10:49, Mis à jour le: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Dans le trading quantitatif et le développement de stratégies automatisées, les services http sont parfois utilisés. La plateforme quantitative Inventor a récemment ajouté_Serve() Fonction offrant aux utilisateurs des capacités flexibles de création de services HTTP, HTTPS et TCP. Grâce à cette fonctionnalité, les développeurs peuvent simplifier le processus de configuration des services et implémenter des services plus personnalisés dans un environnement quantitatif, rendant la conception de la stratégie plus fluide et plus pratique. Cet article présentera_Serve() Les scénarios d’utilisation et les opérations de base de la fonction vous aident à démarrer rapidement avec cette nouvelle fonction d’Inventor Quant.

à propos_Serve()Mis à jour dans la documentation de l’API de la plateforme :

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


besoin

La plateforme a été mise à niveau_Serve()fonction (étant donné que le langage JavaScript n’avait pas auparavant la fonction de création de services, cette fonction ne prend en charge que les politiques du langage JavaScript). En termes simples, elle permet aux politiques d’avoir la possibilité de créer des services réseau. Sur la base de cette fonction, nous pouvons développer de nombreuses fonctions et résoudre de nombreux besoins. Par exemple, la stratégie peut avoir des interfaces externes, la transmission de données et coopérer avec les fonctions de protocole générales de la plateforme pour encapsuler de manière transparente les échanges qui ne sont pas pris en charge par la plateforme FMZ.

Dans cet article, nous prendrons comme exemple l’exigence de « coopérer avec la fonction de protocole général de la plateforme pour encapsuler de manière transparente les échanges qui ne sont pas pris en charge par la plateforme FMZ ». Dans les articles précédents« Guide général du protocole »Dans cet article, nous utilisons le langage Python pour encapsuler l’API de l’échange OKX en mode spot (car FMZ lui-même prend en charge OKX, OKX est utilisé ici simplement à titre d’exemple, et il est applicable à d’autres échanges auxquels la plate-forme FMZ n’est pas connectée). Le programme de protocole général Python de cet article doit être exécuté séparément. Lorsque le langage JavaScript prend en charge_Serve()Après la fonction, il est plus facile d’accéder au protocole commun de la stratégie du langage JavaScript.

Nous encapsulons le protocole général de l’interface d’échange à encapsuler dans une « bibliothèque de modèles » et l’intégrons directement dans la stratégie, afin que la stratégie puisse accéder de manière transparente aux échanges qui ne sont pas pris en charge sur FMZ. Je n’entrerai pas ici dans les détails sur la façon de configurer l’objet d’échange “Protocole Général”, vous pouvez vous référer à l’article :

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

  • La configuration générale du protocole d’échange sur la plateforme est la suivante :

Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Lors de la conception du modèle, vous pouvez/OKXIdentifie à quel échange appartient l’objet d’échange de protocole générique configuré.


Mise en œuvre du modèle de protocole générique

Tout d’abord, créez une nouvelle stratégie dans la plateforme de trading quantitatif Inventor, définissez le type de stratégie sur la bibliothèque de modèles et le langage de stratégie sur JavaScript.

Conception des paramètres du modèle

Ajoutez trois paramètres au modèle de politique créé :

Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Vous pouvez ensuite commencer à concevoir et à écrire du code pour le modèle de protocole général.

Implémentation du code

Le code est écrit dans le style TS.$.startService()La fonction est une fonction d’interface de modèle utilisée pour démarrer le service de protocole général.

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

En raison de l’espace limité, toutes les interfaces ne sont pas implémentées ici, seulesRequête sur le marchéRequête sur les actifs、*Appels IO*Les étudiants intéressés peuvent implémenter toutes les interfaces ; une fois la conception terminée, enregistrez le code du modèle et enregistrez le nom du modèle sous : « Exemple de protocole général de version TypeScript ».


Stratégie de test

Après avoir configuré l’APIkey, la clé secrète, la phrase secrète, etc. de l’échange OKX, nous pouvons écrire une stratégie de test pour tester.

Stratégie Consultez notre bibliothèque de modèles conçus :

Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Code de stratégie de test :

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

Exécution de tests

Les inventeurs quantifient les nouvelles fonctionnalités : en utilisant_La fonction Serve crée facilement des services HTTP

Comme vous pouvez le voir, la stratégie n’a besoin que de vérifier un modèle pour obtenir un accès transparent à l’échange OKX (bien que l’échange OKX le prenne déjà en charge, par exemple, OKX est remplacé ici par un échange auquel FMZ ne s’est pas encore connecté).