Trong giao dịch định lượng và phát triển chiến lược tự động, các dịch vụ http đôi khi được sử dụng._Serve()
gần đây, cung cấp cho người dùng khả năng tạo dịch vụ HTTP, HTTPS và TCP linh hoạt. Với chức năng này, các nhà phát triển có thể đơn giản hóa quá trình cấu hình dịch vụ và thực hiện các dịch vụ tùy chỉnh hơn trong môi trường định lượng, làm cho thiết kế chiến lược mượt mà và thuận tiện hơn. Bài viết này sẽ giới thiệu các kịch bản sử dụng và các hoạt động cơ bản của chức năng_Serve()
để giúp bạn nhanh chóng bắt đầu với chức năng mới này của FMZ Quant.
Tài liệu API nền tảng cho_Serve()
đã được cập nhật:
Nền tảng đã nâng cấp chức năng_Serve()
(vì ngôn ngữ JavaScript không có chức năng tạo dịch vụ trước đây, chức năng này chỉ hỗ trợ các chiến lược trong ngôn ngữ JavaScript). Nói đơn giản, nó cho phép các chiến lược có khả năng tạo các dịch vụ mạng. Dựa trên chức năng này, chúng ta có thể phát triển nhiều chức năng và giải quyết nhiều vấn đề. Ví dụ, các chiến lược có thể có giao diện bên ngoài, chuyển tiếp dữ liệu và hợp tác với chức năng giao thức tùy chỉnh của nền tảng để đóng gói liền mạch các trao đổi không được nền tảng FMZ hỗ trợ.
Trong bài viết này, chúng tôi sẽ sử dụng nhu cầu _Serve()
, nó dễ dàng hơn cho chiến lược ngôn ngữ JavaScript để truy cập vào giao thức tùy chỉnh.
Chúng tôi đóng gói giao thức tùy chỉnh của giao diện trao đổi để được đóng gói dưới dạng thư viện mẫu và tích hợp trực tiếp vào chiến lược, để chiến lược có thể truy cập liền mạch vào các giao dịch không được hỗ trợ trên FMZ. Tôi sẽ không đi sâu vào chi tiết về cách cấu hình đối tượng trao đổi giao thức tùy chỉnh ở đây, bạn có thể tham khảo bài viết:
Khi thiết kế một mẫu, chúng ta có thể sử dụng/OKX
để xác định sàn giao dịch mà đối tượng trao đổi giao thức tùy chỉnh được cấu hình thuộc về.
Đầu tiên, tạo một chiến lược mới trong nền tảng giao dịch FMZ Quant, đặt loại chiến lược vào thư viện mẫu và ngôn ngữ chiến lược vào JavaScript.
Thêm 3 tham số vào mẫu chiến lược được tạo:
Sau đó chúng ta có thể bắt đầu thiết kế và viết mã cho mẫu giao thức tùy chỉnh.
Mã được viết theo phong cách TS.$.startService()
function là một chức năng giao diện mẫu được sử dụng để khởi động dịch vụ giao thức tùy chỉnh.
// @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("Start the custom protocol service, address:", address, ",port:", port, "#FF0000")
if (proxyConfig != "") {
Log("Setting up the proxy:", proxyConfig, "#FF0000")
}
}
Do không gian hạn chế, không phải tất cả các giao diện được thực hiện ở đây.truy vấn thị trường , truy vấn tài sản, vàGọi IONgười dùng quan tâm có thể thực hiện tất cả các giao diện. Sau khi thiết kế được hoàn thành, hãy lưu mã mẫu và lưu tên mẫu như:
Sau khi cấu hình OKX exchange
Chiến lược kiểm tra thư viện mẫu được thiết kế của chúng tôi:
Mã chiến lược thử nghiệm:
function main() {
// Test GetTicker
Log(`exchange.GetTicker():`, exchange.GetTicker())
// Test GetAccount
Log(`exchange.GetAccount():`, exchange.GetAccount())
// Test 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"))
// Output the exchange name added by the custom protocol
Log(`exchange.GetName():`, exchange.GetName())
// Output exchange tags added by the custom protocol
Log(`exchange.GetLabel():`, exchange.GetLabel())
}
Như chúng ta có thể thấy, chiến lược chỉ cần kiểm tra một mẫu để đạt được quyền truy cập liền mạch vào sàn giao dịch OKX (mặc dù sàn giao dịch OKX đã hỗ trợ nó, ví dụ, OKX được thay thế ở đây bằng một sàn giao dịch mà FMZ chưa kết nối).