mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2025-12-11 16:24:10 +08:00
fix(decision+trader): limit candidate coins & fix news collection
## Changes ### 1. decision/engine.go - Fix calculateMaxCandidates to enforce proper limits (was returning all candidates) - Dynamic limits based on position count: * 0 positions: max 30 candidates * 1 position: max 25 candidates * 2 positions: max 20 candidates * 3+ positions: max 15 candidates - Prevents Prompt bloat when users configure many coins ### 2. trader/auto_trader.go - Fix news collection to use actual positions + candidates (was hardcoded to BTC only) - Add extractNewsSymbols() helper function - Collect news for: * All current positions (highest priority) * Top 5 candidate coins * Always include BTC (market indicator) * Max 10 coins total (avoid excessive API calls) - Properly convert symbols for news API (lowercase, remove USDT suffix) ## Impact - Prevents excessive market data fetching - Makes news feature actually useful (was only fetching BTC news) - Better resource utilization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -222,10 +222,31 @@ func fetchMarketDataForContext(ctx *Context) error {
|
||||
|
||||
// calculateMaxCandidates 根据账户状态计算需要分析的候选币种数量
|
||||
func calculateMaxCandidates(ctx *Context) int {
|
||||
// 直接返回候选池的全部币种数量
|
||||
// 因为候选池已经在 auto_trader.go 中筛选过了
|
||||
// 固定分析前20个评分最高的币种(来自AI500)
|
||||
return len(ctx.CandidateCoins)
|
||||
// ⚠️ 重要:限制候选币种数量,避免 Prompt 过大
|
||||
// 根据持仓数量动态调整:持仓越少,可以分析更多候选币
|
||||
const (
|
||||
maxCandidatesWhenEmpty = 30 // 无持仓时最多分析30个候选币
|
||||
maxCandidatesWhenHolding1 = 25 // 持仓1个时最多分析25个候选币
|
||||
maxCandidatesWhenHolding2 = 20 // 持仓2个时最多分析20个候选币
|
||||
maxCandidatesWhenHolding3 = 15 // 持仓3个时最多分析15个候选币(避免 Prompt 过大)
|
||||
)
|
||||
|
||||
positionCount := len(ctx.Positions)
|
||||
var maxCandidates int
|
||||
|
||||
switch positionCount {
|
||||
case 0:
|
||||
maxCandidates = maxCandidatesWhenEmpty
|
||||
case 1:
|
||||
maxCandidates = maxCandidatesWhenHolding1
|
||||
case 2:
|
||||
maxCandidates = maxCandidatesWhenHolding2
|
||||
default: // 3+ 持仓
|
||||
maxCandidates = maxCandidatesWhenHolding3
|
||||
}
|
||||
|
||||
// 返回实际候选币数量和上限中的较小值
|
||||
return min(len(ctx.CandidateCoins), maxCandidates)
|
||||
}
|
||||
|
||||
// buildSystemPromptWithCustom 构建包含自定义内容的 System Prompt
|
||||
|
||||
@@ -761,20 +761,28 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
||||
performance = nil
|
||||
}
|
||||
|
||||
// 6.提取新闻内容
|
||||
// 6. 提取新闻内容(根据持仓和候选币种动态收集)
|
||||
newsItem := make(map[string][]news.NewsItem)
|
||||
for _, newspro := range at.newsProcessor {
|
||||
// TODO: 此出是为后续扩展考虑,当前随意给了个值占位
|
||||
newsMap, err := newspro.FetchNews([]string{"btc"}, 100)
|
||||
// 收集需要新闻的币种(持仓 + 候选币前几个)
|
||||
newsSymbols := at.extractNewsSymbols(positionInfos, candidateCoins)
|
||||
|
||||
if len(newsSymbols) == 0 {
|
||||
log.Printf("⚠️ 没有需要收集新闻的币种,跳过新闻收集")
|
||||
continue
|
||||
}
|
||||
|
||||
newsMap, err := newspro.FetchNews(newsSymbols, 100)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ 获取新闻内容失败: %v", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for symbol, value := range newsMap {
|
||||
newsItem[symbol] = append(newsItem[symbol], value...)
|
||||
}
|
||||
|
||||
log.Printf("📰 收集了 %d 个币种的新闻: %v", len(newsSymbols), newsSymbols)
|
||||
}
|
||||
|
||||
// 7. 构建上下文
|
||||
@@ -1583,6 +1591,49 @@ func normalizeSymbol(symbol string) string {
|
||||
return symbol
|
||||
}
|
||||
|
||||
// extractNewsSymbols 提取需要收集新闻的币种(持仓 + 候选币前几个 + BTC)
|
||||
func (at *AutoTrader) extractNewsSymbols(positions []decision.PositionInfo, candidates []decision.CandidateCoin) []string {
|
||||
const (
|
||||
maxNewsSymbols = 10 // 最多收集10个币种的新闻(避免请求过多)
|
||||
maxCandidatesForNews = 5 // 从候选币中取前5个
|
||||
)
|
||||
|
||||
symbolSet := make(map[string]bool)
|
||||
result := make([]string, 0, maxNewsSymbols)
|
||||
|
||||
// 1. 总是包含 BTC(市场风向标)
|
||||
symbolSet["btc"] = true
|
||||
result = append(result, "btc")
|
||||
|
||||
// 2. 添加所有持仓币种(这些是最重要的)
|
||||
for _, pos := range positions {
|
||||
// 转换为新闻 API 格式(小写,移除 USDT 后缀)
|
||||
baseSymbol := strings.ToLower(strings.TrimSuffix(pos.Symbol, "USDT"))
|
||||
if !symbolSet[baseSymbol] && len(result) < maxNewsSymbols {
|
||||
symbolSet[baseSymbol] = true
|
||||
result = append(result, baseSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 添加候选币种(前几个,按优先级)
|
||||
for i, coin := range candidates {
|
||||
if i >= maxCandidatesForNews {
|
||||
break
|
||||
}
|
||||
if len(result) >= maxNewsSymbols {
|
||||
break
|
||||
}
|
||||
|
||||
baseSymbol := strings.ToLower(strings.TrimSuffix(coin.Symbol, "USDT"))
|
||||
if !symbolSet[baseSymbol] {
|
||||
symbolSet[baseSymbol] = true
|
||||
result = append(result, baseSymbol)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// detectAutoClosedPositions 检测自动平仓的持仓(止损/止盈触发)
|
||||
func (at *AutoTrader) detectAutoClosedPositions(currentPositions []decision.PositionInfo) []logger.DecisionAction {
|
||||
var autoClosedActions []logger.DecisionAction
|
||||
|
||||
Reference in New Issue
Block a user