|
@@ -8,7 +8,7 @@ import { QueryHistoryDatasRsp, QueryQuoteDayRsp, QueryHistoryDatas, CycleType }
|
|
|
import { QueryHistoryDatas as queryHistoryDatas } from '@/services/go/quote';
|
|
import { QueryHistoryDatas as queryHistoryDatas } from '@/services/go/quote';
|
|
|
import { debounce } from '@/utils/time';
|
|
import { debounce } from '@/utils/time';
|
|
|
import EchartBase from '../echart-base/index.vue';
|
|
import EchartBase from '../echart-base/index.vue';
|
|
|
-import { handleEchart } from './setup';
|
|
|
|
|
|
|
+import { handleEchart, Source } from './setup';
|
|
|
import moment from 'moment';
|
|
import moment from 'moment';
|
|
|
|
|
|
|
|
export default defineComponent({
|
|
export default defineComponent({
|
|
@@ -41,62 +41,67 @@ export default defineComponent({
|
|
|
|
|
|
|
|
// 处理图表数据
|
|
// 处理图表数据
|
|
|
const handleData = (rawData: QueryHistoryDatasRsp[]): void => {
|
|
const handleData = (rawData: QueryHistoryDatasRsp[]): void => {
|
|
|
- historyIndexs.length = 0; // 清空数据
|
|
|
|
|
- const datas: number[][] = [],
|
|
|
|
|
- times: string[] = [],
|
|
|
|
|
- volume: number[] = [];
|
|
|
|
|
|
|
+ const { source } = chartData.value;
|
|
|
|
|
+ source.length = 0;
|
|
|
|
|
+ historyIndexs.length = 0;
|
|
|
|
|
|
|
|
rawData.forEach((item, index) => {
|
|
rawData.forEach((item, index) => {
|
|
|
const { o, c, h, l, ts, tv } = item;
|
|
const { o, c, h, l, ts, tv } = item;
|
|
|
- datas.push([o, c, l, h]);
|
|
|
|
|
- times.push(moment(ts).format('YYYY-MM-DD HH:mm:ss'));
|
|
|
|
|
- volume.push(tv);
|
|
|
|
|
|
|
+ source.push({
|
|
|
|
|
+ date: moment(ts).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
|
+ open: o,
|
|
|
|
|
+ close: c,
|
|
|
|
|
+ lowest: l,
|
|
|
|
|
+ highest: h,
|
|
|
|
|
+ ma5: '-',
|
|
|
|
|
+ ma10: '-',
|
|
|
|
|
+ ma15: '-',
|
|
|
|
|
+ vol: tv,
|
|
|
|
|
+ macd: '-',
|
|
|
|
|
+ dif: '-',
|
|
|
|
|
+ dea: '-',
|
|
|
|
|
+ k: '-',
|
|
|
|
|
+ d: '-',
|
|
|
|
|
+ j: '-',
|
|
|
|
|
+ cci: '-',
|
|
|
|
|
+ });
|
|
|
if (!item.f) historyIndexs.push(index); // 排除补充数据
|
|
if (!item.f) historyIndexs.push(index); // 排除补充数据
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- chartData.datas = datas;
|
|
|
|
|
- chartData.times = times;
|
|
|
|
|
- chartData.volume = volume;
|
|
|
|
|
- chartData.ma5 = calcMA(datas, 5);
|
|
|
|
|
- chartData.ma10 = calcMA(datas, 10);
|
|
|
|
|
- chartData.ma15 = calcMA(datas, 15);
|
|
|
|
|
-
|
|
|
|
|
- const { macd, dif, dea } = calcMACD(datas);
|
|
|
|
|
- chartData.macd = macd;
|
|
|
|
|
- chartData.dif = dif;
|
|
|
|
|
- chartData.dea = dea;
|
|
|
|
|
-
|
|
|
|
|
- chartData.kdj = calcKDJ(datas);
|
|
|
|
|
- chartData.cci = clacCCI(datas);
|
|
|
|
|
|
|
+ calcMA('ma5', 5);
|
|
|
|
|
+ calcMA('ma10', 10);
|
|
|
|
|
+ calcMA('ma15', 15);
|
|
|
|
|
+ calcMACD();
|
|
|
|
|
+ calcKDJ();
|
|
|
|
|
+ clacCCI();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 计算平均线
|
|
// 计算平均线
|
|
|
- const calcMA = (data: number[][], count: number) => {
|
|
|
|
|
- const ma: string[] = [];
|
|
|
|
|
- if (data.length >= count) {
|
|
|
|
|
|
|
+ const calcMA = (key: keyof Source, count: number) => {
|
|
|
|
|
+ const { source } = chartData.value;
|
|
|
|
|
+ let result: Source[keyof Source] = '-';
|
|
|
|
|
+
|
|
|
|
|
+ if (source.length >= count) {
|
|
|
// 均线起始位置
|
|
// 均线起始位置
|
|
|
const startIndex = historyIndexs[count - 1] ?? count;
|
|
const startIndex = historyIndexs[count - 1] ?? count;
|
|
|
- for (let i = 0; i < data.length; i++) {
|
|
|
|
|
- if (i < startIndex) {
|
|
|
|
|
- ma.push('-');
|
|
|
|
|
- } else {
|
|
|
|
|
|
|
+ for (let i = 0; i < source.length; i++) {
|
|
|
|
|
+ if (i > startIndex) {
|
|
|
const j = historyIndexs.findIndex((val) => val === i);
|
|
const j = historyIndexs.findIndex((val) => val === i);
|
|
|
// 判断是否补充数据
|
|
// 判断是否补充数据
|
|
|
if (j === -1) {
|
|
if (j === -1) {
|
|
|
- // 取上个平均值
|
|
|
|
|
- ma.push(ma[ma.length - 1]);
|
|
|
|
|
|
|
+ result = source[i - 1][key]; // 取上个平均值
|
|
|
} else {
|
|
} else {
|
|
|
// 向后取MA数
|
|
// 向后取MA数
|
|
|
const maIndexs = historyIndexs.slice(j - (count - 1), j + 1);
|
|
const maIndexs = historyIndexs.slice(j - (count - 1), j + 1);
|
|
|
- // 计算总价,data[val][1]=收盘价
|
|
|
|
|
- const total = maIndexs.reduce((sum, val) => sum + data[val][1], 0);
|
|
|
|
|
|
|
+ // 计算总价
|
|
|
|
|
+ const total = maIndexs.reduce((sum, val) => sum + source[val].close, 0);
|
|
|
// 计算均线
|
|
// 计算均线
|
|
|
- ma.push((total / count).toFixed(2));
|
|
|
|
|
|
|
+ result = (total / count).toFixed(2);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ (<typeof result>source[i][key]) = result;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return ma;
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 计算EMA
|
|
// 计算EMA
|
|
@@ -136,79 +141,83 @@ export default defineComponent({
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 计算MACD
|
|
// 计算MACD
|
|
|
- const calcMACD = (data: number[][]) => {
|
|
|
|
|
- const macd = [],
|
|
|
|
|
- close = data.map((val) => val[1]), // 收盘价数据
|
|
|
|
|
|
|
+ const calcMACD = () => {
|
|
|
|
|
+ const { source } = chartData.value,
|
|
|
|
|
+ close = source.map((item) => item.close),
|
|
|
dif = calcDIF(close),
|
|
dif = calcDIF(close),
|
|
|
dea = calcDEA(dif);
|
|
dea = calcDEA(dif);
|
|
|
|
|
|
|
|
- for (let i = 0; i < close.length; i++) {
|
|
|
|
|
- macd.push(((dif[i] - dea[i]) * 2).toFixed(3));
|
|
|
|
|
|
|
+ for (let i = 0; i < source.length; i++) {
|
|
|
|
|
+ source[i].dif = dif[i].toFixed(2);
|
|
|
|
|
+ source[i].dea = dea[i].toFixed(2);
|
|
|
|
|
+ source[i].macd = ((dif[i] - dea[i]) * 2).toFixed(2);
|
|
|
}
|
|
}
|
|
|
- return {
|
|
|
|
|
- dif: dif.map((val) => val.toFixed(3)),
|
|
|
|
|
- dea: dea.map((val) => val.toFixed(3)),
|
|
|
|
|
- macd,
|
|
|
|
|
- };
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 计算KDJ
|
|
// 计算KDJ
|
|
|
- const calcKDJ = (data: number[][]) => {
|
|
|
|
|
- const kdj: string[][] = []; // [[k,d,j]]
|
|
|
|
|
- for (let i = 0; i < data.length; i++) {
|
|
|
|
|
|
|
+ const calcKDJ = () => {
|
|
|
|
|
+ const { source } = chartData.value;
|
|
|
|
|
+ for (let i = 0; i < source.length; i++) {
|
|
|
|
|
+ const item = source[i];
|
|
|
if (i < 8) {
|
|
if (i < 8) {
|
|
|
- kdj.push(['-', '-', '-']);
|
|
|
|
|
|
|
+ item.k = '-';
|
|
|
|
|
+ item.d = '-';
|
|
|
|
|
+ item.j = '-';
|
|
|
} else {
|
|
} else {
|
|
|
let rsv = 50; // 如果最低价等于最高价,RSV默认值为50
|
|
let rsv = 50; // 如果最低价等于最高价,RSV默认值为50
|
|
|
- if (data[i][2] !== data[i][3]) {
|
|
|
|
|
- const n9 = data.slice(i - 8, i + 1).map((val) => val[1]), // 取前9个收盘价
|
|
|
|
|
|
|
+ if (item.lowest !== item.highest) {
|
|
|
|
|
+ const n9 = source.slice(i - 8, i + 1).map((item) => item.close), // 取前9个收盘价
|
|
|
max = Math.max(...n9),
|
|
max = Math.max(...n9),
|
|
|
min = Math.min(...n9);
|
|
min = Math.min(...n9);
|
|
|
// 计算RSV
|
|
// 计算RSV
|
|
|
- rsv = ((data[i][1] - min) / (max - min)) * 100;
|
|
|
|
|
|
|
+ rsv = ((item.close - min) / (max - min)) * 100;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const yestK = Number(kdj[kdj.length - 1][0]); // 取前一日K值
|
|
|
|
|
- const yestD = Number(kdj[kdj.length - 1][1]); // 取前一日D值
|
|
|
|
|
|
|
+ const yestK = Number(source[i - 1].k); // 取前一日K值
|
|
|
|
|
+ const yestD = Number(source[i - 1].d); // 取前一日D值
|
|
|
|
|
|
|
|
if (isNaN(yestK) || isNaN(yestD)) {
|
|
if (isNaN(yestK) || isNaN(yestD)) {
|
|
|
// 如果前一日的K值或D值不存在则默认值为50
|
|
// 如果前一日的K值或D值不存在则默认值为50
|
|
|
- kdj.push(['50', '50', '50']);
|
|
|
|
|
|
|
+ item.k = '50';
|
|
|
|
|
+ item.d = '50';
|
|
|
|
|
+ item.j = '50';
|
|
|
} else {
|
|
} else {
|
|
|
const k = (2 / 3) * yestK + (1 / 3) * rsv,
|
|
const k = (2 / 3) * yestK + (1 / 3) * rsv,
|
|
|
d = (2 / 3) * yestD + (1 / 3) * yestK,
|
|
d = (2 / 3) * yestD + (1 / 3) * yestK,
|
|
|
j = 3 * k - 2 * d;
|
|
j = 3 * k - 2 * d;
|
|
|
|
|
|
|
|
- kdj.push([k.toFixed(2), d.toFixed(2), j.toFixed(2)]);
|
|
|
|
|
|
|
+ item.k = k.toFixed(2);
|
|
|
|
|
+ item.d = d.toFixed(2);
|
|
|
|
|
+ item.j = j.toFixed(2);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return kdj;
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const clacCCI = (data: number[][]) => {
|
|
|
|
|
- const cci: string[] = [];
|
|
|
|
|
- for (let i = 0; i < data.length; i++) {
|
|
|
|
|
|
|
+ // 计算CCI
|
|
|
|
|
+ const clacCCI = () => {
|
|
|
|
|
+ const { source } = chartData.value;
|
|
|
|
|
+ for (let i = 0; i < source.length; i++) {
|
|
|
|
|
+ const item = source[i];
|
|
|
if (i < 13) {
|
|
if (i < 13) {
|
|
|
- cci.push('-');
|
|
|
|
|
|
|
+ item.cci = '-';
|
|
|
} else {
|
|
} else {
|
|
|
- const tp = (data[i][1] + data[i][2] + data[i][3]) / 3, // (收盘价 + 最低价 + 最高价) / 3
|
|
|
|
|
- n14 = data.slice(i - 13, i + 1), // 取前14条数据
|
|
|
|
|
- ma = n14.reduce((sum, val) => sum + (val[1] + val[2] + val[3]) / 3, 0) / 14, // 计算前14条数据的(TP)价总和÷N
|
|
|
|
|
- md = n14.reduce((sum, val) => sum + Math.abs(ma - (val[1] + val[2] + val[3]) / 3), 0) / 14, // 计算前14条数据的(MA-TP)价总和÷N
|
|
|
|
|
|
|
+ const tp = (item.close + item.lowest + item.highest) / 3, // (收盘价 + 最低价 + 最高价) / 3
|
|
|
|
|
+ n14 = source.slice(i - 13, i + 1), // 取前14条数据
|
|
|
|
|
+ ma = n14.reduce((sum, e) => sum + (e.close + e.lowest + e.highest) / 3, 0) / 14, // 计算前14条数据的(TP)价总和÷N
|
|
|
|
|
+ md = n14.reduce((sum, e) => sum + Math.abs(ma - (e.close + e.lowest + e.highest) / 3), 0) / 14, // 计算前14条数据的(MA-TP)价总和÷N
|
|
|
result = (tp - ma) / md / 0.015;
|
|
result = (tp - ma) / md / 0.015;
|
|
|
|
|
|
|
|
- cci.push(result.toFixed(2));
|
|
|
|
|
|
|
+ item.cci = result.toFixed(2);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return cci;
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 更新图表K线数据
|
|
// 更新图表K线数据
|
|
|
const updateChartData = () => {
|
|
const updateChartData = () => {
|
|
|
- const { datas, times, volume } = chartData,
|
|
|
|
|
- lastIndex = datas.length - 1, // 历史行情最后索引位置
|
|
|
|
|
- lastTime = moment(times[times.length - 1]), // 历史行情最后时间
|
|
|
|
|
|
|
+ const { source } = chartData.value,
|
|
|
|
|
+ lastIndex = source.length - 1, // 历史行情最后索引位置
|
|
|
|
|
+ lastTime = moment(source[lastIndex].date), // 历史行情最后时间
|
|
|
newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
|
|
newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
|
|
|
newPrice = props.quoteData.last; // 实时行情最新价
|
|
newPrice = props.quoteData.last; // 实时行情最新价
|
|
|
|
|
|
|
@@ -239,33 +248,43 @@ export default defineComponent({
|
|
|
// 判断时间差是否大于周期时间
|
|
// 判断时间差是否大于周期时间
|
|
|
if (diffTime > cycleMilliseconds) {
|
|
if (diffTime > cycleMilliseconds) {
|
|
|
lastTime.add(cycleMilliseconds, 'ms');
|
|
lastTime.add(cycleMilliseconds, 'ms');
|
|
|
- times.push(lastTime.format('YYYY-MM-DD HH:mm:ss')); // 添加历史行情时间
|
|
|
|
|
- datas.push([newPrice, newPrice, newPrice, newPrice]); // 添加历史行情数据
|
|
|
|
|
- volume.push(0);
|
|
|
|
|
|
|
+ // 添加历史行情
|
|
|
|
|
+ source.push({
|
|
|
|
|
+ date: lastTime.format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
|
+ open: newPrice,
|
|
|
|
|
+ close: newPrice,
|
|
|
|
|
+ lowest: newPrice,
|
|
|
|
|
+ highest: newPrice,
|
|
|
|
|
+ ma5: '-',
|
|
|
|
|
+ ma10: '-',
|
|
|
|
|
+ ma15: '-',
|
|
|
|
|
+ vol: 0,
|
|
|
|
|
+ macd: '-',
|
|
|
|
|
+ dif: '-',
|
|
|
|
|
+ dea: '-',
|
|
|
|
|
+ k: '-',
|
|
|
|
|
+ d: '-',
|
|
|
|
|
+ j: '-',
|
|
|
|
|
+ cci: '-',
|
|
|
|
|
+ });
|
|
|
historyIndexs.push(lastIndex + 1); // 添加历史行情索引
|
|
historyIndexs.push(lastIndex + 1); // 添加历史行情索引
|
|
|
} else {
|
|
} else {
|
|
|
- const lastPrice = datas[lastIndex];
|
|
|
|
|
- if (lastPrice[2] > newPrice) {
|
|
|
|
|
- lastPrice[2] = newPrice; //更新最低价
|
|
|
|
|
|
|
+ const lastData = source[lastIndex];
|
|
|
|
|
+ if (lastData.lowest > newPrice) {
|
|
|
|
|
+ lastData.lowest = newPrice; //更新最低价
|
|
|
}
|
|
}
|
|
|
- if (lastPrice[3] < newPrice) {
|
|
|
|
|
- lastPrice[3] = newPrice; //更新最高价
|
|
|
|
|
|
|
+ if (lastData.highest < newPrice) {
|
|
|
|
|
+ lastData.highest = newPrice; //更新最高价
|
|
|
}
|
|
}
|
|
|
- lastPrice[1] = newPrice; //更新收盘价
|
|
|
|
|
- datas[lastIndex] = lastPrice; // 更新历史行情数据
|
|
|
|
|
|
|
+ lastData.close = newPrice; //更新收盘价
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- chartData.ma5 = calcMA(datas, 5);
|
|
|
|
|
- chartData.ma10 = calcMA(datas, 10);
|
|
|
|
|
- chartData.ma15 = calcMA(datas, 15);
|
|
|
|
|
-
|
|
|
|
|
- const { dif, dea, macd } = calcMACD(datas);
|
|
|
|
|
- chartData.dif = dif;
|
|
|
|
|
- chartData.dea = dea;
|
|
|
|
|
- chartData.macd = macd;
|
|
|
|
|
-
|
|
|
|
|
- chartData.kdj = calcKDJ(datas);
|
|
|
|
|
- chartData.cci = clacCCI(datas);
|
|
|
|
|
|
|
+ calcMA('ma5', 5);
|
|
|
|
|
+ calcMA('ma10', 10);
|
|
|
|
|
+ calcMA('ma15', 15);
|
|
|
|
|
+ calcMACD();
|
|
|
|
|
+ calcKDJ();
|
|
|
|
|
+ clacCCI();
|
|
|
|
|
|
|
|
// 延迟图表更新,减少卡顿
|
|
// 延迟图表更新,减少卡顿
|
|
|
debounce(() => {
|
|
debounce(() => {
|