Title: Binance Multi-Currencies Automation Trading Strategy API Operation Guide
Post by: henrythebest on January 22, 2019, 02:45:31 AM
article originally from FMZ.COM if the code part is not clear to see, please come to here: https://www.fmz.com/bbs-topic/2720 (https://www.fmz.com/bbs-topic/2720) Although the Binance.com has not been established for a long time, due to its excellent technology, the API is stable and efficient, the request frequency limit is also relaxed, and there are many coins and active transactions, which is already the preferred platform for spot trading. At present, there are more than 150 kinds of currencies that are priced by BTC, and they are still increasing, which makes it difficult to obtain many currency prices and K-line. This article will mainly introduce how to operate multi-currency strategies on the FMZ quantitative trading platform, and even for operating all currencies without problems, mainly for users with a certain knowledge foundation. 1. Get the market quoteIf you want to operate 150 currencies at the same time, it is obviously inappropriate to use REST protocol to get the market quote. It will waste a lot of time after polling, and websocket can't subscribe to so many currencies at the same time. Binance.com realizes the problem of multi-variety strategy to obtain the market quote, and provides an aggregated market quote interface. When directly use this REST interface (/api/v1/ticker/24hr),must note that its weight is 40, meaning that one access is equivalent to ordinary of 40 times of access, even if you access this interface once in 5 or 6 seconds, it is also possible to exceed the limit. Therefore, we need to access the websocket version of this interface, but be aware that due to the huge amount of data, the data is only fixed for 1s to push the data with market changes. For some unpopular currencies that have not been traded for a few minutes, it may not be pushed for a long time. Fixed push times are not suitable for high frequency strategies, but are sufficient for general multi-currency strategies. The specific code is as follows: function main() { var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr"); while (true){ var data = client.read(); var msg = JSON.parse(data); updateTicker(msg);//The updateTicker function handles market quotes and transactions, which will be introduced below. } } 2. Preparation before the tradingBinance has many restrictions on trading, minimum transaction value, minimum transaction volume, price accuracy, and transaction volume accuracy. These need to be prepared in advance. Defined global variables: var totalbtc = 0;//Total value, not necessarily btc var baseCoin = ['BTC', 'ETH', 'BNB', 'USDT'][baseCoin_select];//Base currency selection baseCoin_select is a parameter of the drop-down box var exceptList = Except_list_string.split(',');//Excluded currency, Except_list_string is the strategy parameter //K line cycle selects PERIOD_M1, PERIOD_M5 is FMZ default global variable var period = [PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H1, PERIOD_D1][period_select] var periodSecond = [60, 300, 900, 1800, 3600, 3600*24][period_select]//The number of seconds corresponding to each cycle var lastPeriodTime = 0;//The most recent cycle time, used to update the K line var updateProfitTime = 0//Recently updated earnings time to update revenue var buyList = []//buying order list var sellList = []//selling order list var accountInfo = {};//Used to store transaction related data lists The next step is to improve the content of accountInfo, and all the content related to the trading pair is stored in it.
if (!_G('accountInfo')){//If accountInfo is not stored in the database, reacquire the data. var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'));//Get transaction related data var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'));//First use the rest protocol to get a full amount of ticker var tradeSymbol = exchangeInfo.symbols.filter(function(x){return x.quoteAsset == baseCoin});//Filter the required trading pairs accountInfo[baseCoin] = {free:0, frozen:0, last:1, value:0};//Base currency information for (var i=0; i<tradeSymbol.length; i++){ var info = tradeSymbol[i]; if(exceptList.indexOf(info.symbol.slice(0,info.symbol.length-baseCoin.length)) >= 0){ continue;//Filter out the currencies that was kicked out } for (var j=0; j<ticker.length; j++){ var symbol = info.symbol.slice(0,info.symbol.length-baseCoin.length)//Currency name if(ticker[j].symbol.slice(ticker[j].symbol.length-baseCoin.length) == baseCoin && ticker[j].symbol == info.symbol){ //The stored contents of the exchangeInfo and ticker accountInfo[symbol] = { last:parseFloat(ticker[j].lastPrice), free:0, frozen:0, minQty:parseFloat(info.filters[2].minQty), minNotional:parseFloat(info.filters[3].minNotional) tickerSize:parseFloat(info.filters[0].tickSize), stepSize:parseFloat(info.filters[2].stepSize), ask:parseFloat(ticker[j].askPrice), bid:parseFloat(ticker[j].bidPrice), volume:parseFloat(ticker[j].quoteVolume), lowPrice:parseFloat(ticker[j].lowPrice), highPrice:parseFloat(ticker[j].highPrice), priceChangePercent:parseFloat(ticker[j].priceChangePercent), sellPrice:0, buyPrice:0, state:0, value:0, records:null } break; } } } }else{ accountInfo = _G('accountInfo'); } //Automatically save accountInfo to the database when exiting function onexit(){ _G('accountInfo', accountInfo); } 3. Update account and K line informationUpdate the account information function without real-time updates. function updateAccount(){ account = exchange.GetAccount(); if (!account){ Log('time out'); return;//Returning directly here is to save time, and the account information acquisition is not affected in time. } for (var i=0; i<account.Info.balances.length; i++){ var symbol = account.Info.balances[i].asset //Are stored in accountInfo if (symbol in accountInfo){ accountInfo[symbol].free = parseFloat(account.Info.balances[i].free); accountInfo[symbol].frozen = parseFloat(account.Info.balances[i].locked); accountInfo[symbol].value = (accountInfo[symbol].free + accountInfo[symbol].frozen)*accountInfo[symbol].last } } } //Update the current account total value in the selected base currency function updateTotalBTC(){ var btc = 0; for (var symbol in accountInfo){ btc += accountInfo[symbol].value totalbtc = btc; } }
Update the K line, the initial update can use the GetRecords function in stages, and the later update uses push data synthesis.
function initRecords(){ for (var symbol in accountInfo){ if(symbol == baseCoin){continue} if(!accountInfo[symbol].records){ var currency = symbol + '_' + baseCoin; //Switch trading pair exchange.IO("currency", currency) accountInfo[symbol].records = exchange.GetRecords(period) Log('Update', currency, 'K line', accountInfo[symbol].records[accountInfo[symbol].records.length-1]) Sleep(250)//Update four per second, no limit will be reached } //Recent K-line time lastPeriodTime = Math.max(accountInfo[symbol].records[accountInfo[symbol].records.length-1].Time/1000, lastPeriodTime) } } //Update K line based on push ticker data function updateRecords(msgTime){ //If the current time is greater than the last updated cycle, it indicates that a new K line needs to be generated. if(parseFloat(msgTime)/1000 - lastPeriodTime > periodSecond){ for (var symbol in accountInfo){ if(symbol != baseCoin){ //If the K line missing of a trading pair is too much, it will be re-acquired once, it may be that the transaction is not active, ticker did not push if(parseFloat(msgTime)/1000 - accountInfo[symbol].records[accountInfo[symbol].records.length-1].Time/1000 > 1.5*periodSecond){ var currency = symbol + '_' + baseCoin; exchange.IO("currency", currency) var records = exchange.GetRecords(period) if(records){ accountInfo[symbol].records = exchange.GetRecords(period) } Log(symbol, 'K line is missing, regain') }else{ //Push a new K line accountInfo[symbol].records.push({"Time":parseInt(lastPeriodTime + periodSecond)*1000, "Open":accountInfo[symbol].last, "High":accountInfo[symbol].last, "Low":accountInfo[symbol].last, "Close":accountInfo[symbol].last, "Volume":0}) } } } lastPeriodTime = lastPeriodTime + periodSecond Log(parseFloat(msgTime)/1000, 'Adding K line') }else{ //If it is in the current K line cycle, update the current K line for (var symbol in accountInfo){ if(symbol != baseCoin){ var length = accountInfo[symbol].records.length accountInfo[symbol].records[length-1].Close = accountInfo[symbol].last accountInfo[symbol].records[length-1].Volume += accountInfo[symbol].volume if(accountInfo[symbol].last > accountInfo[symbol].records[length-1].High){ accountInfo[symbol].records[length-1].High = accountInfo[symbol].last } else if(accountInfo[symbol].last < accountInfo[symbol].records[length-1].Low){ accountInfo[symbol].records[length-1].Low = accountInfo[symbol].last } } } } } 4.Trading related function//Cancel current trading pair orders function CancelPendingOrders() { var orders = _C(exchange.GetOrders); for (var j = 0; j < orders.length; j++) { exchange.CancelOrder(orders[j].Id, orders[j]); } } //Cancel all trading pair orders function cancellAll(){ try{ var openOrders = exchange.IO('api', 'GET', '/api/v3/openOrders'); for (var i=0; i<openOrders.length; i++){ var order = openOrders[i]; var currency = order.symbol.slice(0,order.symbol.length-baseCoin.length) + '_' + baseCoin; exchange.IO("currency", currency); exchange.CancelOrder(order.orderId); } } catch(err){ Log('Cancel order failed'); } for (var symbol in accountInfo){ accountInfo[symbol].state = 0; accountInfo[symbol].buyprice = 0; accountInfo[symbol].sellPrice = 0; } } //Placing the buying long order function toBuy(){ //The currencies you need to buy are stored in the buyList if (buyList.length == 0){ return; } for (var i=0; i<buyList.length; i++){ var symbol = buyList[i]; //Slippage is the "selling price 1" plus minimum trading unit, may not be completely executed immediately, you can modify it yourself var buyPrice = accountInfo[symbol].ask + accountInfo[symbol].tickerSize; buyPrice = _N(buyPrice, parseInt((Math.log10(1.1/accountInfo[symbol].tickerSize))));//Meet price accuracy var currency = symbol + '_' + baseCoin; exchange.IO("currency", currency);//Switch trading pair //If you have placed an order and the price is same as this one, do not operate. if (accountInfo[symbol].state && accountInfo[symbol].bid == accountInfo[symbol].buyprice){ continue; }else{ //Order placed first will be cancelled first if (accountInfo[symbol].state == 1){ CancelPendingOrders(); accountInfo[symbol].state = 0; accountInfo[symbol].buyprice = 0; } var amount = (accountInfo[symbol].free + accountInfo[symbol].frozen)*buyPrice; //Value of existing currency var needBuyBTC = HoldAmount - amount;//HoldAmount is a global parameter, which require value of the hold var buyAmount = needBuyBTC/buyPrice; buyAmount = _N(scale*buyAmount, parseInt((Math.log10(1.1/accountInfo[symbol].stepSize))));//Order quantity accuracy //Meet minimum transaction volume and minimum transaction value requirements if (buyAmount > accountInfo[symbol].minQty && buyPrice*buyAmount > accountInfo[symbol].minNotional){ if (accountInfo[baseCoin].free < buyPrice*buyAmount){return;}//Have enough base currency to buy var id = exchange.Buy(buyPrice, buyAmount, currency);//Final order if(id){ accountInfo[symbol].buyprice = buyPrice; accountInfo[symbol].state = 1; } } } //If the buying orders are too much, it need a pause, Binance allows 10 orders every 1s maximum if(buyList.length > 5){ Sleep(200) } } } //Placing the selling orders principles are similar to the buying orders function toSell(){ if (sellList.length == 0){ return; } for (var i=0; i<sellList.length; i++){ var currency = symbol + '_' + baseCoin; exchange.IO("currency", currency); var sellPrice = accountInfo[symbol].bid - accountInfo[symbol].tickerSize; sellPrice = _N(sellPrice, parseInt((Math.log10(1.1/accountInfo[symbol].tickerSize)))); if (accountInfo[symbol].state == 1 && accountInfo[symbol].bid != accountInfo[symbol].buyprice){ CancelPendingOrders(); accountInfo[symbol].state = 0; accountInfo[symbol].sellPrice = 0; } var sellAmount = accountInfo[symbol].free; sellAmount = _N(Math.min(scale*sellAmount,accountInfo[symbol].free), parseInt((Math.log10(1.1/accountInfo[symbol].stepSize)))); if (sellAmount > accountInfo[symbol].minQty && sellPrice*sellAmount > accountInfo[symbol].minNotional){ var id = exchange.Sell(sellPrice, sellAmount, currency); if(id){ accountInfo[symbol].state = 1; accountInfo[symbol].sellPrice = sellPrice; } } if(sellList.length > 5){ Sleep(200) } } } 5.Trading logicThe trading is very simple, just push the currency of the buying and selling to the buyList and sellList. function checkTrade(){ buyList = [] sellList = [] for(var symbol in accountInfo){ if(symbol == baseCoin){ continue } var length = accountInfo[symbol].records.length //Simple moving average, this is a simple demonstration example, don't use it at the real market. var fast = TA.MA(accountInfo[symbol].records, FastPeriod)[length-1] var slow = TA.MA(accountInfo[symbol].records, SlowPeriod)[length-1] if(accountInfo[symbol].value > 2*accountInfo[symbol].minNotional && fast < 0.99*slow){ sellList.push(symbol) } //HoldAmount strategy parameter if(accountInfo[symbol].value < 0.9*HoldAmount && fast > 1.01*slow){ buyList.push(symbol) } } } 6.Update robot interface status and tickerHow to display so many trading currencies is also a problem. Fortunately, the FMZ Quantitative Platform provides a complete table functions. It can also be sorted by number, which is simple and convenient. Every time the websocket pushes the ticker, it is updated because of the event-driven, transactional and various update logic. function updateStatus(msgTime){ //The specific data to be displayed can be defined by itself. var table = {type: 'table', title: 'Position information', cols: ['Currency', 'Bid', 'Ask','Last', 'Lowest price','Highest price','Price Amplitude','Volume','buying price','Selling price', 'frozen','Available','Present value'], rows: []}; for (var symbol in accountInfo){ if(symbol == baseCoin){ var infoList = [symbol,0, 0, 1,0, 0, 0,0, 0, 0, 0, _N(accountInfo[symbol].frozen,4),_N(accountInfo[symbol].free,4), _N(accountInfo[symbol].value,5)]; }else{ var infoList = [symbol,accountInfo[symbol].bid, accountInfo[symbol].ask, accountInfo[symbol].last, accountInfo[symbol].lowPrice, accountInfo[symbol].highPrice, accountInfo[symbol].priceChangePercent, _N(accountInfo[symbol].volume,2), accountInfo[symbol].buyPrice, accountInfo[symbol].sellPrice, _N(accountInfo[symbol].frozen,4),_N(accountInfo[symbol].free,4), _N(accountInfo[symbol].value,5)]; } table.rows.push(infoList); } var logString = _D() + ' Net value:' + _N(totalbtc,6) + (typeof(msgTime) == 'number' ? (', Latest market time: ' + _D(msgTime)) : '') + '\n'; logString += 'The currency to be bought:' + buyList.join(',') + ' \n'; logString += 'The currency to be sold:' + sellList.join(',') + ' \n'; logString += 'Currently available'+ baseCoin + ':' + _N(accountInfo[baseCoin].free,6) + ',frozen:' + _N(accountInfo[baseCoin].frozen,6) + '\n'; LogStatus(logString + '`' + JSON.stringify(table) + '`');//Update to robot interface } //Every time pushes the ticker, it is updated because of the event-driven, transactional and various update logic. function updateTicker(msg){ var ticker = msg; var msgTime = 0; for (var i=0; i<ticker.length; i++){ msgTime = Math.max(msgTime, ticker[i].E); var symbol = ticker[i].s.slice(0,ticker[i].s.length-baseCoin.length) if (ticker[i].s.slice(ticker[i].s.length-baseCoin.length) == baseCoin && parseFloat(ticker[i].c) && symbol in accountInfo){ accountInfo[symbol].last = parseFloat(ticker[i].c); accountInfo[symbol].volume = _N(parseFloat(ticker[i].q),1); accountInfo[symbol].lowPrice = parseFloat(ticker[i].l); accountInfo[symbol].highPrice = parseFloat(ticker[i].h); accountInfo[symbol].ask = parseFloat(ticker[i].a); accountInfo[symbol].bid = parseFloat(ticker[i].b); accountInfo[symbol].priceChangePercent = parseFloat(ticker[i].P); accountInfo[symbol].value = (accountInfo[symbol].free + accountInfo[symbol].frozen)*accountInfo[symbol].last } } if (Date.now() - updateProfitTime > LogProfitTime*1000){ updateAccount(); updateProfitTime = Date.now();//Reset revenue time LogProfit(totalbtc);//Update revenue } updateRecords(msgTime)//Update K line updateTotalBTC();//Update total market value updateStatus(msgTime);//Update robot status checkTrade()//Check which orders need to be placed toBuy();//placing buying order toSell();//placing selling order } 7.Executive summaryfunction main() { cancellAll(); initRecords() updateAccount(); updateTotalBTC() Log('Total transaction digital currency:', Object.keys(accountInfo).length-1); updateStatus(); var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr"); while (true){ var data = client.read(); var msg = JSON.parse(data); updateTicker(msg); } } 8.SummaryThis article mainly shows a basic Binance multi-currency trading framework, which mainly includes how to store trading information, how to synthesize K-line according to ticker, how to place an order, how to display strategy charts, and trigger trading based on ticker push events. There are a lot of places that can be changed and customized. The whole is extracted from my personal strategy. It may imply a bug and is only for users who have a certain knowledge foundation. article originally from FMZ.COM if the code part is not clear to see, please come to here: https://www.fmz.com/bbs-topic/2720 (https://www.fmz.com/bbs-topic/2720)
Title: Re: Binance Multi-Currencies Automation Trading Strategy API Operation Guide
Post by: iamyourfather777 on January 22, 2019, 07:41:44 AM
seems a good one, i just start learning coding these strategies. thanks for sharing. is your platform support Python? i heard a lot of advise that a Quant should start with this programming language.
Title: Re: Binance Multi-Currencies Automation Trading Strategy API Operation Guide
Post by: henrythebest on January 23, 2019, 02:11:43 AM
seems a good one, i just start learning coding these strategies. thanks for sharing. is your platform support Python? i heard a lot of advise that a Quant should start with this programming language.
good for you!, in the future, i think not only the trading tools part, knowing some programming skills is like using calculator nowadays. it just a basic skill set. you don't have to develop the whole system from ground, just for analysis part, such as python on some lower level of data analysis will be a basic tool for the future trading analysis.
Title: Re: Binance Multi-Currencies Automation Trading Strategy API Operation Guide
Post by: iamyourfather777 on January 23, 2019, 06:55:20 AM
thx man! cheers! hoping one day can use your platform to write my own trading bots.
|