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
This commit is contained in:
tinkle-community
2025-12-26 02:43:34 +08:00
parent 4ff9d21783
commit 6a2abfa96d
2 changed files with 68 additions and 35 deletions

View File

@@ -983,10 +983,20 @@ func (e *StrategyEngine) BuildUserPrompt(ctx *Context) string {
sb.WriteString("Current Positions: None\n\n") 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))) sb.WriteString(fmt.Sprintf("## Candidate Coins (%d coins)\n\n", len(ctx.MarketDataMap)))
displayedCount := 0 displayedCount := 0
for _, coin := range ctx.CandidateCoins { 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] marketData, hasData := ctx.MarketDataMap[coin.Symbol]
if !hasData { if !hasData {
continue continue
@@ -1081,6 +1091,8 @@ func (e *StrategyEngine) formatMarketData(data *market.Data) string {
var sb strings.Builder var sb strings.Builder
indicators := e.config.Indicators indicators := e.config.Indicators
// 明确标注币种
sb.WriteString(fmt.Sprintf("=== %s Market Data ===\n\n", data.Symbol))
sb.WriteString(fmt.Sprintf("current_price = %.4f", data.CurrentPrice)) sb.WriteString(fmt.Sprintf("current_price = %.4f", data.CurrentPrice))
if indicators.EnableEMA { if indicators.EnableEMA {
@@ -1252,7 +1264,7 @@ func (e *StrategyEngine) formatQuantData(data *QuantData) string {
} }
var sb strings.Builder var sb strings.Builder
sb.WriteString("📊 Quantitative Data:\n") sb.WriteString(fmt.Sprintf("📊 %s Quantitative Data:\n", data.Symbol))
if len(data.PriceChange) > 0 { if len(data.PriceChange) > 0 {
sb.WriteString("Price Change: ") sb.WriteString("Price Change: ")

View File

@@ -76,10 +76,13 @@ export function AdvancedChart({
const volumeSeriesRef = useRef<ISeriesApi<'Histogram'> | null>(null) const volumeSeriesRef = useRef<ISeriesApi<'Histogram'> | null>(null)
const indicatorSeriesRef = useRef<Map<string, ISeriesApi<any>>>(new Map()) const indicatorSeriesRef = useRef<Map<string, ISeriesApi<any>>>(new Map())
const seriesMarkersRef = useRef<any>(null) // Markers primitive for v5 const seriesMarkersRef = useRef<any>(null) // Markers primitive for v5
const currentMarkersDataRef = useRef<any[]>([]) // 存储当前的标记数据
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [showIndicatorPanel, setShowIndicatorPanel] = useState(false) const [showIndicatorPanel, setShowIndicatorPanel] = useState(false)
const [showOrderMarkers, setShowOrderMarkers] = useState(true) // 订单标记显示开关,默认显示
const isInitialLoadRef = useRef(true) // 跟踪是否为初始加载
// 指标配置 // 指标配置
const [indicators, setIndicators] = useState<IndicatorConfig[]>([ const [indicators, setIndicators] = useState<IndicatorConfig[]>([
@@ -360,6 +363,9 @@ export function AdvancedChart({
// 加载数据和指标 // 加载数据和指标
useEffect(() => { useEffect(() => {
// 当 symbol 或 interval 改变时,重置初始加载标志(以便自动适配新数据)
isInitialLoadRef.current = true
const loadData = async () => { const loadData = async () => {
if (!candlestickSeriesRef.current) return if (!candlestickSeriesRef.current) return
@@ -436,15 +442,20 @@ export function AdvancedChart({
console.log('[AdvancedChart] Markers data:', JSON.stringify(markers, null, 2)) console.log('[AdvancedChart] Markers data:', JSON.stringify(markers, null, 2))
try { try {
// 存储标记数据供后续切换使用
currentMarkersDataRef.current = markers
// 使用 v5 API: createSeriesMarkers // 使用 v5 API: createSeriesMarkers
const markersToShow = showOrderMarkers ? markers : []
if (seriesMarkersRef.current) { if (seriesMarkersRef.current) {
// 如果已经存在,更新标记 // 如果已经存在,更新标记
seriesMarkersRef.current.setMarkers(markers) seriesMarkersRef.current.setMarkers(markersToShow)
} else { } 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) { } catch (err) {
console.error('[AdvancedChart] ❌ Failed to set markers:', 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) setLoading(false)
} catch (err: any) { } catch (err: any) {
console.error('[AdvancedChart] Error loading data:', err) console.error('[AdvancedChart] Error loading data:', err)
@@ -482,6 +496,19 @@ export function AdvancedChart({
return () => clearInterval(refreshInterval) return () => clearInterval(refreshInterval)
}, [symbol, interval, traderID, indicators]) }, [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[]) => { const updateIndicators = (klineData: Kline[]) => {
if (!chartRef.current) return if (!chartRef.current) return
@@ -599,6 +626,20 @@ export function AdvancedChart({
<Settings className="w-3.5 h-3.5" /> <Settings className="w-3.5 h-3.5" />
<span>{language === 'zh' ? '指标' : 'Indicators'}</span> <span>{language === 'zh' ? '指标' : 'Indicators'}</span>
</button> </button>
{/* 订单标记开关 */}
<button
onClick={() => setShowOrderMarkers(!showOrderMarkers)}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-all"
style={{
background: showOrderMarkers ? 'rgba(240, 185, 11, 0.15)' : 'rgba(255, 255, 255, 0.05)',
color: showOrderMarkers ? '#F0B90B' : '#848E9C',
border: `1px solid ${showOrderMarkers ? 'rgba(240, 185, 11, 0.3)' : '#2B3139'}`,
}}
title={language === 'zh' ? '切换订单标记显示' : 'Toggle Order Markers'}
>
<span className="font-bold text-[11px]">B/S</span>
</button>
</div> </div>
</div> </div>
@@ -704,9 +745,8 @@ export function AdvancedChart({
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '50%', bottom: '20%',
left: '50%', right: '5%',
transform: 'translate(-50%, -50%)',
pointerEvents: 'none', pointerEvents: 'none',
userSelect: 'none', userSelect: 'none',
zIndex: 1, zIndex: 1,
@@ -714,13 +754,12 @@ export function AdvancedChart({
> >
<div <div
style={{ style={{
fontSize: '120px', fontSize: '56px',
fontWeight: '900', fontWeight: '700',
color: 'rgba(240, 185, 11, 0.15)', color: 'rgba(240, 185, 11, 0.12)',
letterSpacing: '12px', letterSpacing: '4px',
fontFamily: 'Arial Black, sans-serif', fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
textShadow: '0 0 80px rgba(240, 185, 11, 0.25)', textShadow: '0 2px 30px rgba(240, 185, 11, 0.2)',
opacity: 0.6,
}} }}
> >
NOFX NOFX
@@ -741,24 +780,6 @@ export function AdvancedChart({
</div> </div>
)} )}
{/* 图例说明 - 简化版 */}
<div
className="flex items-center gap-4 px-4 py-2.5 text-xs"
style={{ borderTop: '1px solid #2B3139', background: '#0F1215' }}
>
<div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-bold" style={{ background: '#0ECB81', color: '#0B0E11' }}>
B
</div>
<span style={{ color: '#EAECEF' }}>{language === 'zh' ? '买入 (BUY)' : 'BUY'}</span>
</div>
<div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-bold" style={{ background: '#F6465D', color: '#0B0E11' }}>
S
</div>
<span style={{ color: '#EAECEF' }}>{language === 'zh' ? '卖出 (SELL)' : 'SELL'}</span>
</div>
</div>
</div> </div>
) )
} }