[TOC]
Dalam perdagangan kuantitatif dan pembangunan strategi automatik, perkhidmatan http kadangkala digunakan. Platform Kuantitatif Inventor telah ditambah baru-baru ini_Serve()
Berfungsi, menyediakan pengguna dengan keupayaan penciptaan perkhidmatan HTTP, HTTPS dan TCP yang fleksibel. Dengan ciri ini, pembangun boleh memudahkan proses konfigurasi perkhidmatan dan melaksanakan perkhidmatan yang lebih tersuai dalam persekitaran kuantitatif, menjadikan reka bentuk strategi lebih lancar dan lebih mudah. Artikel ini akan memperkenalkan_Serve()
Senario penggunaan dan operasi asas fungsi membantu anda bermula dengan cepat dengan fungsi baharu Inventor Quant ini.
kira-kira_Serve()
Dikemas kini dalam dokumentasi API Platform:
Platform telah dinaik taraf_Serve()
fungsi (memandangkan bahasa JavaScript tidak mempunyai fungsi mencipta perkhidmatan sebelum ini, fungsi ini hanya menyokong dasar bahasa JavaScript Secara ringkas, ia membolehkan polisi mempunyai keupayaan untuk mencipta perkhidmatan rangkaian). Berdasarkan fungsi ini, kita boleh membangunkan banyak fungsi dan menyelesaikan banyak keperluan. Sebagai contoh, strategi boleh mempunyai antara muka luaran, pemajuan data dan bekerjasama dengan fungsi protokol umum platform untuk merangkum pertukaran yang tidak disokong oleh platform FMZ dengan lancar.
Dalam artikel ini, kami akan mengambil keperluan untuk “bekerjasama dengan fungsi protokol umum platform untuk merangkum pertukaran dengan lancar yang tidak disokong oleh platform FMZ” sebagai contoh. Dalam artikel sebelum ini「Panduan Protokol Am」Dalam artikel ini, kami menggunakan bahasa Python untuk merangkum API pertukaran OKX dalam mod spot (kerana FMZ sendiri menyokong OKX, OKX digunakan di sini hanya sebagai contoh, dan ia boleh digunakan untuk pertukaran lain yang platform FMZ tidak disambungkan). Program protokol umum Python dalam artikel ini perlu dijalankan secara berasingan Apabila bahasa JavaScript menyokong_Serve()
Selepas fungsi itu, lebih mudah untuk mengakses protokol umum strategi bahasa JavaScript.
Kami merangkum protokol umum antara muka pertukaran untuk dirangkumkan ke dalam “pustaka templat” dan menyepadukannya secara langsung ke dalam strategi, supaya strategi boleh mengakses pertukaran yang tidak disokong pada FMZ dengan lancar. Saya tidak akan menerangkan secara terperinci tentang cara mengkonfigurasi objek pertukaran “Protokol Umum” di sini, anda boleh merujuk kepada artikel:
Semasa mereka bentuk templat, anda boleh/OKX
Mengenal pasti pertukaran mana yang dimiliki oleh objek pertukaran protokol generik yang dikonfigurasikan.
Mula-mula, cipta strategi baharu dalam Platform Dagangan Kuantitatif Pencipta, tetapkan jenis strategi kepada perpustakaan templat dan bahasa strategi kepada JavaScript.
Tambahkan tiga parameter pada templat dasar yang dibuat:
Kemudian anda boleh mula mereka bentuk dan menulis kod untuk templat protokol umum.
Kod itu ditulis dalam gaya TS.$.startService()
Fungsi ini ialah fungsi antara muka templat yang digunakan untuk memulakan perkhidmatan 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")
}
}
Oleh kerana ruang yang terhad, tidak semua antara muka dilaksanakan di sini sahajaPertanyaan Pasaran、Pertanyaan Aset、*panggilan IO*Pelajar yang berminat boleh melaksanakan semua antara muka; selepas reka bentuk selesai, simpan kod templat dan simpan nama templat sebagai: “Contoh protokol umum versi TypeScript”.
Selepas mengkonfigurasi apikey, kunci rahsia, frasa laluan, dsb. pertukaran OKX, kita boleh menulis strategi ujian untuk diuji.
Strategi Semak perpustakaan templat kami yang direka bentuk:
Kod strategi ujian:
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 anda lihat, strategi hanya perlu menyemak templat untuk mencapai akses lancar ke bursa OKX (walaupun bursa OKX sudah menyokongnya, contohnya, OKX digantikan di sini dengan pertukaran yang FMZ belum disambungkan lagi).