前几天写了一篇关于tradingView和webSocket的文章传送门,因为代码本身还在整合中,所以比较混乱,而且也没有demo可以运行。
这两天在GitHub上面看到了一个vue写的demo,仔细对比,原来就是我得到的最初版本的tradingView-webSocket代码,很开心,以为可以直接嵌入项目中,省了一番功夫。
然而现实是骨感的,这个demo并没有写的太完善,该缺的功能一样还是缺着的……传送门。
无奈,还是需要对这份代码进行加工。
1、原demo分析
核心代码有4个文件:
- index.vue
- datafees.js
- dataUpdater.js
- scoket.js
index是tradingView的实例化和用户行为的响应,属于业务逻辑实现。
datafees定义了实例化tradingview的对象,提供图表调用的方法,属于数据交互的接口。
dataUpdater是一个数据的触发器,当数据发生变化,通过dataUpdater来通知tradingview进行图表渲染,是业务逻辑和tradingview实例之间的通信人。
scoket定义了webSocket的连接方式和数据收发的方法,是前后端数据交互的实现。
功能缺少,主要指业务逻辑实现上的功能缺少。
缺少的功能主要是历史记录获取、展示的功能。
初始化即获取1440条数据渲染到界面上,增量数据(实时交易记录)直接覆盖第1440条记录。
这样做的结果是,用户往左边拖拽的时候,不能展示更多的数据,同时,最新的增量数据的展示也是错位的。
另外,没有对webSocket的close事件进行监听,当连接关闭之后,增量更新即停止,这样的用户体验是不够的。
而且,初始化即获取1440条记录,将会导致初始化的时候需要请求的数据过多,查询时间和数据接收的时间都受影响,同样影响用户体验。
2、index.vue详解
created(){
this.socket.doOpen()
this.socket.on('open', () => {
this.socket.send({ cmd: 'req', args: [`candle.M5.btcusdt}`, 1440, parseInt(Date.now() / 1000)] })
})
this.socket.on('message', this.onMessage)
}
在创建之后,连接socket,发送历史数据请求,从当前时间往回获取1440条数据,获取的是5分钟时间颗粒的btcusdt商品。然后监听从webSocket返回的数据。
实例化tradingview:
init(){}
通过webSocket发送数据:
sendMessage(){}
订阅实时交易数据:
subscribe(){}
取消订阅实时交易数据:
unSubscribe(){}
以上方法,都是直接调用接口,下面是业务逻辑:
onMessage(data) {
// console.log(data)
if (data.data && data.data.length) {
const list = []
const ticker = `${this.symbol}-${this.interval}`
data.data.forEach(function (element) {
list.push({
time: this.interval !== 'D' || this.interval !== '1D' ? element.id * 1000 : element.id,
open: element.open,
high: element.high,
low: element.low,
close: element.close,
volume: element.quote_vol
})
}, this)
this.cacheData[ticker] = list
this.lastTime = list[list.length - 1].time
this.subscribe()
}
if (data.type && data.type.indexOf(this.symbol.toLowerCase()) !== -1) {
// console.log(' >> sub:', data.type)
this.datafeeds.barsUpdater.updateData()
const ticker = `${this.symbol}-${this.interval}`
const barsData = {
time: data.id * 1000,
open: data.open,
high: data.high,
low: data.low,
close: data.close,
volume: data.quote_vol
}
if (barsData.time >= this.lastTime && this.cacheData[ticker] && this.cacheData[ticker].length) {
this.cacheData[ticker][this.cacheData[ticker].length - 1] = barsData
}
}
}
从socket接到数据,判断数据类型,如果是数组,代表的是历史数据,存入cacheData,记录时间,开始订阅增量数据。
如果不是数组,而且有type属性,代表的是增量数据,通知图表插件渲染数据,替换cacheData的最后一个元素。
下面的方法,是图表插件查询数据的时候使用:
getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) {
// console.log(' >> :', rangeStartDate, rangeEndDate)
if (this.interval !== resolution) {
this.unSubscribe(this.interval)
this.interval = resolution
if (resolution < 60) {
this.sendMessage({ cmd: 'req', args: [`candle.M${this.interval}.${this.symbol.toLowerCase()}`, 1440, parseInt(Date.now() / 1000)] })
} else if (resolution >= 60) {
this.sendMessage({ cmd: 'req', args: [`candle.H${this.interval / 60}.${this.symbol.toLowerCase()}`, 1440, parseInt(Date.now() / 1000)] })
} else {
this.sendMessage({ cmd: 'req', args: [`candle.D1.${this.symbol.toLowerCase()}`, 800, parseInt(Date.now() / 1000)] })
}
}
const ticker = `${this.symbol}-${this.interval}`
if (this.cacheData[ticker] && this.cacheData[ticker].length) {
this.isLoading = false
const newBars = []
this.cacheData[ticker].forEach(item => {
if (item.time >= rangeStartDate * 1000 && item.time <= rangeEndDate * 1000) {
newBars.push(item)
}
})
onLoadedCallback(newBars)
} else {
const self = this
this.getBarTimer = setTimeout(function () {
self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
}, 10)
}
}
如果图表插件查询的时候,resolution不同,则表示用户切换了时间颗粒,需要取消订阅,修改interval,重新获取历史记录。
查询缓存cacheData是否为空,如果为空,表示数据还没有下发,10ms后再查询一次。
如果cacheData有数据,取到当前数据,执行回调onLoadedCallback。
3、结尾
如上,代码已经分析完了,至于怎么修改,未完待续!