From 62ebd58a1f490ec670bf2779f5401a0bc5743a25 Mon Sep 17 00:00:00 2001 From: hzb1115 Date: Mon, 3 Nov 2025 17:22:11 +0000 Subject: [PATCH] style(backend): go fmt code --- api/server.go | 197 +++++++++++++++++------------------ auth/auth.go | 6 +- manager/trader_manager.go | 62 +++++------ trader/aster_trader.go | 36 +++---- trader/auto_trader.go | 18 ++-- trader/binance_futures.go | 8 +- trader/hyperliquid_trader.go | 2 +- 7 files changed, 164 insertions(+), 165 deletions(-) diff --git a/api/server.go b/api/server.go index 92c2f174..28fd70ae 100644 --- a/api/server.go +++ b/api/server.go @@ -71,20 +71,20 @@ func (s *Server) setupRoutes() { { // 健康检查 api.Any("/health", s.handleHealth) - + // 认证相关路由(无需认证) api.POST("/register", s.handleRegister) api.POST("/login", s.handleLogin) api.POST("/verify-otp", s.handleVerifyOTP) api.POST("/complete-registration", s.handleCompleteRegistration) - + // 系统支持的模型和交易所(无需认证) api.GET("/supported-models", s.handleGetSupportedModels) api.GET("/supported-exchanges", s.handleGetSupportedExchanges) - + // 系统配置(无需认证) api.GET("/config", s.handleGetSystemConfig) - + // 系统提示词模板管理(无需认证) api.GET("/prompt-templates", s.handleGetPromptTemplates) api.GET("/prompt-templates/:name", s.handleGetPromptTemplate) @@ -114,10 +114,9 @@ func (s *Server) setupRoutes() { protected.GET("/user/signal-sources", s.handleGetUserSignalSource) protected.POST("/user/signal-sources", s.handleSaveUserSignalSource) - // 竞赛总览 protected.GET("/competition", s.handleCompetition) - + // 指定trader的数据(使用query参数 ?trader_id=xxx) protected.GET("/status", s.handleStatus) protected.GET("/account", s.handleAccount) @@ -151,24 +150,24 @@ func (s *Server) handleGetSystemConfig(c *gin.Context) { // 使用硬编码的默认币种 defaultCoins = []string{"BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT", "DOGEUSDT", "ADAUSDT", "HYPEUSDT"} } - + // 获取杠杆配置 btcEthLeverageStr, _ := s.database.GetSystemConfig("btc_eth_leverage") altcoinLeverageStr, _ := s.database.GetSystemConfig("altcoin_leverage") - + btcEthLeverage := 5 if val, err := strconv.Atoi(btcEthLeverageStr); err == nil && val > 0 { btcEthLeverage = val } - + altcoinLeverage := 5 if val, err := strconv.Atoi(altcoinLeverageStr); err == nil && val > 0 { altcoinLeverage = val } - + c.JSON(http.StatusOK, gin.H{ - "admin_mode": auth.IsAdminMode(), - "default_coins": defaultCoins, + "admin_mode": auth.IsAdminMode(), + "default_coins": defaultCoins, "btc_eth_leverage": btcEthLeverage, "altcoin_leverage": altcoinLeverage, }) @@ -178,20 +177,20 @@ func (s *Server) handleGetSystemConfig(c *gin.Context) { func (s *Server) getTraderFromQuery(c *gin.Context) (*manager.TraderManager, string, error) { userID := c.GetString("user_id") traderID := c.Query("trader_id") - + // 确保用户的交易员已加载到内存中 err := s.traderManager.LoadUserTraders(s.database, userID) if err != nil { log.Printf("⚠️ 加载用户 %s 的交易员失败: %v", userID, err) } - + if traderID == "" { // 如果没有指定trader_id,返回该用户的第一个trader ids := s.traderManager.GetTraderIDs() if len(ids) == 0 { return nil, "", fmt.Errorf("没有可用的trader") } - + // 获取用户的交易员列表,优先返回用户自己的交易员 userTraders, err := s.database.GetTraders(userID) if err == nil && len(userTraders) > 0 { @@ -200,7 +199,7 @@ func (s *Server) getTraderFromQuery(c *gin.Context) (*manager.TraderManager, str traderID = ids[0] } } - + return s.traderManager, traderID, nil } @@ -296,13 +295,13 @@ func (s *Server) handleCreateTrader(c *gin.Context) { // 生成交易员ID traderID := fmt.Sprintf("%s_%s_%d", req.ExchangeID, req.AIModelID, time.Now().Unix()) - + // 设置默认值 isCrossMargin := true // 默认为全仓模式 if req.IsCrossMargin != nil { isCrossMargin = *req.IsCrossMargin } - + // 设置杠杆默认值(从系统配置获取) btcEthLeverage := 5 altcoinLeverage := 5 @@ -326,7 +325,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) { } } } - + // 设置系统提示词模板默认值 systemPromptTemplate := "default" if req.SystemPromptTemplate != "" { @@ -339,8 +338,8 @@ func (s *Server) handleCreateTrader(c *gin.Context) { scanIntervalMinutes = 3 // 默认3分钟 } - // 创建交易员配置(数据库实体) - trader := &config.TraderRecord{ + // 创建交易员配置(数据库实体) + trader := &config.TraderRecord{ ID: traderID, UserID: userID, Name: req.Name, @@ -357,7 +356,7 @@ func (s *Server) handleCreateTrader(c *gin.Context) { SystemPromptTemplate: systemPromptTemplate, IsCrossMargin: isCrossMargin, ScanIntervalMinutes: scanIntervalMinutes, - IsRunning: false, + IsRunning: false, } // 保存到数据库 @@ -386,24 +385,24 @@ func (s *Server) handleCreateTrader(c *gin.Context) { // UpdateTraderRequest 更新交易员请求 type UpdateTraderRequest struct { - Name string `json:"name" binding:"required"` - AIModelID string `json:"ai_model_id" binding:"required"` - ExchangeID string `json:"exchange_id" binding:"required"` - InitialBalance float64 `json:"initial_balance"` - ScanIntervalMinutes int `json:"scan_interval_minutes"` - BTCETHLeverage int `json:"btc_eth_leverage"` - AltcoinLeverage int `json:"altcoin_leverage"` - TradingSymbols string `json:"trading_symbols"` - CustomPrompt string `json:"custom_prompt"` - OverrideBasePrompt bool `json:"override_base_prompt"` - IsCrossMargin *bool `json:"is_cross_margin"` + Name string `json:"name" binding:"required"` + AIModelID string `json:"ai_model_id" binding:"required"` + ExchangeID string `json:"exchange_id" binding:"required"` + InitialBalance float64 `json:"initial_balance"` + ScanIntervalMinutes int `json:"scan_interval_minutes"` + BTCETHLeverage int `json:"btc_eth_leverage"` + AltcoinLeverage int `json:"altcoin_leverage"` + TradingSymbols string `json:"trading_symbols"` + CustomPrompt string `json:"custom_prompt"` + OverrideBasePrompt bool `json:"override_base_prompt"` + IsCrossMargin *bool `json:"is_cross_margin"` } // handleUpdateTrader 更新交易员配置 func (s *Server) handleUpdateTrader(c *gin.Context) { userID := c.GetString("user_id") traderID := c.Param("id") - + var req UpdateTraderRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) @@ -416,7 +415,7 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取交易员列表失败"}) return } - + var existingTrader *config.TraderRecord for _, trader := range traders { if trader.ID == traderID { @@ -424,7 +423,7 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { break } } - + if existingTrader == nil { c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"}) return @@ -435,7 +434,7 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { if req.IsCrossMargin != nil { isCrossMargin = *req.IsCrossMargin } - + // 设置杠杆默认值 btcEthLeverage := req.BTCETHLeverage altcoinLeverage := req.AltcoinLeverage @@ -452,8 +451,8 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { scanIntervalMinutes = existingTrader.ScanIntervalMinutes // 保持原值 } - // 更新交易员配置 - trader := &config.TraderRecord{ + // 更新交易员配置 + trader := &config.TraderRecord{ ID: traderID, UserID: userID, Name: req.Name, @@ -498,14 +497,14 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { func (s *Server) handleDeleteTrader(c *gin.Context) { userID := c.GetString("user_id") traderID := c.Param("id") - + // 从数据库删除 err := s.database.DeleteTrader(userID, traderID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("删除交易员失败: %v", err)}) return } - + // 如果交易员正在运行,先停止它 if trader, err := s.traderManager.GetTrader(traderID); err == nil { status := trader.GetStatus() @@ -514,7 +513,7 @@ func (s *Server) handleDeleteTrader(c *gin.Context) { log.Printf("⏹ 已停止运行中的交易员: %s", traderID) } } - + log.Printf("✓ 交易员已删除: %s", traderID) c.JSON(http.StatusOK, gin.H{"message": "交易员已删除"}) } @@ -522,20 +521,20 @@ func (s *Server) handleDeleteTrader(c *gin.Context) { // handleStartTrader 启动交易员 func (s *Server) handleStartTrader(c *gin.Context) { traderID := c.Param("id") - + trader, err := s.traderManager.GetTrader(traderID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"}) return } - + // 检查交易员是否已经在运行 status := trader.GetStatus() if isRunning, ok := status["is_running"].(bool); ok && isRunning { c.JSON(http.StatusBadRequest, gin.H{"error": "交易员已在运行中"}) return } - + // 启动交易员 go func() { log.Printf("▶️ 启动交易员 %s (%s)", traderID, trader.GetName()) @@ -543,14 +542,14 @@ func (s *Server) handleStartTrader(c *gin.Context) { log.Printf("❌ 交易员 %s 运行错误: %v", trader.GetName(), err) } }() - + // 更新数据库中的运行状态 userID := c.GetString("user_id") err = s.database.UpdateTraderStatus(userID, traderID, true) if err != nil { log.Printf("⚠️ 更新交易员状态失败: %v", err) } - + log.Printf("✓ 交易员 %s 已启动", trader.GetName()) c.JSON(http.StatusOK, gin.H{"message": "交易员已启动"}) } @@ -558,30 +557,30 @@ func (s *Server) handleStartTrader(c *gin.Context) { // handleStopTrader 停止交易员 func (s *Server) handleStopTrader(c *gin.Context) { traderID := c.Param("id") - + trader, err := s.traderManager.GetTrader(traderID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"}) return } - + // 检查交易员是否正在运行 status := trader.GetStatus() if isRunning, ok := status["is_running"].(bool); ok && !isRunning { c.JSON(http.StatusBadRequest, gin.H{"error": "交易员已停止"}) return } - + // 停止交易员 trader.Stop() - + // 更新数据库中的运行状态 userID := c.GetString("user_id") err = s.database.UpdateTraderStatus(userID, traderID, false) if err != nil { log.Printf("⚠️ 更新交易员状态失败: %v", err) } - + log.Printf("⏹ 交易员 %s 已停止", trader.GetName()) c.JSON(http.StatusOK, gin.H{"message": "交易员已停止"}) } @@ -590,24 +589,24 @@ func (s *Server) handleStopTrader(c *gin.Context) { func (s *Server) handleUpdateTraderPrompt(c *gin.Context) { traderID := c.Param("id") userID := c.GetString("user_id") - + var req struct { - CustomPrompt string `json:"custom_prompt"` - OverrideBasePrompt bool `json:"override_base_prompt"` + CustomPrompt string `json:"custom_prompt"` + OverrideBasePrompt bool `json:"override_base_prompt"` } - + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - + // 更新数据库 err := s.database.UpdateTraderCustomPrompt(userID, traderID, req.CustomPrompt, req.OverrideBasePrompt) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("更新自定义prompt失败: %v", err)}) return } - + // 如果trader在内存中,更新其custom prompt和override设置 trader, err := s.traderManager.GetTrader(traderID) if err == nil { @@ -615,7 +614,7 @@ func (s *Server) handleUpdateTraderPrompt(c *gin.Context) { trader.SetOverrideBasePrompt(req.OverrideBasePrompt) log.Printf("✓ 已更新交易员 %s 的自定义prompt (覆盖基础=%v)", trader.GetName(), req.OverrideBasePrompt) } - + c.JSON(http.StatusOK, gin.H{"message": "自定义prompt已更新"}) } @@ -630,7 +629,7 @@ func (s *Server) handleGetModelConfigs(c *gin.Context) { return } log.Printf("✅ 找到 %d 个AI模型配置", len(models)) - + c.JSON(http.StatusOK, models) } @@ -674,7 +673,7 @@ func (s *Server) handleGetExchangeConfigs(c *gin.Context) { return } log.Printf("✅ 找到 %d 个交易所配置", len(exchanges)) - + c.JSON(http.StatusOK, exchanges) } @@ -719,7 +718,7 @@ func (s *Server) handleGetUserSignalSource(c *gin.Context) { }) return } - + c.JSON(http.StatusOK, gin.H{ "coin_pool_url": source.CoinPoolURL, "oi_top_url": source.OITopURL, @@ -733,18 +732,18 @@ func (s *Server) handleSaveUserSignalSource(c *gin.Context) { CoinPoolURL string `json:"coin_pool_url"` OITopURL string `json:"oi_top_url"` } - + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - + err := s.database.CreateUserSignalSource(userID, req.CoinPoolURL, req.OITopURL) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("保存用户信号源配置失败: %v", err)}) return } - + log.Printf("✓ 用户信号源配置已保存: user=%s, coin_pool=%s, oi_top=%s", userID, req.CoinPoolURL, req.OITopURL) c.JSON(http.StatusOK, gin.H{"message": "用户信号源配置已保存"}) } @@ -820,21 +819,21 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) { aiModelID := traderConfig.AIModelID result := map[string]interface{}{ - "trader_id": traderConfig.ID, - "trader_name": traderConfig.Name, - "ai_model": aiModelID, - "exchange_id": traderConfig.ExchangeID, - "initial_balance": traderConfig.InitialBalance, - "scan_interval_minutes": traderConfig.ScanIntervalMinutes, - "btc_eth_leverage": traderConfig.BTCETHLeverage, - "altcoin_leverage": traderConfig.AltcoinLeverage, - "trading_symbols": traderConfig.TradingSymbols, - "custom_prompt": traderConfig.CustomPrompt, - "override_base_prompt": traderConfig.OverrideBasePrompt, - "is_cross_margin": traderConfig.IsCrossMargin, - "use_coin_pool": traderConfig.UseCoinPool, - "use_oi_top": traderConfig.UseOITop, - "is_running": isRunning, + "trader_id": traderConfig.ID, + "trader_name": traderConfig.Name, + "ai_model": aiModelID, + "exchange_id": traderConfig.ExchangeID, + "initial_balance": traderConfig.InitialBalance, + "scan_interval_minutes": traderConfig.ScanIntervalMinutes, + "btc_eth_leverage": traderConfig.BTCETHLeverage, + "altcoin_leverage": traderConfig.AltcoinLeverage, + "trading_symbols": traderConfig.TradingSymbols, + "custom_prompt": traderConfig.CustomPrompt, + "override_base_prompt": traderConfig.OverrideBasePrompt, + "is_cross_margin": traderConfig.IsCrossMargin, + "use_coin_pool": traderConfig.UseCoinPool, + "use_oi_top": traderConfig.UseOITop, + "is_running": isRunning, } c.JSON(http.StatusOK, result) @@ -1001,13 +1000,13 @@ func (s *Server) handleStatistics(c *gin.Context) { // handleCompetition 竞赛总览(对比所有trader) func (s *Server) handleCompetition(c *gin.Context) { userID := c.GetString("user_id") - + // 确保用户的交易员已加载到内存中 err := s.traderManager.LoadUserTraders(s.database, userID) if err != nil { log.Printf("⚠️ 加载用户 %s 的交易员失败: %v", userID, err) } - + competition, err := s.traderManager.GetCompetitionData() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ @@ -1015,7 +1014,7 @@ func (s *Server) handleCompetition(c *gin.Context) { }) return } - + c.JSON(http.StatusOK, competition) } @@ -1142,7 +1141,7 @@ func (s *Server) authMiddleware() gin.HandlerFunc { c.Next() return } - + authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "缺少Authorization头"}) @@ -1225,11 +1224,11 @@ func (s *Server) handleRegister(c *gin.Context) { // 返回OTP设置信息 qrCodeURL := auth.GetOTPQRCodeURL(otpSecret, req.Email) c.JSON(http.StatusOK, gin.H{ - "user_id": userID, - "email": req.Email, - "otp_secret": otpSecret, + "user_id": userID, + "email": req.Email, + "otp_secret": otpSecret, "qr_code_url": qrCodeURL, - "message": "请使用Google Authenticator扫描二维码并验证OTP", + "message": "请使用Google Authenticator扫描二维码并验证OTP", }) } @@ -1314,8 +1313,8 @@ func (s *Server) handleLogin(c *gin.Context) { // 检查OTP是否已验证 if !user.OTPVerified { c.JSON(http.StatusUnauthorized, gin.H{ - "error": "账户未完成OTP设置", - "user_id": user.ID, + "error": "账户未完成OTP设置", + "user_id": user.ID, "requires_otp_setup": true, }) return @@ -1323,9 +1322,9 @@ func (s *Server) handleLogin(c *gin.Context) { // 返回需要OTP验证的状态 c.JSON(http.StatusOK, gin.H{ - "user_id": user.ID, - "email": user.Email, - "message": "请输入Google Authenticator验证码", + "user_id": user.ID, + "email": user.Email, + "message": "请输入Google Authenticator验证码", "requires_otp": true, }) } @@ -1387,7 +1386,7 @@ func (s *Server) handleGetSupportedModels(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取支持的AI模型失败"}) return } - + c.JSON(http.StatusOK, models) } @@ -1400,7 +1399,7 @@ func (s *Server) handleGetSupportedExchanges(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "获取支持的交易所失败"}) return } - + c.JSON(http.StatusOK, exchanges) } @@ -1436,7 +1435,7 @@ func (s *Server) Start() error { func (s *Server) handleGetPromptTemplates(c *gin.Context) { // 导入 decision 包 templates := decision.GetAllPromptTemplates() - + // 转换为响应格式 response := make([]map[string]interface{}, 0, len(templates)) for _, tmpl := range templates { @@ -1444,7 +1443,7 @@ func (s *Server) handleGetPromptTemplates(c *gin.Context) { "name": tmpl.Name, }) } - + c.JSON(http.StatusOK, gin.H{ "templates": response, }) @@ -1453,13 +1452,13 @@ func (s *Server) handleGetPromptTemplates(c *gin.Context) { // handleGetPromptTemplate 获取指定名称的提示词模板内容 func (s *Server) handleGetPromptTemplate(c *gin.Context) { templateName := c.Param("name") - + template, err := decision.GetPromptTemplate(templateName) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("模板不存在: %s", templateName)}) return } - + c.JSON(http.StatusOK, gin.H{ "name": template.Name, "content": template.Content, diff --git a/auth/auth.go b/auth/auth.go index 685d08e6..89c58e5c 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -61,7 +61,7 @@ func GenerateOTPSecret() (string, error) { if err != nil { return "", err } - + key, err := totp.Generate(totp.GenerateOpts{ Issuer: OTPIssuer, AccountName: uuid.New().String(), @@ -69,7 +69,7 @@ func GenerateOTPSecret() (string, error) { if err != nil { return "", err } - + return key.Secret(), nil } @@ -118,4 +118,4 @@ func ValidateJWT(tokenString string) (*Claims, error) { // GetOTPQRCodeURL 获取OTP二维码URL func GetOTPQRCodeURL(secret, email string) string { return fmt.Sprintf("otpauth://totp/%s:%s?secret=%s&issuer=%s", OTPIssuer, email, secret, OTPIssuer) -} \ No newline at end of file +} diff --git a/manager/trader_manager.go b/manager/trader_manager.go index d3861cdb..ebff453c 100644 --- a/manager/trader_manager.go +++ b/manager/trader_manager.go @@ -84,7 +84,7 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro } // 为每个交易员获取AI模型和交易所配置 - for _, traderCfg := range allTraders { + for _, traderCfg := range allTraders { // 获取AI模型配置(使用交易员所属的用户ID) aiModels, err := database.GetAIModels(traderCfg.UserID) if err != nil { @@ -157,7 +157,7 @@ func (tm *TraderManager) LoadTradersFromDatabase(database *config.Database) erro } // 添加到TraderManager - err = tm.addTraderFromDB(traderCfg, aiModelCfg, exchangeCfg, coinPoolURL, oiTopURL, maxDailyLoss, maxDrawdown, stopTradingMinutes, defaultCoins) + err = tm.addTraderFromDB(traderCfg, aiModelCfg, exchangeCfg, coinPoolURL, oiTopURL, maxDailyLoss, maxDrawdown, stopTradingMinutes, defaultCoins) if err != nil { log.Printf("❌ 添加交易员 %s 失败: %v", traderCfg.Name, err) continue @@ -186,7 +186,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel } } } - + // 如果没有指定交易币种,使用默认币种 if len(tradingCoins) == 0 { tradingCoins = defaultCoins @@ -200,7 +200,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel } // 构建AutoTraderConfig - traderConfig := trader.AutoTraderConfig{ + traderConfig := trader.AutoTraderConfig{ ID: traderCfg.ID, Name: traderCfg.Name, AIModel: aiModelCfg.Provider, // 使用provider作为模型标识 @@ -253,7 +253,7 @@ func (tm *TraderManager) addTraderFromDB(traderCfg *config.TraderRecord, aiModel if err != nil { return fmt.Errorf("创建trader失败: %w", err) } - + // 设置自定义prompt(如果有) if traderCfg.CustomPrompt != "" { at.SetCustomPrompt(traderCfg.CustomPrompt) @@ -293,7 +293,7 @@ func (tm *TraderManager) AddTraderFromDB(traderCfg *config.TraderRecord, aiModel } } } - + // 如果没有指定交易币种,使用默认币种 if len(tradingCoins) == 0 { tradingCoins = defaultCoins @@ -359,7 +359,7 @@ func (tm *TraderManager) AddTraderFromDB(traderCfg *config.TraderRecord, aiModel if err != nil { return fmt.Errorf("创建trader失败: %w", err) } - + // 设置自定义prompt(如果有) if traderCfg.CustomPrompt != "" { at.SetCustomPrompt(traderCfg.CustomPrompt) @@ -488,9 +488,9 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) { for _, t := range tm.traders { account, err := t.GetAccountInfo() status := t.GetStatus() - + var traderData map[string]interface{} - + if err != nil { // 如果获取账户信息失败,使用默认值但仍然显示交易员 log.Printf("⚠️ 获取交易员 %s 账户信息失败: %v", t.GetID(), err) @@ -522,7 +522,7 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) { "is_running": status["is_running"], } } - + traders = append(traders, traderData) } comparison["traders"] = traders @@ -708,7 +708,7 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode } } } - + // 如果没有指定交易币种,使用默认币种 if len(tradingCoins) == 0 { tradingCoins = defaultCoins @@ -723,25 +723,25 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode // 构建AutoTraderConfig traderConfig := trader.AutoTraderConfig{ - ID: traderCfg.ID, - Name: traderCfg.Name, - AIModel: aiModelCfg.Provider, // 使用provider作为模型标识 - Exchange: exchangeCfg.ID, // 使用exchange ID - InitialBalance: traderCfg.InitialBalance, - BTCETHLeverage: traderCfg.BTCETHLeverage, - AltcoinLeverage: traderCfg.AltcoinLeverage, - ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute, - CoinPoolAPIURL: effectiveCoinPoolURL, - CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL - CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称 - UseQwen: aiModelCfg.Provider == "qwen", - MaxDailyLoss: maxDailyLoss, - MaxDrawdown: maxDrawdown, - StopTradingTime: time.Duration(stopTradingMinutes) * time.Minute, - IsCrossMargin: traderCfg.IsCrossMargin, - DefaultCoins: defaultCoins, - TradingCoins: tradingCoins, - SystemPromptTemplate: traderCfg.SystemPromptTemplate, // 系统提示词模板 + ID: traderCfg.ID, + Name: traderCfg.Name, + AIModel: aiModelCfg.Provider, // 使用provider作为模型标识 + Exchange: exchangeCfg.ID, // 使用exchange ID + InitialBalance: traderCfg.InitialBalance, + BTCETHLeverage: traderCfg.BTCETHLeverage, + AltcoinLeverage: traderCfg.AltcoinLeverage, + ScanInterval: time.Duration(traderCfg.ScanIntervalMinutes) * time.Minute, + CoinPoolAPIURL: effectiveCoinPoolURL, + CustomAPIURL: aiModelCfg.CustomAPIURL, // 自定义API URL + CustomModelName: aiModelCfg.CustomModelName, // 自定义模型名称 + UseQwen: aiModelCfg.Provider == "qwen", + MaxDailyLoss: maxDailyLoss, + MaxDrawdown: maxDrawdown, + StopTradingTime: time.Duration(stopTradingMinutes) * time.Minute, + IsCrossMargin: traderCfg.IsCrossMargin, + DefaultCoins: defaultCoins, + TradingCoins: tradingCoins, + SystemPromptTemplate: traderCfg.SystemPromptTemplate, // 系统提示词模板 } // 根据交易所类型设置API密钥 @@ -769,7 +769,7 @@ func (tm *TraderManager) loadSingleTrader(traderCfg *config.TraderRecord, aiMode if err != nil { return fmt.Errorf("创建trader失败: %w", err) } - + // 设置自定义prompt(如果有) if traderCfg.CustomPrompt != "" { at.SetCustomPrompt(traderCfg.CustomPrompt) diff --git a/trader/aster_trader.go b/trader/aster_trader.go index e4d7f12d..d9ba82a6 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -27,8 +27,8 @@ import ( // AsterTrader Aster交易平台实现 type AsterTrader struct { ctx context.Context - user string // 主钱包地址 (ERC20) - signer string // API钱包地址 + user string // 主钱包地址 (ERC20) + signer string // API钱包地址 privateKey *ecdsa.PrivateKey // API钱包私钥 client *http.Client baseURL string @@ -99,9 +99,9 @@ func (t *AsterTrader) getPrecision(symbol string) (SymbolPrecision, error) { body, _ := io.ReadAll(resp.Body) var info struct { Symbols []struct { - Symbol string `json:"symbol"` - PricePrecision int `json:"pricePrecision"` - QuantityPrecision int `json:"quantityPrecision"` + Symbol string `json:"symbol"` + PricePrecision int `json:"pricePrecision"` + QuantityPrecision int `json:"quantityPrecision"` Filters []map[string]interface{} `json:"filters"` } `json:"symbols"` } @@ -506,14 +506,14 @@ func (t *AsterTrader) GetPositions() ([]map[string]interface{}, error) { // 返回与Binance相同的字段名 result = append(result, map[string]interface{}{ - "symbol": pos["symbol"], - "side": side, - "positionAmt": posAmt, - "entryPrice": entryPrice, - "markPrice": markPrice, - "unRealizedProfit": unRealizedProfit, - "leverage": leverageVal, - "liquidationPrice": liquidationPrice, + "symbol": pos["symbol"], + "side": side, + "positionAmt": posAmt, + "entryPrice": entryPrice, + "markPrice": markPrice, + "unRealizedProfit": unRealizedProfit, + "leverage": leverageVal, + "liquidationPrice": liquidationPrice, }) } @@ -827,18 +827,18 @@ func (t *AsterTrader) SetMarginMode(symbol string, isCrossMargin bool) error { if !isCrossMargin { marginType = "ISOLATED" } - + params := map[string]interface{}{ "symbol": symbol, "marginType": marginType, } - + // 使用request方法调用API _, err := t.request("POST", "/fapi/v3/marginType", params) if err != nil { // 如果错误表示无需更改,忽略错误 - if strings.Contains(err.Error(), "No need to change") || - strings.Contains(err.Error(), "Margin type cannot be changed") { + if strings.Contains(err.Error(), "No need to change") || + strings.Contains(err.Error(), "Margin type cannot be changed") { log.Printf(" ✓ %s 仓位模式已是 %s 或有持仓无法更改", symbol, marginType) return nil } @@ -846,7 +846,7 @@ func (t *AsterTrader) SetMarginMode(symbol string, isCrossMargin bool) error { // 不返回错误,让交易继续 return nil } - + log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginType) return nil } diff --git a/trader/auto_trader.go b/trader/auto_trader.go index d27fcd35..9a68ed17 100644 --- a/trader/auto_trader.go +++ b/trader/auto_trader.go @@ -68,8 +68,8 @@ type AutoTraderConfig struct { IsCrossMargin bool // true=全仓模式, false=逐仓模式 // 币种配置 - DefaultCoins []string // 默认币种列表(从数据库获取) - TradingCoins []string // 实际交易币种列表 + DefaultCoins []string // 默认币种列表(从数据库获取) + TradingCoins []string // 实际交易币种列表 // 系统提示词模板 SystemPromptTemplate string // 系统提示词模板名称(如 "default", "aggressive") @@ -87,9 +87,9 @@ type AutoTrader struct { decisionLogger *logger.DecisionLogger // 决策日志记录器 initialBalance float64 dailyPnL float64 - customPrompt string // 自定义交易策略prompt - overrideBasePrompt bool // 是否覆盖基础prompt - systemPromptTemplate string // 系统提示词模板名称 + customPrompt string // 自定义交易策略prompt + overrideBasePrompt bool // 是否覆盖基础prompt + systemPromptTemplate string // 系统提示词模板名称 defaultCoins []string // 默认币种列表(从数据库获取) tradingCoins []string // 实际交易币种列表 lastResetTime time.Time @@ -1016,7 +1016,7 @@ func (at *AutoTrader) getCandidateCoins() ([]decision.CandidateCoin, error) { if len(at.tradingCoins) == 0 { // 使用数据库配置的默认币种列表 var candidateCoins []decision.CandidateCoin - + if len(at.defaultCoins) > 0 { // 使用数据库中配置的默认币种 for _, coin := range at.defaultCoins { @@ -1032,7 +1032,7 @@ func (at *AutoTrader) getCandidateCoins() ([]decision.CandidateCoin, error) { } else { // 如果数据库中没有配置默认币种,则使用AI500+OI Top作为fallback const ai500Limit = 20 // AI500取前20个评分最高的币种 - + mergedPool, err := pool.GetMergedCoinPool(ai500Limit) if err != nil { return nil, fmt.Errorf("获取合并币种池失败: %w", err) @@ -1073,11 +1073,11 @@ func (at *AutoTrader) getCandidateCoins() ([]decision.CandidateCoin, error) { func normalizeSymbol(symbol string) string { // 转为大写 symbol = strings.ToUpper(strings.TrimSpace(symbol)) - + // 确保以USDT结尾 if !strings.HasSuffix(symbol, "USDT") { symbol = symbol + "USDT" } - + return symbol } diff --git a/trader/binance_futures.go b/trader/binance_futures.go index c10fadeb..354415a0 100644 --- a/trader/binance_futures.go +++ b/trader/binance_futures.go @@ -139,18 +139,18 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error { } else { marginType = futures.MarginTypeIsolated } - + // 尝试设置仓位模式 err := t.client.NewChangeMarginTypeService(). Symbol(symbol). MarginType(marginType). Do(context.Background()) - + marginModeStr := "全仓" if !isCrossMargin { marginModeStr = "逐仓" } - + if err != nil { // 如果错误信息包含"No need to change",说明仓位模式已经是目标值 if contains(err.Error(), "No need to change margin type") { @@ -166,7 +166,7 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error { // 不返回错误,让交易继续 return nil } - + log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginModeStr) return nil } diff --git a/trader/hyperliquid_trader.go b/trader/hyperliquid_trader.go index 3073b342..c189dbdc 100644 --- a/trader/hyperliquid_trader.go +++ b/trader/hyperliquid_trader.go @@ -17,7 +17,7 @@ type HyperliquidTrader struct { ctx context.Context walletAddr string meta *hyperliquid.Meta // 缓存meta信息(包含精度等) - isCrossMargin bool // 是否为全仓模式 + isCrossMargin bool // 是否为全仓模式 } // NewHyperliquidTrader 创建Hyperliquid交易器