操作api_币安多币种自动化策略API操作指南
幣安雖然成立時間不長,但由于技術出色,API穩定高效,請求頻率限制也寬松,上幣很多,交易活躍,已經是現貨交易的首選平臺。幣安目前僅以BTC定價的幣種就超過了150種,還在不斷增加中,這使得獲取很多幣種行情和K線變的困難。本文將主要介紹如何在FMZ量化平臺上操作多幣種策略,甚至操作所有幣種都沒問題,主要面向有一定基礎的用戶。
1.獲取行情
假如要同時操作150個幣種,使用REST協議獲取行情顯然不合適,一遍輪詢下來會浪費很多時間,websocket也無法同時訂閱如此多的幣種。幣安意識到多品種策略獲取行情的問題,提供了聚合行情接口,但直接使用這個REST接口(/api/v1/ticker/24hr)需要注意,它的權重是40,意思是一次訪問相當于普通訪問的40次,即使5、6秒訪問這個接口一次,也有可能超出限制。
因此我們需要訪問這個接口的websocket版,但要注意由于數據量巨大,數據只是固定1s推送一次有行情變化的數據,對于一些幾分鐘也沒交易的冷門幣種,可能很長時間也沒推送,固定的推送時間對于高頻策略也不合適,但對于一般的多幣種策略是足夠的。具體代碼如下:
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);//updateTicker函數處理行情和交易,接下來介紹} }2.交易前的準備
幣安對交易的限制很多,最小交易價值,最小交易量,價格精度,交易量精度。這些都需要提前做好準備。
定義的全局變量:
var totalbtc = 0;//總價值,不一定是btc var baseCoin = ['BTC', 'ETH', 'BNB', 'USDT'][baseCoin_select];//基礎貨幣選擇baseCoin_select是下拉框的一個參數 var exceptList = Except_list_string.split(',');//排除的幣種,Except_list_string是策略參數 //K線周期選擇PERIOD_M1,PERIOD_M5是FMZ默認的全局變量 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]//各周期對應的秒數 var lastPeriodTime = 0;//最近周期時間,用于更新K線 var updateProfitTime = 0//最近更新收益時間,用于更新收益 var buyList = []//買單列表 var sellList = []//賣單列表 var accountInfo = {};//用于儲存交易相關的數據列表接下來主要是完善accountInfo的內容,所有與交易對相關的內容都儲存到其中。
if (!_G('accountInfo')){//如果accountInfo沒有儲存在數據庫里,重新獲取數據var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'));//獲取交易相關數據var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'));//先用rest協議獲取一次全量ticekrvar tradeSymbol = exchangeInfo.symbols.filter(function(x){return x.quoteAsset == baseCoin});//篩選需要的交易對accountInfo[baseCoin] = {free:0, frozen:0, last:1, value:0};//基礎貨幣的信息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;//過濾掉踢出的幣種}for (var j=0; j<ticker.length; j++){var symbol = info.symbol.slice(0,info.symbol.length-baseCoin.length)//幣種名稱if(ticker[j].symbol.slice(ticker[j].symbol.length-baseCoin.length) == baseCoin && ticker[j].symbol == info.symbol){//儲存的exchangeInfo和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'); } //退出時自動保存accountInfo到數據庫 function onexit(){_G('accountInfo', accountInfo); }3.更新賬戶和K線信息
更新賬戶信息函數,不用實時更新。
function updateAccount(){account = exchange.GetAccount();if (!account){Log('超時');return;//這里直接返回是為了節約時間,賬戶信息獲取不及時影響不大}for (var i=0; i<account.Info.balances.length; i++){var symbol = account.Info.balances[i].asset//都儲存在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}} } //更新當前賬戶總值,以所選的基礎貨幣為單位 function updateTotalBTC(){var btc = 0;for (var symbol in accountInfo){btc += accountInfo[symbol].valuetotalbtc = btc;} }更新K線,初次更新可以分次使用GetRecords函數,后期更新使用推送數據合成
function initRecords(){ for (var symbol in accountInfo){if(symbol == baseCoin){continue}if(!accountInfo[symbol].records){var currency = symbol + '_' + baseCoin;//切換交易對exchange.IO("currency", currency)accountInfo[symbol].records = exchange.GetRecords(period)Log('更新', currency, 'K線', accountInfo[symbol].records[accountInfo[symbol].records.length-1])Sleep(250)//每秒更新四個,不會達到限制}//最近K線時間lastPeriodTime = Math.max(accountInfo[symbol].records[accountInfo[symbol].records.length-1].Time/1000, lastPeriodTime)} } //根據推送ticker數據更新K線 function updateRecords(msgTime){//如果當前時間大于最后更新的一個周期,說明需要產生新的K線if(parseFloat(msgTime)/1000 - lastPeriodTime > periodSecond){for (var symbol in accountInfo){if(symbol != baseCoin){//如果某交易對的K線差太多,就重新獲取一次,可能是交易不活躍,ticker沒推送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線有缺失,重新獲取')}else{//推送一根新K線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 + periodSecondLog(parseFloat(msgTime)/1000, '添加K線')}else{//如果在當前K線周期內,更新當前K線for (var symbol in accountInfo){if(symbol != baseCoin){var length = accountInfo[symbol].records.lengthaccountInfo[symbol].records[length-1].Close = accountInfo[symbol].lastaccountInfo[symbol].records[length-1].Volume += accountInfo[symbol].volumeif(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.交易相關函數
//取消當前交易對訂單 function CancelPendingOrders() {var orders = _C(exchange.GetOrders);for (var j = 0; j < orders.length; j++) {exchange.CancelOrder(orders[j].Id, orders[j]);} } //取消所有交易對訂單 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('取消訂單失敗');}for (var symbol in accountInfo){accountInfo[symbol].state = 0;accountInfo[symbol].buyprice = 0;accountInfo[symbol].sellPrice = 0;} } //下買單 function toBuy(){//需要買的幣種都儲存在buyList種if (buyList.length == 0){return;}for (var i=0; i<buyList.length; i++){var symbol = buyList[i];//滑點是賣一價加最小交易單位,可能不會立即完全成交,可自行設置var buyPrice = accountInfo[symbol].ask + accountInfo[symbol].tickerSize;buyPrice = _N(buyPrice, parseInt((Math.log10(1.1/accountInfo[symbol].tickerSize))));//滿足價格精度var currency = symbol + '_' + baseCoin;exchange.IO("currency", currency);//切換交易對//如果已經下單,并和本次價格相同,不操作if (accountInfo[symbol].state && accountInfo[symbol].bid == accountInfo[symbol].buyprice){continue;}else{//已下單先撤銷if (accountInfo[symbol].state == 1){CancelPendingOrders();accountInfo[symbol].state = 0;accountInfo[symbol].buyprice = 0;}var amount = (accountInfo[symbol].free + accountInfo[symbol].frozen)*buyPrice; //現有幣種的價值var needBuyBTC = HoldAmount - amount;/HoldAmount是全局參數,需要持有的價值var buyAmount = needBuyBTC/buyPrice;buyAmount = _N(scale*buyAmount, parseInt((Math.log10(1.1/accountInfo[symbol].stepSize))));//下單量精度//滿足最小交易量和最小交易價值要求if (buyAmount > accountInfo[symbol].minQty && buyPrice*buyAmount > accountInfo[symbol].minNotional){if (accountInfo[baseCoin].free < buyPrice*buyAmount){return;}//有足夠的基礎貨幣購買var id = exchange.Buy(buyPrice, buyAmount, currency);//最終下單if(id){accountInfo[symbol].buyprice = buyPrice;accountInfo[symbol].state = 1;}}}//如果買單過多,需要休眠,幣安1s最多下10單if(buyList.length > 5){Sleep(200)}} } //下賣單原理與買單相似 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.交易的邏輯
交易很簡單,只要把買賣的幣種推送到buyList和sellList中就可以了
function checkTrade(){buyList = []sellList = []for(var symbol in accountInfo){if(symbol == baseCoin){continue}var length = accountInfo[symbol].records.length//簡單均線,只做一個簡單的演示例子,不要實盤使用,自己交易咯及放在這里就可以了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策略參數if(accountInfo[symbol].value < 0.9*HoldAmount && fast > 1.01*slow){buyList.push(symbol)}} }6.更新機器人界面狀態和ticker
這么多交易幣種如何展示也是個問題,幸好FMZ量化平臺提供了完善的表格功能,還可以按照數字大小排序,簡單直觀方便。每次websocket推送ticker時更新,由于是事件驅動,交易和各類更新的邏輯也放在這里。
function updateStatus(msgTime){//具體要展示的數據信息可以自己定義var table = {type: 'table', title: '持倉信息', cols: ['幣種', 'Bid', 'Ask','Last', '最低價','最高價','漲幅','成交量','買入價','賣出價', '凍結','可用','現值'],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() + ' 凈值為:' + _N(totalbtc,6) + (typeof(msgTime) == 'number' ? (', 最新行情時間: ' + _D(msgTime)) : '') + 'n';logString += '將要買入的幣:' + buyList.join(',') + ' n';logString += '將要賣出的幣:' + sellList.join(',') + ' n';logString += '當前可用'+ baseCoin + ':' + _N(accountInfo[baseCoin].free,6) + ',凍結:' + _N(accountInfo[baseCoin].frozen,6) + 'n';LogStatus(logString + '`' + JSON.stringify(table) + '`');//更新到機器人界面 } //每次推送ticker時更新,由于是事件驅動,交易和各類更新的邏輯也放在這里 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();//重置收益時間LogProfit(totalbtc);//更新收益}updateRecords(msgTime)//更新K線updateTotalBTC();//更新總市值updateStatus(msgTime);//更新機器人狀態checkTrade()//檢查需要下哪些訂單toBuy();//下買單toSell();//下買單 }7.執行匯總
function main() {cancellAll();initRecords()updateAccount();updateTotalBTC()Log('共交易數字貨幣:', 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.總結
本文主要展示了一個基礎的幣安多幣種交易框架,主要包含了如何儲存交易信息、如何根據ticker合成K線、如何下單、如何展示策略圖表以及基于ticker推送事件觸發交易等??梢愿暮投ㄖ频牡胤胶芏?#xff0c;整體由我個人策略摘錄而來,可能隱含Bug,僅供有一定基礎的用戶參考。
總結
以上是生活随笔為你收集整理的操作api_币安多币种自动化策略API操作指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 7A抗菌面膜是贝豪独有的吗?还有谁家有.
- 下一篇: ip动态分配痕迹会保留多久_16,虚拟机