diff --git a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx index 7ee7b9d0..7e9891e4 100644 --- a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx +++ b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx @@ -10,7 +10,7 @@ import type { KLineModuleProps, OverlayMetricData } from '../../../types'; import type { ChartMode } from './constants'; // 子组件导入 -import { KLineToolbar, DailyKLineChart, MinuteChartWithOrderBook } from './components'; +import { KLineToolbar, DailyKLineChart, MinuteChartWithOrderBook, IndicatorGuide } from './components'; // 重新导出类型供外部使用 export type { KLineModuleProps } from '../../../types'; @@ -40,6 +40,7 @@ const KLineModule: React.FC = ({ const [drawingType, setDrawingType] = useState('NONE'); const [overlayMetrics, setOverlayMetrics] = useState([]); const [showOrderBook, setShowOrderBook] = useState(true); + const [showIndicatorGuide, setShowIndicatorGuide] = useState(false); // ========== 计算属性 ========== const hasMinuteData = minuteData && minuteData.data && minuteData.data.length > 0; @@ -73,6 +74,11 @@ const KLineModule: React.FC = ({ setShowOrderBook(prev => !prev); }, []); + // 切换显示/隐藏指标说明 + const handleToggleIndicatorGuide = useCallback(() => { + setShowIndicatorGuide(prev => !prev); + }, []); + // 添加叠加指标 const handleAddOverlayMetric = useCallback((metric: OverlayMetricData) => { setOverlayMetrics(prev => [...prev, metric]); @@ -119,6 +125,8 @@ const KLineModule: React.FC = ({ onAddOverlayMetric={handleAddOverlayMetric} onRemoveOverlayMetric={handleRemoveOverlayMetric} stockDateRange={stockDateRange} + showIndicatorGuide={showIndicatorGuide} + onToggleIndicatorGuide={handleToggleIndicatorGuide} minuteData={minuteData} minuteLoading={minuteLoading} showOrderBook={showOrderBook} @@ -126,6 +134,9 @@ const KLineModule: React.FC = ({ onRefreshMinuteData={onLoadMinuteData} /> + {/* 指标说明面板(可折叠) */} + {mode === 'daily' && } + {/* 图表内容区域 */} {/* 加载中骨架屏 */} diff --git a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/IndicatorGuide.tsx b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/IndicatorGuide.tsx new file mode 100644 index 00000000..a56bfcfe --- /dev/null +++ b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/IndicatorGuide.tsx @@ -0,0 +1,266 @@ +// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/IndicatorGuide.tsx +// 技术指标说明组件 - 可折叠面板 + +import React, { memo } from 'react'; +import { + Box, + Text, + VStack, + HStack, + Collapse, + Badge, + SimpleGrid, + Divider, +} from '@chakra-ui/react'; +import { darkGoldTheme } from '../../../../constants'; + +interface IndicatorGuideProps { + isOpen: boolean; +} + +// 自定义颜色(补充主题中没有的颜色) +const INDICATOR_COLORS = { + cyan: '#00BCD4', + magenta: '#E040FB', + gold: darkGoldTheme.gold, + green: darkGoldTheme.green, + orange: darkGoldTheme.orange, +}; + +// 指标说明数据 +const INDICATOR_DEFINITIONS = { + main: [ + { + name: 'MA(移动平均线)', + abbr: 'MA', + formula: 'MA(N) = 最近N日收盘价之和 / N', + description: '反映一段时间内的平均成本,用于判断趋势方向。', + params: 'MA5、MA10、MA20', + usage: '短期MA上穿长期MA为金叉(买入信号),下穿为死叉(卖出信号)', + color: INDICATOR_COLORS.cyan, + }, + { + name: 'BOLL(布林带)', + abbr: 'BOLL', + formula: '中轨 = MA(20)\n上轨 = 中轨 + 2×标准差\n下轨 = 中轨 - 2×标准差', + description: '基于统计学原理,显示价格的波动范围和趋势。', + params: '周期20,标准差倍数2', + usage: '价格触及上轨可能超买,触及下轨可能超卖;收窄预示变盘', + color: INDICATOR_COLORS.magenta, + }, + ], + sub: [ + { + name: 'MACD(平滑异同移动平均线)', + abbr: 'MACD', + formula: 'DIF = EMA(12) - EMA(26)\nDEA = EMA(DIF, 9)\nMACD柱 = (DIF - DEA) × 2', + description: '趋势跟踪指标,用于判断买卖时机和趋势强弱。', + params: '快线12,慢线26,信号线9', + usage: 'DIF上穿DEA为金叉(买入),下穿为死叉(卖出);柱状体由负转正看多', + color: INDICATOR_COLORS.gold, + }, + { + name: 'KDJ(随机指标)', + abbr: 'KDJ', + formula: 'RSV = (收盘价-最低价)/(最高价-最低价)×100\nK = 2/3×前K + 1/3×RSV\nD = 2/3×前D + 1/3×K\nJ = 3K - 2D', + description: '动量指标,反映价格相对于近期波动范围的位置。', + params: '周期9,K平滑3,D平滑3', + usage: 'K/D<20超卖区,>80超买区;K上穿D为金叉;J值>100超买,<0超卖', + color: INDICATOR_COLORS.cyan, + }, + { + name: 'RSI(相对强弱指标)', + abbr: 'RSI', + formula: 'RS = 平均涨幅 / 平均跌幅\nRSI = 100 - 100/(1+RS)', + description: '衡量价格变动的速度和幅度,判断超买超卖。', + params: 'RSI6(短期)、RSI12(中期)、RSI24(长期)', + usage: 'RSI>70超买区,<30超卖区;背离现象预示反转', + color: INDICATOR_COLORS.magenta, + }, + { + name: 'WR(威廉指标)', + abbr: 'WR', + formula: 'WR = (最高价-收盘价)/(最高价-最低价)×(-100)', + description: '衡量市场的超买超卖程度,数值在-100到0之间。', + params: 'WR6、WR14', + usage: 'WR<-80超卖,>-20超买;从超买/超卖区回落时为反转信号', + color: INDICATOR_COLORS.green, + }, + { + name: 'CCI(商品通道指标)', + abbr: 'CCI', + formula: 'TP = (最高+最低+收盘)/3\nCCI = (TP-MA(TP))/(0.015×平均绝对偏差)', + description: '测量价格偏离统计平均值的程度,适用于周期性波动。', + params: '周期14', + usage: 'CCI>+100超买,<-100超卖;突破±100为趋势启动信号', + color: INDICATOR_COLORS.orange, + }, + { + name: 'BIAS(乖离率)', + abbr: 'BIAS', + formula: 'BIAS = (收盘价-MA)/MA × 100%', + description: '衡量价格偏离移动平均线的程度,用于判断回归。', + params: 'BIAS6、BIAS12、BIAS24', + usage: '正值过大表示超买,负值过大表示超卖;均值回归时机', + color: INDICATOR_COLORS.cyan, + }, + ], +}; + +const IndicatorCard: React.FC<{ + indicator: typeof INDICATOR_DEFINITIONS.main[0]; +}> = ({ indicator }) => { + return ( + + + + + {indicator.abbr} + + + {indicator.name} + + + + + + + 计算公式 + + {indicator.formula} + + + + + 参数设置 + {indicator.params} + + + + 使用方法 + {indicator.usage} + + + + ); +}; + +const IndicatorGuide: React.FC = ({ isOpen }) => { + return ( + + + + {/* 标题 */} + + + 技术指标说明 + + + 点击「指标说明」按钮可收起此面板 + + + + {/* 主图指标 */} + + + + 主图指标 + + + 叠加在K线图上显示 + + + + {INDICATOR_DEFINITIONS.main.map((indicator) => ( + + ))} + + + + + + {/* 副图指标 */} + + + + 副图指标 + + + 显示在K线图下方独立区域 + + + + {INDICATOR_DEFINITIONS.sub.map((indicator) => ( + + ))} + + + + {/* 风险提示 */} + + + ⚠️ 风险提示:技术指标仅供参考,不构成投资建议。市场有风险,投资需谨慎。建议结合基本面分析和市场环境综合判断。 + + + + + + ); +}; + +export default memo(IndicatorGuide); diff --git a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/KLineToolbar.tsx b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/KLineToolbar.tsx index 623a92e7..cd175ed1 100644 --- a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/KLineToolbar.tsx +++ b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/KLineToolbar.tsx @@ -18,7 +18,7 @@ import { MenuItem, Tooltip, } from '@chakra-ui/react'; -import { BarChart2, TrendingUp, Calendar, LineChart, Activity, Pencil, RefreshCw, ChevronDown, Eye, EyeOff } from 'lucide-react'; +import { BarChart2, TrendingUp, Calendar, LineChart, Activity, Pencil, RefreshCw, ChevronDown, Eye, EyeOff, HelpCircle } from 'lucide-react'; import { darkGoldTheme, PERIOD_OPTIONS } from '../../../../constants'; import type { IndicatorType, MainIndicatorType, DrawingType } from '../../../../utils/chartOptions'; @@ -59,6 +59,10 @@ export interface KLineToolbarProps { onRemoveOverlayMetric: (metricId: string) => void; stockDateRange?: { startDate: string; endDate: string }; + // 指标说明 + showIndicatorGuide: boolean; + onToggleIndicatorGuide: () => void; + // 分时模式 minuteData?: MinuteData | null; minuteLoading: boolean; @@ -84,6 +88,8 @@ const KLineToolbar: React.FC = ({ onAddOverlayMetric, onRemoveOverlayMetric, stockDateRange, + showIndicatorGuide, + onToggleIndicatorGuide, minuteData, minuteLoading, showOrderBook, @@ -274,6 +280,20 @@ const KLineToolbar: React.FC = ({ onRemoveMetric={onRemoveOverlayMetric} stockDateRange={stockDateRange} /> + + {/* 指标说明按钮 */} + + + )} diff --git a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/index.ts b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/index.ts index 622ad3c8..2a7476d4 100644 --- a/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/index.ts +++ b/src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/components/index.ts @@ -9,3 +9,5 @@ export type { DailyKLineChartProps } from './DailyKLineChart'; export { default as MinuteChartWithOrderBook } from './MinuteChartWithOrderBook'; export type { MinuteChartWithOrderBookProps } from './MinuteChartWithOrderBook'; + +export { default as IndicatorGuide } from './IndicatorGuide';