From 6a2abfa96dec1edb7bbaa9a68740f087117fe334 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Fri, 26 Dec 2025 02:43:34 +0800 Subject: [PATCH] feat: improve AI decision input format and chart UX Backend changes: - Add symbol headers to market data sections for clarity - Add symbol labels to quantitative data output - Skip position coins in candidate list to avoid duplicate data Frontend changes: - Add B/S toggle button to show/hide order markers - Remove bottom legend (B BUY S SELL) - Fix chart position reset on data refresh - Separate marker toggle logic from data loading --- decision/engine.go | 16 ++++- web/src/components/AdvancedChart.tsx | 87 +++++++++++++++++----------- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/decision/engine.go b/decision/engine.go index f9fdc457..67ee3943 100644 --- a/decision/engine.go +++ b/decision/engine.go @@ -983,10 +983,20 @@ func (e *StrategyEngine) BuildUserPrompt(ctx *Context) string { sb.WriteString("Current Positions: None\n\n") } - // Candidate coins + // Candidate coins (exclude coins already in positions to avoid duplicate data) + positionSymbols := make(map[string]bool) + for _, pos := range ctx.Positions { + positionSymbols[pos.Symbol] = true + } + sb.WriteString(fmt.Sprintf("## Candidate Coins (%d coins)\n\n", len(ctx.MarketDataMap))) displayedCount := 0 for _, coin := range ctx.CandidateCoins { + // Skip if this coin is already a position (data already shown in positions section) + if positionSymbols[coin.Symbol] { + continue + } + marketData, hasData := ctx.MarketDataMap[coin.Symbol] if !hasData { continue @@ -1081,6 +1091,8 @@ func (e *StrategyEngine) formatMarketData(data *market.Data) string { var sb strings.Builder indicators := e.config.Indicators + // 明确标注币种 + sb.WriteString(fmt.Sprintf("=== %s Market Data ===\n\n", data.Symbol)) sb.WriteString(fmt.Sprintf("current_price = %.4f", data.CurrentPrice)) if indicators.EnableEMA { @@ -1252,7 +1264,7 @@ func (e *StrategyEngine) formatQuantData(data *QuantData) string { } var sb strings.Builder - sb.WriteString("📊 Quantitative Data:\n") + sb.WriteString(fmt.Sprintf("📊 %s Quantitative Data:\n", data.Symbol)) if len(data.PriceChange) > 0 { sb.WriteString("Price Change: ") diff --git a/web/src/components/AdvancedChart.tsx b/web/src/components/AdvancedChart.tsx index 653c70f8..ac66ee25 100644 --- a/web/src/components/AdvancedChart.tsx +++ b/web/src/components/AdvancedChart.tsx @@ -76,10 +76,13 @@ export function AdvancedChart({ const volumeSeriesRef = useRef | null>(null) const indicatorSeriesRef = useRef>>(new Map()) const seriesMarkersRef = useRef(null) // Markers primitive for v5 + const currentMarkersDataRef = useRef([]) // 存储当前的标记数据 const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [showIndicatorPanel, setShowIndicatorPanel] = useState(false) + const [showOrderMarkers, setShowOrderMarkers] = useState(true) // 订单标记显示开关,默认显示 + const isInitialLoadRef = useRef(true) // 跟踪是否为初始加载 // 指标配置 const [indicators, setIndicators] = useState([ @@ -360,6 +363,9 @@ export function AdvancedChart({ // 加载数据和指标 useEffect(() => { + // 当 symbol 或 interval 改变时,重置初始加载标志(以便自动适配新数据) + isInitialLoadRef.current = true + const loadData = async () => { if (!candlestickSeriesRef.current) return @@ -436,15 +442,20 @@ export function AdvancedChart({ console.log('[AdvancedChart] Markers data:', JSON.stringify(markers, null, 2)) try { + // 存储标记数据供后续切换使用 + currentMarkersDataRef.current = markers + // 使用 v5 API: createSeriesMarkers + const markersToShow = showOrderMarkers ? markers : [] + if (seriesMarkersRef.current) { // 如果已经存在,更新标记 - seriesMarkersRef.current.setMarkers(markers) + seriesMarkersRef.current.setMarkers(markersToShow) } else { // 首次创建标记 - seriesMarkersRef.current = createSeriesMarkers(candlestickSeriesRef.current, markers) + seriesMarkersRef.current = createSeriesMarkers(candlestickSeriesRef.current, markersToShow) } - console.log('[AdvancedChart] ✅ Markers set successfully!') + console.log('[AdvancedChart] ✅ Markers updated! Count:', markersToShow.length, 'Visible:', showOrderMarkers) } catch (err) { console.error('[AdvancedChart] ❌ Failed to set markers:', err) } @@ -465,8 +476,11 @@ export function AdvancedChart({ }) } - // 自动适配视图 - chartRef.current?.timeScale().fitContent() + // 只在初始加载时自动适配视图,避免刷新时抖动 + if (isInitialLoadRef.current) { + chartRef.current?.timeScale().fitContent() + isInitialLoadRef.current = false + } setLoading(false) } catch (err: any) { console.error('[AdvancedChart] Error loading data:', err) @@ -482,6 +496,19 @@ export function AdvancedChart({ return () => clearInterval(refreshInterval) }, [symbol, interval, traderID, indicators]) + // 单独处理订单标记的显示/隐藏,避免重新加载数据 + useEffect(() => { + if (!seriesMarkersRef.current) return + + try { + const markersToShow = showOrderMarkers ? currentMarkersDataRef.current : [] + seriesMarkersRef.current.setMarkers(markersToShow) + console.log('[AdvancedChart] 🔄 Toggled markers visibility:', showOrderMarkers, 'Count:', markersToShow.length) + } catch (err) { + console.error('[AdvancedChart] ❌ Failed to toggle markers:', err) + } + }, [showOrderMarkers]) + // 更新指标 const updateIndicators = (klineData: Kline[]) => { if (!chartRef.current) return @@ -599,6 +626,20 @@ export function AdvancedChart({ {language === 'zh' ? '指标' : 'Indicators'} + + {/* 订单标记开关 */} + @@ -704,9 +745,8 @@ export function AdvancedChart({
NOFX @@ -741,24 +780,6 @@ export function AdvancedChart({
)} - {/* 图例说明 - 简化版 */} -
-
-
- B -
- {language === 'zh' ? '买入 (BUY)' : 'BUY'} -
-
-
- S -
- {language === 'zh' ? '卖出 (SELL)' : 'SELL'} -
-
) }