TradingView JS API集成教程(三):第2部分-實時圖表更新

在本篇中登刺,我們將在圖表上實現(xiàn)實時價格更新乾翔。請務必閱讀簡介爱葵,以及第1部分第

在本篇中施戴,我們將在圖表上實現(xiàn)實時價格更新。在此之前請務必閱讀簡介萌丈,以及第1部分

為了讓圖表可以實時更新數(shù)據(jù)赞哗,我們將繼續(xù)改造第1部分的示例。

此示例將使用CryptoCompare的websocket來獲取價格更新辆雾。

你可以看到示例部署在glitch上肪笋,并查看第2部分的代碼

第1部分重點介紹如何設置TradingView圖表庫Widget,以及設置JS API以從我們自己的數(shù)據(jù)源獲取歷史K線數(shù)據(jù)度迂。

讓我們可以實時更新圖表的JS API方法是:

  • subscribeBars?- 訂閱商品的實時數(shù)據(jù)
  • unsubscribeBars?- 取消訂閱商品的實時數(shù)據(jù)

基本上我們需要做的就是更新我們圖表上最新的K線藤乙,無論是1分鐘的K線,還是1天的K線惭墓,這個過程幾乎是一樣的坛梁。

我們必須保持圖表上最后一條K線的變量記錄,用最新價格數(shù)據(jù)更新它(該周期的開盤價诅妹,最高價罚勾,最低價,收盤價或成交量是否發(fā)生了變化吭狡?)尖殃,如果時間進入了新的周期,則提供一個新的K線數(shù)據(jù)而不是更新最后一條K線數(shù)據(jù)划煮。

注意:如果你提供1分鐘的TradingView送丰,并讓它建立5分鐘,15分鐘等等弛秋,那么你實際只需更新1分鐘器躏。不用擔心,TradingView將在調用subscribeBars 時指定所需的分辨率蟹略!

首先登失,讓我們來看看我們將要使用的這些新的JS API方法。

subscribeBars

resolveSymbol 假設我們能夠成功解析商品挖炬,則在之后揽浙,圖表庫將調用此方法。如果您不熟悉resolveSymbol請查看本指南的第1部分意敛。

subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {  
    
},

圖表庫將這些參數(shù)傳遞給subscribeBars

  • symbolInfo- ObjectsymbolInfo對象
  • resolution- StringK線周期
  • onRealtimeCallback- Function將我們更新的K線傳遞給此回調以更新圖表
  • subscribeUID- String此交易對的唯一ID和表示訂閱的分辨率馅巷,生成規(guī)則:ticker+'_'+周期
  • onResetCacheNeededCallback- Function調用次回調讓圖表再次請求歷史K線數(shù)據(jù)

在實現(xiàn)方面,當圖表商品或周期發(fā)生變化時草姻,或者圖表需要訂閱新商品時钓猬,圖表庫會調用subscribeBars。

當訂閱K線實時數(shù)據(jù)時撩独,我們需要創(chuàng)建一個ws訂閱記錄敞曹,里面需要包含onRealtimeCallback函數(shù)(可以將onRealtimeCallback公開到window對象中)账月,這樣您就可以使用從實時數(shù)據(jù)源接收的新數(shù)據(jù)調用onRealtimeCallback函數(shù)。

JS API是一個傳遞給圖表庫的JS對象异雁,它必須包含TradingView定義的函數(shù)捶障。這些函數(shù)由圖表庫根據(jù)需要調用,您不能自己調用??它們纲刀,只能調用它們的回調函數(shù)项炼。

你應該做的是保持對訂閱的引用,然后將更新的K線傳遞給subscribeBars函數(shù)的回調realtimeUpdateCallback示绊。


讓我們開始行動锭部!

假設我們有兩個文件,api/index.jsJS API所在的文件面褐,以及api/stream.js我們的實時更新代碼:

// api/index.js

import stream from './stream'

...

subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {

  stream.subscribeBars(symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback)  
 },  
...

請記住拌禾,上面的很多內(nèi)容涉及我正在使用的數(shù)據(jù)源的細節(jié),CryptoCompare的Socket.io websocket頻道展哭。

我們需要了解圖表庫中沒有提供的東西湃窍,我們需要知道圖表上的lastBar。我們需要擁有一個對指定圖表的onRealtimeUpdateCB引用匪傍。

