
FMZ 양적 거래 플랫폼전략 감시 섹션거래소의 수십 개 시장, 심지어 전체 시장의 시장 상황을 동시에 모니터링하는 다중 상품 전략을 보는 것은 흔한 일입니다. 이것은 어떻게 이루어지나요? 그러면 어떻게 설계해야 할까? 이 글에서는 거래소에서 통합된 시장 인터페이스를 사용하여 다중 제품 전략을 구축하는 방법을 살펴보겠습니다.
Binance와 Huobi를 예로 들어, API 문서를 확인해 보세요. 둘 다 통합된 시장 정보 인터페이스를 가지고 있다는 것을 알 수 있습니다.
[
{
"symbol": "BTCUSDT", // 交易对
"bidPrice": "4.00000000", //最优买单价
"bidQty": "431.00000000", //挂单量
"askPrice": "4.00000200", //最优卖单价
"askQty": "9.00000000", //挂单量
"time": 1589437530011 // 撮合引擎时间
}
...
]
[
{
"open":0.044297, // 开盘价
"close":0.042178, // 收盘价
"low":0.040110, // 最低价
"high":0.045255, // 最高价
"amount":12880.8510,
"count":12838,
"vol":563.0388715740,
"symbol":"ethbtc",
"bid":0.007545,
"bidSize":0.008,
"ask":0.008088,
"askSize":0.009
},
...
]
하지만, 그렇지 않습니다. Huobi 인터페이스에서 실제로 반환된 구조는 다음과 같습니다.
{
"status": "ok",
"ts": 1616032188422,
"data": [{
"symbol": "hbcbtc",
"open": 0.00024813,
"high": 0.00024927,
"low": 0.00022871,
"close": 0.00023495,
"amount": 2124.32,
"vol": 0.517656218,
"count": 1715,
"bid": 0.00023427,
"bidSize": 2.3,
"ask": 0.00023665,
"askSize": 2.93
}, ...]
}
인터페이스에서 반환된 데이터를 처리할 때는 주의하세요.
이 두 인터페이스를 전략에 캡슐화하는 방법과 데이터를 처리하는 방법은 무엇입니까? 함께 살펴보도록 하겠습니다.
먼저, 제어 객체를 구성하는 생성자를 작성해 보겠습니다.
// 参数e用于传入exchange交易所对象,参数subscribeList是需要处理的交易对列表,例如["BTCUSDT", "ETHUSDT", "EOSUSDT", "LTCUSDT", "ETCUSDT", "XRPUSDT"]
function createManager(e, subscribeList) {
var self = {}
self.supportList = ["Futures_Binance", "Huobi"] // 支持的交易所的
// 对象属性
self.e = e
self.name = e.GetName()
self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
self.label = e.GetLabel()
self.quoteCurrency = ""
self.subscribeList = subscribeList // subscribeList : [strSymbol1, strSymbol2, ...]
self.tickers = [] // 接口获取的所有行情数据,定义数据格式:{bid1: 123, ask1: 123, symbol: "xxx"}}
self.subscribeTickers = [] // 需要的行情数据,定义数据格式:{bid1: 123, ask1: 123, symbol: "xxx"}}
self.accData = null // 用于记录账户资产数据
// 初始化函数
self.init = function() {
// 判断是否支持该交易所
if (!_.contains(self.supportList, self.name)) {
throw "not support"
}
}
// 判断数据精度
self.judgePrecision = function (p) {
var arr = p.toString().split(".")
if (arr.length != 2) {
if (arr.length == 1) {
return 0
}
throw "judgePrecision error, p:" + String(p)
}
return arr[1].length
}
// 更新资产
self.updateAcc = function(callBackFuncGetAcc) {
var ret = callBackFuncGetAcc(self)
if (!ret) {
return false
}
self.accData = ret
return true
}
// 更新行情数据
self.updateTicker = function(url, callBackFuncGetArr, callBackFuncGetTicker) {
var tickers = []
var subscribeTickers = []
var ret = self.httpQuery(url)
if (!ret) {
return false
}
try {
_.each(callBackFuncGetArr(ret), function(ele) {
var ticker = callBackFuncGetTicker(ele)
tickers.push(ticker)
for (var i = 0 ; i < self.subscribeList.length ; i++) {
if (self.subscribeList[i] == ele.symbol) {
subscribeTickers.push(ticker)
}
}
})
} catch(err) {
Log("错误:", err)
return false
}
self.tickers = tickers
self.subscribeTickers = subscribeTickers
return true
}
self.httpQuery = function(url) {
var ret = null
try {
var retHttpQuery = HttpQuery(url)
ret = JSON.parse(retHttpQuery)
} catch (err) {
// Log("错误:", err)
ret = null
}
return ret
}
self.returnTickersTbl = function() {
var tickersTbl = {
type : "table",
title : "tickers",
cols : ["symbol", "ask1", "bid1"],
rows : []
}
_.each(self.subscribeTickers, function(ticker) {
tickersTbl.rows.push([ticker.symbol, ticker.ask1, ticker.bid1])
})
return tickersTbl
}
// 初始化
self.init()
return self
}
FMZ API 함수 사용HttpQuery해당 함수는 거래소 인터페이스에 접근하기 위한 요청을 보냅니다. 사용HttpQuery예외 처리가 필요합니다try...catch인터페이스 반환 실패와 같은 예외를 처리합니다.
일부 학생들은 “교환 인터페이스에서 반환된 데이터 구조가 다릅니다. 어떻게 처리해야 할까요? 동일한 처리 방법을 사용하는 것은 확실히 불가능합니다.“라고 질문할 수 있습니다.
실제로 교환 인터페이스에서 반환되는 데이터 구조가 다를 뿐만 아니라 반환되는 데이터 필드의 이름도 다릅니다. 같은 의미라도 다르게 명명될 수 있다. 예를 들어, 위에 나열한 인터페이스입니다. 동일한 표현은 매수 가격을 의미하며, 이를 다음과 같이 부릅니다.bidPrice,라고 불리는bid。
여기서는 콜백 함수를 사용하여 이러한 특수 처리 부분을 분리합니다.
따라서 위의 객체를 초기화한 후 사용하면 다음과 같이 됩니다.
(다음 코드는 생성자를 생략합니다.createManager)
Binance Futures와의 다음 계약을 모니터링하세요:["BTCUSDT", "ETHUSDT", "EOSUSDT", "LTCUSDT", "ETCUSDT", "XRPUSDT"]
Huobi Spot은 다음과 같은 코인 대 코인 거래 쌍을 모니터링합니다.["btcusdt", "ethusdt", "eosusdt", "etcusdt", "ltcusdt", "xrpusdt"]예를 들어.
function main() {
var manager1 = createManager(exchanges[0], ["BTCUSDT", "ETHUSDT", "EOSUSDT", "LTCUSDT", "ETCUSDT", "XRPUSDT"])
var manager2 = createManager(exchanges[1], ["btcusdt", "ethusdt", "eosusdt", "etcusdt", "ltcusdt", "xrpusdt"])
while (true) {
// 更新行情数据
var ticker1GetSucc = manager1.updateTicker("https://fapi.binance.com/fapi/v1/ticker/bookTicker",
function(data) {return data},
function (ele) {return {bid1: ele.bidPrice, ask1: ele.askPrice, symbol: ele.symbol}})
var ticker2GetSucc = manager2.updateTicker("https://api.huobi.pro/market/tickers",
function(data) {return data.data},
function(ele) {return {bid1: ele.bid, ask1: ele.ask, symbol: ele.symbol}})
if (!ticker1GetSucc || !ticker2GetSucc) {
Sleep(1000)
continue
}
var tbl1 = {
type : "table",
title : "期货行情数据",
cols : ["期货合约", "期货买一", "期货卖一"],
rows : []
}
_.each(manager1.subscribeTickers, function(ticker) {
tbl1.rows.push([ticker.symbol, ticker.bid1, ticker.ask1])
})
var tbl2 = {
type : "table",
title : "现货行情数据",
cols : ["现货合约", "现货买一", "现货卖一"],
rows : []
}
_.each(manager2.subscribeTickers, function(ticker) {
tbl2.rows.push([ticker.symbol, ticker.bid1, ticker.ask1])
})
LogStatus(_D(), "\n`" + JSON.stringify(tbl1) + "`", "\n`" + JSON.stringify(tbl2) + "`")
Sleep(10000)
}
}
테스트를 실행합니다.
첫 번째 거래소 객체는 Binance Futures를 추가하고 두 번째 거래소 객체는 Huobi Spot을 추가합니다.


