Die Ressourcen sind geladen. Beförderung...

Erfinder quantifizieren neue Funktionen: Erstellen von HTTP-Diensten mit _Serve

Schriftsteller:Die Erfinder quantifizieren - Kleine Träume, Erstellt: 2024-11-12 22:10:49, Aktualisiert: 2024-11-15 09:27:20

[TOC]

img

In der Entwicklung von Quantitative Transaktionen und Automatisierungsstrategien wird manchmal der HTTP-Dienst verwendet._Serve()Funktionen, die den Benutzern die Möglichkeit geben, HTTP, HTTPS und TCP-Dienste flexibel zu erstellen. Mit dieser Funktion können Entwickler die Konfiguration von Diensten vereinfachen und mehr kundenspezifische Dienste in einer quantitativen Umgebung realisieren, was das Konzipieren von Strategien einfacher macht._Serve()Funktionsanwendungsfälle und grundlegende Operationen, um Ihnen zu helfen, diese neue Funktion schnell zu quantifizieren.

Über_Serve()Die Website wurde im April 2013 von der Google Play-Website "Google Play" veröffentlicht.

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


Bedürfnisse

Die Plattform wurde aktualisiert._Serve()Funktionen (da die vorherige JavaScript-Sprache keine Funktionen für die Erstellung von Diensten hatte, unterstützt die Funktion nur die Strategien der JavaScript-Sprache), die einfach gesagt die Fähigkeit haben, Netzdienste zu erstellen. Aufgrund dieser Funktion können wir viele Funktionen entwickeln, um viele Anforderungen zu lösen. Zum Beispiel die Funktionen für die Erstellung von externen Anschlüssen, Datenübertragungen, allgemeine Protokollfunktionen, die mit Plattformen kompatibel sind.

In diesem Artikel stellen wir uns zum Beispiel den Bedarf an einer Plattform vor, die nicht von der FMZ-Plattform unterstützt wird."Die Leitfäden für das Allgemeine Protokoll"In der PHP-Sprache werden die OKX-Transaktionen so umgepackt, dass die APIs in der Standortmodus eingeschlossen sind._Serve()Nach den Funktionen ist die Strategie der JavaScript-Sprache, die Zugang zu den allgemeinen Protokollen ermöglicht, einfacher.

Wir werden das zu wickelnde Exchange-Interface direkt in die Strategie integrieren, die als "Template-Library" eingebunden ist, um nahtlos auf nicht auf FMZ unterstützte Exchanges zuzugreifen.

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

  • Auf der Plattform werden die allgemeinen Protokollbörsen wie folgt konfiguriert:

    img

    Sie können die Vorlage beim Entwerfen durchführen./OKXIdentifizieren Sie, zu welcher Börse das generierte Protokollobjekt gehört.


Implementierung der Universal Protocol Template

Zunächst wurde eine neue Strategie auf der Quantified Trading Platform von Inventors erstellt, deren Strategie-Typ als Template-Library und die Strategie-Sprache als JavaScript-Sprache eingestellt wurde.

Entwurf von Templateparametern

Es wurden drei Parameter hinzugefügt, um eine gute Strategievorlage zu erstellen:

img

Dann kann man anfangen, den Code zu entwerfen und zu schreiben, der für die gpr-Vorlage verwendet wird.

Code-Implementierung

Der Code ist im TS-Stil geschrieben.$.startService()Die Funktion ist eine Schablonen-Interface-Funktion, die verwendet wird, um einen Universal-Protokoll-Dienst zu starten.

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

Es gibt nur eine begrenzte Bandbreite, so dass nicht alle Schnittstellen realisiert werden.ErmittlungenVermögensanfragenIO-Aufruf, die Interessenten können alle Schnittstellen implementieren; Entwurf abgeschlossen, Schablonscode gespeichert, Schablonname als: TypeScript-Version


Teststrategien

Nach der Konfiguration der OKX-Börsen wie Apkey, Secretkey und Passphrase können wir eine Teststrategie schreiben, um sie zu testen.

Die Strategie ist es, die Template-Klasse zu wählen, die wir entworfen haben:

img

Test-Strategiekode:

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

Lauftest

img

Wie Sie sehen können, ist die Strategie, einfach eine Vorlage zu wählen, um nahtlosen Zugang zu OKX-Börsen zu ermöglichen (obwohl OKX-Börsen bereits unterstützt werden, z. B. hier wird OKX durch eine FMZ-Börse ersetzt, die noch nicht angeschlossen ist).


Mehr