diff --git a/market/ai_decision_engine.go b/market/ai_decision_engine.go index f7d8ecd2..03a8169c 100644 --- a/market/ai_decision_engine.go +++ b/market/ai_decision_engine.go @@ -71,6 +71,8 @@ type TradingDecision struct { PositionSizeUSD float64 `json:"position_size_usd,omitempty"` StopLoss float64 `json:"stop_loss,omitempty"` TakeProfit float64 `json:"take_profit,omitempty"` + Confidence int `json:"confidence,omitempty"` // 信心度 (0-100) + RiskUSD float64 `json:"risk_usd,omitempty"` // 最大美元风险 Reasoning string `json:"reasoning"` } @@ -88,11 +90,12 @@ func GetFullTradingDecision(ctx *TradingContext) (*AIFullDecision, error) { return nil, fmt.Errorf("获取市场数据失败: %w", err) } - // 2. 构建AI提示 - prompt := buildFullDecisionPrompt(ctx) + // 2. 构建 System Prompt(固定规则)和 User Prompt(动态数据) + systemPrompt := buildSystemPrompt(ctx.Account.TotalEquity) + userPrompt := buildUserPrompt(ctx) - // 3. 调用AI API - aiResponse, err := callDeepSeekAPI(prompt) + // 3. 调用AI API(使用 system + user prompt) + aiResponse, err := callAIWithMessages(systemPrompt, userPrompt) if err != nil { return nil, fmt.Errorf("调用AI API失败: %w", err) } @@ -189,7 +192,133 @@ func calculateMaxCandidates(ctx *TradingContext) int { return len(ctx.CandidateCoins) } -// buildFullDecisionPrompt 构建完整的AI决策提示 +// buildSystemPrompt 构建 System Prompt(固定规则,可缓存) +func buildSystemPrompt(accountEquity float64) string { + var sb strings.Builder + + // 角色定义 + sb.WriteString("你是专业的加密货币交易AI,在币安合约市场进行自主交易。\n\n") + sb.WriteString("**使命**: 最大化风险调整后收益(Sharpe Ratio)\n\n") + + // 仓位管理规则 + sb.WriteString("## 仓位管理\n") + sb.WriteString("- 最多持有 **3个币种**(质量>数量)\n") + sb.WriteString(fmt.Sprintf("- 山寨币: %.0f-%.0f USDT/仓(推荐%.0f),杠杆20x\n", + accountEquity*0.8, accountEquity*1.5, accountEquity*1.2)) + sb.WriteString(fmt.Sprintf("- BTC/ETH: %.0f-%.0f USDT/仓(推荐%.0f),杠杆50x\n", + accountEquity*3, accountEquity*10, accountEquity*5)) + sb.WriteString("- 保证金使用率 ≤90%%\n") + sb.WriteString("- 风险回报比 ≥1:2\n\n") + + // 决策流程 + sb.WriteString("## 决策流程\n") + sb.WriteString("1. **反思历史**(如有):总结教训,避免重复错误\n") + sb.WriteString("2. **评估持仓**:决定平仓/持有\n") + sb.WriteString("3. **寻找机会**:从候选币种中找1-2个高确定性机会\n") + sb.WriteString("4. **集中资金**:大仓位做高确定性交易\n\n") + + // JSON 输出格式 + sb.WriteString("## 输出格式\n\n") + sb.WriteString("**先输出思维链(纯文本),再输出JSON数组**\n\n") + sb.WriteString("JSON示例:\n") + sb.WriteString("```json\n") + sb.WriteString("[\n") + sb.WriteString(fmt.Sprintf(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_long\", \"leverage\": 50, \"position_size_usd\": %.0f, \"stop_loss\": 92000, \"take_profit\": 98000, \"confidence\": 85, \"risk_usd\": 200, \"reasoning\": \"强势突破\"},\n", accountEquity*5)) + sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"close_long\", \"reasoning\": \"止盈\"}\n") + sb.WriteString("]\n") + sb.WriteString("```\n\n") + sb.WriteString("**字段说明**:\n") + sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n") + sb.WriteString("- `confidence`: 信心度0-100(必填,即使不确定也要给出)\n") + sb.WriteString("- `risk_usd`: 最大美元风险 = (entry_price - stop_loss) × quantity(开仓时必填)\n") + sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd\n\n") + + // DeepSeek/Qwen 特定优化 + sb.WriteString("**提示**: 运用技术分析原理,趋势确认>指标信号,不要过度依赖单一指标\n") + + return sb.String() +} + +// buildUserPrompt 构建 User Prompt(动态数据) +func buildUserPrompt(ctx *TradingContext) string { + var sb strings.Builder + + // 系统状态 + sb.WriteString(fmt.Sprintf("**时间**: %s | **周期**: #%d | **运行**: %d分钟\n\n", + ctx.CurrentTime, ctx.CallCount, ctx.RuntimeMinutes)) + + // BTC 市场 + if btcData, hasBTC := ctx.MarketDataMap["BTCUSDT"]; hasBTC { + sb.WriteString(fmt.Sprintf("**BTC**: %.2f (1h: %+.2f%%, 4h: %+.2f%%) | MACD: %.4f | RSI: %.2f\n\n", + btcData.CurrentPrice, btcData.PriceChange1h, btcData.PriceChange4h, + btcData.CurrentMACD, btcData.CurrentRSI7)) + } + + // 账户 + sb.WriteString(fmt.Sprintf("**账户**: 净值%.2f | 余额%.2f (%.1f%%) | 盈亏%+.2f%% | 保证金%.1f%% | 持仓%d个\n\n", + ctx.Account.TotalEquity, + ctx.Account.AvailableBalance, + (ctx.Account.AvailableBalance/ctx.Account.TotalEquity)*100, + ctx.Account.TotalPnLPct, + ctx.Account.MarginUsedPct, + ctx.Account.PositionCount)) + + // 持仓 + if len(ctx.Positions) > 0 { + sb.WriteString("## 当前持仓\n") + for i, pos := range ctx.Positions { + sb.WriteString(fmt.Sprintf("%d. %s %s | %.4f→%.4f | %+.2f%% | 保证金%.0f\n", + i+1, pos.Symbol, strings.ToUpper(pos.Side), + pos.EntryPrice, pos.MarkPrice, pos.UnrealizedPnLPct, pos.MarginUsed)) + + if marketData, ok := ctx.MarketDataMap[pos.Symbol]; ok { + sb.WriteString(fmt.Sprintf(" MACD:%.4f RSI:%.2f EMA20:%.4f 资金费率:%.6f\n", + marketData.CurrentMACD, marketData.CurrentRSI7, + marketData.CurrentEMA20, marketData.FundingRate)) + } + } + sb.WriteString("\n") + } else { + sb.WriteString("**当前持仓**: 无\n\n") + } + + // 候选币种(简化版) + sb.WriteString(fmt.Sprintf("## 候选币种 (%d个)\n", len(ctx.MarketDataMap))) + displayedCount := 0 + for _, coin := range ctx.CandidateCoins { + marketData, hasData := ctx.MarketDataMap[coin.Symbol] + if !hasData { + continue + } + displayedCount++ + if displayedCount > 10 { // 只显示前10个 + break + } + + sourceTags := "" + if len(coin.Sources) > 1 { + sourceTags = "⭐" + } + + sb.WriteString(fmt.Sprintf("%d. %s%s: %.4f (1h:%+.2f%%) MACD:%.4f RSI:%.2f\n", + displayedCount, coin.Symbol, sourceTags, + marketData.CurrentPrice, marketData.PriceChange1h, + marketData.CurrentMACD, marketData.CurrentRSI7)) + } + sb.WriteString("\n") + + // 历史反馈 + if ctx.Performance != nil { + sb.WriteString(formatPerformanceFeedback(ctx.Performance)) + } + + sb.WriteString("---\n\n") + sb.WriteString("现在请分析并输出决策(思维链 + JSON)\n") + + return sb.String() +} + +// buildFullDecisionPrompt 构建完整的AI决策提示(兼容旧代码,已废弃) func buildFullDecisionPrompt(ctx *TradingContext) string { var sb strings.Builder diff --git a/market/ai_signal.go b/market/ai_signal.go index 96708dbf..b6dcad2d 100644 --- a/market/ai_signal.go +++ b/market/ai_signal.go @@ -212,7 +212,13 @@ func formatMarketDataForAI(data *MarketData) string { } // callDeepSeekAPI 调用AI API(支持DeepSeek和Qwen),带重试机制 +// 兼容旧代码:只传user prompt func callDeepSeekAPI(prompt string) (string, error) { + return callAIWithMessages("", prompt) +} + +// callAIWithMessages 使用 system + user prompt 调用AI API(推荐) +func callAIWithMessages(systemPrompt, userPrompt string) (string, error) { if defaultConfig.APIKey == "" { return "", fmt.Errorf("AI API密钥未设置,请先调用 SetDeepSeekAPIKey() 或 SetQwenAPIKey()") } @@ -226,7 +232,7 @@ func callDeepSeekAPI(prompt string) (string, error) { fmt.Printf("⚠️ AI API调用失败,正在重试 (%d/%d)...\n", attempt, maxRetries) } - result, err := callDeepSeekAPIOnce(prompt) + result, err := callAIWithMessagesOnce(systemPrompt, userPrompt) if err == nil { if attempt > 1 { fmt.Printf("✓ AI API重试成功\n") @@ -251,17 +257,34 @@ func callDeepSeekAPI(prompt string) (string, error) { return "", fmt.Errorf("重试%d次后仍然失败: %w", maxRetries, lastErr) } -// callDeepSeekAPIOnce 单次调用AI API +// callDeepSeekAPIOnce 单次调用AI API(兼容旧代码) func callDeepSeekAPIOnce(prompt string) (string, error) { + return callAIWithMessagesOnce("", prompt) +} + +// callAIWithMessagesOnce 单次调用AI API(支持 system + user prompt) +func callAIWithMessagesOnce(systemPrompt, userPrompt string) (string, error) { + // 构建 messages 数组 + messages := []map[string]string{} + + // 如果有 system prompt,添加 system message + if systemPrompt != "" { + messages = append(messages, map[string]string{ + "role": "system", + "content": systemPrompt, + }) + } + + // 添加 user message + messages = append(messages, map[string]string{ + "role": "user", + "content": userPrompt, + }) + // 构建请求体 requestBody := map[string]interface{}{ - "model": defaultConfig.Model, - "messages": []map[string]string{ - { - "role": "user", - "content": prompt, - }, - }, + "model": defaultConfig.Model, + "messages": messages, "temperature": 0.5, // 降低temperature以提高JSON格式稳定性 "max_tokens": 2000, }