fix: k线图增加macd指标及样式

master
Lxy 2 weeks ago
parent 9990b228c4
commit 705bd802ca

@ -514,7 +514,7 @@
#klineChart { #klineChart {
width: 100%; width: 100%;
height: 500px; height: 650px;
} }
/* Log */ /* Log */
@ -1881,12 +1881,16 @@
const upColor = '#ef4444'; const upColor = '#ef4444';
const downColor = '#10b981'; const downColor = '#10b981';
// 计算MACD指标
const macdData = calculateMACD(candles);
const option = { const option = {
animation: true, animation: true,
legend: { legend: {
bottom: 10, top: 10,
left: 'center', left: 'center',
data: ['K线', 'MA5', 'MA10', 'MA20', 'MA60'] data: ['K线', 'MA5', 'MA10', 'MA20', 'MA60', 'DIF', 'DEA', 'MACD'],
textStyle: { fontSize: 11 }
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -1915,6 +1919,10 @@
result += `<div>${param.seriesName}: ${param.data}</div>`; result += `<div>${param.seriesName}: ${param.data}</div>`;
} else if (param.seriesName === '成交量') { } else if (param.seriesName === '成交量') {
result += `<div>成交量: ${param.data}</div>`; result += `<div>成交量: ${param.data}</div>`;
} else if (param.seriesName === 'DIF' || param.seriesName === 'DEA') {
result += `<div>${param.seriesName}: ${param.data}</div>`;
} else if (param.seriesName === 'MACD') {
result += `<div>MACD: ${param.data}</div>`;
} }
}); });
@ -1922,8 +1930,9 @@
} }
}, },
grid: [ grid: [
{ left: 60, right: 40, height: '60%' }, { left: 60, right: 60, top: 50, height: '48%' },
{ left: 60, right: 40, top: '75%', height: '15%' } { left: 60, right: 60, top: '56%', height: '13%' },
{ left: 60, right: 60, top: '74%', height: '14%' }
], ],
xAxis: [ xAxis: [
{ {
@ -1938,10 +1947,17 @@
data: dates, data: dates,
gridIndex: 1, gridIndex: 1,
axisLine: { lineStyle: { color: '#8392A5' } }, axisLine: { lineStyle: { color: '#8392A5' } },
axisLabel: { show: false }
},
{
type: 'category',
data: dates,
gridIndex: 2,
axisLine: { lineStyle: { color: '#8392A5' } },
axisLabel: { axisLabel: {
show: true, show: true,
formatter: function(value) { formatter: function(value) {
return value.substring(5); return value.substring(11);
} }
} }
} }
@ -1962,23 +1978,33 @@
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { show: false }, axisLabel: { show: false },
splitLine: { show: false } splitLine: { show: false }
},
{
scale: true,
gridIndex: 2,
splitNumber: 3,
axisLine: { lineStyle: { color: '#8392A5' } },
axisLabel: { show: true, fontSize: 11 },
splitLine: { show: true, lineStyle: { color: '#E8EEF4', type: 'dashed' } }
} }
], ],
dataZoom: [ dataZoom: [
{ {
type: 'inside', type: 'inside',
xAxisIndex: [0, 1], xAxisIndex: [0, 1, 2],
start: Math.max(0, 100 - 100 * 100 / candles.length), start: Math.max(0, 100 - 100 * 100 / candles.length),
end: 100 end: 100
}, },
{ {
show: true, show: true,
xAxisIndex: [0, 1], xAxisIndex: [0, 1, 2],
type: 'slider', type: 'slider',
bottom: 10, bottom: 5,
start: Math.max(0, 100 - 100 * 100 / candles.length), start: Math.max(0, 100 - 100 * 100 / candles.length),
end: 100, end: 100,
height: 20 height: 15,
borderColor: 'transparent',
backgroundColor: '#f1f5f9'
} }
], ],
series: [ series: [
@ -1998,28 +2024,28 @@
type: 'line', type: 'line',
data: calculateMA(candles, 5), data: calculateMA(candles, 5),
smooth: true, smooth: true,
lineStyle: { width: 1 } lineStyle: { width: 1, color: '#f59e0b' }
}, },
{ {
name: 'MA10', name: 'MA10',
type: 'line', type: 'line',
data: calculateMA(candles, 10), data: calculateMA(candles, 10),
smooth: true, smooth: true,
lineStyle: { width: 1 } lineStyle: { width: 1, color: '#3b82f6' }
}, },
{ {
name: 'MA20', name: 'MA20',
type: 'line', type: 'line',
data: calculateMA(candles, 20), data: calculateMA(candles, 20),
smooth: true, smooth: true,
lineStyle: { width: 1 } lineStyle: { width: 1, color: '#ef4444' }
}, },
{ {
name: 'MA60', name: 'MA60',
type: 'line', type: 'line',
data: calculateMA(candles, 60), data: calculateMA(candles, 60),
smooth: true, smooth: true,
lineStyle: { width: 1 } lineStyle: { width: 1, color: '#8b5cf6' }
}, },
{ {
name: '成交量', name: '成交量',
@ -2030,7 +2056,43 @@
return { return {
value: v.value, value: v.value,
itemStyle: { itemStyle: {
color: v.close >= v.open ? upColor : downColor color: v.close >= v.open ? upColor : downColor,
opacity: 0.6
}
};
})
},
{
name: 'DIF',
type: 'line',
xAxisIndex: 2,
yAxisIndex: 2,
data: macdData.dif,
smooth: true,
lineStyle: { width: 1.5, color: '#3b82f6' },
symbol: 'none'
},
{
name: 'DEA',
type: 'line',
xAxisIndex: 2,
yAxisIndex: 2,
data: macdData.dea,
smooth: true,
lineStyle: { width: 1.5, color: '#f59e0b' },
symbol: 'none'
},
{
name: 'MACD',
type: 'bar',
xAxisIndex: 2,
yAxisIndex: 2,
data: macdData.macd.map((val, idx) => {
return {
value: val,
itemStyle: {
color: val >= 0 ? upColor : downColor,
opacity: 0.7
} }
}; };
}) })
@ -2061,6 +2123,80 @@
return result; return result;
} }
function calculateMACD(candles) {
const closes = candles.map(c => c.close);
const ema12 = calculateEMA(closes, 12);
const ema26 = calculateEMA(closes, 26);
const dif = [];
for (let i = 0; i < closes.length; i++) {
if (ema12[i] !== '-' && ema26[i] !== '-') {
dif.push(parseFloat(ema12[i]) - parseFloat(ema26[i]));
} else {
dif.push(0);
}
}
const dea = calculateEMARaw(dif, 9);
const macd = dif.map((d, i) => 2 * (d - dea[i]));
return { dif, dea, macd };
}
function calculateEMA(data, period) {
const result = [];
const multiplier = 2 / (period + 1);
let sum = 0;
for (let i = 0; i < period - 1 && i < data.length; i++) {
result.push('-');
sum += data[i];
}
if (data.length >= period) {
sum += data[period - 1];
let ema = sum / period;
result.push(ema.toFixed(2));
for (let i = period; i < data.length; i++) {
ema = (data[i] - ema) * multiplier + ema;
result.push(ema.toFixed(2));
}
}
return result;
}
function calculateEMARaw(data, period) {
const result = [];
const multiplier = 2 / (period + 1);
let sum = 0;
let count = 0;
for (let i = 0; i < data.length; i++) {
if (data[i] === 0 && count < period) {
result.push(0);
continue;
}
if (count < period) {
sum += data[i];
count++;
if (count === period) {
let ema = sum / period;
result.push(ema);
} else {
result.push(0);
}
} else {
const ema = (data[i] - result[result.length - 1]) * multiplier + result[result.length - 1];
result.push(ema);
}
}
return result;
}
function getPeriodLabel(period) { function getPeriodLabel(period) {
const map = { const map = {
'5min': '5分钟', '5min': '5分钟',

Loading…
Cancel
Save