ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue 使用TradingView制作K线图(模仿火币)详解

2022-02-21 19:33:00  阅读:1117  来源: 互联网

标签:function 火币 vue TradingView resolution style ._ mainSeriesProperties true


前言:
项目需求要写K线图,echarts图表实现不了里面的功能,所以要用到TradingView,这个真心让人头疼,百度的东西也很少,花了很久才实现出来了。
效果图:
k线是实时更新的,可以正常根据数据跳动
在这里插入图片描述

index.html 引入js和css文件

我这里是vue-cli4 ,vue-cli4和vue-cli2文件夹不一样
如果你是vue-cli4 里面的css和js 记得放在public文件夹里面 ,vue-cli2的话就是正常使用
在这里插入图片描述

创建容器

先建立一个div,用来保存k线图 (样式根据自己项目修改)

<template>
  <div class="tvContent">
    <div id="tv_chart_container"></div>
  </div>
</template>

<style scoped>
.tvContent {
  width: 100%;
  height: 800px;
  background: #061f46;
  border: 1px solid #4390e4;
  border-radius: 5px;
  padding: 20px;
  box-sizing: border-box;
}
#tv_chart_container {
  width: 100%;
  height: 100%;
}
</style>

父组件截图

引入组件 并将需要的参数传给子组件

在这里插入图片描述

剩下的都是子组件

props希望父组件穿的值

props: {
    symbol: {
      type: String,
      required: true,
      default: "BTC/USDT", //币种类型
    },
    currency_id: {
      type: Number,
      required: true,  //id
    },
  },

监听父组件传过来的类型

watch: {
    listenState: function (a, b) {
      //监听交易对
      if (a != b && b != "") {
        this.widget.setSymbol(
          a,
          localStorage.getItem("tim"),
          function onReadyCallback() {}
        ); //切换币种
      }
    },
    symbol: {
      handler(n) {
      //  这个地方是socket的用法 不会的请看socket那篇文章
        this.$socket.on("reconnect");
      },
      deep: true, // 深度监听父组件传过来对象变化
      immediate: true,
    },
  },

data() 里面的数据

      widget: null, //创建的实例
      symbolInfo: null, //信息
      priceScale: 100000, //价格精度
      aa: null,
      time1: null,  

创建 createWidget() 函数

createWidget() {
      let _this = this;
      this.$nextTick(function () {
        let widget = (_this.widget = new TradingView.widget({
          symbol: _this.symbol,
          interval: 1,
          debug: false,
          fullscreen: false,
          autosize: true,
          container_id: "tv_chart_container",
          datafeed: _this.createFeed(),
          library_path: "static/tradeview/charting_library/",
          custom_css_url: "bundles/new.css",
          locale: "zh",
          width: "100%",
          allow_symbol_change: true,
          drawings_access: {
            type: "black",
            // tools: [{name: "Regression Trend"}]//todo: moje
            tools: [
              { name: "Trend Line", grayed: true },
              { name: "Trend Angle", grayed: true },
            ], //todo: bb
          },
          disabled_features: [
            //  禁用的功能
            "left_toolbar", //左侧菜单栏
            "widget_logo", //底部logo
            "header_saveload", //头部保存功能
            "compare_symbol",
            "display_market_status",
            "go_to_date",
            "header_chart_type", //头部类型 下面有自定义
            "header_compare",
            "header_interval_dialog_button",
            "header_resolutions",
            "header_screenshot", //图片上传
            "header_symbol_search",
            "header_undo_redo",
            // "legend_context_menu", //显示币种名称
            "show_hide_button_in_legend",
            "show_interval_dialog_on_key_press",
            // "symbol_info",
            "timeframes_toolbar", //底部时间信息
            "use_localstorage_for_settings",
            "volume_force_overlay",
          ],
          enabled_features: [
            //  启用的功能(备注:disable_resolution_rebuild 功能用于控制当时间范围为1个月时,日期刻度是否都是每个月1号
            "dont_show_boolean_study_arguments",
            "use_localstorage_for_settings",
            "remove_library_container_border",
            "save_chart_properties_to_local_storage",
            "side_toolbar_in_fullscreen_mode",
            "hide_last_na_study_output",
            "constraint_dialogs_movement",
            "legend_widget",
          ],
          charts_storage_url: "http://saveload.tradingview.com",
          charts_storage_api_version: "1.1",
          toolbar_bg: "transparent",
          timezone: "Asia/Shanghai",
          studies_overrides: {
            "volume.precision": "1000",
          },
          overrides: _this.overrides(),
        }));

        widget.MAStudies = [];
        widget.selectedIntervalButton = null;
        widget.onChartReady(function () {
          let buttonArr = [
            {
              value: "5",
              period: "5min",
              text: "5分",
              chartType: 1,
              type: "5min",
            },
            {
              value: "15",
              period: "15min",
              text: "15分",
              chartType: 1,
              type: "15min",
            },
            {
              value: "30",
              period: "30min",
              text: "30分",
              chartType: 1,
              type: "30min",
            },
            {
              value: "60",
              period: "60min",
              text: "60分",
              chartType: 1,
              type: "60min",
            },
            {
              value: "1D",
              period: "1D",
              text: "1天",
              chartType: 1,
              type: "1day",
            },
            {
              value: "1W",
              period: "1W",
              text: "1周",
              chartType: 1,
              type: "1week",
            },
            {
              value: "1M",
              period: "1mon",
              text: "1月",
              chartType: 1,
              type: "1mon",
            },
          ];
          let btn = {};
          let nowTime = "";

          buttonArr.forEach((v, i) => {
            let button = widget.createButton();
            button.attr("title", v.text).addClass("my2").text(v.text);
            if (v.text === "5分") {
              button.css({
                color: "#5786d2",
                "border-bottom": "1px solid #5786d2",
              });
              localStorage.setItem("tim", "5");
            }
            btn = button.on("click", function (e) {
              $(this).parents(".left").children().find(".my2").removeAttr("style");
              handleClick(e, v.value, v.type);
              button.css({
                color: "#5786d2",
                "border-bottom": "1px solid #5786d2",
              });
              _this.$store.commit("upType", v.type);
              widget.chart().setChartType(v.chartType); //改变K线类型
            });
          });
          let handleClick = (e, value, type) => {
            _this.setSymbol = function (symbol, value) {
              gh.chart().setSymbol(symbol, value);
            };
            widget.chart().setResolution(value, function onReadyCallback() {}); //改变分辨率
            $(e.target)
              .addClass("mydate")
              .closest("div.space-single")
              .siblings("div.space-single")
              .find("div.button")
              .removeClass("mydate");
          };
        });

        _this.widget = widget;
      });
    },

