tradingView在坑中摸出来的

 新手摸索出来的,这个是没有用框架写的,比较简单,直接上代码,K线版本为1.14,不需要引入jq,写的没啥技术含量不喜勿喷

初始化图表

var _TVjsApi = new TVjsApi("交易对");

_TVjsApi.init();

<script src="/Home/TradingView/charting_library/charting_library.min.js" type="text/javascript" charset="utf-8"></script>
<script src="Home/TradingView/js/dataUpdater.js" type="text/javascript" charset="utf-8"></script>
<script src="/Home/TradingView/js/datafees.js" type="text/javascript" charset="utf-8"></script>
<script src="/Home/TradingView/js/socket.js" type="text/javascript" charset="utf-8"></script>
<script>
 
    localStorage._interval = localStorage._interval||"1"
    var _interval = localStorage._interval || "1"
    var TVjsApi = (function () {
        var TVjsApi = function () {
            this.pricescale = "100000"
            //价格精度
           var urls = 'wss://***';
            this.widgets = null;
            this.socket = new socket(urls);
            this.datafeeds = new datafeeds(this);
            this.symbol = "交易对名称" || 'btcusdt';
            this.interval = _interval;
            this.cacheData = {};
            this.lastTime = null;
            this.getBarTimer = null;
            this.isLoading = true;
            var that = this;
            this.socket.doOpen()
            this.socket.on('open', function () {
                //页面初始化的时候,为了快速加载,先请求150条记录
                //scoket自己定义,我这边和后端约定的是发送当前时间然后,后端根据当前时间往前查询出150条数据
                that.socket.send({
                    cmd: 'req',
                    args: ["candle." + that.interval + "." + that.symbol.toLowerCase(), 150, parseInt(Date.now() / 1000)]
                })
                that.socket.send({
                    cmd: 'req',
                    args: ["deep." + $(".deepness_check").attr("data-type") + "." + that.symbol.toLowerCase()]
                })
                that.socket.send({
                    cmd: 'sub',
                    args: ["trade_list"]
                })
            })
            this.socket.on('message', that.onMessage.bind(this))
            this.socket.on('close', that.onClose.bind(this))
        }
        TVjsApi.prototype.init = function () {
            var resolution = localStorage._interval != "undefined" ? localStorage._interval : "1";
            var chartType = (localStorage.getItem('tradingview.chartType') || '1') * 1;
            var skin = localStorage.getItem('tradingViewTheme') || 'white';
            if (!this.widgets) {
                this.widgets = new TradingView.widget({
                    autosize: true,
                    symbol: this.symbol,
                    interval: resolution,
                    container_id: 'tv_chart_container',
                    //K线渲染的dom
                    datafeed: this.datafeeds,
                    library_path: '/Home/TradingView/charting_library/',
                    //K线静态文件地址
                    enabled_features: [],
                    timezone: localStorage.timezone || 'Asia/Shanghai',
                    // custom_css_url: './css/tradingview_'+skin+'.css',
                    locale: localStorage.lang || 'zh',
                    debug: false,
                    toolbar_bg: "#fff",
                    disabled_features: [
                        "save_chart_properties_to_local_storage",//本地存储
                        "header_symbol_search", //搜索
                        "symbol_search_hot_key",
                        "volume_force_overlay",
                        "header_interval_dialog_button",
                        "header_screenshot", //照相机
                        "header_compare",
                        "use_localstorage_for_settings", //用户本地存储
                        "timeframes_toolbar", //底部时间栏目
                        "volume_force_overlay", //k线与销量分开
                        "header_undo_redo", //左右箭头
                        // "header_settings",//设置按钮
                        // "header_indicators",//技术指标线
                        "header_chart_type", //图表类型
                        "pane_context_menu", //图表右键菜单
                        "header_resolutions", //系统默认时间按钮
                        // "hide_left_toolbar", //左边工具栏 hide_left_toolbar_by_default 
                        "header_saveload",
                        "main_series_scale_menu",//显示图表右下角的设置按钮
                        "control_bar",
                        "caption_buttons_text_if_possible"
                    ],
                    enabled_features: [
                        "keep_left_toolbar_visible_on_small_screens", //防止左侧工具栏在小屏幕上消失
                        "adaptive_logo",
                        "property_pages",
                        "display_market_status",
                        "remove_library_container_border",
                        "move_logo_to_main_pane",
                        "dont_show_boolean_study_arguments", //是否隐藏指标参数
                        "countdown",
                        // "caption_buttons_text_if_possible", //在可能的情况下,在标题中的“指标”和“比较”按钮上显示文字而不是图标
                        "header_settings",
                        "hide_last_na_study_output", //隐藏最后一次指标输出
                        "symbol_info", //商品信息对话框
                        "hide_left_toolbar_by_default",
                    ],
                    //preset: "mobile",
                    overrides: {
                        " paneProperties.rightAxisProperties.autoScale": true,
                        " paneProperties.leftAxisProperties.autoScale": true,
                        "volumePaneSize": "small", //"volumePaneSize" : "large"支持的值: large(默认), medium, small, tiny//  白色蜡烛样式
                        "mainSeriesProperties.candleStyle.downColor": "#ee6560", //K线颜色
                        "mainSeriesProperties.candleStyle.upColor": "#4db872",
                        "mainSeriesProperties.candleStyle.borderDownColor": "#ee6560", //边框颜色
                        "mainSeriesProperties.candleStyle.borderUpColor": "#4db872",
                        "mainSeriesProperties.candleStyle.wickDownColor": '#ee6560', //烛芯颜色
                        "mainSeriesProperties.candleStyle.wickUpColor": "#4db872",
                        "paneProperties.vertGridProperties.color": "#f8f8fa",//格子线条
                        "paneProperties.horzGridProperties.color": "#f8f8fa",
                        "paneProperties.vertGridProperties.style": 0,
                        "paneProperties.horzGridProperties.style": 0,
                        "toolbar_bg": "#fff",
                        "scalesProperties.textColor": "#818b9d",
                        'paneProperties.legendProperties.showLegend': false,
                        'scalesProperties.fontSize': 12,
                        'paneProperties.topMargin': 30, //K线面板属性,
                        'price-axis.leftMargin': 30,
                        'hide_left_toolbar_by_default': "hidden",
                        // "symbolWatermarkProperties.color": "#ffffff",
                        'scalesProperties.lineColor': "#ccd0d8",//边框线条颜色

                    },
                    studies_overrides: {
                        "bollinger bands.median.color": "#33FF88",
                        "bollinger bands.upper.linewidth": 7,
                        // "volume.precision" : 1

                    },
                    studies_overrides: {
                        "volume.volume.color.0": "#ee6560",
                        "volume.volume.color.1": "#4db872",
                    }
                })

                var thats = this.widgets
                this.widgets.headerReady().then(function () {
                    //指标线
                    thats.chart().createStudy("Moving Average", false, true, [5], null, { "plot.color": "#99aac7" });
                    thats.chart().createStudy("Moving Average", false, true, [15], null, { "plot.color": "#e9e12f" });
                    thats.chart().createStudy("Moving Average", false, true, [30], null, { "plot.color": "#2026dc" });
                    thats.chart().createStudy("Moving Average", false, true, [60], null, { "plot.color": "#a109ef" });
                    //重新构建时间切换按钮
                    let chart = thats.chart();
                    const btnList = [{
                        label: "分时",
                        resolution: localStorage.resolution || "1",
                        chartType: 3,
                    }, {

                        label: '1分',
                        resolution: "1",
                        chartType: 1,
                    }, {
                        label: '5分',
                        resolution: "5",
                        chartType: 1,
                    }, {
                        label: '15分',
                        resolution: "15",
                        chartType: 1,

                    }, {
                        label: '30分',
                        resolution: "30",
                        chartType: 1,
                    }, {
                        label: '1小时',
                        resolution: "60",
                        chartType: 1,
                    },
                    {
                        label: '1天',
                        resolution: "1D",
                        chartType: 1,
                    }, {
                        label: '1周',
                        resolution: "1W",
                        chartType: 1,
                    }, {
                        label: '1月',
                        resolution: "1M",
                        chartType: 1,
                    }];
                    btnList.forEach(function (item, index) {
                        let button = thats.createButton({
                            align: "left"
                        })
                        if (item.resolution == (localStorage.resolution || "1") && item.label != "分时") {
                            button.classList.add("active")
                        } else {

                        }
                        button.textContent = item.label
                        button.setAttribute('title', item.resolution)
                        button.setAttribute("data-chart-type", item.chartType === undefined ? 8 : item.chartType)
                        button.setAttribute("data-resolution", item.resolution)
                        button.addEventListener('click', function (e) {
                            // TVjsApi.interval=item.resolution
                            localStorage.resolution = item.resolution
                            localStorage._interval = button.getAttribute("data-resolution")
                            let chartType = button.getAttribute("data-chart-type") * 1;
                            button.classList.add("active")
                            var list = TVjsApi.prototype.sibling(this.parentNode.parentNode)
                            for (var i = 0; i < list.length; i++) {
                                if (list[i].childNodes[0]) {
                                    var list_btn = list[i].childNodes[0]
                                    var list_btn_chil = list_btn.children[0]
                                    list_btn_chil.className = list_btn_chil.className.replace("active", " ")
                                }
                            }
                            if (thats.chart().chartType() !== chartType) {
                                thats.chart().setChartType(chartType);
                            }
                            if (item.label != "分时") {
                                thats.chart().setResolution(item.resolution, function onReadyCallback() { });
                            } else {
                                var type = this.getAttribute("data-resolution")
                                thats.chart().setResolution(type, function onReadyCallback() { });
                            }

                        });
                    });
                    let zb_button = thats.createButton({
                        align: "left"
                    })
                    zb_button.addEventListener('click', function (e) {
                        // console.log(thats.getStudiesList())
                    })
                    zb_button.textContent = '指标';
                    zb_button.classList.add("tv_indicators");
                });
            }
        }
        TVjsApi.prototype.sibling = function (elem) {
            var r = [];
            var n = elem.parentNode.firstChild;
            for (; n; n = n.nextSibling) {
                if (n.nodeType === 1 && n !== elem) {
                    r.push(n);
                }
            }
            return r;
        }
        TVjsApi.prototype.sendMessage = function (data, type) {
            var that = this;
            // console.log("这是要发送的数据:"+JSON.stringify(data) )
            if (this.socket.checkOpen()) {
                this.socket.send(data)
            } else {
                this.socket.on('open', function () {
                    that.socket.send(data)
                })
            }
        }
        TVjsApi.prototype.reqsend = function () {
            this.socket.send({
                    cmd: 'req',
                    args: ["deep." + $(".deepness_check").attr("data-type") + "." + this.symbol.toLowerCase()]
                })
        }
        TVjsApi.prototype.unSubscribe = function (interval) {
            var thats = this;
            //停止订阅,删除过期缓存、缓存时间、缓存状态
            this.interval = interval
            var ticker = thats.symbol + "-" + interval;
            var tickertime = ticker + "load";
            var tickerstate = ticker + "state";
            var tickerCallback = ticker + "Callback";
            delete thats.cacheData[ticker];
            delete thats.cacheData[tickertime];
            delete thats.cacheData[tickerstate];
            delete thats.cacheData[tickerCallback];
            //取消订阅
            this.sendMessage({
                cmd: 'unsub',
                args: ["candle." + thats.interval + "." + thats.symbol.toLowerCase()]
            })
        }
        //订阅
        TVjsApi.prototype.subscribe = function () {
            this.sendMessage({
                cmd: 'req',
                args: ["candle." + this.interval + "." + this.symbol.toLowerCase(), 1440, parseInt(Date.now() / 1000)]
            })
        }

        TVjsApi.prototype.onMessage = function (data) {
            var thats = this;
            if (data.name == "kline") {
                //websocket返回的值,数组代表时间段历史数据,不是增量
                var list = []
                var ticker = thats.symbol + "-" + thats.interval;
                var tickerstate = ticker + "state";
                var tickerCallback = ticker + "Callback";
                var onLoadedCallback = thats.cacheData[tickerCallback];
                var list = data.data.kline||"";
               //入托将后台返回的实时数据变更成TV需要的数据格式
                    格式示例
                    //list=[{time: 1566983700000, close: 0.008618, open: 0.008618, high: 0.008622, low: 0.008616, volume: 492.6057},{time: 1566983700000, close: 0.008618, open: 0.008618, high: 0.008622, low: 0.008616, volume: 492.6057}]
                       
                if (data.config) {
                    thats["getSymbol"] = data.config.data;
                }
             
                //如果没有缓存数据,则直接填充,发起订阅
                if (!thats.cacheData[ticker]) {
                    thats.cacheData[ticker] = list;
                    // thats.subscribe()
                }
                //新数据即当前时间段需要的数据,直接喂给图表插件
                if (onLoadedCallback) {
                    onLoadedCallback(list);
                    delete thats.cacheData[tickerCallback];
                }
                //请求完成,设置状态为false
                thats.cacheData[tickerstate] = !1;
                //记录当前缓存时间,即数组最后一位的时间
                if(thats.cacheData[ticker].length>0){
                    thats.lastTime = thats.cacheData[ticker][thats.cacheData[ticker].length - 1].time
                }
            }
            if (data.name == "kline_real") {
                //实时数据
                var data = data.data
                // data带有type,即返回的是订阅数据,
                //缓存的key
                var ticker = thats.symbol + "-" + thats.interval;
                //将后台返回的实时数据变更成TV需要的数据格式
                var barsData = {
                    time: data.t * 1000,
                    open: data.o,
                    high: data.h,
                    low: data.l,
                    close: data.c,
                    volume: data.v
                }
               
                //如果实时数据的时间大于缓存时间,而且缓存有数据,数据长度大于0
                if (barsData.time > thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) {
                    //实时的数据直接加入缓存数组
                    thats.cacheData[ticker].push(barsData)
                    //修改缓存时间
                    thats.lastTime = barsData.time
                } else if (barsData.time == thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) {
                    //如果增量更新的时间等于缓存时间,即在当前时间颗粒内产生了新数据,更新当前数据
                    thats.cacheData[ticker][thats.cacheData[ticker].length - 1] = barsData
                }
               
                // 通知图表插件,可以开始增量更新的渲染了
                thats.datafeeds.barsUpdater.updateData()
            } 
        }
        TVjsApi.prototype.onClose = function () {
            var thats = this;
            // console.log(' >> : 连接已断开... 正在重连')
            thats.socket.doOpen()
            thats.socket.on('open', function () {
                // console.log(' >> : 已重连')
                thats.subscribe()
            });
        }
        TVjsApi.prototype.onError = function () {
            conosle.log("....")
        }
        TVjsApi.prototype.initMessage = function (symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) {
            // console.log('发起请求,从websocket获取当前时间段的数据');
            var that = this;
            //保留当前回调
            var tickerCallback = this.symbol + "-" + resolution + "Callback";
            that.cacheData[tickerCallback] = onLoadedCallback;
            //获取需要请求的数据数目
            var limit = that.initLimit(resolution, rangeStartDate, rangeEndDate);
            //商品名
            var symbol = that.symbol;
            //如果当前时间节点已经改变,停止上一个时间节点的订阅,修改时间节点值
         
            if (that.interval !== resolution) {
                that.unSubscribe(that.interval)
                that.interval = resolution;
            }
            // 获取当前时间段的数据,在onMessage中执行回调onLoadedCallback
            that.socket.send({
                cmd: 'req',
                args: ["candle." + that.interval + "." + symbol.toLowerCase(), limit, rangeEndDate],
               
            })
        }
        TVjsApi.prototype.initLimit = function (resolution, rangeStartDate, rangeEndDate) {
            var limit = 0;
            switch (resolution) {
                //计算当前请求需要的K线条数
                case '1D': limit = Math.ceil((rangeEndDate- rangeStartDate) / 60 / 60 / 24); break;
                case '1W': limit = Math.ceil((rangeEndDate - rangeStartDate) / 60 / 60 / 24 / 7); break;
                case '1M': limit = Math.ceil((rangeEndDate - rangeStartDate) / 60 / 60 / 24 / 31); break;
                default: limit = Math.ceil((rangeEndDate - rangeStartDate) / 60 / resolution); break;
            }
            return limit;
        }
        TVjsApi.prototype.getBars = function (symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) {
            //console.log(' >> :', rangeStartDate, rangeEndDate)
            var ticker = this.symbol + "-" + resolution;
            var tickerload = ticker + "load";
            var tickerstate = ticker + "state";

            if (!this.cacheData[ticker] && !this.cacheData[tickerstate]) {
                //如果缓存没有数据,而且未发出请求,记录当前节点开始时间
                this.cacheData[tickerload] = rangeStartDate;
                //发起请求,从websocket获取当前时间段的数据
                this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback);
                //设置状态为true
                this.cacheData[tickerstate] = !0;
                return false;
            }
            if (!this.cacheData[tickerload] || this.cacheData[tickerload] > rangeStartDate) {
                //如果缓存有数据,但是没有当前时间段的数据,更新当前节点时间
                this.cacheData[tickerload] = rangeStartDate;
                //发起请求,从websocket获取当前时间段的数据
                this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback);
                //设置状态为true
                this.cacheData[tickerstate] = !0;
                return false;
            }
            if (this.cacheData[tickerstate]) {
                //正在从websocket获取数据,禁止一切操作
                return false;
            }
            ticker = this.symbol + "-" + this.interval;
            if (this.cacheData[ticker] && this.cacheData[ticker].length) {
                this.isLoading = false
                var newBars = []
                this.cacheData[ticker].forEach(item => {
                    if (item.time >= rangeStartDate * 1000 && item.time <= rangeEndDate * 1000) {
                        newBars.push(item)
                    }
                })
                onLoadedCallback(newBars)
            } else {
                var self = this
                this.getBarTimer = setTimeout(function () {
                    self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback)
                }, 10)
            }
        }

        TVjsApi.prototype.getStudiesOverrides = function (theme) {

        }
        TVjsApi.prototype.resetTheme = function (skin) {
            this.widgets.addCustomCSSFile('./css/tradingview_' + skin + '.css');
            this.widgets.applyOverrides(this.getOverrides(skin));
            this.widgets.applyStudiesOverrides(this.getStudiesOverrides(skin));
        }
       
        return TVjsApi;
    })();

</script>

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值