Build a multi-variety strategy with an aggregated market interface using a digital currency exchange

Author: The Little Dream, Created: 2021-03-17 18:38:47, Updated: 2023-09-26 20:58:11

img

Build a multi-variety strategy with an aggregated market interface using a digital currency exchange

FMZ is a quantitative trading platformStrategic outline of the sectorIt is common to see a variety of strategies, while detecting dozens or even one market-wide market. How do you do this? and how do you need to design it?

Listing the two exchanges, Binance and Token, a look at the API documentation of the exchange reveals that both have an aggregated market interface:

The business interface

  • The Binance contract:https://fapi.binance.com/fapi/v1/ticker/bookTickerThe interface returns data

    [
        {
            "symbol": "BTCUSDT", // 交易对
            "bidPrice": "4.00000000", //最优买单价
            "bidQty": "431.00000000", //挂单量
            "askPrice": "4.00000200", //最优卖单价
            "askQty": "9.00000000", //挂单量
            "time": 1589437530011   // 撮合引擎时间
        }
        ...
    ]
    
  • The coin is in cash:https://api.huobi.pro/market/tickersThe interface returns data

    [  
        {  
            "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
        }, 
        ...
    ]
    

    However, this is not the case, the actual structure returned by the token interface is:

    {
        "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
        }, ...]
    }
    

    Care must be taken when processing the data returned by the interface.

Building a strategic process framework

How do we encapsulate these two interfaces in the strategy and how do we process the data? I'm going to take it slowly.

First, write a constructor function to construct control objects.

// 参数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 
}

API functions with FMZHttpQueryThe function sends requests, accesses the exchange interface.HttpQueryWhen it is necessary to use exception handlingtry...catchDealing with anomalies such as interface return failure. Some of the students who see this may be asking: How do you handle the different data structures that are returned by the exchange's interface? Indeed, not only is the data structure of the exchange interface returned different, but even the names of the data fields returned are different. The same meaning may be different names. For example, the interface we listed above.bidPriceIn the token, it is calledbid

We use the callback function here to solve these parts of the special treatments separately. When the above object is initialized, it will look like this when used specifically: (The following code omits the constructor function)createManagerWhat is it? These contracts are monitored by the Binance Futures:["BTCUSDT", "ETHUSDT", "EOSUSDT", "LTCUSDT", "ETCUSDT", "XRPUSDT"]Coin Cash monitors these coin transactions for:["btcusdt", "ethusdt", "eosusdt", "etcusdt", "ltcusdt", "xrpusdt"]For example:

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)
    }
}

Running the test: The first exchange object adds a bitcoin futures, the second exchange object adds a token cashimg

img

As you can see, here, operations such as how to retrieve the data returned by the interface are specialized for different exchanges using the callback function.

    	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}})

The market acquisition is defined, and then the account asset acquisition can be defined, because the multi-variety strategy, account asset data must also be multiple.

In constructing functionscreateManagerAdding methods to access assets

    // 更新资产
    self.updateAcc = function(callBackFuncGetAcc) {
        var ret = callBackFuncGetAcc(self)
        if (!ret) {
        	return false 
        }
        self.accData = ret 
        return true 
    }

Also, the format of the exchange interface returns, the field names vary, which also requires the use of callback function specification processing.

In the example of token cash and currency futures, the callback function can be written like this:

    // 获取账户资产的回调函数
    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 
    }

Operate a strategic framework with access to markets, asset functions

The case:img

The assets:img

You can see that once you have access to the market data, you can process the data to calculate the differences between the different varieties and monitor the futures differences between multiple trading pairs. This can be used to design a multi-variety futures hedging strategy.

This is the way the exchange is designed, and it is possible to expand it to other exchanges, so that students who are interested can try it out.


Related

More

I love Jimmy.What does callBackFuncGetAcc have to do with callBackFuncGetAcc? I feel like it's a set of functions in a function, where did I learn that?

I love Jimmy.How do you extract the bid1 and ask1 prices of a futures pair at the same time?

zltimThat's great.

The Little DreamBaidu callback function is also called Baidu callback function.

The Little DreamThe two circles are joined and crossed.

The Little DreamI'm telling you the difference between the spot and the futures, not all the screenshots in the article, the spot difference. The data is there, what exactly do you do, do you subtract or do you have it?

I love Jimmy.If you want to get the difference between BTCUSDT spot price and BTCUSDT futures price: first go through the futures market matrix manager 1.subscribe Tickers, find out the BTCUSDT sell price manager 1.subscribe Tickers[i].ask1, the question is how to extract the bid price of BTCUSDT spot when the futures market matrix is done, is it possible to spy through the spot market matrix manager 1.subscribe Tickers?

I love Jimmy.I mean the difference between futures and spot, not the difference between futures or spot buy and sell; the problem is that it is not possible to get the same transaction for both the buy and the sell price of the futures. For example: how to get the difference between the buy and the sell price of the BTCUSDT futures. The function GetBAspot ((syboml, tickerspot, BA) { is a function that is used to generate a function. for (var i = 0; i < tickerspot.length; i++) { If (tickerspot [i].syboml!==syboml) { is the number of tickers in the tickerspot Continue Other If (tickerspot [i].syboml ===syboml) { If (tickerspot [i].syboml ===syboml) { If (tickerspot [i].syboml) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If (tickerspot) { If) { If (tickerspot) { If (tickerspot) { If (symboml) } If (symboml) { If (symboml) { If (symbol) { If (symbol) { If (symbol) { If (symbol var bidspot=tickerspot[i].bid1 This is a list of bids var askspot=tickerspot[i].ask1 is the number of tickerspot I'm not sure. I'm not sure. If (BA ==="bid") return bidspot is the bid spot. If == BA ==="ask") return the askspot I'm not sure. function main (()) { _.each ((manager1.subscribe Tickers, function ((ticker)) { This is a list of all the different ways Var symbols is credited in the database. var symb1=manager1.symFuturesToSpot (symb) is the name of the function. This is a list of all the different ways Tbl1.rows.push is credited in the database. It's not. I'm not sure.

The Little DreamThis specific writing works, buy one sell one, the difference is not less than the other.

I love Jimmy.Why isn't the source code of the image of the futures price difference in the article published?

The Little DreamI don't know what you mean.

I love Jimmy.I see that, but there's no unified specification for the same transaction pairs.

The Little DreamYou can see the data in the code, buy one, sell one.