Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Erstellt in: 2024-11-12 22:10:49, aktualisiert am: 2024-11-15 09:27:20
comments   0
hits   210

[TOC]

Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Im quantitativen Handel und der automatisierten Strategieentwicklung werden manchmal http-Dienste verwendet. Die Inventor Quantitative Platform wurde kürzlich um_Serve() Funktion, die Benutzern flexible Möglichkeiten zur Erstellung von HTTP-, HTTPS- und TCP-Diensten bietet. Mit dieser Funktion können Entwickler den Servicekonfigurationsprozess vereinfachen und mehr individuelle Services in einer quantitativen Umgebung implementieren, wodurch die Strategiegestaltung reibungsloser und bequemer wird. Dieser Artikel behandelt_Serve() Die Anwendungsszenarien und grundlegenden Operationen der Funktion erleichtern Ihnen den schnellen Einstieg in diese neue Funktion von Inventor Quant.

um_Serve()In der Plattform-API-Dokumentation aktualisiert:

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


brauchen

Die Plattform wurde aktualisiert_Serve()Funktion (da die JavaScript-Sprache zuvor nicht über die Funktion zum Erstellen von Diensten verfügte, unterstützt diese Funktion nur Richtlinien in der JavaScript-Sprache). Einfach ausgedrückt ermöglicht sie Richtlinien die Erstellung von Netzwerkdiensten. Basierend auf dieser Funktion können wir viele Funktionen entwickeln und viele Bedürfnisse erfüllen. Die Strategie kann beispielsweise über externe Schnittstellen und Datenweiterleitungen verfügen und mit den allgemeinen Protokollfunktionen der Plattform zusammenarbeiten, um Austausche nahtlos zu kapseln, die von der FMZ-Plattform nicht unterstützt werden.

In diesem Artikel nehmen wir als Beispiel die Anforderung „Zusammenarbeit mit der allgemeinen Protokollfunktion der Plattform, um Austausche nahtlos zu kapseln, die von der FMZ-Plattform nicht unterstützt werden“. In früheren Artikeln„Allgemeiner Protokollleitfaden“In diesem Artikel verwenden wir die Programmiersprache Python, um die API der OKX-Börse im Spot-Modus zu kapseln (da FMZ selbst OKX unterstützt, wird OKX hier nur als Beispiel verwendet und ist auf andere Börsen anwendbar, mit denen die FMZ-Plattform nicht verbunden ist). Das allgemeine Python-Protokollprogramm in diesem Artikel muss separat ausgeführt werden. Wenn die JavaScript-Sprache unterstützt_Serve()Nach der Funktion ist es einfacher, auf das allgemeine Protokoll der JavaScript-Sprachstrategie zuzugreifen.

Wir kapseln das allgemeine Protokoll der zu kapselnden Börsenschnittstelle in eine „Vorlagenbibliothek“ und integrieren es direkt in die Strategie, sodass die Strategie nahtlos auf Börsen zugreifen kann, die auf FMZ nicht unterstützt werden. Auf die Konfiguration des Austauschobjekts „Allgemeines Protokoll“ werde ich hier nicht näher eingehen. Lesen Sie hierzu einfach den folgenden Artikel:

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

  • Die allgemeine Protokollaustauschkonfiguration auf der Plattform ist wie folgt:

Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Beim Gestalten der Vorlage können Sie/OKXIdentifiziert, zu welchem ​​Austausch das konfigurierte generische Protokollaustauschobjekt gehört.


Implementierung allgemeiner Protokollvorlagen

Erstellen Sie zunächst eine neue Strategie in der Inventor Quantitative Trading Platform, legen Sie den Strategietyp auf Vorlagenbibliothek und die Strategiesprache auf JavaScript fest.

Entwurf von Vorlagenparametern

Fügen Sie der erstellten Richtlinienvorlage drei Parameter hinzu:

Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Anschließend können Sie mit dem Entwerfen und Schreiben des Codes für die allgemeine Protokollvorlage beginnen.

Code-Implementierung

Der Code ist im TS-Stil geschrieben.$.startService()Bei der Funktion handelt es sich um eine Vorlagenschnittstellenfunktion, die zum Starten des allgemeinen Protokolldienstes verwendet wird.

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

Aus Platzgründen sind hier nicht alle Schnittstellen implementiert, sondern nurMarktabfrageAsset-Abfrage、*IO-Aufrufe*Interessierte Studierende können alle Schnittstellen implementieren; nach Abschluss des Entwurfs den Vorlagencode speichern und den Vorlagennamen wie folgt speichern: „Beispiel für allgemeines Protokoll in TypeScript-Version“.


Teststrategie

Nachdem wir den API-Schlüssel, den geheimen Schlüssel, die Passphrase usw. der OKX-Börse konfiguriert haben, können wir eine Teststrategie zum Testen schreiben.

Strategie: Schauen Sie sich unsere Bibliothek mit Designvorlagen an:

Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Teststrategiecode:

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

Ausführen von Tests

Erfinder quantifizieren neue Eigenschaften: mit_Mit der Serve-Funktion lassen sich ganz einfach HTTP-Dienste erstellen

Wie man sieht, muss die Strategie nur eine Vorlage prüfen, um einen nahtlosen Zugang zur OKX-Börse zu erreichen (obwohl die OKX-Börse dies bereits unterstützt, wird OKX hier beispielsweise durch eine Börse ersetzt, mit der FMZ noch nicht verbunden ist).