createWidget() 函数 里面的注意事项

1. 如果你是vue-cli4 的话 library_pathcustom_css_url 的路径必须要和我的一样,如果用相对路径会报错,vue-cli2的话就是正常使用

在这里插入图片描述
2. 禁用的功能和启用的功能的选择 代码中用到的是一部分功能 根据我的备注 可自行删减 没有的可去文档中寻找

TradingView 中文开发文档 (功能集地址)
https://aitrade.ga/books/tradingview/book/Featuresets.htm

在这里插入图片描述

3. 自定义导航栏周期

在这里插入图片描述
设置默认时间样式以及切换
在这里插入图片描述
创建 createFeed() 函数

createFeed() {
      let this_vue = this;
      let Datafeed = {};

      Datafeed.DataPulseUpdater = function (datafeed, updateFrequency) {
        this._datafeed = datafeed;
        this._subscribers = {};

        this._requestsPending = 0;
        var that = this;

        var update = function () {
          if (that._requestsPending > 0) {
            return;
          }

          for (var listenerGUID in that._subscribers) {
            var subscriptionRecord = that._subscribers[listenerGUID];
            var resolution = subscriptionRecord.resolution;

            var datesRangeRight = parseInt(new Date().valueOf() / 1000);

            //	BEWARE: please note we really need 2 bars, not the only last one
            //	see the explanation below. `10` is the `large enough` value to work around holidays
            var datesRangeLeft =
              datesRangeRight - that.periodLengthSeconds(resolution, 10);

            that._requestsPending++;

            (function (_subscriptionRecord) {
              // eslint-disable-line
              that._datafeed.getBars(
                _subscriptionRecord.symbolInfo,
                resolution,
                datesRangeLeft,
                datesRangeRight,
                function (bars) {
                  that._requestsPending--;

                  //	means the subscription was cancelled while waiting for data
                  if (!that._subscribers.hasOwnProperty(listenerGUID)) {
                    return;
                  }

                  if (bars.length === 0) {
                    return;
                  }

                  var lastBar = bars[bars.length - 1];
                  if (
                    !isNaN(_subscriptionRecord.lastBarTime) &&
                    lastBar.time < _subscriptionRecord.lastBarTime
                  ) {
                    return;
                  }

                  var subscribers = _subscriptionRecord.listeners;

                  //	BEWARE: this one isn't working when first update comes and this update makes a new bar. In this case
                  //	_subscriptionRecord.lastBarTime = NaN
                  var isNewBar =
                    !isNaN(_subscriptionRecord.lastBarTime) &&
                    lastBar.time > _subscriptionRecord.lastBarTime;

                  //	Pulse updating may miss some trades data (ie, if pulse period = 10 secods and new bar is started 5 seconds later after the last update, the
                  //	old bar's last 5 seconds trades will be lost). Thus, at fist we should broadcast old bar updates when it's ready.
                  if (isNewBar) {
                    if (bars.length < 2) {
                      throw new Error(
                        "Not enough bars in history for proper pulse update. Need at least 2."
                      );
                    }

                    var previousBar = bars[bars.length - 2];
                    for (var i = 0; i < subscribers.length; ++i) {
                      subscribers[i](previousBar);
                    }
                  }

                  _subscriptionRecord.lastBarTime = lastBar.time;

                  for (var i = 0; i < subscribers.length; ++i) {
                    subscribers[i](lastBar);
                  }
                },

                //	on error
                function () {
                  that._requestsPending--;
                }
              );
            })(subscriptionRecord);
          }
        };

        if (typeof updateFrequency != "undefined" && updateFrequency > 0) {
          setInterval(update, updateFrequency);
        }
      };

      Datafeed.DataPulseUpdater.prototype.periodLengthSeconds = function (
        resolution,
        requiredPeriodsCount
      ) {
        var daysCount = 0;
        if (resolution === "D") {
          daysCount = requiredPeriodsCount;
        } else if (resolution === "M") {
          daysCount = 31 * requiredPeriodsCount;
        } else if (resolution === "W") {
          daysCount = 7 * requiredPeriodsCount;
        } else {
          daysCount = (requiredPeriodsCount * resolution) / (24 * 60);
        }

        return daysCount * 24 * 60 * 60;
      };

      Datafeed.DataPulseUpdater.prototype.subscribeDataListener = function (
        symbolInfo,
        resolution,
        newDataCallback,
        listenerGUID
      ) {
        this._datafeed._logMessage("Subscribing " + listenerGUID);

        if (!this._subscribers.hasOwnProperty(listenerGUID)) {
          this._subscribers[listenerGUID] = {
            symbolInfo: symbolInfo,
            resolution: resolution,
            lastBarTime: NaN,
            listeners: [],
          };
        }

        this._subscribers[listenerGUID].listeners.push(newDataCallback);
      };

      Datafeed.DataPulseUpdater.prototype.unsubscribeDataListener = function (
        listenerGUID
      ) {
        this._datafeed._logMessage("Unsubscribing " + listenerGUID);
        delete this._subscribers[listenerGUID];
      };

      Datafeed.Container = function (updateFrequency) {
        this._configuration = {
          supports_search: false,
          supports_group_request: false,
          supported_resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
          supports_marks: true,
          supports_timescale_marks: true,
          exchanges: ["gh"],
        };

        this._barsPulseUpdater = new Datafeed.DataPulseUpdater(
          this,
          updateFrequency || 10 * 1000
        );
        // this._quotesPulseUpdater = new Datafeed.QuotesPulseUpdater(this);

        this._enableLogging = true;
        this._callbacks = {};

        this._initializationFinished = true;
        this._fireEvent("initialized");
        this._fireEvent("configuration_ready");
      };

      Datafeed.Container.prototype._fireEvent = function (event, argument) {
        if (this._callbacks.hasOwnProperty(event)) {
          var callbacksChain = this._callbacks[event];
          for (var i = 0; i < callbacksChain.length; ++i) {
            callbacksChain[i](argument);
          }

          this._callbacks[event] = [];
        }
      };

      Datafeed.Container.prototype._logMessage = function (message) {
        if (this._enableLogging) {
          var now = new Date();
        }
      };

      Datafeed.Container.prototype.on = function (event, callback) {
        if (!this._callbacks.hasOwnProperty(event)) {
          this._callbacks[event] = [];
        }

        this._callbacks[event].push(callback);
        return this;
      };

      Datafeed.Container.prototype.onReady = function (callback) {
        let that = this;
        if (that._configuration) {
          setTimeout(function () {
            callback(that._configuration);
          }, 0);
        } else {
          this.on("configuration_ready", function () {
            callback(that._configuration);
          });
        }
      };

      Datafeed.Container.prototype.resolveSymbol = function (
        symbolName,
        onSymbolResolvedCallback,
        onResolveErrorCallback
      ) {
        this._logMessage("GOWNO :: resolve symbol " + symbolName);
        Promise.resolve().then(() => {
          onSymbolResolvedCallback({
            name: this_vue.symbol,
            timezone: "Asia/Shanghai",
            pricescale: this_vue.priceScale,
            minmov: 1, //minmov(最小波动), pricescale(价格精度), minmove2, fractional(分数)
            minmov2: 0, //这是一个神奇的数字来格式化复杂情况下的价格。
            ticker: this_vue.symbol,
            description: "",
            type: "bitcoin",
            volume_precision: 8,
            // "exchange-traded": "sdt",
            // "exchange-listed": "sdt",
            //现在,这两个字段都为某个交易所的略称。将被显示在图表的图例中,以表示此商品。目前此字段不用于其他目的。
            has_intraday: true,
            has_weekly_and_monthly: true,
            has_no_volume: false, //布尔表示商品是否拥有成交量数据。
            session: "24x7",
            supported_resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
          });
        });
      };

      //初始化数据
      Datafeed.Container.prototype.getBars = async function (
        symbolInfo,
        resolution,
        rangeStartDate,
        rangeEndDate,
        onHistoryCallback,
        one rrorCallback
      ) {
        if (
          resolution.indexOf("D") == -1 &&
          resolution.indexOf("W") == -1 &&
          resolution.indexOf("M") == -1
        ) {
          resolution = resolution + "min";
        } else if (resolution.indexOf("W") != -1 || resolution.indexOf("M") != -1) {
          resolution = resolution;
        }
        //this_vue.newTimeshar  我请求历史数据的封装方法 换成自己的
        const res = await this_vue.newTimeshar({
          from: rangeStartDate,
          to: rangeEndDate,
          symbol: symbolInfo.name,
          period: resolution,
          currency_id: this_vue.currency_id,
        });

        if (res.code == 1 && res.data.length > 0) {
          this_vue.$store.commit("upSma1", res.data[res.data.length - 2].sma1);
          this_vue.$store.commit("upSma2", res.data[res.data.length - 2].sma2);
          //我是实时传送数据到后台 如果用不到自己删除
          this_vue.time1 = setInterval(function () {
            this_vue.$socket.emit("sub", this_vue.emitData); //触发socket连接
          }, 1000);
          //清楚计时器
          this_vue.$once("hook:beforeDestroy", () => {
            clearInterval(this_vue.time1);
          });
          res.data.forEach((item, i) => {
            item.open = Number(item.open);
            item.close = Number(item.close);
            item.high = Number(item.high);
            item.low = Number(item.low);
          });
          onHistoryCallback(res.data, { noData: false });
          onHistoryCallback([], { noData: true });
        }
        if (!res.data || res.code == -1) {
          onHistoryCallback([], { noData: true });
        }
        if (res.data && res.data.length == 0) {
          onHistoryCallback([], { noData: true });
        }
      };
      //实时数据
      Datafeed.Container.prototype.subscribeBars = function (
        symbolInfo,
        resolution,
        onRealtimeCallback,
        listenerGUID,
        onResetCacheNeededCallback
      ) {
        this_vue.connect(onRealtimeCallback);

        //this._barsPulseUpdater.subscribeDataListener(symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback);
      };

      Datafeed.Container.prototype.unsubscribeBars = function (listenerGUID) {
        this._barsPulseUpdater.unsubscribeDataListener(listenerGUID);
      };

      return new Datafeed.Container();
    },