여기서는 인터페이스에서 반환된 데이터를 얻는 방법 등의 작업이 콜백 함수를 사용하여 다양한 교환에 대해 특별히 처리되는 것을 볼 수 있습니다.
var ticker1GetSucc = manager1.updateTicker("https://fapi.binance.com/fapi/v1/ticker/bookTicker",
function(data) {return data},
function (ele) {return {bid1: ele.bidPrice, ask1: ele.askPrice, symbol: ele.symbol}})
var ticker2GetSucc = manager2.updateTicker("https://api.huobi.pro/market/tickers",
function(data) {return data.data},
function(ele) {return {bid1: ele.bid, ask1: ele.ask, symbol: ele.symbol}})
시장 정보 획득을 공식화한 후, 다음으로 계정 자산 획득을 공식화할 수 있습니다. 다중 제품 전략 때문에 계정 자산 데이터도 여러 개여야 합니다. 다행히도 거래소 계정 자산 인터페이스는 일반적으로 모든 자산 데이터를 반환합니다.
생성자에서createManager자산을 가져오는 방법 추가
// 更新资产
self.updateAcc = function(callBackFuncGetAcc) {
var ret = callBackFuncGetAcc(self)
if (!ret) {
return false
}
self.accData = ret
return true
}
또한, 교환 인터페이스에서 반환되는 형식과 필드 이름이 다르기 때문에 특수 처리를 위해 콜백 함수도 필요합니다.
Huobi 현물과 Binance 선물 거래를 예로 들면, 콜백 함수는 다음과 같이 작성할 수 있습니다.
// 获取账户资产的回调函数
var callBackFuncGetHuobiAcc = function(self) {
var account = self.e.GetAccount()
var ret = []
if (!account) {
return false
}
// 构造资产的数组结构
var list = account.Info.data.list
_.each(self.subscribeList, function(symbol) {
var coinName = symbol.split("usdt")[0]
var acc = {symbol: symbol}
for (var i = 0 ; i < list.length ; i++) {
if (coinName == list[i].currency) {
if (list[i].type == "trade") {
acc.Stocks = parseFloat(list[i].balance)
} else if (list[i].type == "frozen") {
acc.FrozenStocks = parseFloat(list[i].balance)
}
} else if (list[i].currency == "usdt") {
if (list[i].type == "trade") {
acc.Balance = parseFloat(list[i].balance)
} else if (list[i].type == "frozen") {
acc.FrozenBalance = parseFloat(list[i].balance)
}
}
}
ret.push(acc)
})
return ret
}
var callBackFuncGetFutures_BinanceAcc = function(self) {
self.e.SetCurrency("BTC_USDT") // 设置为U本位合约的交易对
self.e.SetContractType("swap") // 合约都是永续合约
var account = self.e.GetAccount()
var ret = []
if (!account) {
return false
}
var balance = account.Balance
var frozenBalance = account.FrozenBalance
// 构造资产数据结构
_.each(self.subscribeList, function(symbol) {
var acc = {symbol: symbol}
acc.Balance = balance
acc.FrozenBalance = frozenBalance
ret.push(acc)
})
return ret
}
인용 부호:

자산:

시장 데이터를 얻은 후, 데이터를 처리하여 각 품종의 가격 차이를 계산하고 여러 거래 쌍의 현물-선물 가격 차이를 모니터링할 수 있습니다. 그러면 다양한 선물 및 현물 헤지 전략을 설계할 수 있습니다.
이 설계 방법에 따르면 다른 교류도 확장될 수 있습니다. 관심 있는 학생들은 시도해 볼 수 있습니다.