币安虽然成立时间不长,但由于技术出色,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推送事件触发交易等。可以更改和定制的地方很多,整体由我个人策略摘录而来,可能隐含Bug,仅供有一定基础的用户参考。

操作api_币安多币种自动化策略API操作指南相关推荐

  1. 《 自动化测试最佳实践:来自全球的经典自动化测试案例解析》一一1.3 建立自动化策略...

    1.3 建立自动化策略 我们需要在不破坏现有功能的前提下发布产品的新功能特性.而且,需要尽快知道一个新的代码变动是否会引起回归测试的失败.手动回归测试在每两周的迭代后期才能给予我们反馈,以至于没有时间 ...

  2. 2020-12-26 工作常用 Linux 操作:磁盘卸载、 自动化挂盘脚本 、磁盘分区合并、ansible、git 设置相关

    [工作常用 Linux 操作:磁盘卸载. 自动化挂盘脚本 .磁盘多个分区合并.ansible .git 设置相关] 1.查看 欧拉系统 的版本号:rpm -q --provides euler0rel ...

  3. python自动化交易通达信_GitHub - sz982005/ShiPanE-Python-SDK: 实盘易(ShiPanE)Python SDK,通达信自动化交易 API。...

    ShiPanE-Python-SDK 实盘易(ShiPanE)Python SDK,通达信自动化交易 API. 实盘易是爱股网旗下的股票自动化解决方案:可管理通达信等交易终端,并为用户提供基于 HTT ...

  4. python自动化交易通达信_GitHub - wenjinglee/ShiPanE-Python-SDK: 实盘易(ShiPanE)Python SDK,通达信自动化交易 API。...

    ShiPanE-Python-SDK 实盘易(ShiPanE)Python SDK,通达信自动化交易 API. 实盘易是爱股网旗下的股票自动化解决方案:可管理通达信等交易终端,并为用户提供基于 HTT ...

  5. CCR炒币机器人:量化策略炒币机器人成新一代网红

    随着人工智能和大数据的飞跃发展,币圈量化策略成为量化投资中的"网红",深得无数应用. 无数人因为"币圈一天,股市一年"这句话冲进了币圈,说的就是在币圈有时候一天 ...

  6. Java 程序优化:字符串操作、基本运算方法等优化策略

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 字符串操作优化 字符串对象 字符串对象 ...

  7. jenkins api_接触Jenkins(Hudson)API,第1部分

    jenkins api 哪一个-哈德森还是詹金斯? 都. 几个月前,我开始使用Hudson v1.395来从事这个小项目,在出现巨大分歧之后又回到了这个项目. 我以此为契机,看我将来选择永久搬到詹金斯 ...

  8. 异步api_如何设计无服务器异步API

    异步api by Garrett Vargas 通过Garrett Vargas 如何设计无服务器异步API (How To Design a Serverless Async API) I rece ...

  9. 如何使用Postman和Newman在CI环境中自动化REST API端到端测试

    Postman is a great tool to explore REST APIs. You can build requests and try them out to get quick f ...

最新文章

  1. Python 【大风号】短视频的自动上传与发布实例演示,同时支持抖音、快手、哔哩哔哩、小红书、微视、西瓜视频、微信视频号等平台的视频自动化同步发布
  2. selenium | TypeError:object of type ‘WebElement’ has no len()
  3. Flink的滚动策略
  4. 收集100 个网络基础知识
  5. linux ini文件,Shell script - Linux下解析ini配置文件
  6. reddit_我在3天内疯狂地审查了Reddit上的50个投资组合,从中学到了什么。
  7. JAVA第三方包导入但找不到类,解决:导入第三方包报错java.lang.NoClassDefFoundError:XXX.XXX,XXXXXX...
  8. vba教程视频,VBA(基础篇+提高篇+实战篇)Excel数据处理教学视频
  9. 汽车CAN总线关闭故障的诊断与恢复
  10. c语言 最大子段和,最大子段和 C语言源码
  11. 创立仅一年GMV突破3亿!这个新锐品牌如何在快手实现爆发?
  12. 落花已去,相思成冢。十月的杜鹃雨,下得纷纷扬扬。我走在花瓣雨下,回忆我们曾经的甜蜜温馨,一回首,一抬头,仿佛你就在灯火阑珊处。那些掉落在地上的杜鹃,成了相思的墓,也许是为了祭奠我们曾经的美好。 杜鹃
  13. 淘宝电商数据分析-Tableau
  14. springboot整合tk-mybatis框架搭建
  15. USB Type-A/Type-B/Type-C/mini-AB/micro-AB接口简介
  16. SpringBoot_liquibase使用
  17. 关于0x016f2818这个幻数
  18. 车载5G+4G多网聚合通信解决方案
  19. 入门了解学hybris和sap的需要内容(自己整理)
  20. 寻找《幸福派》和《不文集》

热门文章

  1. Scikit-learn数据预处理分类变量编码之多标签二值化
  2. 百度语音识别合成案例
  3. 《C#编程风格》还记得多少
  4. 深度学习框架的比较(MXNet, Caffe, TensorFlow, Torch, Theano)
  5. 透露一下Java软件工程师面试常见问题集锦之一
  6. maven 的安装配置 和编译java程序
  7. SpringMVC Hello World 实例
  8. 数组---进制转换(查表法)
  9. 关于海量数据的SQL查询优化.........
  10. 爱情,是我一生中最虔诚的信仰