|
|
@@ -1,50 +1,51 @@
|
|
|
-import moment from 'moment';
|
|
|
-import { Candlestick, Dataset } from './interface'
|
|
|
+import { reactive } from 'vue'
|
|
|
+import { EchartsDataset, Candlestick, MACD } from './interface'
|
|
|
+import moment from 'moment'
|
|
|
|
|
|
export function useDataset() {
|
|
|
- // 行情历史数据中所有补充数据的索引位置(用于指标计算)
|
|
|
- const invalidData: number[] = [];
|
|
|
-
|
|
|
- // K线数据
|
|
|
- const klineData: Dataset<Candlestick> = {
|
|
|
- dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15'],
|
|
|
- source: [],
|
|
|
- }
|
|
|
-
|
|
|
- // MACD数据
|
|
|
- const macdData: Dataset<Candlestick.MACD> = {
|
|
|
- dimensions: ['date', 'macd', 'dif', 'dea'],
|
|
|
- source: [],
|
|
|
- }
|
|
|
-
|
|
|
- // VOL数据
|
|
|
- const volData: Dataset<Candlestick.VOL> = {
|
|
|
- dimensions: ['date', 'vol'],
|
|
|
- source: [],
|
|
|
- }
|
|
|
-
|
|
|
- // KDJ线数据
|
|
|
- const kdjData: Dataset<Candlestick.KDJ> = {
|
|
|
- dimensions: ['date', 'k', 'd', 'j'],
|
|
|
- source: [],
|
|
|
- }
|
|
|
-
|
|
|
- // VOL线数据
|
|
|
- const cciData: Dataset<Candlestick.CCI> = {
|
|
|
- dimensions: ['date', 'cci'],
|
|
|
- source: [],
|
|
|
+ const dataset = reactive<EchartsDataset>({
|
|
|
+ invalid: [],
|
|
|
+ candlestick: {
|
|
|
+ dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15'],
|
|
|
+ source: [],
|
|
|
+ },
|
|
|
+ macd: {
|
|
|
+ dimensions: ['date', 'macd', 'dif', 'dea'],
|
|
|
+ source: [],
|
|
|
+ },
|
|
|
+ vol: {
|
|
|
+ dimensions: ['date', 'vol'],
|
|
|
+ source: [],
|
|
|
+ },
|
|
|
+ kdj: {
|
|
|
+ dimensions: ['date', 'k', 'd', 'j'],
|
|
|
+ source: [],
|
|
|
+ },
|
|
|
+ cci: {
|
|
|
+ dimensions: ['date', 'cci'],
|
|
|
+ source: [],
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 清空数据
|
|
|
+ const clearData = () => {
|
|
|
+ dataset.invalid = [];
|
|
|
+ dataset.candlestick.source = [];
|
|
|
+ dataset.macd.source = [];
|
|
|
+ dataset.vol.source = [];
|
|
|
+ dataset.kdj.source = [];
|
|
|
+ dataset.cci.source = [];
|
|
|
}
|
|
|
|
|
|
// 处理行情数据
|
|
|
- const handleData = (rawData: Ermcp.HistoryDatas[], onReady?: () => void) => {
|
|
|
- invalidData.length = 0;
|
|
|
+ const handleData = (rawData: Ermcp.QueryHistoryDatasRsp[], onReady?: () => void) => {
|
|
|
for (let i = 0; i < rawData.length; i++) {
|
|
|
const { o, c, h, l, ts, f, tv } = rawData[i];
|
|
|
const date = moment(ts).format('YYYY-MM-DD HH:mm:ss');
|
|
|
|
|
|
- if (f) invalidData.push(i); // 添加补充数据的索引位置
|
|
|
+ if (f) dataset.invalid.push(i); // 添加补充数据的索引位置
|
|
|
|
|
|
- klineData.source.push({
|
|
|
+ dataset.candlestick.source.push({
|
|
|
date,
|
|
|
open: o,
|
|
|
close: c,
|
|
|
@@ -55,7 +56,7 @@ export function useDataset() {
|
|
|
ma15: '-',
|
|
|
})
|
|
|
|
|
|
- macdData.source.push({
|
|
|
+ dataset.macd.source.push({
|
|
|
date,
|
|
|
ema12: 0,
|
|
|
ema26: 0,
|
|
|
@@ -64,19 +65,19 @@ export function useDataset() {
|
|
|
macd: 0,
|
|
|
})
|
|
|
|
|
|
- volData.source.push({
|
|
|
+ dataset.vol.source.push({
|
|
|
date,
|
|
|
vol: tv,
|
|
|
})
|
|
|
|
|
|
- kdjData.source.push({
|
|
|
+ dataset.kdj.source.push({
|
|
|
date,
|
|
|
k: '-',
|
|
|
d: '-',
|
|
|
j: '-'
|
|
|
})
|
|
|
|
|
|
- cciData.source.push({
|
|
|
+ dataset.cci.source.push({
|
|
|
date,
|
|
|
cci: '-',
|
|
|
})
|
|
|
@@ -87,23 +88,23 @@ export function useDataset() {
|
|
|
}
|
|
|
|
|
|
// 计算MA
|
|
|
- const calcMA = (key: keyof Candlestick, count: number, startIndex = 0) => {
|
|
|
- type T = Candlestick[keyof Candlestick];
|
|
|
- const klineSource = klineData.source;
|
|
|
+ const calcMA = (count: 5 | 10 | 15, startIndex = 0) => {
|
|
|
+ const key: keyof Candlestick = count === 5 ? 'ma5' : count === 10 ? 'ma10' : 'ma15';
|
|
|
+ const candlestickSource = dataset.candlestick.source;
|
|
|
|
|
|
- for (let i = startIndex; i < klineSource.length; i++) {
|
|
|
+ for (let i = startIndex; i < candlestickSource.length; i++) {
|
|
|
// 判断是否补充数据
|
|
|
- if (invalidData.includes(i)) {
|
|
|
- const value = i > 0 ? klineSource[i - 1][key].toString() : '-'; // 如果存在,取上一条记录的MA值
|
|
|
- (<T>klineSource[i][key]) = value;
|
|
|
+ if (dataset.invalid.includes(i)) {
|
|
|
+ const value = i > 0 ? candlestickSource[i - 1][key] : '-'; // 如果存在,取上一条记录的MA值
|
|
|
+ candlestickSource[i][key] = value;
|
|
|
} else {
|
|
|
let n = 0;
|
|
|
const tmpList: Candlestick[] = [];
|
|
|
|
|
|
for (let j = i; j >= 0; j--) {
|
|
|
- if (invalidData.includes(j)) continue; // 如果是补充数据,跳过本次循环
|
|
|
+ if (dataset.invalid.includes(j)) continue; // 如果是补充数据,跳过本次循环
|
|
|
if (n === count) break; // 如果 n 等于计数,结束循环
|
|
|
- tmpList.push(klineSource[j]);
|
|
|
+ tmpList.push(candlestickSource[j]);
|
|
|
n++;
|
|
|
}
|
|
|
|
|
|
@@ -111,31 +112,31 @@ export function useDataset() {
|
|
|
// 计算总价(收盘价总和)
|
|
|
const total = tmpList.reduce((res, e) => res + e.close, 0);
|
|
|
// 计算均价
|
|
|
- (<T>klineSource[i][key]) = (total / count).toFixed(2);
|
|
|
+ candlestickSource[i][key] = (total / count).toFixed(2);
|
|
|
} else {
|
|
|
- (<T>klineSource[i][key]) = '-';
|
|
|
+ candlestickSource[i][key] = '-';
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 计算EMA
|
|
|
- const calcEMA = (key: keyof Candlestick.MACD, count: number, startIndex = 0) => {
|
|
|
- type T = Candlestick.MACD[keyof Candlestick.MACD];
|
|
|
- const macdSource = macdData.source;
|
|
|
- const klineSource = klineData.source;
|
|
|
+ const calcEMA = (count: 12 | 26, startIndex = 0) => {
|
|
|
+ const key: keyof MACD = count === 12 ? 'ema12' : 'ema26';
|
|
|
+ const macdSource = dataset.macd.source;
|
|
|
+ const candlestickSource = dataset.candlestick.source;
|
|
|
const a = 2 / (count + 1); // 平滑系数
|
|
|
|
|
|
- for (let i = startIndex; i < klineSource.length; i++) {
|
|
|
- const close = klineSource[i].close; // 收盘价
|
|
|
+ for (let i = startIndex; i < candlestickSource.length; i++) {
|
|
|
+ const close = candlestickSource[i].close; // 收盘价
|
|
|
|
|
|
if (i === 0) {
|
|
|
- (<T>macdSource[i][key]) = close; // 初始EMA取收盘价
|
|
|
+ macdSource[i][key] = close; // 初始EMA取收盘价
|
|
|
} else {
|
|
|
const prevEMA = macdSource[i - 1][key]; // 昨日EMA
|
|
|
const value = a * close + (1 - a) * Number(prevEMA);
|
|
|
|
|
|
- (<T>macdSource[i][key]) = value;
|
|
|
+ macdSource[i][key] = value;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -143,10 +144,10 @@ export function useDataset() {
|
|
|
// 计算MACD
|
|
|
const calcMACD = (startIndex = 0) => {
|
|
|
// 先计算EMA
|
|
|
- calcEMA('ema12', 12, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
|
|
|
- calcEMA('ema26', 26, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
|
|
|
+ calcEMA(12, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
|
|
|
+ calcEMA(26, startIndex);
|
|
|
|
|
|
- const macdSource = macdData.source;
|
|
|
+ const macdSource = dataset.macd.source;
|
|
|
|
|
|
for (let i = startIndex; i < macdSource.length; i++) {
|
|
|
const { ema12, ema26 } = macdSource[i];
|
|
|
@@ -165,21 +166,21 @@ export function useDataset() {
|
|
|
// 计算KDJ
|
|
|
const calcKDJ = (startIndex = 0) => {
|
|
|
const count = 9; // 以9日周期计算
|
|
|
- const klineSource = klineData.source;
|
|
|
- const kdjSource = kdjData.source;
|
|
|
+ const candlestickSource = dataset.candlestick.source;
|
|
|
+ const kdjSource = dataset.kdj.source;
|
|
|
|
|
|
- for (let i = startIndex; i < klineSource.length; i++) {
|
|
|
+ for (let i = startIndex; i < candlestickSource.length; i++) {
|
|
|
let n = 0;
|
|
|
const tmpList: Candlestick[] = [];
|
|
|
|
|
|
for (let j = i; j >= 0; j--) {
|
|
|
if (n === count) break; // 如果 n 等于计数,结束循环
|
|
|
- tmpList.push(klineSource[j]);
|
|
|
+ tmpList.push(candlestickSource[j]);
|
|
|
n++;
|
|
|
}
|
|
|
|
|
|
if (n === count) {
|
|
|
- const close = klineSource[i].close, // 收盘价
|
|
|
+ const close = candlestickSource[i].close, // 收盘价
|
|
|
n9 = tmpList.map((item) => item.close),
|
|
|
h9 = Math.max(...n9), // 9天内最高价
|
|
|
l9 = Math.min(...n9); // 9天内最低价
|
|
|
@@ -205,22 +206,22 @@ export function useDataset() {
|
|
|
// 计算CCI
|
|
|
const calcCCI = (startIndex = 0) => {
|
|
|
const count = 14; // 以14日周期计算
|
|
|
- const klineSource = klineData.source;
|
|
|
- const cciSource = cciData.source;
|
|
|
+ const candlestickSource = dataset.candlestick.source;
|
|
|
+ const cciSource = dataset.cci.source;
|
|
|
|
|
|
- for (let i = startIndex; i < klineSource.length; i++) {
|
|
|
+ for (let i = startIndex; i < candlestickSource.length; i++) {
|
|
|
let n = 0;
|
|
|
const tmpList: Candlestick[] = [];
|
|
|
|
|
|
for (let j = i; j >= 0; j--) {
|
|
|
if (n === count) break; // 如果 n 等于计数,结束循环
|
|
|
- tmpList.push(klineSource[j]);
|
|
|
+ tmpList.push(candlestickSource[j]);
|
|
|
n++;
|
|
|
}
|
|
|
|
|
|
if (n === count) {
|
|
|
const calcTP = (item: Candlestick) => (item.highest + item.lowest + item.close) / 3; // TP = (最高价 + 最低价 + 收盘价) ÷ 3
|
|
|
- const tp = calcTP(klineSource[i]),
|
|
|
+ const tp = calcTP(candlestickSource[i]),
|
|
|
ma = tmpList.reduce((res, e) => res + calcTP(e), 0) / count, // MA = 近N日TP的累计之和 ÷ N
|
|
|
md = tmpList.reduce((res, e) => res + Math.abs(calcTP(e) - ma), 0) / count; // MD = 近N日TP的绝对值的累计之和 ÷ N
|
|
|
|
|
|
@@ -230,23 +231,20 @@ export function useDataset() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 计算各种指标
|
|
|
+ // 计算各项指标
|
|
|
const calcIndicator = (startIndex = 0) => {
|
|
|
- calcMA('ma5', 5, startIndex);
|
|
|
- calcMA('ma10', 10, startIndex);
|
|
|
- calcMA('ma15', 15, startIndex);
|
|
|
+ calcMA(5, startIndex);
|
|
|
+ calcMA(10, startIndex);
|
|
|
+ calcMA(15, startIndex);
|
|
|
calcMACD(startIndex);
|
|
|
calcKDJ(startIndex);
|
|
|
calcCCI(startIndex);
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
- klineData,
|
|
|
- macdData,
|
|
|
- volData,
|
|
|
- kdjData,
|
|
|
- cciData,
|
|
|
+ dataset,
|
|
|
handleData,
|
|
|
+ clearData,
|
|
|
calcIndicator,
|
|
|
}
|
|
|
}
|