createFeed() 函数 里面的注意事项

1. 获取历史数据

this_vue.newTimeshar()是我封装的获取数据的接口 换成你自己的就可
在这里插入图片描述
在这里插入图片描述
2. 实时更新数据
this_vue.connect()实时回调在这里插入图片描述

创建 overrides() 函数

overrides() {
      let style = {
        up: "#12b886", //升
        down: "#fa5252", //降
        bg: "#061f46",  //背景
        grid: "rgba(122, 152, 247, .2)",
        cross: "#fff", //十字线
        border: "rgba(122, 152, 247, .2)",
        text: "rgba(122, 152, 247, .6)", //文字
        areatop: "rgba(122, 152, 247, .2)",
        areadown: "rgba(122, 152, 247, .2)",
        line: "rgba(122, 152, 247, .2)",
      };
      return {
        volumePaneSize: "medium", //large, medium, small, tiny
        "paneProperties.topMargin": "20",
        "scalesProperties.lineColor": style.text,
        "scalesProperties.textColor": style.text,
        "paneProperties.background": style.bg, //改变背景色的重要代码
        "paneProperties.vertGridProperties.color": style.grid,
        "paneProperties.horzGridProperties.color": style.grid,
        "paneProperties.crossHairProperties.color": style.cross,
        "paneProperties.crossHairProperties.lineType": 2,
        "paneProperties.legendProperties.showLegend": true,
        "paneProperties.legendProperties.showStudyArguments": true,
        "paneProperties.legendProperties.showStudyTitles": true,
        "paneProperties.legendProperties.showStudyValues": true,
        "paneProperties.legendProperties.showSeriesTitle": true,
        "paneProperties.legendProperties.showSeriesOHLC": true,
        "mainSeriesProperties.candleStyle.upColor": style.up,
        "mainSeriesProperties.candleStyle.downColor": style.down,
        "mainSeriesProperties.candleStyle.drawWick": true,
        "mainSeriesProperties.candleStyle.drawBorder": true,
        "mainSeriesProperties.candleStyle.borderColor": style.border,
        "mainSeriesProperties.candleStyle.borderUpColor": style.up,
        "mainSeriesProperties.candleStyle.borderDownColor": style.down,
        "mainSeriesProperties.candleStyle.wickUpColor": style.up,
        "mainSeriesProperties.candleStyle.wickDownColor": style.down,
        "mainSeriesProperties.candleStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.hollowCandleStyle.upColor": style.up,
        "mainSeriesProperties.hollowCandleStyle.downColor": style.down,

        "mainSeriesProperties.hollowCandleStyle.drawWick": true,
        "mainSeriesProperties.hollowCandleStyle.drawBorder": true,
        "mainSeriesProperties.hollowCandleStyle.borderColor": style.border,
        "mainSeriesProperties.hollowCandleStyle.borderUpColor": style.up,
        "mainSeriesProperties.hollowCandleStyle.borderDownColor": style.down,
        "mainSeriesProperties.hollowCandleStyle.wickColor": style.line,
        "mainSeriesProperties.haStyle.upColor": style.up,
        "mainSeriesProperties.haStyle.downColor": style.down,
        "mainSeriesProperties.haStyle.drawWick": true,
        "mainSeriesProperties.haStyle.drawBorder": true,
        "mainSeriesProperties.haStyle.borderColor": style.border,
        "mainSeriesProperties.haStyle.borderUpColor": style.up,
        "mainSeriesProperties.haStyle.borderDownColor": style.down,
        "mainSeriesProperties.haStyle.wickColor": style.border,
        "mainSeriesProperties.haStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.barStyle.upColor": style.up,
        "mainSeriesProperties.barStyle.downColor": style.down,
        "mainSeriesProperties.barStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.barStyle.dontDrawOpen": false,
        "mainSeriesProperties.lineStyle.color": style.border,
        "mainSeriesProperties.lineStyle.linewidth": 2,
        "mainSeriesProperties.lineStyle.styleType": 2,
        "mainSeriesProperties.lineStyle.linestyle": 2,
        "mainSeriesProperties.lineStyle.priceSource": "close",
        "mainSeriesProperties.areaStyle.color1": style.areatop,
        "mainSeriesProperties.areaStyle.color2": style.areadown,
        "mainSeriesProperties.areaStyle.linecolor": style.border,
        "mainSeriesProperties.areaStyle.linewidth": 2,
        "mainSeriesProperties.areaStyle.linestyle": 2,
        "mainSeriesProperties.areaStyle.priceSource": "close",
      };
    },

