简单介绍
工作中需要绘制公司的分时和日k,但是接口要自己找。于是我就使用了腾讯的接口调分时和日k数据,新浪的接口调股票基本信息。如股票名字、今日开盘价、昨日收盘价、当前价格等等。均价线目前还没有。
参考资料:
https://blog.csdn.net/m0_37992075/article/details/84288021
https://blog.csdn.net/luanpeng825485697/article/details/78442062?locationNum=5&fps=1
配置echarts遇到的问题
配置参数过程中,遇到的最大问题就是处理分时图的y轴坐标。如图,分时线涨幅计算是以前天收盘价为基准。为了能让前一天收盘价显示出来,折腾了我好久。
解决思路:
y轴上的数据是一个等差数列,要想让前一天收盘价展示出来,需要数列中含有它。
y轴坐标值主要与 max
(y轴最大值),min
(最小值),interval
(分割间隔)有关。
所以我们根据已经有的数据赋值三个变量即可。
**已知数据:**前一天收盘价(baseNumber)
计算max、min:
通过Math.max.apply()
,Math.min.apply()
方法拿到已有数据中的最大最小成交价并计算出涨幅,对比最大和最小涨幅比取较大的值( handle_num() ),就可以计算出y轴目前需要的最高(max)和最低(min)范围。
var _minVal = Number(baseNumber-baseNumberhandle_num()).toFixed(2);
var _maxVal = (Number(baseNumber)+baseNumberhandle_num()).toFixed(2);
计算涨幅比后去求得的范围才是我们需要的,不直接取数组中最大最小值的原因是,最低价不一定小于baseNumber,如果今日开盘价全部高于baseNumber,图表绘制出来是错误的。用最大涨幅比计算的涵盖所有数据,较为可行。而且这样计算出来的,涨跌幅绝对值也一致。
计算interval:
interval 其实就是 折线图data的一个公差,套用an=a1+(n-1)d公式便可以计算出来。
由于y轴赋值min后,起点就会为min值,求公差时可以以 baseNumber 为 an,min 为a1,n
为5。
这里n
作用和splitNumber类似,但也不同,n的大小会影响y轴坐标密集程度,具体可根据数据大小调整。
yAxis 配置:
因为是双y轴,所以第二个配置中,min、max、interval 需要设置相同数值才能在同一水平线。
ps: 此图表的 双y轴,其实是绘制了两条折线,通过position偏移一条,并将折线宽度设置为0,因此两条y轴数值完全一样便于配置。涨幅比是通过axisLabel>formatter更改坐标轴的。
yAxis: [ //y轴
{
type: 'value',
min: _minVal,
max: _maxVal,
interval: _interval,
gridIndex: 0,
scale: true,
axisTick: { // 分割线 短
show: false
},
axisLine: {
lineStyle: {
color: borderColor,
}
},
axisPointer: {
show: true,
label: {
formatter: function (params) {
return (params.value).toFixed(2);
}
}
},
axisLabel: {
color:'#333',
formatter: format_y,
rich: {
red: {
color: 'red',
lineHeight: 10
},
green: {
color: 'green',
lineHeight: 10
}
}
},
z: 4,
splitLine: { //分割线设置
show: true,
lineStyle: {
type: 'dashed'
}
},
},{
scale: true,
gridIndex: 1,
min: _minVal,
max: _maxVal,
interval: _interval,
position: 'right',
z: 4,
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: borderColor,
}
},
axisLabel: { //label文字设置
color: function (val) {
val = Number(val).toFixed(2)
if (val == baseNumber) {
return '#333'
}
return val > baseNumber ? upColor : downColor;
},
formatter: function (val) {
var resul = ratioCalculate(val, baseNumber);
return Number(resul).toFixed(2) + '%'
}
},
splitLine: { //分割线设置
show: false,
lineStyle: {
color: '#181a23'
}
},
axisPointer: {
show: true,
label: {
formatter: function (params) { //计算右边Y轴对应的当前价的涨幅比例
return ratioCalculate(params.value, baseNumber) + '%';
}
}
}
},{// 成交量的y轴配置
//...已省略
}
]
demo放在这里了 ❤,欢迎指正和交流。
--------------2024.4.8更新----------
因为很久很久之后又做了个画图的功能,发现之前计算的有问题,本来不想维护这个文章的,但是还偶尔有朋友点赞,让我很不好意思。
更新一下计算的逻辑。好像主要是昨日收盘价和今日最高最低价的计算要区分下,影响了分割值。时间太久了,我翻了半天才找出来当年的代码。
下面的代码和demo里的不一样哈,仅供参考帮助梳理。
如果帮助到你了,很荣幸~ 以后这个就不更新了,主要下面的项目也是很久之前的了,我记不太清了。敲代码越来越累,脑子不好使,我想去烤串卖蛋糕,,,,
function splitTimeSaringData(mapRawData) { // map存储的值
let categoryData = [];
let volumes = []; // 成交量 [index, ]
let price = []; // 成交价
let closingPrice = 0;
let chgValue = [];
let minVal = 0;
let maxVal = 0;
const entries = mapRawData.entries();
for (let i = 0; i < mapRawData.size; i++) {
const entry = entries.next().value;
const val = entry[1];
if (!closingPrice) {
closingPrice = Number(val.preClosePrice);
}
if (!Number.isNaN(parseInt(val["tradePrice"]) - 0) && val["tradePrice"]) {
const _val = Number(val["tradePrice"]);
if (!minVal) {
minVal = _val;
} else {
minVal = _val < minVal ? _val : minVal;
}
if (!maxVal) {
maxVal = Number(val["tradePrice"]);
} else {
maxVal = _val > maxVal ? _val : maxVal;
}
}
// categoryData.push(entry[0]);
price.push(val["tradePrice"]);
chgValue.push(val["chgValue"]);
// [x, 分钟成交量, 分钟涨跌: false 跌、true 涨、 其他 不变]
volumes.push([
val["timestamp"],
val["minTradeVolume"],
val["minChgInc"] ? 1 : -1,
]);
}
return {
// categoryData: categoryData,
price: price,
volumes: volumes,
closingPrice: closingPrice,
chgValue: chgValue,
minVal: minVal,
maxVal: maxVal,
};
}
// 分时图 配置项-赋值
export const getTimeSharingChartData = (mapData) => {
const data = splitTimeSaringData(mapData);
const closingPrice = data.closingPrice; // 昨日收盘价
let minVal = data.minVal; // Math.min(...data.price);
let maxVal = data.maxVal; // Math.max(...data.price);
let interval;
if (closingPrice > maxVal) {
interval = (closingPrice - minVal) / 2;
} else {
const absMaxDiff = Math.abs(closingPrice - maxVal);
const absMinDiff = Math.abs(closingPrice - minVal);
const diff = absMaxDiff > absMinDiff ? absMaxDiff : absMinDiff;
interval = diff / 2 + 5; // 增加20的空间
}
minVal = (closingPrice - interval * 2).toFixed(2);
maxVal = (closingPrice + interval * 2).toFixed(2);
let _per = (((maxVal - closingPrice) / closingPrice / 2) * 100).toFixed(2);
return {
title: {
text: "分时图",
subtext: closingPrice, // 昨日收盘价
show: false,
},
xAxis: [
{
type: "category",
// data.categoryData
data: timeSharingXAxis().timeSharingXAxisArray,
boundaryGap: true,
axisLine: { onZero: false, show: false },
axisLabel: { show: false },
axisTick: {show: false},
splitLine: {
show: true,
interval: 80,
lineStyle: {
color: '#f0f0f0'
}
},
axisLine: {
show: true,
lineStyle: {
color: '#f0f0f0'
}
},
axisPointer: {
label: {
show: true,
margin: -10
},
},
},
{
type: "category",
gridIndex: 1,
data: timeSharingXAxis().timeSharingXAxisArray,
boundaryGap: true,
splitLine: { show: false },
axisTick: {show: false},
axisLine: { show: false },
axisPointer: {
label: {
show: false,
},
},
axisLabel: {
show: true,
fontSize: 10,
width: 100,
showMinLabel: true,
showMaxLabel: true,
margin: 5,
interval: 119,
color: '#333333',
formatter: (value) => {
if (value === "09:30") {
return "{x0930|" + value + "}";
} else if (value === "15:00") {
return "{x1500|" + value + "}";
} else {
return "{value|" + value + "}";
}
},
rich: {
x0930: {
align: "right",
fontSize: 10,
padding: [0, 20, 0, 0],
},
x1500: {
align: "left",
fontSize: 10,
padding: [0, 0, 0, 20],
},
value: {
fontSize: 10,
},
},
},
min: 0,
max: 240,
}
],
yAxis: [
{
gridIndex: 0,
onZeroAxisIndex: false,
boundaryGap: true,
type: "value",
min: Number(minVal),
max: Number(maxVal),
interval: interval,
fontSize: 10,
axisLine: {
show: true,
lineStyle: {
color: '#f0f0f0'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#f0f0f0'
}
},
axisPointer: {
label: {
show: true,
}
},
axisLabel: {
inside: true,
verticalAlign: "bottom",
fontSize: 10,
color: function (val) {
val = Number(val).toFixed(2);
if (val == closingPrice) {
return "#333";
}
return val > closingPrice ? upColor : downColor;
},
formatter: function (value) {
return value.toFixed(2);
},
}
},
{
gridIndex: 0,
position: "right",
alignTicks: true,
boundaryGap: true,
type: "value",
onZeroAxisIndex: false,
max: Number(_per) * 2,
min: Number(-_per) * 2,
interval: Number(_per),
splitLine: { show: false },
axisLabel: {
inside: true,
formatter: "{value}%",
fontSize: 10,
verticalAlign: "bottom",
color: function (val) {
val = Number(val).toFixed(2);
if (val == 0) {
return "#333";
}
return val > 0 ? upColor : downColor;
},
},
axisPointer: {
label: {
show: true,
formatter: "{value}%",
},
},
},
{
gridIndex: 1,
scale: true,
splitNumber: 3,
onZeroAxisIndex: false,
axisLabel: { show: false },
axisLine: { show: false },
axisTick: { show: false },
splitLine: {
show: false,
lineStyle: {
color: '#f0f0f0'
}
},
boundaryGap: true,
},
],
series: [
{
name: "index",
type: "line",
connectNulls: true,
lineStyle: {
width: 1,
},
data: data.price,
showSymbol: false,
emphasis: {
itemStyle: {
color: '#617cd2',
borderColor: '#a3b9ff'
}
},
z: 100
},
{
name: "per",
type: "line",
lineStyle: {
width: 0,
},
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 1,
data: data.chgValue,
},
{
name: "Volume",
type: "bar",
barMaxWidth: 10,
xAxisIndex: 1,
yAxisIndex: 2,
data: data.volumes,
},
]
};
};