From 3d6d01964dd663ded42775f2abae27acc0f221ec Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Tue, 16 Dec 2025 15:23:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(MarketDataView):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8=E9=85=8D=E7=BD=AE=E5=B7=A5=E5=85=B7=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MarketDataView/utils/chartOptions.ts | 557 ++++++++++++++++++ 1 file changed, 557 insertions(+) diff --git a/src/views/Company/components/MarketDataView/utils/chartOptions.ts b/src/views/Company/components/MarketDataView/utils/chartOptions.ts index 1e206c45..204810e4 100644 --- a/src/views/Company/components/MarketDataView/utils/chartOptions.ts +++ b/src/views/Company/components/MarketDataView/utils/chartOptions.ts @@ -441,6 +441,434 @@ export const getMinuteKLineOption = (theme: Theme, minuteData: MinuteData | null }; }; +/** + * 生成日K线图配置 - 黑金主题 + */ +export const getKLineDarkGoldOption = ( + tradeData: TradeDayData[], + analysisMap: Record +): 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 = `${paramsArr[0].name}
`; + 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}
`; + result += `开盘: ${open.toFixed(2)}
`; + result += `收盘: ${close.toFixed(2)}
`; + result += `最高: ${high.toFixed(2)}
`; + result += `最低: ${low.toFixed(2)}
`; + result += `涨跌: ${changePercent}%
`; + } else if (param.seriesName === '均价线') { + result += `${param.marker} ${param.seriesName}: ${(param.value as number).toFixed(2)}
`; + } else if (param.seriesName === '成交量') { + result += `${param.marker} ${param.seriesName}: ${formatNumber(param.value as number, 0)}
`; + } + }); + 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, };