overrides() 函数 里面的注意事项

颜色根据自己的项目进行修改 (其他样式参考中文文档)

TradingView 中文开发文档 (形状与覆盖地址)
https://aitrade.ga/books/tradingview/book/Shapes-and-Overrides.html

在这里插入图片描述

tv全部完整代码:
我里面用的是socket.io连接的后台,进行的实时对接数据,
如果你是用的其他方法,直接把socket.io里面的东西替换就行。
如果你对socket.io感兴趣,请看链接: https://blog.csdn.net//article/details/106191646.

<template>
  <div class="tvContent">
    <div id="tv_chart_container"></div>
  </div>
</template>

<style scoped>
.tvContent {
  width: 100%;
  height: 800px;
  background: #061f46;
  border: 1px solid #4390e4;
  border-radius: 5px;
  padding: 20px;
  box-sizing: border-box;
}
#tv_chart_container {
  width: 100%;
  height: 100%;
}
</style>
<script>
import { mapActions } from "vuex";
export default {
  name: "tv",
  props: {
    symbol: {
      type: String,
      required: true,
      default: "BTC/USDT",
    },
    currency_id: {
      type: Number,
      required: true,
    },
  },
  data() {
    return {
      widget: null, //创建的实例
      symbolInfo: null, //信息
      priceScale: 100000, //价格精度
      aa: null,
      time1: null, 
    };
  },
  // 我使用的socket
  sockets: {
    //查看socket是否渲染成功
    connect() {},
    disconnect() {
      console.log("断开链接");
    }, //检测socket断开链接
    reconnect() {
      console.log("重新链接");
      // this.$socket.emit("connection", 1);
      this.$socket.open();
    },
    //客户端接收后台传输的socket事件
    kline: function (msg) {
      let obj = {};
      var that = this;
      let type = that.$store.state.type;

      if (that.symbol == msg.symbol && msg.period == type) {
        obj.open = Number(msg.open);
        obj.low = Number(msg.low);
        obj.high = Number(msg.high);
        obj.close = Number(msg.close);
        obj.volume = Number(msg.volume);
        obj.time = Number(msg.time);
        if (that.$store.state.nextId != msg.id) {
          that.$store.commit("upId", msg.id);
          that.$store.commit("upSma1", msg.sma1);
          that.$store.commit("upSma2", msg.sma2);
        }

        this.aa && this.aa(obj);
      }
    },
  },
  computed: {
    listenState() {
      //监听交易对
      return this.symbol;
    },
    kind() {
      return this.symbol.split("/")[0].toLowerCase();
    },
    // 我传给后台的东西 自己进行删减
    emitData() {
      return (
        this.kind +
        "-" +
        this.$store.state.type +
        "-" +
        this.$store.state.sma1 +
        "-" +
        this.$store.state.sma2
      );
    },
  },
  watch: {
    listenState: function (a, b) {
      //监听交易对
      if (a != b && b != "") {
        this.widget.setSymbol(
          a,
          localStorage.getItem("tim"),
          function onReadyCallback() {}
        ); //切换币种
      }
    },
    symbol: {
      handler(n) {
        this.$socket.on("reconnect");
      },
      deep: true, // 深度监听父组件传过来对象变化
      immediate: true,
    },
  },
  mounted() {
    this.createWidget();
  },
  destroyed() {
    this.removeWidget();
  },

  methods: {
    ...mapActions({
      newTimeshar: "home/newTimeshar",
    }),
    connect(real) {
      this.aa = real;
      let that = this;
      //实时数据进行回调链接socket
      this.$socket.on("connect", function () {
        this.$socket.on("kline", (msg) => {
          let obj = {};
          let type = that.$store.state.type;
          if (that.symbol == msg.symbol && msg.period == type) {
            obj.open = Number(msg.open);
            obj.low = Number(msg.low);
            obj.high = Number(msg.high);
            obj.close = Number(msg.close);
            obj.volume = Number(msg.volume);
            obj.time = Number(msg.time);
            if (that.$store.state.nextId != msg.id) {
              that.$store.commit("upId", msg.id);
              that.$store.commit("upSma1", msg.sma1);
              that.$store.commit("upSma2", msg.sma2);
            }

            real(obj);
          }
        });
      });
    },
    createWidget() {
      let _this = this;
      this.$nextTick(function () {
        let widget = (_this.widget = new TradingView.widget({
          symbol: _this.symbol,
          interval: 1,
          debug: false,
          fullscreen: false,
          autosize: true,
          container_id: "tv_chart_container",
          // datafeed: new Datafeeds.UDFCompatibleDatafeed(
          //   "http://demo_feed.tradingview.com"
          // ),
          datafeed: _this.createFeed(),
          library_path: "static/tradeview/charting_library/",
          custom_css_url: "bundles/new.css",
          locale: "zh",
          width: "100%",
          allow_symbol_change: true,
          drawings_access: {
            type: "black",
            // tools: [{name: "Regression Trend"}]//todo: moje
            tools: [
              { name: "Trend Line", grayed: true },
              { name: "Trend Angle", grayed: true },
            ], //todo: bb
          },
          disabled_features: [
            //  禁用的功能
            // "left_toolbar", //左侧菜单栏
            "widget_logo", //底部logo
            "header_saveload", //头部保存功能
            "compare_symbol",
            "display_market_status",
            "go_to_date",
            "header_chart_type", //头部类型 下面有自定义
            "header_compare",
            "header_interval_dialog_button",
            "header_resolutions",
            "header_screenshot", //图片上传
            "header_symbol_search",
            "header_undo_redo",
            // "legend_context_menu", //显示币种名称
            "show_hide_button_in_legend",
            "show_interval_dialog_on_key_press",
            // "symbol_info",
            "timeframes_toolbar", //底部时间信息
            "use_localstorage_for_settings",
            "volume_force_overlay",
          ],
          enabled_features: [
            //  启用的功能(备注:disable_resolution_rebuild 功能用于控制当时间范围为1个月时,日期刻度是否都是每个月1号
            "dont_show_boolean_study_arguments",
            "use_localstorage_for_settings",
            "remove_library_container_border",
            "save_chart_properties_to_local_storage",
            "side_toolbar_in_fullscreen_mode",
            "hide_last_na_study_output",
            "constraint_dialogs_movement",
            "legend_widget",
          ],
          charts_storage_url: "http://saveload.tradingview.com",
          charts_storage_api_version: "1.1",
          toolbar_bg: "transparent",
          timezone: "Asia/Shanghai",
          studies_overrides: {
            "volume.precision": "1000",
          },
          overrides: _this.overrides(),
        }));

        widget.MAStudies = [];
        widget.selectedIntervalButton = null;
        widget.onChartReady(function () {
          let buttonArr = [
            {
              value: "1",
              period: "1min",
              text: "1分",
              chartType: 1,
              type: "1min",
            },
            {
              value: "5",
              period: "5min",
              text: "5分",
              chartType: 1,
              type: "5min",
            },
            {
              value: "30",
              period: "30min",
              text: "30分",
              chartType: 1,
              type: "30min",
            },
            {
              value: "60",
              period: "60min",
              text: "60分",
              chartType: 1,
              type: "60min",
            },
            {
              value: "1D",
              period: "1D",
              text: "1天",
              chartType: 1,
              type: "1day",
            },
            {
              value: "1W",
              period: "1W",
              text: "1周",
              chartType: 1,
              type: "1week",
            },
            {
              value: "1M",
              period: "1mon",
              text: "1月",
              chartType: 1,
              type: "1mon",
            },
          ];
          let btn = {};
          let nowTime = "";

          buttonArr.forEach((v, i) => {
            let button = widget.createButton();
            button.attr("title", v.text).addClass("my2").text(v.text);
            if (v.text === "5分") {
              button.css({
                color: "#5786d2",
                "border-bottom": "1px solid #5786d2",
              });
              localStorage.setItem("tim", "5");
            }
            btn = button.on("click", function (e) {
              $(this).parents(".left").children().find(".my2").removeAttr("style");
              handleClick(e, v.value, v.type);
              button.css({
                color: "#5786d2",
                "border-bottom": "1px solid #5786d2",
              });
              _this.$store.commit("upType", v.type);
              widget.chart().setChartType(v.chartType); //改变K线类型
            });
          });
          let handleClick = (e, value, type) => {
            _this.setSymbol = function (symbol, value) {
              gh.chart().setSymbol(symbol, value);
            };
            widget.chart().setResolution(value, function onReadyCallback() {}); //改变分辨率
            $(e.target)
              .addClass("mydate")
              .closest("div.space-single")
              .siblings("div.space-single")
              .find("div.button")
              .removeClass("mydate");
          };
        });

        _this.widget = widget;
      });
    },
    createFeed() {
      let this_vue = this;
      let Datafeed = {};

      Datafeed.DataPulseUpdater = function (datafeed, updateFrequency) {
        this._datafeed = datafeed;
        this._subscribers = {};

        this._requestsPending = 0;
        var that = this;

        var update = function () {
          if (that._requestsPending > 0) {
            return;
          }

          for (var listenerGUID in that._subscribers) {
            var subscriptionRecord = that._subscribers[listenerGUID];
            var resolution = subscriptionRecord.resolution;

            var datesRangeRight = parseInt(new Date().valueOf() / 1000);

            //	BEWARE: please note we really need 2 bars, not the only last one
            //	see the explanation below. `10` is the `large enough` value to work around holidays
            var datesRangeLeft =
              datesRangeRight - that.periodLengthSeconds(resolution, 10);

            that._requestsPending++;

            (function (_subscriptionRecord) {
              // eslint-disable-line
              that._datafeed.getBars(
                _subscriptionRecord.symbolInfo,
                resolution,
                datesRangeLeft,
                datesRangeRight,
                function (bars) {
                  that._requestsPending--;

                  //	means the subscription was cancelled while waiting for data
                  if (!that._subscribers.hasOwnProperty(listenerGUID)) {
                    return;
                  }

                  if (bars.length === 0) {
                    return;
                  }

                  var lastBar = bars[bars.length - 1];
                  if (
                    !isNaN(_subscriptionRecord.lastBarTime) &&
                    lastBar.time < _subscriptionRecord.lastBarTime
                  ) {
                    return;
                  }

                  var subscribers = _subscriptionRecord.listeners;

                  //	BEWARE: this one isn't working when first update comes and this update makes a new bar. In this case
                  //	_subscriptionRecord.lastBarTime = NaN
                  var isNewBar =
                    !isNaN(_subscriptionRecord.lastBarTime) &&
                    lastBar.time > _subscriptionRecord.lastBarTime;

                  //	Pulse updating may miss some trades data (ie, if pulse period = 10 secods and new bar is started 5 seconds later after the last update, the
                  //	old bar's last 5 seconds trades will be lost). Thus, at fist we should broadcast old bar updates when it's ready.
                  if (isNewBar) {
                    if (bars.length < 2) {
                      throw new Error(
                        "Not enough bars in history for proper pulse update. Need at least 2."
                      );
                    }

                    var previousBar = bars[bars.length - 2];
                    for (var i = 0; i < subscribers.length; ++i) {
                      subscribers[i](previousBar);
                    }
                  }

                  _subscriptionRecord.lastBarTime = lastBar.time;

                  for (var i = 0; i < subscribers.length; ++i) {
                    subscribers[i](lastBar);
                  }
                },

                //	on error
                function () {
                  that._requestsPending--;
                }
              );
            })(subscriptionRecord);
          }
        };

        if (typeof updateFrequency != "undefined" && updateFrequency > 0) {
          setInterval(update, updateFrequency);
        }
      };

      Datafeed.DataPulseUpdater.prototype.periodLengthSeconds = function (
        resolution,
        requiredPeriodsCount
      ) {
        var daysCount = 0;
        if (resolution === "D") {
          daysCount = requiredPeriodsCount;
        } else if (resolution === "M") {
          daysCount = 31 * requiredPeriodsCount;
        } else if (resolution === "W") {
          daysCount = 7 * requiredPeriodsCount;
        } else {
          daysCount = (requiredPeriodsCount * resolution) / (24 * 60);
        }

        return daysCount * 24 * 60 * 60;
      };

      Datafeed.DataPulseUpdater.prototype.subscribeDataListener = function (
        symbolInfo,
        resolution,
        newDataCallback,
        listenerGUID
      ) {
        this._datafeed._logMessage("Subscribing " + listenerGUID);

        if (!this._subscribers.hasOwnProperty(listenerGUID)) {
          this._subscribers[listenerGUID] = {
            symbolInfo: symbolInfo,
            resolution: resolution,
            lastBarTime: NaN,
            listeners: [],
          };
        }

        this._subscribers[listenerGUID].listeners.push(newDataCallback);
      };

      Datafeed.DataPulseUpdater.prototype.unsubscribeDataListener = function (
        listenerGUID
      ) {
        this._datafeed._logMessage("Unsubscribing " + listenerGUID);
        delete this._subscribers[listenerGUID];
      };

      Datafeed.Container = function (updateFrequency) {
        this._configuration = {
          supports_search: false,
          supports_group_request: false,
          supported_resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
          supports_marks: true,
          supports_timescale_marks: true,
          exchanges: ["gh"],
        };

        this._barsPulseUpdater = new Datafeed.DataPulseUpdater(
          this,
          updateFrequency || 10 * 1000
        );
        // this._quotesPulseUpdater = new Datafeed.QuotesPulseUpdater(this);

        this._enableLogging = true;
        this._callbacks = {};

        this._initializationFinished = true;
        this._fireEvent("initialized");
        this._fireEvent("configuration_ready");
      };

      Datafeed.Container.prototype._fireEvent = function (event, argument) {
        if (this._callbacks.hasOwnProperty(event)) {
          var callbacksChain = this._callbacks[event];
          for (var i = 0; i < callbacksChain.length; ++i) {
            callbacksChain[i](argument);
          }

          this._callbacks[event] = [];
        }
      };

      Datafeed.Container.prototype._logMessage = function (message) {
        if (this._enableLogging) {
          var now = new Date();
        }
      };

      Datafeed.Container.prototype.on = function (event, callback) {
        if (!this._callbacks.hasOwnProperty(event)) {
          this._callbacks[event] = [];
        }

        this._callbacks[event].push(callback);
        return this;
      };

      Datafeed.Container.prototype.onReady = function (callback) {
        let that = this;
        if (that._configuration) {
          setTimeout(function () {
            callback(that._configuration);
          }, 0);
        } else {
          this.on("configuration_ready", function () {
            callback(that._configuration);
          });
        }
      };

      Datafeed.Container.prototype.resolveSymbol = function (
        symbolName,
        onSymbolResolvedCallback,
        onResolveErrorCallback
      ) {
        this._logMessage("GOWNO :: resolve symbol " + symbolName);
        Promise.resolve().then(() => {
          onSymbolResolvedCallback({
            name: this_vue.symbol,
            timezone: "Asia/Shanghai",
            pricescale: this_vue.priceScale,
            minmov: 1, //minmov(最小波动), pricescale(价格精度), minmove2, fractional(分数)
            minmov2: 0, //这是一个神奇的数字来格式化复杂情况下的价格。
            ticker: this_vue.symbol,
            description: "",
            type: "bitcoin",
            volume_precision: 8,
            // "exchange-traded": "sdt",
            // "exchange-listed": "sdt",
            //现在,这两个字段都为某个交易所的略称。将被显示在图表的图例中,以表示此商品。目前此字段不用于其他目的。
            has_intraday: true,
            has_weekly_and_monthly: true,
            has_no_volume: false, //布尔表示商品是否拥有成交量数据。
            session: "24x7",
            supported_resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
          });
        });
      };

      //初始化数据
      Datafeed.Container.prototype.getBars = async function (
        symbolInfo,
        resolution,
        rangeStartDate,
        rangeEndDate,
        onHistoryCallback,
        one rrorCallback
      ) {
        if (
          resolution.indexOf("D") == -1 &&
          resolution.indexOf("W") == -1 &&
          resolution.indexOf("M") == -1
        ) {
          resolution = resolution + "min";
        } else if (resolution.indexOf("W") != -1 || resolution.indexOf("M") != -1) {
          resolution = resolution;
        }

        //this_vue.newTimeshar  我请求历史数据的封装方法 换成自己的
        const res = await this_vue.newTimeshar({
          from: rangeStartDate,
          to: rangeEndDate,
          symbol: symbolInfo.name,
          period: resolution,
          currency_id: this_vue.currency_id,
        });

        if (res.code == 1 && res.data.length > 0) {
          this_vue.$store.commit("upSma1", res.data[res.data.length - 2].sma1);
          this_vue.$store.commit("upSma2", res.data[res.data.length - 2].sma2);
          //我是实时传送数据到后台 如果用不到自己删除
          this_vue.time1 = setInterval(function () {
            this_vue.$socket.emit("sub", this_vue.emitData); //触发socket连接
          }, 1000);
          //清楚计时器
          this_vue.$once("hook:beforeDestroy", () => {
            clearInterval(this_vue.time1);
          });
          res.data.forEach((item, i) => {
            item.open = Number(item.open);
            item.close = Number(item.close);
            item.high = Number(item.high);
            item.low = Number(item.low);
          });
          onHistoryCallback(res.data, { noData: false });
          onHistoryCallback([], { noData: true });
        }
        if (!res.data || res.code == -1) {
          onHistoryCallback([], { noData: true });
        }
        if (res.data && res.data.length == 0) {
          onHistoryCallback([], { noData: true });
        }
      };
      //实时数据
      Datafeed.Container.prototype.subscribeBars = function (
        symbolInfo,
        resolution,
        onRealtimeCallback,
        listenerGUID,
        onResetCacheNeededCallback
      ) {
        this_vue.connect(onRealtimeCallback);

        //this._barsPulseUpdater.subscribeDataListener(symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback);
      };

      Datafeed.Container.prototype.unsubscribeBars = function (listenerGUID) {
        this._barsPulseUpdater.unsubscribeDataListener(listenerGUID);
      };

      return new Datafeed.Container();
    },

    updateData(data) {
      if (data) {
        this.$emit("real-time", data);
      }
    },

    updateWidget(item) {
      this.symbolInfo = {
        name: item,
        ticker: item,
        description: "",
        session: "24x7",
        supported_resolutions: ["5", "15", "30", "60", "1D", "1W", "1M"],
        has_intraday: true,
        has_daily: true,
        has_weekly_and_monthly: true,
        timezone: "UTC",
      };

      this.removeWidget();
      this.createWidget();
    },
    removeWidget() {
      if (this.widget) {
        this.widget.remove();
        this.widget = null;
      }
    },
    overrides() {
      let style = {
        up: "#12b886", //升
        down: "#fa5252", //降
        bg: "#061f46",  //背景
        grid: "rgba(122, 152, 247, .2)",
        cross: "#fff", //十字线
        border: "rgba(122, 152, 247, .2)",
        text: "rgba(122, 152, 247, .6)", //文字
        areatop: "rgba(122, 152, 247, .2)",
        areadown: "rgba(122, 152, 247, .2)",
        line: "rgba(122, 152, 247, .2)",
      };
      return {
        volumePaneSize: "medium", //large, medium, small, tiny
        "paneProperties.topMargin": "20",
        "scalesProperties.lineColor": style.text,
        "scalesProperties.textColor": style.text,
        "paneProperties.background": style.bg, //改变背景色的重要代码
        "paneProperties.vertGridProperties.color": style.grid,
        "paneProperties.horzGridProperties.color": style.grid,
        "paneProperties.crossHairProperties.color": style.cross,
        "paneProperties.crossHairProperties.lineType": 2,
        "paneProperties.legendProperties.showLegend": true,
        "paneProperties.legendProperties.showStudyArguments": true,
        "paneProperties.legendProperties.showStudyTitles": true,
        "paneProperties.legendProperties.showStudyValues": true,
        "paneProperties.legendProperties.showSeriesTitle": true,
        "paneProperties.legendProperties.showSeriesOHLC": true,
        "mainSeriesProperties.candleStyle.upColor": style.up,
        "mainSeriesProperties.candleStyle.downColor": style.down,
        "mainSeriesProperties.candleStyle.drawWick": true,
        "mainSeriesProperties.candleStyle.drawBorder": true,
        "mainSeriesProperties.candleStyle.borderColor": style.border,
        "mainSeriesProperties.candleStyle.borderUpColor": style.up,
        "mainSeriesProperties.candleStyle.borderDownColor": style.down,
        "mainSeriesProperties.candleStyle.wickUpColor": style.up,
        "mainSeriesProperties.candleStyle.wickDownColor": style.down,
        "mainSeriesProperties.candleStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.hollowCandleStyle.upColor": style.up,
        "mainSeriesProperties.hollowCandleStyle.downColor": style.down,

        "mainSeriesProperties.hollowCandleStyle.drawWick": true,
        "mainSeriesProperties.hollowCandleStyle.drawBorder": true,
        "mainSeriesProperties.hollowCandleStyle.borderColor": style.border,
        "mainSeriesProperties.hollowCandleStyle.borderUpColor": style.up,
        "mainSeriesProperties.hollowCandleStyle.borderDownColor": style.down,
        "mainSeriesProperties.hollowCandleStyle.wickColor": style.line,
        "mainSeriesProperties.haStyle.upColor": style.up,
        "mainSeriesProperties.haStyle.downColor": style.down,
        "mainSeriesProperties.haStyle.drawWick": true,
        "mainSeriesProperties.haStyle.drawBorder": true,
        "mainSeriesProperties.haStyle.borderColor": style.border,
        "mainSeriesProperties.haStyle.borderUpColor": style.up,
        "mainSeriesProperties.haStyle.borderDownColor": style.down,
        "mainSeriesProperties.haStyle.wickColor": style.border,
        "mainSeriesProperties.haStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.barStyle.upColor": style.up,
        "mainSeriesProperties.barStyle.downColor": style.down,
        "mainSeriesProperties.barStyle.barColorsOnPrevClose": false,
        "mainSeriesProperties.barStyle.dontDrawOpen": false,
        "mainSeriesProperties.lineStyle.color": style.border,
        "mainSeriesProperties.lineStyle.linewidth": 2,
        "mainSeriesProperties.lineStyle.styleType": 2,
        "mainSeriesProperties.lineStyle.linestyle": 2,
        "mainSeriesProperties.lineStyle.priceSource": "close",
        "mainSeriesProperties.areaStyle.color1": style.areatop,
        "mainSeriesProperties.areaStyle.color2": style.areadown,
        "mainSeriesProperties.areaStyle.linecolor": style.border,
        "mainSeriesProperties.areaStyle.linewidth": 2,
        "mainSeriesProperties.areaStyle.linestyle": 2,
        "mainSeriesProperties.areaStyle.priceSource": "close",
      };
    },
  },
};
</script>

TradingView 拓展

TradingView设置均线和隐藏均线: https://blog.csdn.net//article/details/118582663.

vue TradingView为k线做标记: https://blog.csdn.net//article/details/118584281.

标签:function,火币,vue,TradingView,resolution,style,._,mainSeriesProperties,true
来源: https://blog.csdn.net/web15870125901/article/details/123053879

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有