diff --git a/decision/engine.go b/decision/engine.go index 56633b58..076a2a13 100644 --- a/decision/engine.go +++ b/decision/engine.go @@ -731,8 +731,14 @@ func validateDecision(d *Decision, accountEquity float64, btcEthLeverage, altcoi maxPositionValue = accountEquity * 10 // BTC/ETH最多10倍账户净值 } - if d.Leverage <= 0 || d.Leverage > maxLeverage { - return fmt.Errorf("杠杆必须在1-%d之间(%s,当前配置上限%d倍): %d", maxLeverage, d.Symbol, maxLeverage, d.Leverage) + // ✅ Fallback 机制:杠杆超限时自动修正为上限值(而不是直接拒绝决策) + if d.Leverage <= 0 { + return fmt.Errorf("杠杆必须大于0: %d", d.Leverage) + } + if d.Leverage > maxLeverage { + log.Printf("⚠️ [Leverage Fallback] %s 杠杆超限 (%dx > %dx),自动调整为上限值 %dx", + d.Symbol, d.Leverage, maxLeverage, maxLeverage) + d.Leverage = maxLeverage // 自动修正为上限值 } if d.PositionSizeUSD <= 0 { return fmt.Errorf("仓位大小必须大于0: %.2f", d.PositionSizeUSD) diff --git a/decision/validate_test.go b/decision/validate_test.go new file mode 100644 index 00000000..faac4fe5 --- /dev/null +++ b/decision/validate_test.go @@ -0,0 +1,100 @@ +package decision + +import ( + "testing" +) + +// TestLeverageFallback 测试杠杆超限时的自动修正功能 +func TestLeverageFallback(t *testing.T) { + tests := []struct { + name string + decision Decision + accountEquity float64 + btcEthLeverage int + altcoinLeverage int + wantLeverage int // 期望修正后的杠杆值 + wantError bool + }{ + { + name: "山寨币杠杆超限_自动修正为上限", + decision: Decision{ + Symbol: "SOLUSDT", + Action: "open_long", + Leverage: 20, // 超过上限 + PositionSizeUSD: 100, + StopLoss: 50, + TakeProfit: 200, + }, + accountEquity: 100, + btcEthLeverage: 10, + altcoinLeverage: 5, // 上限 5x + wantLeverage: 5, // 应该修正为 5 + wantError: false, + }, + { + name: "BTC杠杆超限_自动修正为上限", + decision: Decision{ + Symbol: "BTCUSDT", + Action: "open_long", + Leverage: 20, // 超过上限 + PositionSizeUSD: 1000, + StopLoss: 90000, + TakeProfit: 110000, + }, + accountEquity: 100, + btcEthLeverage: 10, // 上限 10x + altcoinLeverage: 5, + wantLeverage: 10, // 应该修正为 10 + wantError: false, + }, + { + name: "杠杆在上限内_不修正", + decision: Decision{ + Symbol: "ETHUSDT", + Action: "open_short", + Leverage: 5, // 未超限 + PositionSizeUSD: 500, + StopLoss: 4000, + TakeProfit: 3000, + }, + accountEquity: 100, + btcEthLeverage: 10, + altcoinLeverage: 5, + wantLeverage: 5, // 保持不变 + wantError: false, + }, + { + name: "杠杆为0_应该报错", + decision: Decision{ + Symbol: "SOLUSDT", + Action: "open_long", + Leverage: 0, // 无效 + PositionSizeUSD: 100, + StopLoss: 50, + TakeProfit: 200, + }, + accountEquity: 100, + btcEthLeverage: 10, + altcoinLeverage: 5, + wantLeverage: 0, + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateDecision(&tt.decision, tt.accountEquity, tt.btcEthLeverage, tt.altcoinLeverage) + + // 检查错误状态 + if (err != nil) != tt.wantError { + t.Errorf("validateDecision() error = %v, wantError %v", err, tt.wantError) + return + } + + // 如果不应该报错,检查杠杆是否被正确修正 + if !tt.wantError && tt.decision.Leverage != tt.wantLeverage { + t.Errorf("Leverage not corrected: got %d, want %d", tt.decision.Leverage, tt.wantLeverage) + } + }) + } +} diff --git a/manager/trader_manager.go b/manager/trader_manager.go index f3ead124..9e1e9fb7 100644 --- a/manager/trader_manager.go +++ b/manager/trader_manager.go @@ -850,6 +850,7 @@ func (tm *TraderManager) LoadUserTraders(database *config.Database, userID strin // - database: 数据库实例 // - userID: 用户ID // - traderID: 交易员ID +// // 返回: // - error: 如果交易员不存在、配置无效或加载失败则返回错误 func (tm *TraderManager) LoadTraderByID(database *config.Database, userID, traderID string) error {