From ceaedca2532bae2ad09ddac63585dc0553a5bbca Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Wed, 29 Oct 2025 04:44:17 +0800 Subject: [PATCH] Refactor: Improve AI decision system and Sharpe ratio calculation Major improvements: - Use period-level Sharpe ratio (range -2 to +2) instead of annualized - Save full user prompt in decision logs for debugging - Format complete market data (3m + 4h candles) for AI analysis - Prevent position stacking with duplicate position checks - Update Sharpe ratio interpretation thresholds Market data enhancements: - Display full technical indicators in user prompt - Include 3-minute and 4-hour timeframe data - Add OI (Open Interest) change and funding rate signals Risk control: - Block opening duplicate positions (same symbol + direction) - Suggest close action first before opening new position - Prevent margin usage from exceeding limits UI improvements: - Update multi-language translations - Refine AI learning dashboard display Co-Authored-By: tinkle-community --- logger/decision_logger.go | 22 +- market/ai_decision_engine.go | 447 +++++------------------------- trader/auto_trader.go | 6 +- web/src/App.tsx | 22 +- web/src/components/AILearning.tsx | 66 +---- web/src/i18n/translations.ts | 2 + 6 files changed, 104 insertions(+), 461 deletions(-) diff --git a/logger/decision_logger.go b/logger/decision_logger.go index e82f4fd0..7ac07c85 100644 --- a/logger/decision_logger.go +++ b/logger/decision_logger.go @@ -13,7 +13,8 @@ import ( type DecisionRecord struct { Timestamp time.Time `json:"timestamp"` // 决策时间 CycleNumber int `json:"cycle_number"` // 周期编号 - CoTTrace string `json:"cot_trace"` // AI思维链 + InputPrompt string `json:"input_prompt"` // 发送给AI的输入prompt + CoTTrace string `json:"cot_trace"` // AI思维链(输出) DecisionJSON string `json:"decision_json"` // 决策JSON AccountState AccountSnapshot `json:"account_state"` // 账户状态快照 Positions []PositionSnapshot `json:"positions"` // 持仓快照 @@ -469,13 +470,12 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna // 基于账户净值的变化计算风险调整后收益 func (l *DecisionLogger) calculateSharpeRatio(records []*DecisionRecord) float64 { if len(records) < 2 { - return 0.0 // 至少需要2个数据点才能计算收益率 + return 0.0 } // 提取每个周期的账户净值 var equities []float64 for _, record := range records { - // 使用TotalBalance作为净值(包含未实现盈亏) equity := record.AccountState.TotalBalance + record.AccountState.TotalUnrealizedProfit if equity > 0 { equities = append(equities, equity) @@ -533,19 +533,7 @@ func (l *DecisionLogger) calculateSharpeRatio(records []*DecisionRecord) float64 } // 计算夏普比率(假设无风险利率为0) + // 注:直接返回周期级别的夏普比率(非年化),正常范围 -2 到 +2 sharpeRatio := meanReturn / stdDev - - // 年化夏普比率 - // 假设每个周期是3分钟,一天有480个周期 - // 年化因子 = sqrt(一年的周期数) = sqrt(480 * 365) ≈ sqrt(175200) ≈ 419 - // 简化:使用每日周期数作为年化基准 - periodsPerDay := 480.0 // 24小时 * 60分钟 / 3分钟 - annualizationFactor := 1.0 - for i := 0; i < 10; i++ { - annualizationFactor = (annualizationFactor + periodsPerDay/annualizationFactor) / 2 - } - - annualizedSharpe := sharpeRatio * annualizationFactor - - return annualizedSharpe + return sharpeRatio } diff --git a/market/ai_decision_engine.go b/market/ai_decision_engine.go index e7703404..6e1a8ab3 100644 --- a/market/ai_decision_engine.go +++ b/market/ai_decision_engine.go @@ -78,9 +78,10 @@ type TradingDecision struct { // AIFullDecision AI的完整决策(包含思维链) type AIFullDecision struct { - CoTTrace string `json:"cot_trace"` // 思维链分析 - Decisions []TradingDecision `json:"decisions"` // 具体决策列表 - Timestamp time.Time `json:"timestamp"` + UserPrompt string `json:"user_prompt"` // 发送给AI的输入prompt + CoTTrace string `json:"cot_trace"` // 思维链分析(AI输出) + Decisions []TradingDecision `json:"decisions"` // 具体决策列表 + Timestamp time.Time `json:"timestamp"` } // GetFullTradingDecision 获取AI的完整交易决策(批量分析所有币种和持仓) @@ -107,6 +108,7 @@ func GetFullTradingDecision(ctx *TradingContext) (*AIFullDecision, error) { } decision.Timestamp = time.Now() + decision.UserPrompt = userPrompt // 保存输入prompt return decision, nil } @@ -202,17 +204,12 @@ func buildSystemPrompt(accountEquity float64) string { // 自我进化核心 sb.WriteString("## 🧬 自我进化机制\n") - sb.WriteString("每次调用你都会收到**夏普比率**作为你的业绩指标:\n\n") - sb.WriteString("**夏普比率解读**:\n") - sb.WriteString("- < 0:平均亏损 → 🔴 极度保守策略\n") - sb.WriteString("- 0-1:正收益但波动大 → 🟡 保守策略\n") - sb.WriteString("- 1-2:良好表现 → 🟢 维持当前策略\n") - sb.WriteString("- > 2:优异表现 → 🟢 可适度扩大\n\n") - sb.WriteString("**关键要求**: 严格遵循历史表现反馈中的「自适应行为建议」,根据夏普比率动态调整:\n") - sb.WriteString("- 仓位大小(夏普比率低时减仓)\n") - sb.WriteString("- 止损幅度(夏普比率低时收紧)\n") - sb.WriteString("- 选币标准(夏普比率低时提高信心度阈值)\n") - sb.WriteString("- 持仓数量(夏普比率低时减少持仓数)\n\n") + sb.WriteString("每次调用你都会收到**夏普比率**作为你的业绩指标(周期级别,非年化):\n\n") + sb.WriteString("**夏普比率解读**(正常范围 -2 到 +2):\n") + sb.WriteString("- < -0.5:持续亏损 → 🔴 极度保守策略(减仓、收紧止损、减少持仓数)\n") + sb.WriteString("- -0.5 到 0:轻微亏损 → 🟡 优化策略(保守仓位、提高选币标准)\n") + sb.WriteString("- 0 到 0.7:正收益 → 🟢 维持/优化当前策略\n") + sb.WriteString("- > 0.7:优异表现 → 🟢 可适度扩大仓位\n\n") // 仓位管理规则 sb.WriteString("## 仓位管理\n") @@ -226,12 +223,10 @@ func buildSystemPrompt(accountEquity float64) string { // 决策流程 sb.WriteString("## 决策流程\n") - sb.WriteString("1. **检查夏普比率**:首先查看历史表现反馈中的夏普比率,理解当前策略效果\n") - sb.WriteString("2. **应用自适应建议**:严格遵循自适应行为建议中的仓位、止损、选币要求\n") - sb.WriteString("3. **反思历史**:分析之前交易的得失,找出可改进点\n") - sb.WriteString("4. **评估持仓**:根据自适应建议决定平仓/持有\n") - sb.WriteString("5. **寻找机会**:按照调整后的标准筛选机会\n") - sb.WriteString("6. **执行决策**:使用调整后的仓位大小和风险参数\n\n") + sb.WriteString("1. **检查夏普比率**:理解当前策略效果,根据夏普比率调整策略\n") + sb.WriteString("2. **评估持仓**:决定平仓/持有\n") + sb.WriteString("3. **寻找机会**:筛选候选币种\n") + sb.WriteString("4. **执行决策**:输出思维链和JSON决策\n\n") // JSON 输出格式 sb.WriteString("## 输出格式\n\n") @@ -279,27 +274,27 @@ func buildUserPrompt(ctx *TradingContext) string { 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", + sb.WriteString(fmt.Sprintf("%d. %s %s | 入场价%.4f 当前价%.4f | 盈亏%+.2f%% | 杠杆%dx | 保证金%.0f | 强平价%.4f\n\n", i+1, pos.Symbol, strings.ToUpper(pos.Side), - pos.EntryPrice, pos.MarkPrice, pos.UnrealizedPnLPct, pos.MarginUsed)) + pos.EntryPrice, pos.MarkPrice, pos.UnrealizedPnLPct, + pos.Leverage, pos.MarginUsed, pos.LiquidationPrice)) + // 使用FormatMarketData输出完整市场数据 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(FormatMarketData(marketData)) + sb.WriteString("\n") } } - sb.WriteString("\n") } else { sb.WriteString("**当前持仓**: 无\n\n") } - // 候选币种(简化版) - sb.WriteString(fmt.Sprintf("## 候选币种 (%d个)\n", len(ctx.MarketDataMap))) + // 候选币种(完整市场数据) + sb.WriteString(fmt.Sprintf("## 候选币种 (%d个)\n\n", len(ctx.MarketDataMap))) displayedCount := 0 for _, coin := range ctx.CandidateCoins { marketData, hasData := ctx.MarketDataMap[coin.Symbol] @@ -307,25 +302,33 @@ func buildUserPrompt(ctx *TradingContext) string { continue } displayedCount++ - if displayedCount > 10 { // 只显示前10个 - break - } sourceTags := "" if len(coin.Sources) > 1 { - sourceTags = "⭐" + sourceTags = " (AI500+OI_Top双重信号)" + } else if len(coin.Sources) == 1 && coin.Sources[0] == "oi_top" { + sourceTags = " (OI_Top持仓增长)" } - 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)) + // 使用FormatMarketData输出完整市场数据 + sb.WriteString(fmt.Sprintf("### %d. %s%s\n\n", displayedCount, coin.Symbol, sourceTags)) + sb.WriteString(FormatMarketData(marketData)) + sb.WriteString("\n") } sb.WriteString("\n") - // 历史反馈 + // 夏普比率(直接传值,不要复杂格式化) if ctx.Performance != nil { - sb.WriteString(formatPerformanceFeedback(ctx.Performance, ctx.Account.TotalEquity)) + // 直接从interface{}中提取SharpeRatio + type PerformanceData struct { + SharpeRatio float64 `json:"sharpe_ratio"` + } + var perfData PerformanceData + if jsonData, err := json.Marshal(ctx.Performance); err == nil { + if err := json.Unmarshal(jsonData, &perfData); err == nil { + sb.WriteString(fmt.Sprintf("## 📊 夏普比率: %.2f\n\n", perfData.SharpeRatio)) + } + } } sb.WriteString("---\n\n") @@ -334,331 +337,6 @@ func buildUserPrompt(ctx *TradingContext) string { return sb.String() } -// buildFullDecisionPrompt 构建完整的AI决策提示(兼容旧代码,已废弃) -func buildFullDecisionPrompt(ctx *TradingContext) string { - var sb strings.Builder - - sb.WriteString("# 🤖 加密货币交易AI竞赛系统\n\n") - sb.WriteString("你是专业的加密货币交易AI,根据市场数据自主决策,做多做空均可。\n\n") - - // 添加BTC市场趋势 - sb.WriteString("## 🌍 BTC市场趋势\n") - if btcData, hasBTC := ctx.MarketDataMap["BTCUSDT"]; hasBTC { - sb.WriteString(fmt.Sprintf("- 价格: %.2f | 1h: %+.2f%% | 4h: %+.2f%%\n", - btcData.CurrentPrice, btcData.PriceChange1h, btcData.PriceChange4h)) - sb.WriteString(fmt.Sprintf("- MACD: %.4f | RSI: %.2f | 资金费率: %.6f\n\n", - btcData.CurrentMACD, btcData.CurrentRSI7, btcData.FundingRate)) - } else { - sb.WriteString("BTC数据暂无\n\n") - } - - // 系统状态 - sb.WriteString("## 📊 系统状态\n") - sb.WriteString(fmt.Sprintf("- **当前时间**: %s\n", ctx.CurrentTime)) - sb.WriteString(fmt.Sprintf("- **运行时长**: %d 分钟\n", ctx.RuntimeMinutes)) - sb.WriteString(fmt.Sprintf("- **调用次数**: 第 %d 次\n\n", ctx.CallCount)) - - // 账户信息 - sb.WriteString("## 💰 账户信息\n") - sb.WriteString(fmt.Sprintf("- **账户净值**: %.2f USDT\n", ctx.Account.TotalEquity)) - sb.WriteString(fmt.Sprintf("- **可用余额**: %.2f USDT (%.1f%%)\n", - ctx.Account.AvailableBalance, - (ctx.Account.AvailableBalance/ctx.Account.TotalEquity)*100)) - sb.WriteString(fmt.Sprintf("- **总盈亏**: %.2f USDT (%+.2f%%)\n", - ctx.Account.TotalPnL, ctx.Account.TotalPnLPct)) - sb.WriteString(fmt.Sprintf("- **已用保证金**: %.2f USDT (%.1f%%)\n", - ctx.Account.MarginUsed, ctx.Account.MarginUsedPct)) - sb.WriteString(fmt.Sprintf("- **持仓数量**: %d\n\n", ctx.Account.PositionCount)) - - // 当前持仓详情 - if len(ctx.Positions) > 0 { - sb.WriteString("## 📈 当前持仓\n") - for i, pos := range ctx.Positions { - sb.WriteString(fmt.Sprintf("\n### 持仓 #%d: %s %s\n", i+1, pos.Symbol, strings.ToUpper(pos.Side))) - sb.WriteString(fmt.Sprintf("- **入场价**: %.4f USDT\n", pos.EntryPrice)) - sb.WriteString(fmt.Sprintf("- **当前价**: %.4f USDT\n", pos.MarkPrice)) - sb.WriteString(fmt.Sprintf("- **数量**: %.4f\n", pos.Quantity)) - sb.WriteString(fmt.Sprintf("- **杠杆**: %dx\n", pos.Leverage)) - sb.WriteString(fmt.Sprintf("- **未实现盈亏**: %.2f USDT (%+.2f%%)\n", - pos.UnrealizedPnL, pos.UnrealizedPnLPct)) - sb.WriteString(fmt.Sprintf("- **强平价**: %.4f USDT\n", pos.LiquidationPrice)) - sb.WriteString(fmt.Sprintf("- **占用保证金**: %.2f USDT\n", pos.MarginUsed)) - - // 添加市场数据 - if marketData, ok := ctx.MarketDataMap[pos.Symbol]; ok { - sb.WriteString(formatMarketDataBrief(marketData)) - } - } - sb.WriteString("\n") - } else { - sb.WriteString("## 📈 当前持仓\n") - sb.WriteString("暂无持仓\n\n") - } - - // 候选币种池 - sb.WriteString("## 🎯 候选币种池\n") - sb.WriteString(fmt.Sprintf("共 %d 个币种(已过滤持仓价值<15M USD的低流动性币种)\n\n", len(ctx.MarketDataMap))) - - displayedCount := 0 - for _, coin := range ctx.CandidateCoins { - // 只显示已获取市场数据的币种 - marketData, hasData := ctx.MarketDataMap[coin.Symbol] - if !hasData { - continue - } - displayedCount++ - - // 显示币种来源标签 - 使用圆括号避免与JSON混淆 - sourceTags := "" - hasAI500 := false - hasOITop := false - for _, source := range coin.Sources { - if source == "ai500" { - hasAI500 = true - } else if source == "oi_top" { - hasOITop = true - } - } - - if hasAI500 && hasOITop { - sourceTags = "(AI500+OI_Top双重信号)" - } else if hasAI500 { - sourceTags = "(AI500高评分)" - } else if hasOITop { - sourceTags = "(OI_Top持仓增长)" - } - - sb.WriteString(fmt.Sprintf("\n### 币种 #%d: %s %s\n", displayedCount, coin.Symbol, sourceTags)) - sb.WriteString(formatMarketDataBrief(marketData)) - - // 如果有OI Top数据,也显示出来 - if oiTopData, hasOI := ctx.OITopDataMap[coin.Symbol]; hasOI { - sb.WriteString(fmt.Sprintf("**市场热度数据** (OI Top排名 #%d):\n", oiTopData.Rank)) - sb.WriteString(fmt.Sprintf(" - 持仓量1h变化: %+.2f%% (价值: $%.0f)\n", - oiTopData.OIDeltaPercent, oiTopData.OIDeltaValue)) - sb.WriteString(fmt.Sprintf(" - 价格1h变化: %+.2f%% | 净多仓: %.0f | 净空仓: %.0f\n", - oiTopData.PriceDeltaPercent, oiTopData.NetLong, oiTopData.NetShort)) - } - } - - // 添加历史表现反馈(如果有) - if ctx.Performance != nil { - sb.WriteString(formatPerformanceFeedback(ctx.Performance, ctx.Account.TotalEquity)) - } - - // AI决策要求 - sb.WriteString("## 🎯 任务\n\n") - sb.WriteString("分析市场数据,自主决策:\n") - sb.WriteString("1. **如有历史数据,先进行自我反思**:回顾之前的交易,总结经验教训\n") - sb.WriteString("2. 评估现有持仓 → 持有或平仓\n") - sb.WriteString(fmt.Sprintf("3. 从%d个候选币种中找交易机会\n", len(ctx.MarketDataMap))) - sb.WriteString("4. 开新仓(如果有机会)\n\n") - - sb.WriteString("## 📋 规则 - **重要:集中资金,精选标的**\n\n") - sb.WriteString("### 🎯 仓位管理(核心规则)\n") - sb.WriteString("1. **最大持仓数量**: 同时最多持有 **3个币种**(质量 > 数量)\n") - sb.WriteString("2. **单个仓位大小**: \n") - sb.WriteString(fmt.Sprintf(" - 山寨币: %.0f-%.0f USDT(推荐%.0f USDT)\n", - ctx.Account.TotalEquity*0.8, ctx.Account.TotalEquity*1.5, ctx.Account.TotalEquity*1.2)) - sb.WriteString(fmt.Sprintf(" - BTC/ETH: %.0f-%.0f USDT(推荐%.0f USDT)\n", - ctx.Account.TotalEquity*3, ctx.Account.TotalEquity*10, ctx.Account.TotalEquity*5)) - sb.WriteString("3. **杠杆**: 山寨币=20倍 | BTC/ETH=50倍\n") - sb.WriteString("4. **保证金上限**: 总使用率≤90%%\n") - sb.WriteString("5. **风险回报比**: ≥1:2\n\n") - sb.WriteString("### ⚠️ 仓位策略\n") - sb.WriteString("- **集中火力**: 宁可持有1-2个大仓位,也不要持有5-6个小仓位\n") - sb.WriteString("- **严格筛选**: 只做最有把握的机会,不确定的机会宁可不做\n") - sb.WriteString("- **快速止损**: 亏损超过2%%立即止损,不要让小亏变大亏\n") - sb.WriteString("- **及时止盈**: 盈利达到目标立即止盈,落袋为安\n\n") - - sb.WriteString("### 📤 输出格式\n\n") - sb.WriteString("先输出思维链分析(纯文本),然后输出JSON数组:\n\n") - sb.WriteString("**思维链分析**:\n") - sb.WriteString("1. **历史经验反思**(如有历史数据): 回顾表现,总结教训,是否仓位太分散?\n") - sb.WriteString("2. **市场分析**: 分析BTC趋势和当前持仓\n") - sb.WriteString("3. **仓位检查**: 当前持仓数量是否>3个?如果是,平掉表现差的,集中资金\n") - sb.WriteString("4. **机会识别**: 从候选币种中找1-2个最好的机会(不是3-5个)\n") - sb.WriteString("5. **仓位大小**: 确保单个仓位足够大(山寨币1200+ USDT,BTC 5000+ USDT)\n") - sb.WriteString("6. **风险控制**: 检查账户保证金和仓位限制\n") - sb.WriteString("7. **最终决策摘要**: 列出所有决策(最多3个币种持仓)\n\n") - sb.WriteString("---\n\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, \"reasoning\": \"强势突破,集中资金\"},\n", ctx.Account.TotalEquity*5)) - sb.WriteString(fmt.Sprintf(" {\"symbol\": \"SOLUSDT\", \"action\": \"open_long\", \"leverage\": 20, \"position_size_usd\": %.0f, \"stop_loss\": 180, \"take_profit\": 200, \"reasoning\": \"技术面强势\"}\n", ctx.Account.TotalEquity*1.2)) - sb.WriteString("]\n\n") - sb.WriteString("action类型: open_long | open_short | close_long | close_short | hold | wait\n") - sb.WriteString("开仓必填: leverage, position_size_usd, stop_loss, take_profit\n\n") - - sb.WriteString("### 📝 完整示例(集中资金策略)\n\n") - - // 示例仓位:集中资金策略 - btcSize := ctx.Account.TotalEquity * 5 // BTC:5倍净值(推荐值) - - sb.WriteString("【历史经验反思】\n") - sb.WriteString("回顾最近10笔交易:仓位太分散,同时持有5个币种但单个仓位太小,赚不到钱。\n") - sb.WriteString("SOLUSDT做多3次,2次小盈1次止损,净盈利很少。决策:应该用更大仓位做确定性高的机会。\n") - sb.WriteString("BTCUSDT做多2次,1胜1负,但因为仓位太小,盈利不明显。\n") - sb.WriteString("**改进策略**: 集中资金在1-2个最有把握的币种,加大仓位。\n\n") - sb.WriteString("【市场分析】\n") - sb.WriteString("BTC突破95000,MACD金叉,RSI 65,趋势强势。\n") - sb.WriteString("当前持有ETHUSDT(小仓位+0.8%)、SOLUSDT(小仓位-0.3%)、LINKUSDT(小仓位+0.2%)→ 太分散!\n") - sb.WriteString("决定:平掉所有小仓位,集中资金做BTC大仓位。\n\n") - sb.WriteString("【最终决策】平掉3个小仓位,集中5000 USDT做BTC多头(仓位是之前的3倍+)。\n\n") - sb.WriteString("---\n\n") - sb.WriteString("[\n") - sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"close_long\", \"reasoning\": \"小仓位盈利太少,释放资金\"},\n") - sb.WriteString(" {\"symbol\": \"SOLUSDT\", \"action\": \"close_long\", \"reasoning\": \"小亏损,释放资金\"},\n") - sb.WriteString(" {\"symbol\": \"LINKUSDT\", \"action\": \"close_long\", \"reasoning\": \"小仓位盈利太少,释放资金\"},\n") - sb.WriteString(fmt.Sprintf(" {\"symbol\": \"BTCUSDT\", \"action\": \"open_long\", \"leverage\": 50, \"position_size_usd\": %.0f, \"stop_loss\": 92000, \"take_profit\": 98000, \"reasoning\": \"强势突破,集中资金做大仓位\"}\n", btcSize)) - sb.WriteString("]\n\n") - sb.WriteString("**说明**: 这样只持有1个BTC大仓位,盈利空间是之前的3倍+,止损也更清晰。\n\n") - - sb.WriteString("现在请开始分析并给出你的决策!\n") - - return sb.String() -} - -// formatPerformanceFeedback 格式化历史表现反馈 -// accountEquity 参数用于计算自适应建议 -func formatPerformanceFeedback(perfInterface interface{}, accountEquity float64) string { - // 类型断言(避免循环依赖,使用interface{}) - type TradeOutcome struct { - Symbol string - Side string - OpenPrice float64 - ClosePrice float64 - PnL float64 - PnLPct float64 - Duration string - } - type SymbolPerformance struct { - Symbol string - TotalTrades int - WinningTrades int - LosingTrades int - WinRate float64 - TotalPnL float64 - AvgPnL float64 - } - type PerformanceAnalysis struct { - TotalTrades int - WinningTrades int - LosingTrades int - WinRate float64 - AvgWin float64 - AvgLoss float64 - ProfitFactor float64 - SharpeRatio float64 - RecentTrades []TradeOutcome - SymbolStats map[string]*SymbolPerformance - BestSymbol string - WorstSymbol string - } - - // 使用JSON转换进行类型转换(避免直接类型断言) - jsonData, _ := json.Marshal(perfInterface) - var perf PerformanceAnalysis - if err := json.Unmarshal(jsonData, &perf); err != nil { - return "" - } - - var sb strings.Builder - - sb.WriteString("## 📊 历史表现反馈\n\n") - - // 夏普比率(风险调整后收益)- 即使没有完成交易也要显示! - if perf.SharpeRatio != 0 { - sharpeStatus := interpretSharpeRatio(perf.SharpeRatio) - sb.WriteString(fmt.Sprintf("**夏普比率**: %.2f (%s)\n\n", perf.SharpeRatio, sharpeStatus)) - } - - if perf.TotalTrades == 0 { - sb.WriteString("暂无已完成交易(仅基于账户净值变化计算夏普比率)\n\n") - // ⚠️ 不要提前返回!继续显示自适应建议 - } else { - // 整体统计(有已完成交易时才显示) - sb.WriteString("### 整体表现\n") - sb.WriteString(fmt.Sprintf("- **总交易数**: %d 笔 (盈利: %d | 亏损: %d)\n", - perf.TotalTrades, perf.WinningTrades, perf.LosingTrades)) - sb.WriteString(fmt.Sprintf("- **胜率**: %.1f%%\n", perf.WinRate)) - sb.WriteString(fmt.Sprintf("- **平均盈利**: +%.2f%% | 平均亏损: %.2f%%\n", - perf.AvgWin, perf.AvgLoss)) - if perf.ProfitFactor > 0 { - sb.WriteString(fmt.Sprintf("- **盈亏比**: %.2f:1\n", perf.ProfitFactor)) - } - sb.WriteString("\n") - } - - // 最近交易 - if len(perf.RecentTrades) > 0 { - sb.WriteString("### 最近交易\n") - displayCount := len(perf.RecentTrades) - if displayCount > 5 { - displayCount = 5 - } - for i := 0; i < displayCount; i++ { - trade := perf.RecentTrades[i] - outcome := "✓" - if trade.PnL < 0 { - outcome = "✗" - } - sb.WriteString(fmt.Sprintf("%d. %s %s: %.4f → %.4f = %+.2f%% %s\n", - i+1, trade.Symbol, strings.ToUpper(trade.Side), - trade.OpenPrice, trade.ClosePrice, - trade.PnLPct, outcome)) - } - sb.WriteString("\n") - } - - // 币种表现(显示前3个最好和最差) - if len(perf.SymbolStats) > 0 { - sb.WriteString("### 币种表现\n") - - if perf.BestSymbol != "" { - if stats, exists := perf.SymbolStats[perf.BestSymbol]; exists { - sb.WriteString(fmt.Sprintf("- **最佳**: %s (胜率%.0f%%, 平均%+.2f%%)\n", - stats.Symbol, stats.WinRate, stats.AvgPnL)) - } - } - - if perf.WorstSymbol != "" { - if stats, exists := perf.SymbolStats[perf.WorstSymbol]; exists { - sb.WriteString(fmt.Sprintf("- **最差**: %s (胜率%.0f%%, 平均%+.2f%%)\n", - stats.Symbol, stats.WinRate, stats.AvgPnL)) - } - } - sb.WriteString("\n") - } - - // 添加自适应行为建议(基于夏普比率) - if perf.SharpeRatio != 0 { - sb.WriteString(getAdaptiveBehaviorRecommendation(perf.SharpeRatio, accountEquity)) - } - - return sb.String() -} - -// formatMarketDataBrief 格式化市场数据(简洁版) -func formatMarketDataBrief(data *MarketData) string { - var sb strings.Builder - - sb.WriteString("**市场数据** (3分钟线):\n") - sb.WriteString(fmt.Sprintf(" - 价格: %.4f | 1h变化: %+.2f%% | 4h变化: %+.2f%% (%s)\n", - data.CurrentPrice, data.PriceChange1h, data.PriceChange4h, priceTrend(data.PriceChange1h, data.PriceChange4h))) - sb.WriteString(fmt.Sprintf(" - EMA20: %.4f (%s) | MACD: %.4f (%s) | RSI(7): %.2f (%s)\n", - data.CurrentEMA20, pricePosition(data.CurrentPrice, data.CurrentEMA20), - data.CurrentMACD, macdTrend(data.CurrentMACD), data.CurrentRSI7, rsiStatus(data.CurrentRSI7))) - - if data.OpenInterest != nil { - oiChange := ((data.OpenInterest.Latest - data.OpenInterest.Average) / data.OpenInterest.Average) * 100 - fundingSignal := fundingRateSignal(data.FundingRate) - sb.WriteString(fmt.Sprintf(" - 持仓量: %+.2f%% | 资金费率: %.6f (%s)\n", oiChange, data.FundingRate, fundingSignal)) - } - - return sb.String() -} - // parseFullDecisionResponse 解析AI的完整决策响应 func parseFullDecisionResponse(aiResponse string, accountEquity float64) (*AIFullDecision, error) { // 1. 提取思维链 @@ -833,15 +511,18 @@ func validateDecision(d *TradingDecision, accountEquity float64) error { return nil } -// interpretSharpeRatio 解释夏普比率的含义(参考 nof1.ai) +// interpretSharpeRatio 解释夏普比率的含义 +// 注:这里是周期级别(非年化)的夏普比率,正常范围在 -2 到 +2 func interpretSharpeRatio(sharpe float64) string { - if sharpe < 0 { - return "负收益,需要调整策略" - } else if sharpe < 1 { + if sharpe < -0.5 { + return "持续亏损,策略需大幅调整" + } else if sharpe < 0 { + return "轻微亏损,需优化策略" + } else if sharpe < 0.3 { return "正收益但波动大" - } else if sharpe < 2 { + } else if sharpe < 0.7 { return "良好表现" - } else if sharpe < 3 { + } else if sharpe < 1.0 { return "优秀表现" } else { return "卓越表现" @@ -850,13 +531,14 @@ func interpretSharpeRatio(sharpe float64) string { // getAdaptiveBehaviorRecommendation 根据夏普比率生成自适应行为建议 // 这是AI自我进化的核心:根据风险调整后收益动态调整交易策略 +// 注:sharpe是周期级别(非年化),正常范围 -2 到 +2 func getAdaptiveBehaviorRecommendation(sharpe float64, accountEquity float64) string { var sb strings.Builder sb.WriteString("### 🎯 自适应行为建议(基于夏普比率)\n\n") - if sharpe < 0 { - // 🔴 负夏普比率:平均亏损,需要极度保守 + if sharpe < -0.5 { + // 🔴 持续亏损:需要极度保守 sb.WriteString("**⚠️ 警告:当前策略产生负收益,立即调整!**\n\n") sb.WriteString("**策略调整**:\n") sb.WriteString(fmt.Sprintf("- 仓位规模:**减半**(山寨币: %.0f USDT, BTC/ETH: %.0f USDT)\n", @@ -870,8 +552,19 @@ func getAdaptiveBehaviorRecommendation(sharpe float64, accountEquity float64) st sb.WriteString("- 是否追涨杀跌?是否逆势交易?\n") sb.WriteString("- 止损是否执行到位?\n\n") - } else if sharpe < 1 { - // 🟡 0-1:正收益但波动性较大 + } else if sharpe < 0 { + // 🟡 -0.5 到 0:轻微亏损 + sb.WriteString("**状态:轻微亏损,需要优化策略**\n\n") + sb.WriteString("**策略调整**:\n") + sb.WriteString(fmt.Sprintf("- 仓位规模:**保守**(山寨币: %.0f USDT, BTC/ETH: %.0f USDT)\n", + accountEquity*0.8, accountEquity*3.5)) + sb.WriteString("- 止损幅度:**收紧至-1.5%**\n") + sb.WriteString("- 选币标准:**提高阈值**(信心度≥80%,风险回报比≥1:2.5)\n") + sb.WriteString("- 持仓数量:**最多2个**\n") + sb.WriteString("- 重点改进:**找出亏损原因**,调整选币或时机\n\n") + + } else if sharpe < 0.7 { + // 🟢 0-0.7:正收益但可继续优化 sb.WriteString("**状态:正收益但风险较高,需要优化**\n\n") sb.WriteString("**策略调整**:\n") sb.WriteString(fmt.Sprintf("- 仓位规模:**保守**(山寨币: %.0f USDT, BTC/ETH: %.0f USDT)\n", @@ -885,8 +578,8 @@ func getAdaptiveBehaviorRecommendation(sharpe float64, accountEquity float64) st sb.WriteString("- 减少交易频率,提高单笔交易质量\n") sb.WriteString("- 盈利时及时止盈,不要贪多\n\n") - } else if sharpe < 2 { - // 🟢 1-2:风险调整后表现良好 + } else if sharpe < 1.0 { + // 🟢 0.7-1.0:优秀表现 sb.WriteString("**状态:表现良好,继续保持当前策略**\n\n") sb.WriteString("**策略调整**:\n") sb.WriteString(fmt.Sprintf("- 仓位规模:**标准**(山寨币: %.0f USDT, BTC/ETH: %.0f USDT)\n", @@ -901,7 +594,7 @@ func getAdaptiveBehaviorRecommendation(sharpe float64, accountEquity float64) st sb.WriteString("- 保持冷静客观,不要因为短期盈利而冒进\n\n") } else { - // 🟢 >2:风险调整后表现优异 + // 🟢 >1.0:卓越表现 sb.WriteString("**状态:卓越表现,策略非常有效!**\n\n") sb.WriteString("**策略调整**:\n") sb.WriteString(fmt.Sprintf("- 仓位规模:**可适度放大**(山寨币: %.0f USDT, BTC/ETH: %.0f USDT)\n", diff --git a/trader/auto_trader.go b/trader/auto_trader.go index 08a715d7..a83dc8fe 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -224,8 +224,9 @@ func (at *AutoTrader) runCycle() error { log.Println("🤖 正在请求AI分析并决策...") decision, err := market.GetFullTradingDecision(ctx) - // 即使有错误,也保存思维链和决策(用于debug) + // 即使有错误,也保存思维链、决策和输入prompt(用于debug) if decision != nil { + record.InputPrompt = decision.UserPrompt record.CoTTrace = decision.CoTTrace if len(decision.Decisions) > 0 { decisionJSON, _ := json.MarshalIndent(decision.Decisions, "", " ") @@ -428,7 +429,8 @@ func (at *AutoTrader) buildTradingContext() (*market.TradingContext, error) { performance, err := at.decisionLogger.AnalyzePerformance(20) if err != nil { log.Printf("⚠️ 分析历史表现失败: %v", err) - // 不影响主流程,继续执行 + // 不影响主流程,继续执行(但设置performance为nil以避免传递错误数据) + performance = nil } // 6. 构建上下文 diff --git a/web/src/App.tsx b/web/src/App.tsx index 0ee53253..026c3e2a 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -559,6 +559,7 @@ function StatCard({ // Decision Card Component with CoT Trace - Binance Style function DecisionCard({ decision, language }: { decision: DecisionRecord; language: Language }) { + const [showInput, setShowInput] = useState(false); const [showCoT, setShowCoT] = useState(false); return ( @@ -582,6 +583,25 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua + {/* AI Input Prompt - Collapsible */} + {decision.input_prompt && ( +
+ + {showInput && ( +
+ {decision.input_prompt} +
+ )} +
+ )} + {/* AI Chain of Thought - Collapsible */} {decision.cot_trace && (
@@ -590,7 +610,7 @@ function DecisionCard({ decision, language }: { decision: DecisionRecord; langua className="flex items-center gap-2 text-sm transition-colors" style={{ color: '#F0B90B' }} > - {t('aiThinking', language)} + 📤 {t('aiThinking', language)} {showCoT ? t('collapse', language) : t('expand', language)} {showCoT && ( diff --git a/web/src/components/AILearning.tsx b/web/src/components/AILearning.tsx index 9f6a1351..0155bdd1 100644 --- a/web/src/components/AILearning.tsx +++ b/web/src/components/AILearning.tsx @@ -47,6 +47,7 @@ interface AILearningProps { interface DecisionRecord { timestamp: string; cycle_number: number; + input_prompt: string; cot_trace: string; success: boolean; } @@ -104,9 +105,6 @@ export default function AILearning({ traderId }: AILearningProps) { (a, b) => (b.total_pn_l || 0) - (a.total_pn_l || 0) ); - // 提取AI的最新反思(从CoT trace中) - const latestReflection = extractReflectionFromCoT(latestDecisions?.[0]?.cot_trace); - return (
{/* 标题区 - 更简洁 */} @@ -128,68 +126,8 @@ export default function AILearning({ traderId }: AILearningProps) { {/* 主要内容:现代化网格布局 */}
- {/* 左侧大区域:AI反思 + 核心指标 (5列) */} + {/* 左侧大区域:核心指标 (5列) */}
- {/* AI最新反思 - 渐变卡片 */} - {latestReflection && latestDecisions && latestDecisions.length > 0 && ( -
- {/* 背景装饰 */} -
- -
-
-
- 🎯 -
-
-

- {t('latestReflection', language)} -

-

- {t('cycle', language)} #{latestDecisions[0].cycle_number} · {new Date(latestDecisions[0].timestamp).toLocaleTimeString()} -

-
-
- -
- {latestReflection} -
- - {latestDecisions[0].cot_trace && ( -
- - {t('fullCoT', language)} - -
- {latestDecisions[0].cot_trace} -
-
- )} -
-
- )} - {/* 核心指标网格 - 玻璃态设计 */}
{/* 总交易数 */} diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts index 397bd08e..9f43ef4e 100644 --- a/web/src/i18n/translations.ts +++ b/web/src/i18n/translations.ts @@ -47,6 +47,7 @@ export const translations = { cycle: 'Cycle', success: 'Success', failed: 'Failed', + inputPrompt: 'Input Prompt', aiThinking: 'AI Chain of Thought', collapse: 'Collapse', expand: 'Expand', @@ -164,6 +165,7 @@ export const translations = { cycle: '周期', success: '成功', failed: '失败', + inputPrompt: '输入提示', aiThinking: '💭 AI思维链分析', collapse: '▼ 收起', expand: '▶ 展开',