1. 程式人生 > >強大的金融類圖表庫 TradingView 使用分享

強大的金融類圖表庫 TradingView 使用分享

這段時間剛好做幣圈交易所,運用到了現在最火的金融類圖表庫 -- TradingView ,就是強大,基本上現在的火幣網(https://www.huobi.com),幣安網(https://www.binance.com/  )等大型交易所都在使用。

簡介:

帶有開放API的可下載圖表庫。這是一個獨立的解決方案,可以將其下載,託管在自己的伺服器上,連線自己的資料,在自己的網站/應用程式免費使用。

適用於手機和桌面應用程式、 入口網站、部落格和新聞網站。 當您想要完全控制圖表並希望顯示您所控制的資料時,此為最佳選擇。

使用教程:

本教程使用的是nodejs 提供的介面,所有的資料都本地mock出來的,這樣大家可以更加方便理解資料使用的問題。

步驟:

1.註冊TradingView賬號,然後申請TradingView的圖表庫(申請地址:https://cn.tradingview.com/how-it-works/),(注意,必須是以公司名義申請,不允許個人名義申請,如果以個人名義申請或者你所在行業經過中國區經銷商瞭解後不需要用到tradingview將無法給你提供github的開原始碼下載。原始碼結構如下圖:)

  • /charting\_library 包含全部的圖表庫檔案。
  • /charting\_library/charting\_library.min.js 包含外部圖表庫widget介面。此檔案不應該被修改。
  • /charting_library/charting_library.min.d.ts 包含TypeScript定義的widget介面
  • /charting_library/datafeed-api.d.ts 包含TypeScript定義的datafeed介面。
  • /charting_library/datafeeds/udf/ 包含UDF-compatible 的datafeed包裝類(用於實現JS API通過UDF傳輸資料給圖表庫)。例子中的datafeed包裝器類實現了脈衝實時模擬資料。您可以自由編輯此檔案。
  • /charting\_library/static
     資料夾中儲存圖表庫內部資源,不適用於其他目的。
  • /index.html 為使用Charting Library widget 的html例子。
  • /test.html 為不同的圖表庫自定義功能使用的示例。
  • /mobile\*.html 也是Widget自定義的示例。

然後把整個專案clone 下來到本地。

2.安裝nodejs  (安裝教程:https://www.cnblogs.com/zhouyu2017/p/6485265.html)

3.安裝koa  (Koa -- 基於 Node.js 平臺的下一代 web 開發框架) 

$ npm i koa

4.安裝koa-generator  (Koa 腳手架)

$ npm i -g koa-generator

5.使用koa-generator 快速建立專案

$ koa2 TrandingPiewProject && cd TrandingPiewProject && npm install

koa-generator建立的專案已經集成了nodemon,所以大家可以直接使用nodemon啟動專案

$ nodemon run start

 開啟瀏覽器: http://localhost:3001

就可以看到nodejs專案已經啟動了

6.在nodejs建立3個介面

地址1:http://localhost:3001/api/config (存放trandingview的 datafeed配置資料)

地址2:http://localhost:3001/api/symbols (trandingview的 商品解析資料)

地址3:http://localhost:3001/api/history (trandingview的 K線資料)

 

7.nodejs專案檔案修改如下

app.js (修改部分黃色標出)

const Koa = require('koa')
const views = require('koa-views')
const json = require('koa-json')
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger') // 引入koa2-cors "允許跨域" const cors = require('koa2-cors') const index = require('./routes/index') const api = require('./routes/api') const app = new Koa() app.use(cors()) // error handler onerror(app) // middlewares app.use(bodyparser({ enableTypes:['json', 'form', 'text'] })) app.use(json()) app.use(logger()) app.use(require('koa-static')(__dirname + '/public')) app.use(views(__dirname + '/views', { extension: 'pug' })) // logger app.use(async (ctx, next) => { const start = new Date() await next() const ms = new Date() - start console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) }) // routes app.use(index.routes(), index.allowedMethods()) app.use(api.routes(), api.allowedMethods()) // error-handling app.on('error', (err, ctx) => { console.error('server error', err, ctx) }); module.exports = app

  

把原來的routes下面的users.js改成api.js,

貼上我的api.js

api.js (修改部分黃色標出)

const router = require('koa-router')()

router.prefix('/api')

// 產生隨機數的方法
function random(lower, upper) {
    return Math.floor(Math.random() * (upper - lower)) + lower;
}
// 獲取兩數中一數的方法
function rd(n, m) {
    var c = m - n + 1;
    return Math.floor(Math.random() * c + n);
}

// config介面
router.get('/config', async (ctx, next) => {
    ctx.body = { 
        "supports_search": true, //是否支援搜尋
        "supports_group_request": false, //是否支援搜尋或品種解析
        "supported_resolutions": ["30", "60", "240", "D"], //中的值D代表天day,W代表周week,M代表月;2D是兩天;3W是三週;6M是6個月,表示支援顯示的哪幾種圖日線圖、2日線
        "supports_marks": false, 
        "supports_timescale_marks": false, 
    }
})
// symbols介面
router.get('/symbols', async (ctx, next) => {
    ctx.body = {
        "name": "CDCC/ETH", //品種名稱
        "session": "24x7", //開盤時間
        "has_intraday": true, //顯示符號是否具有歷史盤中資料
        "timezone": "Asia/Shanghai", //時區
        "data_status": "delayed_streaming", 
        "supported_resolutions": ["5", "10", "15", "30", "60", "120", "240", "D", "W"],
        "intraday_multipliers": ["5", "60", "D"],
        "minmov": 20, //用於格式化用途
        "pricescale": 100000000, //資料顯示的小數位數(例如:100顯示0.01)
        "type": "bitcoin" //型別
    }
})
// history介面
router.get('/history', async (ctx, next) => {
    let resolution = ctx.query.resolution // 商品名稱或者程式碼
    let from = new Date(ctx.query.from * 1000) //獲取資料的開始時間戳
    let to = new Date(ctx.query.to * 1000) //獲取資料的結束時間戳


    if (resolution == '1' || resolution == '5' || resolution == '10' || resolution == '15' || resolution == '30') {
        from.setMilliseconds(0)
        to.setMilliseconds(0)
        from.setSeconds(0)
        to.setSeconds(0)
    } else if (resolution == '60') {
        from.setMilliseconds(0)
        to.setMilliseconds(0)
        from.setSeconds(0)
        to.setSeconds(0)
        from.setMinutes(0)
        to.setMinutes(0)
    } else if (resolution == 'D') {
        from.setMilliseconds(0)
        to.setMilliseconds(0)
        from.setSeconds(0)
        to.setSeconds(0)
        from.setMinutes(0)
        to.setMinutes(0)
        from.setHours(0)
        to.setHours(0)
    }
    let text = from
    const time = Math.floor((to.getTime() - from.getTime()) / 60 / 1000)
    let num
    if (resolution == 'D') {
        num = time / (60 * 24)
    } else {
        num = time / resolution
    }
    let o = []
    let c = []
    let h = []
    let l = []
    let v = []
    let t = []
    let newnum = 3000;
    for (var i = 0; i < num; i++) {
        t.push(text.getTime() / 1000)
        if (resolution == 'D') {
            text.setMinutes(text.getMinutes() + Number(24 * 60))
        } else {
            text.setMinutes(text.getMinutes() + Number(resolution))
        }
        if (rd(1, 2) == 1) {
            newnum += random(1, 10)
            o.push(newnum)
            let h1 = 0;
            let l1 = 0;
            if (rd(1, 2) == 1) {
                h1 = newnum + random(1, 10)
                h.push(h1)
                l1 = h1 - random(1, 10)
                l.push(l1)
                c.push(rd(l1, h1))
            } else {
                h1 = newnum - random(1, 10)
                h.push(h1)
                l1 = h1 - random(1, 10)
                l.push(l1)
                c.push(rd(l1, h1))
            }
        } else {
            newnum -= random(1, 10)
            if (newnum < 0) {
                newnum = 0
            }
            o.push(newnum)
            if (rd(1, 2) == 1) {
                h.push(newnum + random(1, 10))
                l.push(newnum + random(1, 10))
                c.push(newnum + random(1, 10))
            } else {
                if ((newnum - random(1, 10)) < 0) {
                    newnum = 0
                    h.push(0)
                } else {
                    h.push(newnum - random(1, 10))
                }
                if ((newnum - random(1, 10)) < 0) {
                    newnum = 0
                    l.push(0)
                } else {
                    l.push(newnum - random(1, 10))
                }
                if ((newnum - random(1, 10)) < 0) {
                    newnum = 0
                    c.push(0)
                } else {
                    c.push(newnum - random(1, 10))
                }
            }
        }

        v.push(random(1, 1000))
    }
    ctx.body = { 
        t: t, //表示時間,將t的數值補000後就代表毫秒數,比如js的(new Date).getTime()
        o: o, //表示open開盤價
        c: c, //表示close收盤價
        h: h, //表示high最高價
        l: l, //表示low最低價
        v: v, //表示volume成交量
        s: "ok" //表示狀態,返回資料是否成功,ok表示成功
    }
})

module.exports = router

 

修改完之後,其實上面建立的3個就可以訪問了。mock 資料也完成了。

8.把github上clone下來的專案啟動在自己本地的伺服器,我個人用了代理伺服器nginx,nginx 具體的安裝流程請大家自己去搜索,以及為什麼用nginx,這個就是因為介面跨域問題,大家自己私下了解下就可以了,我在這裡就不多做解釋了。

接著把克隆下來的charting_library檔案移動到nginx的html檔案下就可以了。

這裡我貼出我的nginx.conf 檔案

 

注意:修改下里面charting_library資料夾根目錄下的 test.html 檔案,把名字改成 index3.html。因為nginx跟噁心,對test這個詞有bug。

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
 #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main;  sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 8083; server_name localhost; location / { root ./html/charting_library; index index3.html index3.htm; } } server { listen 8084; server_name localhost; location / { proxy_pass http://localhost:8083; proxy_redirect default; } location /apis { rewrite ^/apis/(.*)$ /$1 break; proxy_pass http://localhost:3001;  } } # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} }

 

好了,這個時候專案就已經啟動了。

9.然後修改Tradingview的配置就可以連線自己的nodesj mock出來的資料了。

tradingview 目錄結構

然後只需要修改裡裡面的index.html(其實就是原來的test.html,只是我這裡把它改成index3.html)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>TradingView Charting Library demo -- testing mess</title>
    <!-- Fix for iOS Safari zooming bug -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <script src="http://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
    <script type="text/javascript" src="charting_library/charting_library.min.js"></script>
    <script type="text/javascript" src="datafeeds/udf/dist/polyfills.js"></script>
    <script type="text/javascript" src="datafeeds/udf/dist/bundle.js"></script>
    <script type="text/javascript">
    function getParameterByName(name) {
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } TradingView.onready(function() { var widget = window.tvWidget = new TradingView.widget({ debug: false, // 是否全屏  fullscreen: true, // 初始商品  symbol: 'CDCC/ETH', // 週期  interval: 60, // 時區  timezone: "Asia/Shanghai", // id屬性為指定要包含widget的DOM元素id  container_id: "tv_chart_container", // 語言  locale: "zh", // static資料夾的路徑  library_path: "charting_library/", // JavaScript物件的實現介面 JS API 以反饋圖表及資料  datafeed: new Datafeeds.UDFCompatibleDatafeed("http://localhost:3001/api"), // 自動調節大小  autosize: true, // 主題顏色(dark,light)  theme: 'dark', time_frames: false, style: "1", overrides: { "timeScale.rightOffset": 1, "timeScale.barSpacing": 12.709329141645004, "headerToolbarIndicators.backgroundColor": "#dc1a5a", "headerToolbarIndicators.backgroundColor": "#dc1a5a", "mainSeriesProperties.candleStyle.borderColor": "#212c3f", "mainSeriesProperties.candleStyle.borderDownColor": "#dc1a5a", "mainSeriesProperties.candleStyle.borderUpColor": "#0fbb89", "mainSeriesProperties.candleStyle.downColor": "#dc1a5a", "mainSeriesProperties.candleStyle.upColor": "#0fbb89", "mainSeriesProperties.candleStyle.wickDownColor": "#dc1a5a", "mainSeriesProperties.candleStyle.wickUpColor": "#0fbb89", "mainSeriesProperties.hollowCandleStyle.borderColor": "#212c3f", "mainSeriesProperties.hollowCandleStyle.borderDownColor": "#dc1a5a", "mainSeriesProperties.hollowCandleStyle.borderUpColor": "#0fbb89", "mainSeriesProperties.hollowCandleStyle.downColor": "#dc1a5a", "mainSeriesProperties.hollowCandleStyle.upColor": "#0fbb89", "mainSeriesProperties.showCountdown": false, "mainSeriesProperties.style": 1, "paneProperties.background": "#131722", "paneProperties.crossHairProperties.color": "#999", "paneProperties.horzGridProperties.color": "#252c40", "paneProperties.horzGridProperties.style": 2, "paneProperties.legendProperties.showLegend": true, "paneProperties.vertGridProperties.color": "#131722", "paneProperties.vertGridProperties.style": 2, "symbolWatermarkProperties.color": "#0e192b", "volumePaneSize": "small", }, studies_overrides: { "volume.volume.color.0": "#0fbb89", "volume.volume.color.1": "#dc1a5a", "volume.options.showStudyArguments": false, "bollinger bands.median.color": "#33FF88", "bollinger bands.upper.linewidth": 0 }, disabled_features: [ "header_symbol_search", "header_saveload", "header_screenshot", "header_chart_type", "header_compare", "header_undo_redo", "timeframes_toolbar", "volume_force_overlay", "header_resolutions", "display_market_status", "timezone_menu", "countdown", "symbol_info" ], enabled_features: [ "legend_context_menu" ], // 指標模板  charts_storage_api_version: '1.1', // 定製載入進度條  loading_screen: { backgroundColor: "#000000" }, 'scalesProperties.fontSize': 14, // 設定座標軸字型大小  }); // 參考線、 按鈕、 時間切換: var thats = widget; var buttons = [ { title: '5分', resolution: '5', chartType: 1 }, { title: '10分', resolution: '10', chartType: 1 }, { title: '15分', resolution: '15', chartType: 1 }, { title: '30分', resolution: '30', chartType: 1 }, { title: '1小時', resolution: '60', chartType: 1 }, { title: '2小時', resolution: '120', chartType: 1 }, { title: '4小時', resolution: '240', chartType: 1 }, { title: '1天', resolution: 'D', chartType: 1 }, { title: '1周', resolution: 'W', chartType: 1 }, ]; var studies = []; function createButton(buttons) { for (var i = 0; i < buttons.length; i++) { thats.createButton() .attr('title', buttons[i].title) .attr('data-interval', buttons[i].resolution) .addClass('btn-m') .text(buttons[i].title) .on('click', function(e) { tvWidget.setSymbol("CDCC/ETH",$(this).attr('data-interval'),function(){}); }) } } function createStudy() { var id = widget.chart().createStudy('Moving Average', false, false, [5], null, { 'Plot.color': 'rgb(150, 95, 196)' }); studies.push(id); id = widget.chart().createStudy('Moving Average', false, false, [10], null, { 'Plot.color': 'rgb(116,149,187)' }); studies.push(id); id = widget.chart().createStudy('Moving Average', false, false, [20], null, { "plot.color": "rgb(58,113,74)" }); studies.push(id); id = widget.chart().createStudy('Moving Average', false, false, [30], null, { "plot.color": "rgb(118,32,99)" }); studies.push(id); } widget.onChartReady(function() { //設定均線種類 均線樣式  createStudy(); //生成時間按鈕  createButton(buttons); }); }); </script> </head> <body style="margin:0;"> <div id="tv_chart_container"></div> </body> </html>

 

上面是我的index3.html檔案

修改完之後大家開啟http://localhost:8083/ 就可以看到自己的TradingView 專案連線著自己的nodejs伺服器mock出來的資料運行了,是不是很有成就感啦!!!

這個給大家介紹個好東西,圖表功能展示

地址: https://tradingview.gitee.io/featuresets/

這裡,我就是介紹了簡單的ajax請求方法的,要WS的後面我再慢慢更新哈。

 

以上只是一部分,後續會繼續更新完善。

期待!期待!期待!