| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- import { reactive } from 'vue'
- import { QueryHistoryDatasRsp } from '@/services/go/quote/interface'
- import { EchartsDataset, Candlestick, MACD } from './interface'
- import moment from 'moment'
- export function useDataset() {
- 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: 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) dataset.invalid.push(i); // 添加补充数据的索引位置
- dataset.candlestick.source.push({
- date,
- open: o,
- close: c,
- lowest: l,
- highest: h,
- ma5: '-',
- ma10: '-',
- ma15: '-',
- })
- dataset.macd.source.push({
- date,
- ema12: 0,
- ema26: 0,
- dif: 0,
- dea: 0,
- macd: 0,
- })
- dataset.vol.source.push({
- date,
- vol: tv,
- })
- dataset.kdj.source.push({
- date,
- k: '-',
- d: '-',
- j: '-'
- })
- dataset.cci.source.push({
- date,
- cci: '-',
- })
- }
- // 计算各种指标
- calcIndicator();
- onReady && onReady();
- }
- // 计算MA
- 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 < candlestickSource.length; i++) {
- // 判断是否补充数据
- 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 (dataset.invalid.includes(j)) continue; // 如果是补充数据,跳过本次循环
- if (n === count) break; // 如果 n 等于计数,结束循环
- tmpList.push(candlestickSource[j]);
- n++;
- }
- if (n === count) {
- // 计算总价(收盘价总和)
- const total = tmpList.reduce((res, e) => res + e.close, 0);
- // 计算均价
- candlestickSource[i][key] = (total / count).toFixed(2);
- } else {
- candlestickSource[i][key] = '-';
- }
- }
- }
- }
- // 计算EMA
- 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 < candlestickSource.length; i++) {
- const close = candlestickSource[i].close; // 收盘价
- if (i === 0) {
- macdSource[i][key] = close; // 初始EMA取收盘价
- } else {
- const prevEMA = macdSource[i - 1][key]; // 昨日EMA
- const value = a * close + (1 - a) * Number(prevEMA);
- macdSource[i][key] = value;
- }
- }
- }
- // 计算MACD
- const calcMACD = (startIndex = 0) => {
- // 先计算EMA
- calcEMA(12, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
- calcEMA(26, startIndex);
- const macdSource = dataset.macd.source;
- for (let i = startIndex; i < macdSource.length; i++) {
- const { ema12, ema26 } = macdSource[i];
- const prevDEA = i > 0 ? macdSource[i - 1].dea : 0; // 昨日DEA
- const dif = ema12 - ema26; // DIF = 今日EMA(12) - 今日EMA(26)
- const dea = prevDEA * 8 / 10 + dif * 2 / 10; // DEA = 昨日DEA × 8 ÷ 10 + 今日DIF × 2 ÷ 10
- const macd = (dif - dea) * 2; // MACD = (DIFF - DEA) × 2
- macdSource[i].dif = Number(dif.toFixed(2));
- macdSource[i].dea = Number(dea.toFixed(2));
- macdSource[i].macd = Number(macd.toFixed(2));
- }
- }
- // 计算KDJ
- const calcKDJ = (startIndex = 0) => {
- const count = 9; // 以9日周期计算
- const candlestickSource = dataset.candlestick.source;
- const kdjSource = dataset.kdj.source;
- 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(candlestickSource[j]);
- n++;
- }
- if (n === count) {
- const close = candlestickSource[i].close, // 收盘价
- n9 = tmpList.map((item) => item.close),
- h9 = Math.max(...n9), // 9天内最高价
- l9 = Math.min(...n9); // 9天内最低价
- const rsv = (close - l9) / (h9 - l9) * 100, // RSV = (Ct-L9) ÷ (H9-L9) * 100
- prevK = Number(kdjSource[i - 1].k), // 昨日K值
- prevD = Number(kdjSource[i - 1].d); // 昨日D值
- const kValue = isNaN(prevK) ? 50 : prevK; // 若无昨日K值,则可用50来代替
- const dValue = isNaN(prevD) ? 50 : prevD; // 若无昨日D值,则可用50来代替
- const k = (2 / 3) * kValue + (1 / 3) * rsv, // K = 2 ÷ 3 × 昨日K值 + 1 ÷ 3 × RSV
- d = (2 / 3) * dValue + (1 / 3) * k, // D = 2 ÷ 3 × 昨日D值 + 1 ÷ 3 × K值
- j = 3 * k - 2 * d; // J = 3 * K值 - 2 * D值
- kdjSource[i].k = k.toFixed(2);
- kdjSource[i].d = d.toFixed(2);
- kdjSource[i].j = j.toFixed(2);
- }
- }
- }
- // 计算CCI
- const calcCCI = (startIndex = 0) => {
- const count = 14; // 以14日周期计算
- const candlestickSource = dataset.candlestick.source;
- const cciSource = dataset.cci.source;
- 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(candlestickSource[j]);
- n++;
- }
- if (n === count) {
- const calcTP = (item: Candlestick) => (item.highest + item.lowest + item.close) / 3; // TP = (最高价 + 最低价 + 收盘价) ÷ 3
- 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
- const cci = (tp - ma) / (md * 0.015);
- cciSource[i].cci = cci.toFixed(2);
- }
- }
- }
- // 计算各项指标
- const calcIndicator = (startIndex = 0) => {
- calcMA(5, startIndex);
- calcMA(10, startIndex);
- calcMA(15, startIndex);
- calcMACD(startIndex);
- calcKDJ(startIndex);
- calcCCI(startIndex);
- }
- return {
- dataset,
- handleData,
- clearData,
- calcIndicator,
- }
- }
|