mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 16:26:57 +08:00
chore: run go fmt to fix formatting issues
This commit is contained in:
@@ -88,7 +88,7 @@ func (s *Server) setupRoutes() {
|
|||||||
// 系统提示词模板管理(无需认证)
|
// 系统提示词模板管理(无需认证)
|
||||||
api.GET("/prompt-templates", s.handleGetPromptTemplates)
|
api.GET("/prompt-templates", s.handleGetPromptTemplates)
|
||||||
api.GET("/prompt-templates/:name", s.handleGetPromptTemplate)
|
api.GET("/prompt-templates/:name", s.handleGetPromptTemplate)
|
||||||
|
|
||||||
// 公开的竞赛数据(无需认证)
|
// 公开的竞赛数据(无需认证)
|
||||||
api.GET("/traders", s.handlePublicTraderList)
|
api.GET("/traders", s.handlePublicTraderList)
|
||||||
api.GET("/competition", s.handlePublicCompetition)
|
api.GET("/competition", s.handlePublicCompetition)
|
||||||
@@ -168,7 +168,7 @@ func (s *Server) handleGetSystemConfig(c *gin.Context) {
|
|||||||
if val, err := strconv.Atoi(altcoinLeverageStr); err == nil && val > 0 {
|
if val, err := strconv.Atoi(altcoinLeverageStr); err == nil && val > 0 {
|
||||||
altcoinLeverage = val
|
altcoinLeverage = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取内测模式配置
|
// 获取内测模式配置
|
||||||
betaModeStr, _ := s.database.GetSystemConfig("beta_mode")
|
betaModeStr, _ := s.database.GetSystemConfig("beta_mode")
|
||||||
betaMode := betaModeStr == "true"
|
betaMode := betaModeStr == "true"
|
||||||
@@ -531,14 +531,14 @@ func (s *Server) handleDeleteTrader(c *gin.Context) {
|
|||||||
func (s *Server) handleStartTrader(c *gin.Context) {
|
func (s *Server) handleStartTrader(c *gin.Context) {
|
||||||
userID := c.GetString("user_id")
|
userID := c.GetString("user_id")
|
||||||
traderID := c.Param("id")
|
traderID := c.Param("id")
|
||||||
|
|
||||||
// 校验交易员是否属于当前用户
|
// 校验交易员是否属于当前用户
|
||||||
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
|
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
trader, err := s.traderManager.GetTrader(traderID)
|
trader, err := s.traderManager.GetTrader(traderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
|
||||||
@@ -574,14 +574,14 @@ func (s *Server) handleStartTrader(c *gin.Context) {
|
|||||||
func (s *Server) handleStopTrader(c *gin.Context) {
|
func (s *Server) handleStopTrader(c *gin.Context) {
|
||||||
userID := c.GetString("user_id")
|
userID := c.GetString("user_id")
|
||||||
traderID := c.Param("id")
|
traderID := c.Param("id")
|
||||||
|
|
||||||
// 校验交易员是否属于当前用户
|
// 校验交易员是否属于当前用户
|
||||||
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
|
_, _, _, err := s.database.GetTraderConfig(userID, traderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在或无访问权限"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
trader, err := s.traderManager.GetTrader(traderID)
|
trader, err := s.traderManager.GetTrader(traderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "交易员不存在"})
|
||||||
@@ -796,7 +796,7 @@ func (s *Server) handleTraderList(c *gin.Context) {
|
|||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"trader_id": trader.ID,
|
"trader_id": trader.ID,
|
||||||
"trader_name": trader.Name,
|
"trader_name": trader.Name,
|
||||||
"ai_model": trader.AIModelID, // 使用完整 ID
|
"ai_model": trader.AIModelID, // 使用完整 ID
|
||||||
"exchange_id": trader.ExchangeID,
|
"exchange_id": trader.ExchangeID,
|
||||||
"is_running": isRunning,
|
"is_running": isRunning,
|
||||||
"initial_balance": trader.InitialBalance,
|
"initial_balance": trader.InitialBalance,
|
||||||
@@ -1574,7 +1574,7 @@ func (s *Server) handlePublicCompetition(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, competition)
|
c.JSON(http.StatusOK, competition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1587,7 +1587,7 @@ func (s *Server) handleTopTraders(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, topTraders)
|
c.JSON(http.StatusOK, topTraders)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1596,7 +1596,7 @@ func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
|
|||||||
var requestBody struct {
|
var requestBody struct {
|
||||||
TraderIDs []string `json:"trader_ids"`
|
TraderIDs []string `json:"trader_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试解析POST请求的JSON body
|
// 尝试解析POST请求的JSON body
|
||||||
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
||||||
// 如果JSON解析失败,尝试从query参数获取(兼容GET请求)
|
// 如果JSON解析失败,尝试从query参数获取(兼容GET请求)
|
||||||
@@ -1610,13 +1610,13 @@ func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
traders, ok := topTraders["traders"].([]map[string]interface{})
|
traders, ok := topTraders["traders"].([]map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "交易员数据格式错误"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "交易员数据格式错误"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取trader IDs
|
// 提取trader IDs
|
||||||
traderIDs := make([]string, 0, len(traders))
|
traderIDs := make([]string, 0, len(traders))
|
||||||
for _, trader := range traders {
|
for _, trader := range traders {
|
||||||
@@ -1624,24 +1624,24 @@ func (s *Server) handleEquityHistoryBatch(c *gin.Context) {
|
|||||||
traderIDs = append(traderIDs, traderID)
|
traderIDs = append(traderIDs, traderID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := s.getEquityHistoryForTraders(traderIDs)
|
result := s.getEquityHistoryForTraders(traderIDs)
|
||||||
c.JSON(http.StatusOK, result)
|
c.JSON(http.StatusOK, result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析逗号分隔的trader IDs
|
// 解析逗号分隔的trader IDs
|
||||||
requestBody.TraderIDs = strings.Split(traderIDsParam, ",")
|
requestBody.TraderIDs = strings.Split(traderIDsParam, ",")
|
||||||
for i := range requestBody.TraderIDs {
|
for i := range requestBody.TraderIDs {
|
||||||
requestBody.TraderIDs[i] = strings.TrimSpace(requestBody.TraderIDs[i])
|
requestBody.TraderIDs[i] = strings.TrimSpace(requestBody.TraderIDs[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限制最多20个交易员,防止请求过大
|
// 限制最多20个交易员,防止请求过大
|
||||||
if len(requestBody.TraderIDs) > 20 {
|
if len(requestBody.TraderIDs) > 20 {
|
||||||
requestBody.TraderIDs = requestBody.TraderIDs[:20]
|
requestBody.TraderIDs = requestBody.TraderIDs[:20]
|
||||||
}
|
}
|
||||||
|
|
||||||
result := s.getEquityHistoryForTraders(requestBody.TraderIDs)
|
result := s.getEquityHistoryForTraders(requestBody.TraderIDs)
|
||||||
c.JSON(http.StatusOK, result)
|
c.JSON(http.StatusOK, result)
|
||||||
}
|
}
|
||||||
@@ -1651,31 +1651,31 @@ func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]inter
|
|||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
histories := make(map[string]interface{})
|
histories := make(map[string]interface{})
|
||||||
errors := make(map[string]string)
|
errors := make(map[string]string)
|
||||||
|
|
||||||
for _, traderID := range traderIDs {
|
for _, traderID := range traderIDs {
|
||||||
if traderID == "" {
|
if traderID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
trader, err := s.traderManager.GetTrader(traderID)
|
trader, err := s.traderManager.GetTrader(traderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors[traderID] = "交易员不存在"
|
errors[traderID] = "交易员不存在"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取历史数据(用于对比展示,限制数据量)
|
// 获取历史数据(用于对比展示,限制数据量)
|
||||||
records, err := trader.GetDecisionLogger().GetLatestRecords(500)
|
records, err := trader.GetDecisionLogger().GetLatestRecords(500)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors[traderID] = fmt.Sprintf("获取历史数据失败: %v", err)
|
errors[traderID] = fmt.Sprintf("获取历史数据失败: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建收益率历史数据
|
// 构建收益率历史数据
|
||||||
history := make([]map[string]interface{}, 0, len(records))
|
history := make([]map[string]interface{}, 0, len(records))
|
||||||
for _, record := range records {
|
for _, record := range records {
|
||||||
// 计算总权益(余额+未实现盈亏)
|
// 计算总权益(余额+未实现盈亏)
|
||||||
totalEquity := record.AccountState.TotalBalance + record.AccountState.TotalUnrealizedProfit
|
totalEquity := record.AccountState.TotalBalance + record.AccountState.TotalUnrealizedProfit
|
||||||
|
|
||||||
history = append(history, map[string]interface{}{
|
history = append(history, map[string]interface{}{
|
||||||
"timestamp": record.Timestamp,
|
"timestamp": record.Timestamp,
|
||||||
"total_equity": totalEquity,
|
"total_equity": totalEquity,
|
||||||
@@ -1683,16 +1683,16 @@ func (s *Server) getEquityHistoryForTraders(traderIDs []string) map[string]inter
|
|||||||
"balance": record.AccountState.TotalBalance,
|
"balance": record.AccountState.TotalBalance,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
histories[traderID] = history
|
histories[traderID] = history
|
||||||
}
|
}
|
||||||
|
|
||||||
result["histories"] = histories
|
result["histories"] = histories
|
||||||
result["count"] = len(histories)
|
result["count"] = len(histories)
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
result["errors"] = errors
|
result["errors"] = errors
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1726,4 +1726,3 @@ func (s *Server) handleGetPublicTraderConfig(c *gin.Context) {
|
|||||||
|
|
||||||
c.JSON(http.StatusOK, result)
|
c.JSON(http.StatusOK, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -258,17 +258,17 @@ func (d *Database) initDefaultData() error {
|
|||||||
|
|
||||||
// 初始化系统配置 - 创建所有字段,设置默认值,后续由config.json同步更新
|
// 初始化系统配置 - 创建所有字段,设置默认值,后续由config.json同步更新
|
||||||
systemConfigs := map[string]string{
|
systemConfigs := map[string]string{
|
||||||
"admin_mode": "true", // 默认开启管理员模式,便于首次使用
|
"admin_mode": "true", // 默认开启管理员模式,便于首次使用
|
||||||
"beta_mode": "false", // 默认关闭内测模式
|
"beta_mode": "false", // 默认关闭内测模式
|
||||||
"api_server_port": "8080", // 默认API端口
|
"api_server_port": "8080", // 默认API端口
|
||||||
"use_default_coins": "true", // 默认使用内置币种列表
|
"use_default_coins": "true", // 默认使用内置币种列表
|
||||||
"default_coins": `["BTCUSDT","ETHUSDT","SOLUSDT","BNBUSDT","XRPUSDT","DOGEUSDT","ADAUSDT","HYPEUSDT"]`, // 默认币种列表(JSON格式)
|
"default_coins": `["BTCUSDT","ETHUSDT","SOLUSDT","BNBUSDT","XRPUSDT","DOGEUSDT","ADAUSDT","HYPEUSDT"]`, // 默认币种列表(JSON格式)
|
||||||
"max_daily_loss": "10.0", // 最大日损失百分比
|
"max_daily_loss": "10.0", // 最大日损失百分比
|
||||||
"max_drawdown": "20.0", // 最大回撤百分比
|
"max_drawdown": "20.0", // 最大回撤百分比
|
||||||
"stop_trading_minutes": "60", // 停止交易时间(分钟)
|
"stop_trading_minutes": "60", // 停止交易时间(分钟)
|
||||||
"btc_eth_leverage": "5", // BTC/ETH杠杆倍数
|
"btc_eth_leverage": "5", // BTC/ETH杠杆倍数
|
||||||
"altcoin_leverage": "5", // 山寨币杠杆倍数
|
"altcoin_leverage": "5", // 山寨币杠杆倍数
|
||||||
"jwt_secret": "", // JWT密钥,默认为空,由config.json或系统生成
|
"jwt_secret": "", // JWT密钥,默认为空,由config.json或系统生成
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range systemConfigs {
|
for key, value := range systemConfigs {
|
||||||
@@ -1037,7 +1037,7 @@ func (d *Database) LoadBetaCodesFromFile(filePath string) error {
|
|||||||
log.Printf("插入内测码 %s 失败: %v", code, err)
|
log.Printf("插入内测码 %s 失败: %v", code, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if rowsAffected, _ := result.RowsAffected(); rowsAffected > 0 {
|
if rowsAffected, _ := result.RowsAffected(); rowsAffected > 0 {
|
||||||
insertedCount++
|
insertedCount++
|
||||||
}
|
}
|
||||||
|
|||||||
22
main.go
22
main.go
@@ -64,15 +64,15 @@ func syncConfigToDatabase(database *config.Database) error {
|
|||||||
|
|
||||||
// 同步各配置项到数据库
|
// 同步各配置项到数据库
|
||||||
configs := map[string]string{
|
configs := map[string]string{
|
||||||
"admin_mode": fmt.Sprintf("%t", configFile.AdminMode),
|
"admin_mode": fmt.Sprintf("%t", configFile.AdminMode),
|
||||||
"beta_mode": fmt.Sprintf("%t", configFile.BetaMode),
|
"beta_mode": fmt.Sprintf("%t", configFile.BetaMode),
|
||||||
"api_server_port": strconv.Itoa(configFile.APIServerPort),
|
"api_server_port": strconv.Itoa(configFile.APIServerPort),
|
||||||
"use_default_coins": fmt.Sprintf("%t", configFile.UseDefaultCoins),
|
"use_default_coins": fmt.Sprintf("%t", configFile.UseDefaultCoins),
|
||||||
"coin_pool_api_url": configFile.CoinPoolAPIURL,
|
"coin_pool_api_url": configFile.CoinPoolAPIURL,
|
||||||
"oi_top_api_url": configFile.OITopAPIURL,
|
"oi_top_api_url": configFile.OITopAPIURL,
|
||||||
"max_daily_loss": fmt.Sprintf("%.1f", configFile.MaxDailyLoss),
|
"max_daily_loss": fmt.Sprintf("%.1f", configFile.MaxDailyLoss),
|
||||||
"max_drawdown": fmt.Sprintf("%.1f", configFile.MaxDrawdown),
|
"max_drawdown": fmt.Sprintf("%.1f", configFile.MaxDrawdown),
|
||||||
"stop_trading_minutes": strconv.Itoa(configFile.StopTradingMinutes),
|
"stop_trading_minutes": strconv.Itoa(configFile.StopTradingMinutes),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步default_coins(转换为JSON字符串存储)
|
// 同步default_coins(转换为JSON字符串存储)
|
||||||
@@ -112,7 +112,7 @@ func syncConfigToDatabase(database *config.Database) error {
|
|||||||
// loadBetaCodesToDatabase 加载内测码文件到数据库
|
// loadBetaCodesToDatabase 加载内测码文件到数据库
|
||||||
func loadBetaCodesToDatabase(database *config.Database) error {
|
func loadBetaCodesToDatabase(database *config.Database) error {
|
||||||
betaCodeFile := "beta_codes.txt"
|
betaCodeFile := "beta_codes.txt"
|
||||||
|
|
||||||
// 检查内测码文件是否存在
|
// 检查内测码文件是否存在
|
||||||
if _, err := os.Stat(betaCodeFile); os.IsNotExist(err) {
|
if _, err := os.Stat(betaCodeFile); os.IsNotExist(err) {
|
||||||
log.Printf("📄 内测码文件 %s 不存在,跳过加载", betaCodeFile)
|
log.Printf("📄 内测码文件 %s 不存在,跳过加载", betaCodeFile)
|
||||||
@@ -126,7 +126,7 @@ func loadBetaCodesToDatabase(database *config.Database) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("🔄 发现内测码文件 %s (%.1f KB),开始加载...", betaCodeFile, float64(fileInfo.Size())/1024)
|
log.Printf("🔄 发现内测码文件 %s (%.1f KB),开始加载...", betaCodeFile, float64(fileInfo.Size())/1024)
|
||||||
|
|
||||||
// 加载内测码到数据库
|
// 加载内测码到数据库
|
||||||
err = database.LoadBetaCodesFromFile(betaCodeFile)
|
err = database.LoadBetaCodesFromFile(betaCodeFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ type CompetitionCache struct {
|
|||||||
|
|
||||||
// TraderManager 管理多个trader实例
|
// TraderManager 管理多个trader实例
|
||||||
type TraderManager struct {
|
type TraderManager struct {
|
||||||
traders map[string]*trader.AutoTrader // key: trader ID
|
traders map[string]*trader.AutoTrader // key: trader ID
|
||||||
competitionCache *CompetitionCache
|
competitionCache *CompetitionCache
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTraderManager 创建trader管理器
|
// NewTraderManager 创建trader管理器
|
||||||
@@ -506,19 +506,19 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
|
|||||||
tm.competitionCache.mu.RUnlock()
|
tm.competitionCache.mu.RUnlock()
|
||||||
|
|
||||||
tm.mu.RLock()
|
tm.mu.RLock()
|
||||||
|
|
||||||
// 获取所有交易员列表
|
// 获取所有交易员列表
|
||||||
allTraders := make([]*trader.AutoTrader, 0, len(tm.traders))
|
allTraders := make([]*trader.AutoTrader, 0, len(tm.traders))
|
||||||
for _, t := range tm.traders {
|
for _, t := range tm.traders {
|
||||||
allTraders = append(allTraders, t)
|
allTraders = append(allTraders, t)
|
||||||
}
|
}
|
||||||
tm.mu.RUnlock()
|
tm.mu.RUnlock()
|
||||||
|
|
||||||
log.Printf("🔄 重新获取竞赛数据,交易员数量: %d", len(allTraders))
|
log.Printf("🔄 重新获取竞赛数据,交易员数量: %d", len(allTraders))
|
||||||
|
|
||||||
// 并发获取交易员数据
|
// 并发获取交易员数据
|
||||||
traders := tm.getConcurrentTraderData(allTraders)
|
traders := tm.getConcurrentTraderData(allTraders)
|
||||||
|
|
||||||
// 按收益率排序(降序)
|
// 按收益率排序(降序)
|
||||||
sort.Slice(traders, func(i, j int) bool {
|
sort.Slice(traders, func(i, j int) bool {
|
||||||
pnlPctI, okI := traders[i]["total_pnl_pct"].(float64)
|
pnlPctI, okI := traders[i]["total_pnl_pct"].(float64)
|
||||||
@@ -531,14 +531,14 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
return pnlPctI > pnlPctJ
|
return pnlPctI > pnlPctJ
|
||||||
})
|
})
|
||||||
|
|
||||||
// 限制返回前50名
|
// 限制返回前50名
|
||||||
totalCount := len(traders)
|
totalCount := len(traders)
|
||||||
limit := 50
|
limit := 50
|
||||||
if len(traders) > limit {
|
if len(traders) > limit {
|
||||||
traders = traders[:limit]
|
traders = traders[:limit]
|
||||||
}
|
}
|
||||||
|
|
||||||
comparison := make(map[string]interface{})
|
comparison := make(map[string]interface{})
|
||||||
comparison["traders"] = traders
|
comparison["traders"] = traders
|
||||||
comparison["count"] = len(traders)
|
comparison["count"] = len(traders)
|
||||||
@@ -559,21 +559,21 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
|
|||||||
index int
|
index int
|
||||||
data map[string]interface{}
|
data map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建结果通道
|
// 创建结果通道
|
||||||
resultChan := make(chan traderResult, len(traders))
|
resultChan := make(chan traderResult, len(traders))
|
||||||
|
|
||||||
// 并发获取每个交易员的数据
|
// 并发获取每个交易员的数据
|
||||||
for i, t := range traders {
|
for i, t := range traders {
|
||||||
go func(index int, trader *trader.AutoTrader) {
|
go func(index int, trader *trader.AutoTrader) {
|
||||||
// 设置单个交易员的超时时间为3秒
|
// 设置单个交易员的超时时间为3秒
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// 使用通道来实现超时控制
|
// 使用通道来实现超时控制
|
||||||
accountChan := make(chan map[string]interface{}, 1)
|
accountChan := make(chan map[string]interface{}, 1)
|
||||||
errorChan := make(chan error, 1)
|
errorChan := make(chan error, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
account, err := trader.GetAccountInfo()
|
account, err := trader.GetAccountInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -582,10 +582,10 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
|
|||||||
accountChan <- account
|
accountChan <- account
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
status := trader.GetStatus()
|
status := trader.GetStatus()
|
||||||
var traderData map[string]interface{}
|
var traderData map[string]interface{}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case account := <-accountChan:
|
case account := <-accountChan:
|
||||||
// 成功获取账户信息
|
// 成功获取账户信息
|
||||||
@@ -634,18 +634,18 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
|
|||||||
"error": "获取超时",
|
"error": "获取超时",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultChan <- traderResult{index: index, data: traderData}
|
resultChan <- traderResult{index: index, data: traderData}
|
||||||
}(i, t)
|
}(i, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收集所有结果
|
// 收集所有结果
|
||||||
results := make([]map[string]interface{}, len(traders))
|
results := make([]map[string]interface{}, len(traders))
|
||||||
for i := 0; i < len(traders); i++ {
|
for i := 0; i < len(traders); i++ {
|
||||||
result := <-resultChan
|
result := <-resultChan
|
||||||
results[result.index] = result.data
|
results[result.index] = result.data
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,20 +656,20 @@ func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从竞赛数据中提取前5名
|
// 从竞赛数据中提取前5名
|
||||||
allTraders, ok := competitionData["traders"].([]map[string]interface{})
|
allTraders, ok := competitionData["traders"].([]map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("竞赛数据格式错误")
|
return nil, fmt.Errorf("竞赛数据格式错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限制返回前5名
|
// 限制返回前5名
|
||||||
limit := 5
|
limit := 5
|
||||||
topTraders := allTraders
|
topTraders := allTraders
|
||||||
if len(allTraders) > limit {
|
if len(allTraders) > limit {
|
||||||
topTraders = allTraders[:limit]
|
topTraders = allTraders[:limit]
|
||||||
}
|
}
|
||||||
|
|
||||||
result := map[string]interface{}{
|
result := map[string]interface{}{
|
||||||
"traders": topTraders,
|
"traders": topTraders,
|
||||||
"count": len(topTraders),
|
"count": len(topTraders),
|
||||||
|
|||||||
@@ -280,8 +280,8 @@ func isRetryableError(err error) bool {
|
|||||||
"connection refused",
|
"connection refused",
|
||||||
"temporary failure",
|
"temporary failure",
|
||||||
"no such host",
|
"no such host",
|
||||||
"stream error", // HTTP/2 stream 错误
|
"stream error", // HTTP/2 stream 错误
|
||||||
"INTERNAL_ERROR", // 服务端内部错误
|
"INTERNAL_ERROR", // 服务端内部错误
|
||||||
}
|
}
|
||||||
for _, retryable := range retryableErrors {
|
for _, retryable := range retryableErrors {
|
||||||
if strings.Contains(errStr, retryable) {
|
if strings.Contains(errStr, retryable) {
|
||||||
|
|||||||
Reference in New Issue
Block a user