Les ressources ont été chargées... Je charge...

Nouvelles fonctionnalités quantifiées par les inventeurs: créer facilement des services HTTP avec la fonction _Serve

Auteur:L'inventeur de la quantification - un petit rêve, Créé: 2024-11-12 22:10:49, Mis à jour: 2024-11-15 09:27:20

[TOC] Je vous en prie.

img

Les services HTTP sont parfois utilisés dans le développement de transactions quantitatives et de stratégies d'automatisation._Serve()Fonction qui permet aux utilisateurs de créer des services HTTP, HTTPS et TCP flexibles. Grâce à cette fonction, les développeurs peuvent simplifier le processus de configuration des services et réaliser plus de services personnalisés dans des environnements quantifiés, ce qui rend la conception de stratégies plus fluide et facile._Serve()Les scénarios d'utilisation et les opérations de base des fonctions vous aident à quantifier rapidement cette nouvelle fonctionnalité.

À propos_Serve()Le site Web de la plateforme est en cours de mise à jour:

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


Les besoins

La plateforme est mise à niveau_Serve()Les fonctions (à cause de l'absence de fonctionnalités de création de services dans le langage JavaScript précédent, la fonctionnalité ne supportait que les stratégies du langage JavaScript) sont simplement des fonctions qui permettent aux stratégies de créer des services Web. Nous pouvons développer de nombreuses fonctionnalités basées sur cette fonctionnalité pour répondre à de nombreux besoins. Par exemple, les stratégies peuvent avoir des interfaces externes, des transmissions de données, des fonctionnalités de protocole général compatibles avec les plateformes, des échanges qui ne sont pas pris en charge par la plateforme FMZ, etc.

Dans cet article, nous avons utilisé l'exemple de la demande: "Envelopper les plateformes qui ne sont pas prises en charge par la plate-forme FMZ avec les fonctionnalités du protocole général de la plate-forme".Le guide du protocole généralNous utilisons le langage Python pour les transactions OKX, donc le mode instantané est enveloppé dans l'API (parce que FMZ lui-même prend en charge OKX, l'utilisation d'OKX ici n'est qu'un exemple, applicable aux autres plateformes FMZ sans accès)._Serve()Après les fonctions, les stratégies du langage JavaScript pour accéder au protocole général sont plus simples.

Nous intégrerons directement dans la stratégie le protocole général de l'interface de l'échange qui doit être enveloppé en une bibliothèque de modèles, ce qui permettra d'accéder de manière transparente aux échanges non pris en charge sur FMZ.

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

  • Les échanges de protocoles généraux sur la plateforme sont configurés comme suit:

    img

    Il est possible d'utiliser des modèles de conception en ligne./OKXIdentifier à quel échange appartient l'objet de l'échange de protocole général de configuration.


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

Tout d'abord, la création d'une nouvelle stratégie sur la plate-forme de négociation quantitative des inventeurs, dont le type de stratégie est défini comme une bibliothèque de modèles et dont le langage de stratégie est le langage JavaScript.

Conception des paramètres du modèle

Il y a trois paramètres ajoutés à un bon modèle de stratégie:

img

Ensuite, vous pouvez commencer à concevoir et à écrire du code pour les modèles de protocole général.

Mise en œuvre du code

Le code est écrit dans le style TS.$.startService()Les fonctions sont des fonctions d'interface de modèle utilisées pour démarrer un 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")
    }
}

La taille est limitée, il n'est pas possible d'implémenter toutes les interfaces ici, il suffit d'implémenterLes enquêtesRecherche d'actifsAppel à l'aideLes étudiants intéressés peuvent mettre en œuvre toutes les interfaces; la conception est terminée, le code du modèle est sauvegardé, le nom du modèle est sauvegardé comme:


Stratégies de test

Une fois la configuration de l'échange OKX apekey, secretkey ou passphrase terminée, nous pouvons écrire une stratégie de test pour le tester.

La stratégie consiste à sélectionner la bibliothèque de modèles que nous avons conçus:

img

Le code de la 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())
}

Les tests sont en cours

img

Comme vous pouvez le voir, la stratégie consiste simplement à sélectionner un modèle et à réaliser un accès transparent à l'échange OKX (bien que l'échange OKX soit déjà pris en charge, par exemple ici OKX remplacé par un FMZ qui n'est pas encore connecté).


Plus de