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

master
Lxy 2 weeks ago
parent 9990b228c4
commit 705bd802ca

@ -514,7 +514,7 @@
#klineChart {
width: 100%;
height: 500px;
height: 650px;
}
/* Log */
@ -1881,12 +1881,16 @@
const upColor = '#ef4444';
const downColor = '#10b981';
// 计算MACD指标
const macdData = calculateMACD(candles);
const option = {
animation: true,
legend: {
bottom: 10,
top: 10,
left: 'center',
data: ['K线', 'MA5', 'MA10', 'MA20', 'MA60']
data: ['K线', 'MA5', 'MA10', 'MA20', 'MA60', 'DIF', 'DEA', 'MACD'],
textStyle: { fontSize: 11 }
},
tooltip: {
trigger: 'axis',
@ -1915,6 +1919,10 @@
result += `<div>${param.seriesName}: ${param.data}</div>`;
} else if (param.seriesName === '成交量') {
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: [
{ left: 60, right: 40, height: '60%' },
{ left: 60, right: 40, top: '75%', height: '15%' }
{ left: 60, right: 60, top: 50, height: '48%' },
{ left: 60, right: 60, top: '56%', height: '13%' },
{ left: 60, right: 60, top: '74%', height: '14%' }
],
xAxis: [
{
@ -1938,10 +1947,17 @@
data: dates,
gridIndex: 1,
axisLine: { lineStyle: { color: '#8392A5' } },
axisLabel: { show: false }
},
{
type: 'category',
data: dates,
gridIndex: 2,
axisLine: { lineStyle: { color: '#8392A5' } },
axisLabel: {
show: true,
formatter: function(value) {
return value.substring(5);
return value.substring(11);
}
}
}
@ -1962,23 +1978,33 @@
axisTick: { show: false },
axisLabel: { 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: [
{
type: 'inside',
xAxisIndex: [0, 1],
xAxisIndex: [0, 1, 2],
start: Math.max(0, 100 - 100 * 100 / candles.length),
end: 100
},
{
show: true,
xAxisIndex: [0, 1],
xAxisIndex: [0, 1, 2],
type: 'slider',
bottom: 10,
bottom: 5,
start: Math.max(0, 100 - 100 * 100 / candles.length),
end: 100,
height: 20
height: 15,
borderColor: 'transparent',
backgroundColor: '#f1f5f9'
}
],
series: [
@ -1998,28 +2024,28 @@
type: 'line',
data: calculateMA(candles, 5),
smooth: true,
lineStyle: { width: 1 }
lineStyle: { width: 1, color: '#f59e0b' }
},
{
name: 'MA10',
type: 'line',
data: calculateMA(candles, 10),
smooth: true,
lineStyle: { width: 1 }
lineStyle: { width: 1, color: '#3b82f6' }
},
{
name: 'MA20',
type: 'line',
data: calculateMA(candles, 20),
smooth: true,
lineStyle: { width: 1 }
lineStyle: { width: 1, color: '#ef4444' }
},
{
name: 'MA60',
type: 'line',
data: calculateMA(candles, 60),
smooth: true,
lineStyle: { width: 1 }
lineStyle: { width: 1, color: '#8b5cf6' }
},
{
name: '成交量',
@ -2030,7 +2056,43 @@
return {
value: v.value,
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;
}
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) {
const map = {
'5min': '5分钟',

Loading…
Cancel
Save