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

正式开始了
第一步:
index.html 引入js和css文件
我这里是vue-cli4 ,vue-cli4和vue-cli2文件夹不一样 如果你是vue-cli4 里面的css和js 记得放在public文件夹里面,vue-cli2的话就是正常使用

第二步:
先建立一个div,用来保存k线图

我的data里面的数据
(里面都是我用到的东西,懒得删了,用不到你可以删了)
widget
: null, //创建实例
symbolInfo
: null,
feed
: null,
wsEx
: null,
ws
: null,
reals
: null,
lists
: [],
newData
: "",
symbol
: "",
priceScale
: 100000,
histime
: "",
socketTypr
: false,
bg_
: "#fff", //背景颜色
isDay
: "",
grid
: "#cbcbcb", //线条颜色
cross
: "#cbcbcb"
第三步:
开始准备数据
this.symbol 是我获取的币种类型,如果你不需要获取直接在下面写死就可以了

第四步:
创建 createWidget() 函数 (它和正常函数一样是放在methods里面的)
里面的 symbol 要么是上面获取的,要么你自己写死
container_id 对应的id就是你创建的div的id

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

createWidget()完整代码
createWidget() {
let _this
= this;
this.$nextTick(function () {
let widget
= (_this
.widget
= new TradingView.widget({
symbol
: "BTC/USDT",
interval
: 1,
debug
: true,
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
: _this
.setLanguage
,
width
: "100%",
height
: 516,
drawings_access
: {
type
: "black",
tools
: [{ name
: "Regression Trend" }],
},
disabled_features
: [
// 禁用的功能
"left_toolbar",
"widget_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",
"snapshot_trading_drawings",
"symbol_info",
"timeframes_toolbar",
"use_localstorage_for_settings",
"volume_force_overlay",
],
enabled_features
: [
// 启用的功能(备注:disable_resolution_rebuild 功能用于控制当时间范围为1个月时,日期刻度是否都是每个月1号
"dont_show_boolean_study_arguments",
"hide_last_na_study_output",
"move_logo_to_main_pane",
"same_data_requery",
"side_toolbar_in_fullscreen_mode",
"disable_resolution_rebuild",
],
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.setLanguage('en')
widget
.onChartReady(function () {
//图表方法
// document.getElementById('trade-view').childNodes[0].setAttribute('style', 'display:block;width:100%;height:100%;');
//let that =this
widget
.chart()
.createStudy(
"Moving Average",
false,
true,
[15, "close", 0],
null,
{
"Plot.color": "#e843da",
}
);
// widget.chart().createStudy("MA Cross", false, false, [10, 20]);
let buttonArr
= [
{
value
: "1min",
period
: "1",
text
: "分时",
chartType
: 3,
type
: "min",
},
{
value
: "1",
period
: "1m",
text
: "1分",
chartType
: 1,
type
: "1min",
},
{
value
: "5",
period
: "5m",
text
: "5分",
chartType
: 1,
type
: "5min",
},
// {
// value: "15",
// period: "15m",
// text: "15分钟",
// chartType: 1,
// type:"15min"
// },
{
value
: "30",
period
: "30m",
text
: "30分",
chartType
: 1,
type
: "30min",
},
{
value
: "60",
period
: "60分",
text
: "1小时",
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
: "1M",
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
== "1分") {
button
.css({
color
: "#5786d2",
"border-bottom": "1px solid #5786d2",
});
localStorage
.setItem("tim", "1"); //默认为1分钟
localStorage
.setItem("type", "1min"); //默认为1
}
btn
= button
.on("click", function (e
) {
$(this)
.parents(".left")
.children()
.find(".my2")
.removeAttr("style"); //去掉1分钟的
handleClick(e
, v
.value
, v
.type
);
button
.css({
color
: "#5786d2",
"border-bottom": "1px solid #5786d2",
});
widget
.chart().setChartType(v
.chartType
); //改变K线类型
});
});
let handleClick = (e
, value
, type
) => {
console
.log(value
);
_this
.setSymbol = function (symbol
, value
) {
gh
.chart().setSymbol(symbol
, value
);
};
localStorage
.setItem("tim", value
);
localStorage
.setItem("type", type
);
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
;
});
},
});
},
buttonArr 是头部分离出来的导航,没有用自带的样式,你想写几种分类,就在这里面添加或者删除

然后循环buttonArr,开始给选中的导航增加css样式,这个可以自己定义
css在你引入的css文件里面添加,当前这个vue修改不了

第五步:
上面createWidget() 里面用到了createFeed() 方法,现在开始写createFeed() 方法

createFeed()完整代码
createFeed() {
let this_vue
= this;
let Datafeed
= {};
Datafeed
.DataPulseUpdater = function (datafeed
, updateFrequency
) {
this._datafeed
= datafeed
;
this._subscribers
= {};
this._re
= 0;
var that
= this;
var update = function () {
if (that
._re
> 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
._re
++;
(function (_subscriptionRecord
) {
// eslint-disable-line
that
._datafeed
.getBars(
_subscriptionRecord
.symbolInfo
,
resolution
,
datesRangeLeft
,
datesRangeRight
,
function (bars
) {
that
._re
--;
// 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
if (bars
.length
subscribers
[i
](previousBar
);
}
}
_subscriptionRecord
.lastBarTime
= lastBar
.time
;
for (var i
= 0; i
that
._re
--;
}
);
})(subscriptionRecord
);
}
};
if (typeof updateFrequency
!= "undefined" && updateFrequency
> 0) {
setInterval(update
, updateFrequency
);
}
};
Datafeed
.DataPulseUpdater
.prototype
.periodLengthSeconds = function (
resolution
,
re
) {
var daysCount
= 0;
if (resolution
=== "D") {
daysCount
= re
;
} else if (resolution
=== "M") {
daysCount
= 31 * re
;
} else if (resolution
=== "W") {
daysCount
= 7 * re
;
} else {
daysCount
= (re
* 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
: [
"1",
"3",
"5",
"15",
"30",
"60",
"120",
"240",
"360",
"720",
"1D",
"3D",
"1W",
"1M",
],
supports_marks
: true,
supports_timescale_marks
: true,
exchanges
: ["gh"],
};
this._barsPulseUpdater
= new Datafeed.DataPulseUpdater(
this,
updateFrequency
|| 10 * 1000
);
// this._ = new Datafeed.(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
if (this._enableLogging
) {
var now
= new Date();
// console.log("CHART LOGS: "+now.toLocaleTimeString() + '.' + now.getMilliseconds() + '> ' + message);
}
};
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(() => {
// console.log(this_vue.priceScale,'12345s313123122adaslast')
// this._logMessage("GOWNO :: onResultReady inject "+'AAPL');
// console.log(this_vue.priceScale,'123stf')
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
: [
"1",
"3",
"5",
"15",
"30",
"60",
"120",
"240",
"360",
"720",
"1D",
"3D",
"1W",
"1M",
],
});
});
};
//初始化数据
Datafeed
.Container
.prototype
.getBars = function (
symbolInfo
,
resolution
,
rangeStartDate
,
rangeEndDate
,
onHistoryCallback
,
onErrorCallback
) {
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
;
}
$
.ajax({
//XXXXXXXXXXXXX 是后台给你的接口
url
:
"XXXXXXXXXXXXX?" +
"from=" +
rangeStartDate
+
"&to=" +
rangeEndDate
+
"&symbol=" +
symbolInfo
.name
+
"&period=" +
resolution
,
type
: "get",
success
: function (res
) {
if (res
.code
== 1 && res
.data
&& res
.data
.length
> 0) {
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();
},
第六步:
开始增加线条颜色
下面颜色是上面获取的,不需要写死就行

overrides() 完整代码
overrides() {
let style
= {
up
: "#fa5252",
down
: "#12b886",
bg
: this.bg_
,
grid
: this.grid
,
cross
: this.cross
,
border
: "#4e5b85",
text
: "#61688A",
areatop
: "rgba(122, 152, 247, .1)",
areadown
: "rgba(122, 152, 247, .02)",
};
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.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.priceSource": "close",
"mainSeriesProperties.areaStyle.color1": style
.areatop
,
"mainSeriesProperties.areaStyle.color2": style
.areadown
,
"mainSeriesProperties.areaStyle.linecolor": style
.border
,
"mainSeriesProperties.areaStyle.linewidth": 2,
"mainSeriesProperties.areaStyle.priceSource": "close",
};
},
第七步:
构建 updateData() 函数 ,updateWidget() 函数,removeWidget() 函数 (也都是放在methods里面,内容不是特别多就放一块写了)
完整代码
// 更新图表
updateData(data
) {
if (data
) {
this.$emit("real-time", data
);
}
},
updateWidget(item
) {
this.symbolInfo
= {
name
: item
,
ticker
: item
,
description
: "",
session
: "24x7",
supported_resolutions
: [
"1",
"3",
"5",
"15",
"30",
"60",
"120",
"240",
"360",
"720",
"1D",
"3D",
"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;
}
},
第八步:
createWidget() 记得要在mounted里面引用
记得在destroyed里面销毁一下

第九步:
tv-top的建立,显示高低价和涨幅榜
(numFilter为我的截取小数点的管道符,根据自己的进行调节)

然后数据展示内容是后台返回实时更新的
全部代码如下:
{{lastPrice
| numFilter
}}
lastPrice
*usprice
}}CNY -->
涨幅:
{{downUp
}}%
高
{{coin
.high
|| 0 | numFilter
}}
低
{{coin
.low
|| 0 | numFilter
}}
export default {
data() {
return {
leftName
: "",
rightName
: "",
legalId
: "",
curencyId
: "",
coin
: {},
lastPrice
: "",
downUp
: "",
showMarket
: false,
usprice
: "",
socket
: "",
symbol
: "",
};
},
computed
: {
},
destroyed() {
if (this.socket
) {
this.socket
.disconnect();
}
},
created() {
// this.leftName = window.localStorage.getItem("legal_name") || "";
// this.rightName = window.localStorage.getItem("currency_name") || "";
this.currencyId
= window
.localStorage
.getItem("legal_id");
this.legalId
= window
.localStorage
.getItem("currency_id");
//console.log(this.currencyId+"||||||||"+this.legalId);
this.init(this.legalId
, this.currencyId
);
this.downUp
= window
.localStorage
.getItem("downUp");
this.getKlinke();
this.symbol
= "BTC/USDT";
this.leftName
= this.symbol
.split("/")[1] || "";
this.rightName
= this.symbol
.split("/")[0] || "";
},
mounted() {
this.leftName
= this.symbol
.split("/")[1] || "";
this.rightName
= this.symbol
.split("/")[0] || "";
var that
= this;
eventBus
.$on("toNew", function (msg
) {
console
.log("laaaaaaaaa", msg
);
var thattoken
= that
.rightName
+ "/" + that
.leftName
;
if (msg
.istoken
== thattoken
) {
that
.lastPrice
= (msg
.newprice
- 0).toFixed(4);
that
.downUp
= (msg
.newup
- 0).toFixed(2);
// that.coin.total = msg.hour24;
}
});
this.socket
= this.$socketio
.connect("http://8.210.87.26:2120", {
transports
: ["websocket", "xhr-polling", "jsonp-polling"]
});
var socket
= this.socket
;
socket
.on("daymarket", msg
=> {
if (msg
.type
== "transaction") {
if (that
.rightName
+ "/" + that
.leftName
== msg
.token
) {
if (msg
["24h"]) {
that
.coin
= JSON.parse(msg
["24h"]);
}
}
}
});
},
sockets
: {
daymarket
: function(msg
) {
let that
= this;
if (msg
.type
== "daymarket") {
if (that
.rightName
+ "/" + that
.leftName
== msg
.symbol
) {
if (msg
["24h"]) {
that
.coin
= JSON.parse(msg
["24h"]);
}
that
.lastPrice
= msg
.now_price
;
that
.downUp
= msg
.change
;
that
.coin
.high
= msg
.high
;
that
.coin
.low
= msg
.low
;
that
.coin
.total
= Number(msg
.volume
).toFixed(4);
}
}
}
},
methods
: {
init(legalId
, currencyId
) {
this.$http
.post("http://www.pkex.in/api/transaction/deal", {
legal_id
: 23,
currency_id
: 32,
})
.then((res
) => {
//console.log(res);
if (res
.type
== "ok") {
this.lastPrice
= res
.message
.last_price
;
}
});
},
getKlinke() {
let that
= this;
this.socket
= this.$socketio
.connect("http://8.210.87.26:2120", {
transports
: ["websocket", "xhr-polling", "jsonp- 标签组:[vue]
上一篇:华尔街股神:炒股一辈子牢记“五个数字”,10、20、50、60、721
下一篇:中国股市:死记“四个数字”10、20、50、60,做到从不亏钱