[TOC]
양적 거래 및 자동화 전략 개발에 있어서는 때때로 http 서비스가 사용된다._Serve()
기능, 사용자에게 유연한 HTTP, HTTPS 및 TCP 서비스를 만들 수있는 능력을 제공합니다. 이 기능을 통해 개발자는 서비스 구성 프로세스를 단순화하고 양적 환경에서 더 많은 사용자 정의 서비스를 구현하여 정책 설계를 더욱 원활하게 할 수 있습니다._Serve()
함수의 사용 시나리오와 기본 동작은 이 새로운 기능의 가량화를 빠르게 할 수 있도록 도와줍니다.
에 대해_Serve()
이 사이트의 API 문서를 업데이트합니다:
플랫폼 업그레이드_Serve()
함수 (예전에 자바스크립트 언어가 서비스를 만들 수 있는 기능이 없었기 때문에 자바스크립트 언어의 정책만을 지원하는 함수) 는 간단히 말해서, 정책이 네트워크 서비스를 만들 수 있는 능력을 갖는 것이다. 이 기능에 기초하여 우리는 많은 기능을 개발할 수 있으며, 많은 요구를 해결할 수 있다. 예를 들어, 정책이 외부 인터페이스, 데이터 전송, 플랫폼을 지원하는 일반 프로토콜 기능을 갖는 것, FMZ 플랫폼이 지원하지 않는 거래소를 무선 포괄하는 것 등.
이 기사는 "FMZ 플랫폼이 지원하지 않는 거래소를 원활하게 포괄하는 플랫폼의 일반 프로토콜 기능을 지원하는" 필요성을 예로 들 수 있습니다."일본 프로토콜 가이드"중에서는 우리는 OKX 거래에 대해 파이썬 언어를 사용하므로 현장 모드가 포괄된 API (FMZ가 OKX를 지원하기 때문에 OKX를 사용하는 것은 예제일 뿐이며 다른 FMZ 플랫폼에 액세스 할 수없는 거래소에 적용됩니다.)_Serve()
함수 이후, 자바스크립트 언어의 전략은 일반 프로토콜에 접근하는 것이 더 간단합니다.
우리는 포장될 거래소 인터페이스의 일반 프로토콜을 "템플릿 라이브러리"로 포장하여 정책에 직접 통합하여 FMZ에서 지원되지 않는 거래소에 무선 액세스 할 수 있습니다. "전용 프로토콜" 거래소 객체를 구성하는 방법에 대해 더 이상 설명하지 않습니다.
이 플랫폼의 기본 프로토콜 거래소는 다음과 같이 구성됩니다.
템플릿을 디자인할 때,/OKX
정렬된 일반 프로토콜 거래소의 객체가 어느 거래소에 속하는지 식별합니다.
먼저 발명가들의 양적 거래 플랫폼에서 새로운 전략을 만들고, 전략 유형은 템플릿 라이브러리로 설정하고, 전략 언어는 자바스크립트 언어로 설정합니다.
좋은 전략 템플릿을 만들기 위해 세 가지 매개 변수를 추가합니다.
그리고 나서, 우리는 UNPROTOCOL 템플릿의 코드를 설계하고 작성할 수 있습니다.
코드는 TS 스타일로 작성되어 있습니다.$.startService()
함수는 템플릿의 인터페이스 함수이며, UTP 서비스를 시작하기 위해 사용됩니다.
// @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")
}
}
이 부분의 본문은 한정되어 있습니다. 모든 인터페이스를 구현하지 않았습니다.사건 조사、자산 조회、IO 호출, 관심 있는 동료는 모든 인터페이스를 구현할 수 있습니다; 설계 완료, 템플릿 코드를 저장, 템플릿 이름을 저장:
OKX 거래소의 apkey, secretkey, passphrase 등의 구성이 완료되면 테스트를 위해 테스트 정책을 작성할 수 있습니다.
이 문서는 우리가 디자인한 템플릿 범주를 선택하기 위한 전략입니다.
테스트 전략 코드:
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())
}
보시다시피, 전략은 하나의 템플릿을 클릭하는 것만으로 OKX 거래소 (OKX 거래소가 이미 지원하고 있지만, 예를 들어 여기 OKX가 FMZ가 아직 접속하지 않은 거래소로 대체된 경우) 에 대한 무선 액세스를 구현합니다.