3

前几天写了一篇关于tradingView和webSocket的文章传送门,因为代码本身还在整合中,所以比较混乱,而且也没有demo可以运行。
这两天在GitHub上面看到了一个vue写的demo,仔细对比,原来就是我得到的最初版本的tradingView-webSocket代码,很开心,以为可以直接嵌入项目中,省了一番功夫。
然而现实是骨感的,这个demo并没有写的太完善,该缺的功能一样还是缺着的……传送门

无奈,还是需要对这份代码进行加工。

1、原demo分析

核心代码有4个文件:

  1. index.vue
  2. datafees.js
  3. dataUpdater.js
  4. scoket.js

index是tradingView的实例化和用户行为的响应,属于业务逻辑实现。
datafees定义了实例化tradingview的对象,提供图表调用的方法,属于数据交互的接口。
dataUpdater是一个数据的触发器,当数据发生变化,通过dataUpdater来通知tradingview进行图表渲染,是业务逻辑和tradingview实例之间的通信人。
scoket定义了webSocket的连接方式和数据收发的方法,是前后端数据交互的实现。

功能缺少,主要指业务逻辑实现上的功能缺少。
缺少的功能主要是历史记录获取、展示的功能。
初始化即获取1440条数据渲染到界面上,增量数据(实时交易记录)直接覆盖第1440条记录。
这样做的结果是,用户往左边拖拽的时候,不能展示更多的数据,同时,最新的增量数据的展示也是错位的。
另外,没有对webSocket的close事件进行监听,当连接关闭之后,增量更新即停止,这样的用户体验是不够的。
而且,初始化即获取1440条记录,将会导致初始化的时候需要请求的数据过多,查询时间和数据接收的时间都受影响,同样影响用户体验。

2、index.vue详解

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、结尾

如上,代码已经分析完了,至于怎么修改,未完待续!


陈其文
430 声望19 粉丝

前端