TradingView特有的部分如下:

  • 創(chuàng)建ws訂閱記錄您市,以便我們可以存儲對updateCB的引用并將數(shù)據(jù)傳遞到右側圖表
  • lastBar通過我們數(shù)據(jù)源的更新或創(chuàng)建新K線
  • 在TradingView調用subscribeBars時,將更新/創(chuàng)建的K線數(shù)據(jù)傳遞給updateCB

從TradingView圖表庫的角度來看役衡,就這么簡單茵休。但是,使用您自己的數(shù)據(jù)源來實現(xiàn)這個流程可以是簡單或復雜的手蝎。

TV要求

通過更新圖表上最新K線(當前的K線)來實現(xiàn)K線的實時更新榕莺。當實時價格數(shù)據(jù)進入時,您需要為圖表庫提供該K線數(shù)據(jù)的更新版本棵介。

JS API提供了兩個管理它的功能钉鸯。susbcribeBarsunsubscribeBars

當圖表加載商品邮辽,或者當前圖表的周期發(fā)生變化時亏拉,TV將通過使用想要訂閱的商品和周期調用subscribeBars來請求訂閱該K線的實時更新。

subscribeBars: (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {  
  console.log('=====subscribeBars runnning')  
}

unsubscribeBars

TV將調用subscribeBars函數(shù)以開啟商品的實時訂閱逆巍。為了將更新數(shù)據(jù)傳遞給圖表,您需要調用onRealtimeCallback莽使。

請注意锐极,TV只調用一次subscribeBars,當您需要更新該訂閱的圖表時芳肌,您需要保留對onRealtimeCallback函數(shù)的引用灵再。

所以您要負責:

  • 連接到實時數(shù)據(jù)源
  • 管理訂閱肋层,TV會通過JS API subscribeBarsunsubscribeBars方法通知您。
  • 持續(xù)更新圖表上最新K線
  • 將數(shù)據(jù)修改為TV格式

TV的K線格式是一個如下所示的對象:

{   
time: 1528322340000翎迁,//K線時間必須以毫秒為單位  
open: 7678.85栋猖,  
high: 7702.55,  
low:  7656.98汪榔,  
close: 7658.25蒲拉,//收盤價是開盤后最新的價格  
volume: 0.9834   
}

了解TV的需求與您自己的數(shù)據(jù)實現(xiàn)之間的區(qū)別非常重要。您可以使用任何數(shù)據(jù)痴腌,只要您以正確的格式將其提供給TV圖表庫即可雌团。

下面是我自己實現(xiàn)CryptoCompare的 socket.io交易數(shù)據(jù)websocket 的例子。

這只是完整JS API實現(xiàn)的一部分士聪,我們從這個文件中導出兩個方法锦援,這些方法將從我們傳遞給TV的JS API對象中調用。有關JS API的更多信息剥悟,請參閱本教程的第1部分

// api/stream.js
import historyProvider from './historyProvider.js';
// 我們使用Socket.io客戶端連接cryptocompare的socket.io數(shù)據(jù)流
var io = require('socket.io-client');
var socket_url = 'wss://streamer.cryptocompare.com';
var socket = io(socket_url);
// 正在訂閱的對象數(shù)組
var _subs = [];

export default {
  subscribeBars: function(symbolInfo, resolution, updateCb, uid, resetCache) {
    const channelString = createChannelString(symbolInfo);
    socket.emit('SubAdd', { subs: [channelString] });

    var newSub = {
      channelString,
      uid,
      resolution,
      symbolInfo,
      lastBar: historyProvider.history[symbolInfo.name].lastBar,
      listener: updateCb,
    };
    _subs.push(newSub);
  },
  unsubscribeBars: function(uid) {
    var subIndex = _subs.findIndex((e) => e.uid === uid);
    if (subIndex === -1) {
      //console.log("No subscription found for ",uid)
      return;
    }
    var sub = _subs[subIndex];
    socket.emit('SubRemove', { subs: [sub.channelString] });
    _subs.splice(subIndex, 1);
  },
};

socket.on('connect', () => {
  console.log('===Socket connected');
});
socket.on('disconnect', (e) => {
  console.log('===Socket disconnected:', e);
});
socket.on('error', (err) => {
  console.log('====socket error', err);
});
socket.on('m', (e) => {
  //這里我們得到CryptoCompare連接訂閱的所有事件
  //我們需要將這些新數(shù)據(jù)發(fā)送到我們訂閱的圖表上
  const _data = e.split('~');
  if (_data[0] === '3') {
    // console.log('Websocket Snapshot load event complete')
    return;
  }
  const data = {
    sub_type: parseInt(_data[0], 10),
    exchange: _data[1],
    to_sym: _data[2],
    from_sym: _data[3],
    trade_id: _data[5],
    ts: parseInt(_data[6], 10),
    volume: parseFloat(_data[7]),
    price: parseFloat(_data[8]),
  };

  const channelString = `${data.sub_type}~${data.exchange}~${data.to_sym}~${data.from_sym}`;

  const sub = _subs.find((e) => e.channelString === channelString);

  if (sub) {
    // 如果是歷史K線數(shù)據(jù)灵寺,則跳過
    if (data.ts < sub.lastBar.time / 1000) {
      return;
    }

    var _lastBar = updateBar(data, sub);

    // 將最新的K線發(fā)送給TV的realtimeUpdate回調
    sub.listener(_lastBar);
    // 更新我們自己的lastBar記錄
    sub.lastBar = _lastBar;
  }
});

// 取得交易數(shù)據(jù)和訂閱記錄, 返回更新后的最新K線
function updateBar(data, sub) {
  var lastBar = sub.lastBar;
  let resolution = sub.resolution;
  if (resolution.includes('D')) {
    // 一天的分鐘數(shù) === 1440
    resolution = 1440;
  } else if (resolution.includes('W')) {
    // 一周的分鐘數(shù) === 10080
    resolution = 10080;
  }
  var coeff = resolution * 60;
  // console.log({coeff})
  var rounded = Math.floor(data.ts / coeff) * coeff;
  var lastBarSec = lastBar.time / 1000;
  var _lastBar;

  if (rounded > lastBarSec) {
    // 創(chuàng)建一個新的K線,使用最后的收盤價作為開盤價**個人選擇**
    _lastBar = {
      time: rounded * 1000,
      open: lastBar.close,
      high: lastBar.close,
      low: lastBar.close,
      close: data.price,
      volume: data.volume,
    };
  } else {
    // 更新最新的K線
    if (data.price < lastBar.low) {
      lastBar.low = data.price;
    } else if (data.price > lastBar.high) {
      lastBar.high = data.price;
    }

    lastBar.volume += data.volume;
    lastBar.close = data.price;
    _lastBar = lastBar;
  }
  return _lastBar;
}

// 將symbolInfo對象作為輸入?yún)?shù)区岗,并創(chuàng)建要發(fā)送到CryptoCompare的訂閱字符串
function createChannelString(symbolInfo) {
  var channel = symbolInfo.name.split(/[:/]/);
  const exchange = channel[0] === 'GDAX' ? 'Coinbase' : channel[0];
  const to = channel[2];
  const from = channel[1];
  // 訂閱指定交易所和交易對的CryptoCompare交易頻道
  return `0~${exchange}~${from}~${to}`;
}

不過粉渠,您的數(shù)據(jù)源實現(xiàn)可能與我的不同叼架,除非您也使用CryptoCompare!

一般步驟是:

  • 提供擁有subscribe/unsubscribeBars方法的JS API對象給TV
  • 記錄相應的訂閱對象,保存TV傳遞給您realtimeUpdateCallback 媒佣、subscriberUID和resolution。您還需要圖表上的最新K線观蜗,我的historyProvider負責提供最新K線缕坎。
  • 訂閱您的自己數(shù)據(jù)源的ws頻道
  • 獲取來自數(shù)據(jù)源的數(shù)據(jù),更新您的lastBar(或者如果最新K線自上次收到數(shù)據(jù)后相應周期已結束教藻,則創(chuàng)建一個新K線)
  • 將更新的K線數(shù)據(jù)傳遞給realtimeCallback距帅。
  • 處理取消訂閱

讓我們分析一下我正在做些什么來實現(xiàn)CryptoCompare的特定實時數(shù)據(jù)來處理這個問題。 這僅限于TV括堤,因為我正在按照他們指定的格式碌秸,格式化我可用的數(shù)據(jù)。

  • 為指定的交易對/交易所訂閱交易級別的更新頻道悄窃。Cryptocompare處理許多交易所讥电,以及列出的所有交易對,這就是我將它們寫到商品名稱中的原因轧抗,例如恩敌,Coinbase:BTC/USD為我提供了使用CryptoCompare訂閱正確頻道所需的信息
  • 在訂閱頻道時,收聽從CryptoCompare發(fā)送的“快照”横媚,快照數(shù)據(jù)是該頻道最后幾分鐘的交易纠炮,這使我可以在價格發(fā)生變化時月趟,更新歷史數(shù)據(jù)。這些更新的發(fā)送方式與所有未來的更新一樣恢口,因此除了忽略比圖表上的最新K線更舊的過時更新之外孝宗,我不必做任何事情。
  • 我正在將每個更新的時間戳修改為我們使用的交易周期的相應時間戳耕肩,這是為了更新最新K線所必需的因妇,并確定何時我們需要創(chuàng)建一個新的K線。
  • 更新我們的訂閱記錄中的最新K線看疗。記錄新的高沙峻、低、開两芳、交易量和收盤價摔寨。收盤價始終是您獲得的最后一次價格更新。對于周期為結束的K線怖辆,“收盤價”為當前價格是复。
  • 將更新后的K線發(fā)送到updateCB以進行正確的訂閱處理。

目前為止就這么多了竖螃!

你可以看到部署在演示在Glitch淑廊,并查看第2部分的代碼

希望這有助于回答有關Trading View 圖表庫實時更新的一些問題!

這是我在圖表庫中系列中最受歡迎的部分特咆,我打算隨著時間的推移更新它季惩,因此我簡化了這個過程。

本系列的第3部分將介紹更多TradingView功能(有非常多D甯瘛)画拾,并將很快推出。我計劃創(chuàng)建一些更好的起始項目菜职,以加快開發(fā)tradingview的過程青抛。


關于譯者

我是一名現(xiàn)就職于日本最大數(shù)字貨幣交易所bitbank的全棧開發(fā)人員,住在東京酬核。如果您需要加快將TradingView放入到您的網(wǎng)站或交易網(wǎng)頁蜜另,我可以付費幫忙。

您可以聯(lián)系我:zlq4863947@gmail.com

您也可以加入tradingview開發(fā)交流qq群:313839516

關于本篇譯文

英文原文地址: TradingView Charting Library JS API Setup for Crypto: Realtime Chart Updates

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嫡意,一起剝皮案震驚了整個濱河市举瑰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔬螟,老刑警劉巖嘶居,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡邮屁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門菠齿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佑吝,“玉大人,你說我怎么就攤上這事绳匀∮蠓蓿” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵疾棵,是天一觀的道長戈钢。 經(jīng)常有香客問我,道長是尔,這世上最難降的妖魔是什么殉了? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮拟枚,結果婚禮上薪铜,老公的妹妹穿的比我還像新娘。我一直安慰自己恩溅,他們只是感情好隔箍,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脚乡,像睡著了一般蜒滩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奶稠,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天俯艰,我揣著相機與錄音,去河邊找鬼窒典。 笑死蟆炊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瀑志。 我是一名探鬼主播涩搓,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劈猪!你這毒婦竟也來了昧甘?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤战得,失蹤者是張志新(化名)和其女友劉穎充边,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡浇冰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年贬媒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肘习。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡际乘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漂佩,到底是詐尸還是另有隱情脖含,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布投蝉,位于F島的核電站养葵,受9級特大地震影響,放射性物質發(fā)生泄漏瘩缆。R本人自食惡果不足惜关拒,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咳榜。 院中可真熱鬧夏醉,春花似錦、人聲如沸涌韩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽臣樱。三九已至靶擦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雇毫,已是汗流浹背玄捕。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棚放,地道東北人枚粘。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像飘蚯,于是被迫代替她去往敵國和親馍迄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 查看本教程系列的簡介(如果您還沒有觀看)局骤。設置TradingView圖表可能是一個復雜的過程攀圈,所以請事先查看免責聲...
    Ellite閱讀 23,715評論 3 13
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料峦甩? 從這篇文章中你...
    hw1212閱讀 12,693評論 2 59
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,513評論 25 707
  • 關于Mongodb的全面總結 MongoDB的內(nèi)部構造《MongoDB The Definitive Guide》...
    中v中閱讀 31,898評論 2 89
  • 夢想赘来,一個多么美好的字眼。今天我想說說這個既抽象又具體,既遠在天邊又近在眼前的東西犬辰。 我想所謂夢想嗦篱,與其說是一個想...
    qcppp80閱讀 861評論 0 1