[TOC]
Dalam perdagangan kuantitatif dan pengembangan strategi otomatis, layanan http terkadang digunakan. Platform Kuantitatif Inventor baru-baru ini menambahkan_Serve()
Fungsi yang menyediakan pengguna dengan kemampuan pembuatan layanan HTTP, HTTPS, dan TCP yang fleksibel. Dengan fitur ini, pengembang dapat menyederhanakan proses konfigurasi layanan dan menerapkan layanan yang lebih disesuaikan dalam lingkungan kuantitatif, membuat desain strategi lebih lancar dan nyaman. Artikel ini akan memperkenalkan_Serve()
Skenario penggunaan dan operasi dasar fungsi membantu Anda memulai dengan cepat fungsi baru Inventor Quant ini.
tentang_Serve()
Diperbarui dalam dokumentasi API Platform:
Platform telah ditingkatkan_Serve()
fungsi (karena bahasa JavaScript sebelumnya tidak memiliki fungsi untuk membuat layanan, fungsi ini hanya mendukung kebijakan bahasa JavaScript). Secara sederhana, fungsi ini memungkinkan kebijakan memiliki kemampuan untuk membuat layanan jaringan. Berdasarkan fungsi ini, kita dapat mengembangkan banyak fungsi dan menyelesaikan banyak kebutuhan. Misalnya, strategi tersebut dapat memiliki antarmuka eksternal, penerusan data, dan bekerja sama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak didukung oleh platform FMZ secara mulus.
Dalam artikel ini, kami akan mengambil persyaratan “bekerja sama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak didukung oleh platform FMZ secara mulus” sebagai contoh. Pada artikel sebelumnya「Panduan Protokol Umum」Dalam artikel ini, kami menggunakan bahasa Python untuk merangkum API bursa OKX dalam mode spot (karena FMZ sendiri mendukung OKX, OKX digunakan di sini hanya sebagai contoh, dan ini berlaku untuk bursa lain yang tidak terhubung dengan platform FMZ). Program protokol umum Python dalam artikel ini perlu dijalankan secara terpisah. Ketika bahasa JavaScript mendukung_Serve()
Setelah fungsi tersebut, lebih mudah untuk mengakses protokol umum strategi bahasa JavaScript.
Kami merangkum protokol umum antarmuka pertukaran yang akan dienkapsulasi ke dalam “perpustakaan templat” dan langsung mengintegrasikannya ke dalam strategi, sehingga strategi tersebut dapat mengakses pertukaran yang tidak didukung pada FMZ dengan lancar. Saya tidak akan membahas secara detail tentang cara mengonfigurasi objek pertukaran “Protokol Umum” di sini, Anda dapat merujuk ke artikel:
Saat mendesain template, Anda dapat/OKX
Mengidentifikasi bursa tempat objek pertukaran protokol generik yang dikonfigurasi berada.
Pertama, buat strategi baru di Inventor Quantitative Trading Platform, atur jenis strategi ke pustaka templat, dan bahasa strategi ke JavaScript.
Tambahkan tiga parameter ke templat kebijakan yang dibuat:
Kemudian Anda dapat mulai merancang dan menulis kode untuk templat protokol umum.
Kode ditulis dalam gaya TS.$.startService()
Fungsi ini adalah fungsi antarmuka templat yang digunakan untuk memulai layanan protokol umum.
// @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")
}
}
Karena keterbatasan ruang, tidak semua antarmuka diimplementasikan di sini, hanyaPertanyaan Pasar、Kueri Aset、*Panggilan IO*Siswa yang berminat dapat mengimplementasikan semua antarmuka; setelah desain selesai, simpan kode templat dan simpan nama templat sebagai: “Contoh protokol umum versi TypeScript”.
Setelah mengonfigurasi apikey, secretkey, passphrase, dll. di bursa OKX, kita dapat menulis strategi pengujian untuk diuji.
Strategi Periksa pustaka templat yang kami desain:
Kode strategi pengujian:
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())
}
Seperti yang bisa Anda lihat, strategi tersebut hanya perlu memeriksa templat untuk mencapai akses lancar ke bursa OKX (walaupun bursa OKX sudah mendukungnya, misalnya, OKX diganti di sini dengan bursa yang belum terhubung dengan FMZ).