Use a interface de mercado agregada de trocas de moeda digital para construir estratégias de vários produtos
Plataforma de negociação quantitativa FMZSeção de observação de estratégiaÉ comum ver algumas estratégias multiprodutos que monitoram simultaneamente as condições de mercado de dezenas ou até mesmo de todo o mercado de uma bolsa. Como isso é feito? E como ele deve ser projetado? Este artigo explorará como usar a interface de mercado agregado de câmbio para criar uma estratégia multiproduto.
Tome Binance e Huobi como exemplos e verifique seus documentos de API. Descobrimos que ambos têm interfaces de informações de mercado agregadas:
Interface de mercado
-
Futuros da Binance:
https://fapi.binance.com/fapi/v1/ticker/bookTicker
Dados de retorno da interface[ { "symbol": "BTCUSDT", // 交易对 "bidPrice": "4.00000000", //最优买单价 "bidQty": "431.00000000", //挂单量 "askPrice": "4.00000200", //最优卖单价 "askQty": "9.00000000", //挂单量 "time": 1589437530011 // 撮合引擎时间 } ... ] -
Moeda atual da Huobi:
https://api.huobi.pro/market/tickers
Dados de retorno da interface[ { "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 }, ... ]No entanto, esse não é o caso. A estrutura realmente retornada pela interface 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 }, ...] }Tenha cuidado ao processar dados retornados pela interface.
Construindo uma estrutura de programa de políticas
Como encapsular essas duas interfaces na estratégia e como processar os dados?
Vamos dar uma olhada nisso juntos.
Vamos primeiro escrever um construtor para construir o objeto de controle.
// 参数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
}
Usando funções da API FMZHttpQueryA função envia uma solicitação para acessar a interface de troca. usarHttpQueryO tratamento de exceções é necessáriotry...catchLide com exceções, como falhas de retorno de interface.
Alguns alunos podem perguntar: "As estruturas de dados retornadas pelas interfaces de troca são diferentes. Como devemos lidar com elas? Definitivamente não é possível usar o mesmo método de processamento."
De fato, não apenas a estrutura de dados retornada pela interface de troca é diferente, mas até mesmo a nomenclatura dos campos de dados retornados é diferente. O mesmo significado pode ter nomes diferentes. Por exemplo, as interfaces que listamos acima. A mesma expressão significa o preço de compra, que é chamado:bidPrice, que é chamadobid。
Usamos funções de retorno de chamada aqui para separar essas partes especiais de processamento.
Então, depois que o objeto acima é inicializado, ele fica assim quando usado:
(O código a seguir omite o construtorcreateManager)
Monitore esses contratos com a Binance Futures:["BTCUSDT", "ETHUSDT", "EOSUSDT", "LTCUSDT", "ETCUSDT", "XRPUSDT"]
O Huobi Spot monitora estes pares de negociação moeda a moeda:["btcusdt", "ethusdt", "eosusdt", "etcusdt", "ltcusdt", "xrpusdt"]Por exemplo.
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)
}
}
Execute os testes:
O primeiro objeto de troca adiciona futuros da Binance e o segundo objeto de troca adiciona o Huobi spot

Pode-se observar que aqui, as operações como obter os dados retornados pela interface são processadas especificamente para diferentes trocas usando funções de retorno de chamada.
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}})
Após formular a aquisição de informações de mercado, podemos formular a aquisição de ativos de conta. Devido à estratégia de multiprodutos, os dados de ativos de conta também devem ser múltiplos. Felizmente, a interface de ativos da conta de câmbio geralmente retorna todos os dados de ativos.
No construtorcreateManagerAdicione um método para obter ativos
// 更新资产
self.updateAcc = function(callBackFuncGetAcc) {
var ret = callBackFuncGetAcc(self)
if (!ret) {
return false
}
self.accData = ret
return true
}
Além disso, devido aos diferentes formatos e nomes de campos retornados pela interface de troca, funções de retorno de chamada também são necessárias para processamento especial.
Tomando como exemplos o Huobi spot e o Binance futures, a função de retorno de chamada pode ser escrita assim:
// 获取账户资产的回调函数
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
}
Executar uma estrutura de estratégia com a função de obter informações de mercado e ativos
Pode-se observar que, após obter os dados de mercado, os dados podem ser processados para calcular a diferença de preço de cada variedade e monitorar a diferença de preço spot-futuros de vários pares de negociação.
Então, uma estratégia de hedge multivariada de futuros e spot pode ser projetada.
De acordo com esse método de design, outras trocas também podem ser expandidas. Estudantes interessados podem experimentar.
我的意思是期货与现货的差价,不是期货或现货买一卖一的差价;碰到的问题是,不能同时获取同一交易对期货买一价和现货的卖一价。比如:如何获取BTCUSDT现货买一价和BTCUSDT期货卖一价的差价。我写了下,下面代码不知道问题出在哪里?
function GetBAspot(syboml,tickerspot,BA){
for (var i = 0; i < tickerspot.length; i++) {
if(tickerspot[i].syboml!==syboml){
continue
}else
if(tickerspot[i].syboml===syboml){
var bidspot=tickerspot[i].bid1
var askspot=tickerspot[i].ask1
}
}
if(BA==="bid")return bidspot
if(BA==="ask")return askspot
}
function main(){
_.each(manager1.subscribeTickers, function(ticker) {
var symb=ticker.symbol
var symb1=manager1.symFuturesToSpot(symb)
tbl1.rows.push([ticker.symbol, ticker.bid1, ticker.ask1,manager1.Getfundingrate(symb),manager1.Getrealrate(symb,50),GetBAspot(symb1,SpotTickers,"ask")])
})
}
- 1





