feat(MarketDataView): 新增图表配置工具函数

This commit is contained in:
zdl
2025-12-16 15:23:49 +08:00
parent 3f3e13bddd
commit 3d6d01964d

View File

@@ -441,6 +441,434 @@ export const getMinuteKLineOption = (theme: Theme, minuteData: MinuteData | null
};
};
/**
* 生成日K线图配置 - 黑金主题
*/
export const getKLineDarkGoldOption = (
tradeData: TradeDayData[],
analysisMap: Record<number, RiseAnalysis>
): EChartsOption => {
if (!tradeData || tradeData.length === 0) return {};
// 黑金主题色
const gold = '#D4AF37';
const goldLight = '#F4D03F';
const orange = '#FF9500';
const red = '#FF4444';
const green = '#00C851';
const textColor = 'rgba(255, 255, 255, 0.85)';
const textMuted = 'rgba(255, 255, 255, 0.5)';
const borderColor = 'rgba(212, 175, 55, 0.2)';
const dates = tradeData.map((item) => item.date.substring(5, 10));
const kData = tradeData.map((item) => [item.open, item.close, item.low, item.high]);
const volumes = tradeData.map((item) => item.volume);
const closePrices = tradeData.map((item) => item.close);
const ma5 = calculateMA(closePrices, 5);
const ma10 = calculateMA(closePrices, 10);
const ma20 = calculateMA(closePrices, 20);
// 创建涨幅分析标记点
const scatterData: [number, number][] = [];
Object.keys(analysisMap).forEach((dateIndex) => {
const idx = parseInt(dateIndex);
if (tradeData[idx]) {
const value = tradeData[idx].high * 1.02;
scatterData.push([idx, value]);
}
});
return {
backgroundColor: 'transparent',
animation: true,
legend: {
data: ['K线', 'MA5', 'MA10', 'MA20'],
top: 10,
textStyle: {
color: textColor,
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
lineStyle: {
color: gold,
width: 1,
opacity: 0.8,
},
},
backgroundColor: 'rgba(26, 26, 46, 0.95)',
borderColor: gold,
borderWidth: 1,
textStyle: {
color: textColor,
},
},
xAxis: [
{
type: 'category',
data: dates,
boundaryGap: false,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted },
splitLine: { show: false },
},
{
type: 'category',
gridIndex: 1,
data: dates,
boundaryGap: false,
axisLine: { onZero: false, lineStyle: { color: borderColor } },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
},
],
yAxis: [
{
scale: true,
splitLine: {
show: true,
lineStyle: {
color: borderColor,
type: 'dashed',
},
},
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted },
},
{
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: { show: false },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
},
],
grid: [
{
left: '10%',
right: '10%',
height: '50%',
},
{
left: '10%',
right: '10%',
top: '65%',
height: '20%',
},
],
series: [
{
name: 'K线',
type: 'candlestick',
data: kData,
itemStyle: {
color: red,
color0: green,
borderColor: red,
borderColor0: green,
},
},
{
name: 'MA5',
type: 'line',
data: ma5,
smooth: true,
lineStyle: {
color: gold,
width: 1,
},
itemStyle: {
color: gold,
},
},
{
name: 'MA10',
type: 'line',
data: ma10,
smooth: true,
lineStyle: {
color: goldLight,
width: 1,
},
itemStyle: {
color: goldLight,
},
},
{
name: 'MA20',
type: 'line',
data: ma20,
smooth: true,
lineStyle: {
color: orange,
width: 1,
},
itemStyle: {
color: orange,
},
},
{
name: '涨幅分析',
type: 'scatter',
data: scatterData,
symbolSize: 30,
symbol: 'pin',
itemStyle: {
color: goldLight,
shadowBlur: 10,
shadowColor: 'rgba(244, 208, 63, 0.5)',
},
label: {
show: true,
formatter: '★',
fontSize: 20,
position: 'inside',
color: '#FF6B6B',
},
emphasis: {
scale: 1.5,
itemStyle: {
color: orange,
},
},
z: 100,
},
{
name: '成交量',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: volumes,
itemStyle: {
color: (params: { dataIndex: number }) => {
const item = tradeData[params.dataIndex];
return item.change_percent >= 0
? 'rgba(255, 68, 68, 0.6)'
: 'rgba(0, 200, 81, 0.6)';
},
},
},
],
};
};
/**
* 生成分钟K线图配置 - 黑金主题
*/
export const getMinuteKLineDarkGoldOption = (minuteData: MinuteData | null): EChartsOption => {
if (!minuteData || !minuteData.data || minuteData.data.length === 0) return {};
// 黑金主题色
const gold = '#D4AF37';
const goldLight = '#F4D03F';
const orange = '#FF9500';
const red = '#FF4444';
const green = '#00C851';
const textColor = 'rgba(255, 255, 255, 0.85)';
const textMuted = 'rgba(255, 255, 255, 0.5)';
const borderColor = 'rgba(212, 175, 55, 0.2)';
const times = minuteData.data.map((item) => item.time);
const kData = minuteData.data.map((item) => [item.open, item.close, item.low, item.high]);
const volumes = minuteData.data.map((item) => item.volume);
const closePrices = minuteData.data.map((item) => item.close);
const avgPrice = calculateMA(closePrices, 5);
const openPrice = minuteData.data.length > 0 ? minuteData.data[0].open : 0;
return {
backgroundColor: 'transparent',
title: {
text: `${minuteData.name} 分钟K线 (${minuteData.trade_date})`,
left: 'center',
textStyle: {
color: gold,
fontSize: 16,
fontWeight: 'bold',
},
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' },
backgroundColor: 'rgba(26, 26, 46, 0.95)',
borderColor: gold,
borderWidth: 1,
textStyle: {
color: textColor,
fontSize: 12,
},
formatter: (params: unknown) => {
const paramsArr = params as { name: string; marker: string; seriesName: string; data: number[] | number; value: number }[];
let result = `<span style="color: ${gold}">${paramsArr[0].name}</span><br/>`;
paramsArr.forEach((param) => {
if (param.seriesName === '分钟K线') {
const [open, close, , high] = param.data as number[];
const low = (param.data as number[])[2];
const changePercent =
openPrice > 0 ? (((close - openPrice) / openPrice) * 100).toFixed(2) : '0.00';
result += `${param.marker} ${param.seriesName}<br/>`;
result += `开盘: <span style="font-weight: bold; color: ${goldLight}">${open.toFixed(2)}</span><br/>`;
result += `收盘: <span style="font-weight: bold; color: ${close >= open ? red : green}">${close.toFixed(2)}</span><br/>`;
result += `最高: <span style="font-weight: bold; color: ${goldLight}">${high.toFixed(2)}</span><br/>`;
result += `最低: <span style="font-weight: bold; color: ${goldLight}">${low.toFixed(2)}</span><br/>`;
result += `涨跌: <span style="font-weight: bold; color: ${close >= openPrice ? red : green}">${changePercent}%</span><br/>`;
} else if (param.seriesName === '均价线') {
result += `${param.marker} ${param.seriesName}: <span style="font-weight: bold; color: ${goldLight}">${(param.value as number).toFixed(2)}</span><br/>`;
} else if (param.seriesName === '成交量') {
result += `${param.marker} ${param.seriesName}: <span style="font-weight: bold; color: ${goldLight}">${formatNumber(param.value as number, 0)}</span><br/>`;
}
});
return result;
},
},
legend: {
data: ['分钟K线', '均价线', '成交量'],
top: 35,
textStyle: {
color: textColor,
fontSize: 12,
},
itemWidth: 25,
itemHeight: 14,
},
grid: [
{
left: '8%',
right: '8%',
top: '20%',
height: '60%',
},
{
left: '8%',
right: '8%',
top: '83%',
height: '12%',
},
],
xAxis: [
{
type: 'category',
data: times,
boundaryGap: false,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: {
color: textMuted,
fontSize: 10,
interval: 'auto',
},
splitLine: { show: false },
},
{
type: 'category',
gridIndex: 1,
data: times,
boundaryGap: false,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: {
color: textMuted,
fontSize: 10,
},
splitLine: { show: false },
},
],
yAxis: [
{
scale: true,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted, fontSize: 10 },
splitLine: {
lineStyle: {
color: borderColor,
type: 'dashed',
},
},
},
{
gridIndex: 1,
scale: true,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted, fontSize: 10 },
splitLine: { show: false },
},
],
dataZoom: [
{
type: 'inside',
xAxisIndex: [0, 1],
start: 70,
end: 100,
minValueSpan: 20,
},
{
show: true,
xAxisIndex: [0, 1],
type: 'slider',
top: '95%',
start: 70,
end: 100,
height: 20,
handleSize: '100%',
handleStyle: {
color: gold,
},
textStyle: {
color: textMuted,
},
borderColor: borderColor,
fillerColor: 'rgba(212, 175, 55, 0.2)',
},
],
series: [
{
name: '分钟K线',
type: 'candlestick',
data: kData,
itemStyle: {
color: red,
color0: green,
borderColor: red,
borderColor0: green,
borderWidth: 1,
},
barWidth: '60%',
},
{
name: '均价线',
type: 'line',
data: avgPrice,
smooth: true,
symbol: 'none',
lineStyle: {
color: gold,
width: 2,
opacity: 0.8,
},
},
{
name: '成交量',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: volumes,
barWidth: '50%',
itemStyle: {
color: (params: { dataIndex: number }) => {
const item = minuteData.data[params.dataIndex];
return item.close >= item.open
? 'rgba(255, 68, 68, 0.6)'
: 'rgba(0, 200, 81, 0.6)';
},
},
},
],
};
};
/**
* 生成融资融券图表配置
*/
@@ -837,11 +1265,140 @@ export const getPledgeOption = (theme: Theme, pledgeData: PledgeData[]): ECharts
};
};
/**
* 生成股权质押图表配置 - 黑金主题
*/
export const getPledgeDarkGoldOption = (pledgeData: PledgeData[]): EChartsOption => {
if (!pledgeData || pledgeData.length === 0) return {};
const dates = pledgeData.map((item) => item.end_date.substring(5, 10));
const ratios = pledgeData.map((item) => item.pledge_ratio);
const counts = pledgeData.map((item) => item.pledge_count);
// 黑金主题色
const gold = '#D4AF37';
const goldLight = '#F4D03F';
const orange = '#FF9500';
const textColor = 'rgba(255, 255, 255, 0.85)';
const textMuted = 'rgba(255, 255, 255, 0.5)';
const borderColor = 'rgba(212, 175, 55, 0.2)';
return {
backgroundColor: 'transparent',
title: {
text: '股权质押趋势',
left: 'center',
textStyle: {
color: gold,
fontSize: 16,
fontWeight: 'bold',
},
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(26, 26, 46, 0.95)',
borderColor: gold,
borderWidth: 1,
textStyle: {
color: textColor,
},
},
legend: {
data: ['质押比例', '质押笔数'],
bottom: 10,
textStyle: {
color: textColor,
},
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true,
},
xAxis: {
type: 'category',
data: dates,
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted },
splitLine: { show: false },
},
yAxis: [
{
type: 'value',
name: '质押比例(%)',
nameTextStyle: { color: textMuted },
splitLine: {
lineStyle: {
color: borderColor,
type: 'dashed',
},
},
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted },
},
{
type: 'value',
name: '质押笔数',
nameTextStyle: { color: textMuted },
axisLine: { lineStyle: { color: borderColor } },
axisLabel: { color: textMuted },
splitLine: { show: false },
},
],
series: [
{
name: '质押比例',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: {
color: gold,
width: 2,
shadowBlur: 10,
shadowColor: 'rgba(212, 175, 55, 0.5)',
},
itemStyle: {
color: gold,
borderColor: goldLight,
borderWidth: 2,
},
data: ratios,
},
{
name: '质押笔数',
type: 'bar',
yAxisIndex: 1,
barWidth: '50%',
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: orange },
{ offset: 1, color: 'rgba(255, 149, 0, 0.3)' },
],
},
borderRadius: [4, 4, 0, 0],
},
data: counts,
},
],
};
};
export default {
calculateMA,
getKLineOption,
getKLineDarkGoldOption,
getMinuteKLineOption,
getMinuteKLineDarkGoldOption,
getFundingOption,
getFundingDarkGoldOption,
getPledgeOption,
getPledgeDarkGoldOption,
};