refactor: standardize code comments

This commit is contained in:
tinkle-community
2025-12-08 01:40:48 +08:00
parent 0636ced476
commit a12c0ae8c9
103 changed files with 5466 additions and 5468 deletions

View File

@@ -11,21 +11,21 @@ import (
"time"
)
// CompetitionCache 竞赛数据缓存
// CompetitionCache competition data cache
type CompetitionCache struct {
data map[string]interface{}
timestamp time.Time
mu sync.RWMutex
}
// TraderManager 管理多个trader实例
// TraderManager manages multiple trader instances
type TraderManager struct {
traders map[string]*trader.AutoTrader // key: trader ID
competitionCache *CompetitionCache
mu sync.RWMutex
}
// NewTraderManager 创建trader管理器
// NewTraderManager creates a trader manager
func NewTraderManager() *TraderManager {
return &TraderManager{
traders: make(map[string]*trader.AutoTrader),
@@ -35,19 +35,19 @@ func NewTraderManager() *TraderManager {
}
}
// GetTrader 获取指定ID的trader
// GetTrader retrieves a trader by ID
func (tm *TraderManager) GetTrader(id string) (*trader.AutoTrader, error) {
tm.mu.RLock()
defer tm.mu.RUnlock()
t, exists := tm.traders[id]
if !exists {
return nil, fmt.Errorf("trader ID '%s' 不存在", id)
return nil, fmt.Errorf("trader ID '%s' does not exist", id)
}
return t, nil
}
// GetAllTraders 获取所有trader
// GetAllTraders retrieves all traders
func (tm *TraderManager) GetAllTraders() map[string]*trader.AutoTrader {
tm.mu.RLock()
defer tm.mu.RUnlock()
@@ -59,7 +59,7 @@ func (tm *TraderManager) GetAllTraders() map[string]*trader.AutoTrader {
return result
}
// GetTraderIDs 获取所有trader ID列表
// GetTraderIDs retrieves all trader IDs
func (tm *TraderManager) GetTraderIDs() []string {
tm.mu.RLock()
defer tm.mu.RUnlock()
@@ -71,43 +71,43 @@ func (tm *TraderManager) GetTraderIDs() []string {
return ids
}
// StartAll 启动所有trader
// StartAll starts all traders
func (tm *TraderManager) StartAll() {
tm.mu.RLock()
defer tm.mu.RUnlock()
logger.Info("🚀 启动所有Trader...")
logger.Info("🚀 Starting all traders...")
for id, t := range tm.traders {
go func(traderID string, at *trader.AutoTrader) {
logger.Infof("▶️ 启动 %s...", at.GetName())
logger.Infof("▶️ Starting %s...", at.GetName())
if err := at.Run(); err != nil {
logger.Infof("❌ %s 运行错误: %v", at.GetName(), err)
logger.Infof("❌ %s runtime error: %v", at.GetName(), err)
}
}(id, t)
}
}
// StopAll 停止所有trader
// StopAll stops all traders
func (tm *TraderManager) StopAll() {
tm.mu.RLock()
defer tm.mu.RUnlock()
logger.Info("⏹ 停止所有Trader...")
logger.Info("⏹ Stopping all traders...")
for _, t := range tm.traders {
t.Stop()
}
}
// AutoStartRunningTraders 自动启动数据库中标记为运行中的交易员
// AutoStartRunningTraders automatically starts traders marked as running in the database
func (tm *TraderManager) AutoStartRunningTraders(st *store.Store) {
// 先获取所有交易员配置(一次性查询)
// Get all trader configurations (single query)
traderList, err := st.Trader().ListAll()
if err != nil {
logger.Infof("⚠️ 获取交易员列表失败: %v", err)
logger.Infof("⚠️ Failed to get trader list: %v", err)
return
}
// 构建运行中交易员的ID集合
// Build set of running trader IDs
runningTraderIDs := make(map[string]bool)
for _, traderCfg := range traderList {
if traderCfg.IsRunning {
@@ -116,7 +116,7 @@ func (tm *TraderManager) AutoStartRunningTraders(st *store.Store) {
}
if len(runningTraderIDs) == 0 {
logger.Info("📋 没有需要自动恢复的交易员")
logger.Info("📋 No traders to auto-restore")
return
}
@@ -127,9 +127,9 @@ func (tm *TraderManager) AutoStartRunningTraders(st *store.Store) {
for id, t := range tm.traders {
if runningTraderIDs[id] {
go func(traderID string, at *trader.AutoTrader) {
logger.Infof("▶️ 自动恢复启动 %s...", at.GetName())
logger.Infof("▶️ Auto-restoring %s...", at.GetName())
if err := at.Run(); err != nil {
logger.Infof("❌ %s 运行错误: %v", at.GetName(), err)
logger.Infof("❌ %s runtime error: %v", at.GetName(), err)
}
}(id, t)
startedCount++
@@ -137,11 +137,11 @@ func (tm *TraderManager) AutoStartRunningTraders(st *store.Store) {
}
if startedCount > 0 {
logger.Infof("✓ 自动恢复启动了 %d 个交易员", startedCount)
logger.Infof("✓ Auto-restored %d traders", startedCount)
}
}
// GetComparisonData 获取对比数据
// GetComparisonData retrieves comparison data
func (tm *TraderManager) GetComparisonData() (map[string]interface{}, error) {
tm.mu.RLock()
defer tm.mu.RUnlock()
@@ -178,38 +178,38 @@ func (tm *TraderManager) GetComparisonData() (map[string]interface{}, error) {
return comparison, nil
}
// GetCompetitionData 获取竞赛数据(全平台所有交易员)
// GetCompetitionData retrieves competition data (all traders across platform)
func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
// 检查缓存是否有效30秒内
// Check if cache is valid (within 30 seconds)
tm.competitionCache.mu.RLock()
if time.Since(tm.competitionCache.timestamp) < 30*time.Second && len(tm.competitionCache.data) > 0 {
// 返回缓存数据
// Return cached data
cachedData := make(map[string]interface{})
for k, v := range tm.competitionCache.data {
cachedData[k] = v
}
tm.competitionCache.mu.RUnlock()
logger.Infof("📋 返回竞赛数据缓存 (缓存时间: %.1fs)", time.Since(tm.competitionCache.timestamp).Seconds())
logger.Infof("📋 Returning competition data cache (cache age: %.1fs)", time.Since(tm.competitionCache.timestamp).Seconds())
return cachedData, nil
}
tm.competitionCache.mu.RUnlock()
tm.mu.RLock()
// 获取所有交易员列表
// Get all trader list
allTraders := make([]*trader.AutoTrader, 0, len(tm.traders))
for id, t := range tm.traders {
allTraders = append(allTraders, t)
logger.Infof("📋 竞赛数据包含交易员: %s (%s)", t.GetName(), id)
logger.Infof("📋 Competition data includes trader: %s (%s)", t.GetName(), id)
}
tm.mu.RUnlock()
logger.Infof("🔄 重新获取竞赛数据,交易员数量: %d", len(allTraders))
logger.Infof("🔄 Refreshing competition data, trader count: %d", len(allTraders))
// 并发获取交易员数据
// Concurrently fetch trader data
traders := tm.getConcurrentTraderData(allTraders)
// 按收益率排序(降序)
// Sort by profit rate (descending)
sort.Slice(traders, func(i, j int) bool {
pnlPctI, okI := traders[i]["total_pnl_pct"].(float64)
pnlPctJ, okJ := traders[j]["total_pnl_pct"].(float64)
@@ -222,7 +222,7 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
return pnlPctI > pnlPctJ
})
// 限制返回前50
// Limit to top 50
totalCount := len(traders)
limit := 50
if len(traders) > limit {
@@ -232,9 +232,9 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
comparison := make(map[string]interface{})
comparison["traders"] = traders
comparison["count"] = len(traders)
comparison["total_count"] = totalCount // 总交易员数量
comparison["total_count"] = totalCount // Total number of traders
// 更新缓存
// Update cache
tm.competitionCache.mu.Lock()
tm.competitionCache.data = comparison
tm.competitionCache.timestamp = time.Now()
@@ -243,24 +243,24 @@ func (tm *TraderManager) GetCompetitionData() (map[string]interface{}, error) {
return comparison, nil
}
// getConcurrentTraderData 并发获取多个交易员的数据
// getConcurrentTraderData concurrently fetches data for multiple traders
func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) []map[string]interface{} {
type traderResult struct {
index int
data map[string]interface{}
}
// 创建结果通道
// Create result channel
resultChan := make(chan traderResult, len(traders))
// 并发获取每个交易员的数据
// Concurrently fetch data for each trader
for i, t := range traders {
go func(index int, trader *trader.AutoTrader) {
// 设置单个交易员的超时时间为3秒
// Set timeout to 3 seconds for single trader
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// 使用通道来实现超时控制
// Use channel for timeout control
accountChan := make(chan map[string]interface{}, 1)
errorChan := make(chan error, 1)
@@ -278,7 +278,7 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
select {
case account := <-accountChan:
// 成功获取账户信息
// Successfully got account info
traderData = map[string]interface{}{
"trader_id": trader.GetID(),
"trader_name": trader.GetName(),
@@ -293,8 +293,8 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
"system_prompt_template": trader.GetSystemPromptTemplate(),
}
case err := <-errorChan:
// 获取账户信息失败
logger.Infof("⚠️ 获取交易员 %s 账户信息失败: %v", trader.GetID(), err)
// Failed to get account info
logger.Infof("⚠️ Failed to get account info for trader %s: %v", trader.GetID(), err)
traderData = map[string]interface{}{
"trader_id": trader.GetID(),
"trader_name": trader.GetName(),
@@ -307,11 +307,11 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
"margin_used_pct": 0.0,
"is_running": status["is_running"],
"system_prompt_template": trader.GetSystemPromptTemplate(),
"error": "账户数据获取失败",
"error": "Failed to get account data",
}
case <-ctx.Done():
// 超时
logger.Infof("⏰ 获取交易员 %s 账户信息超时", trader.GetID())
// Timeout
logger.Infof("⏰ Timeout getting account info for trader %s", trader.GetID())
traderData = map[string]interface{}{
"trader_id": trader.GetID(),
"trader_name": trader.GetName(),
@@ -324,7 +324,7 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
"margin_used_pct": 0.0,
"is_running": status["is_running"],
"system_prompt_template": trader.GetSystemPromptTemplate(),
"error": "获取超时",
"error": "Request timeout",
}
}
@@ -332,7 +332,7 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
}(i, t)
}
// 收集所有结果
// Collect all results
results := make([]map[string]interface{}, len(traders))
for i := 0; i < len(traders); i++ {
result := <-resultChan
@@ -342,21 +342,21 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
return results
}
// GetTopTradersData 获取前5名交易员数据用于表现对比
// GetTopTradersData retrieves top 5 traders data (for performance comparison)
func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, error) {
// 复用竞赛数据缓存因为前5名是从全部数据中筛选出来的
// Reuse competition data cache, as top 5 is filtered from all data
competitionData, err := tm.GetCompetitionData()
if err != nil {
return nil, err
}
// 从竞赛数据中提取前5名
// Extract top 5 from competition data
allTraders, ok := competitionData["traders"].([]map[string]interface{})
if !ok {
return nil, fmt.Errorf("竞赛数据格式错误")
return nil, fmt.Errorf("invalid competition data format")
}
// 限制返回前5名
// Limit to top 5
limit := 5
topTraders := allTraders
if len(allTraders) > limit {
@@ -372,53 +372,53 @@ func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, error) {
}
// RemoveTrader 从内存中移除指定的trader不影响数据库
// 用于更新trader配置时强制重新加载
// RemoveTrader removes a trader from memory (does not affect database)
// Used to force reload when updating trader configuration
func (tm *TraderManager) RemoveTrader(traderID string) {
tm.mu.Lock()
defer tm.mu.Unlock()
if _, exists := tm.traders[traderID]; exists {
delete(tm.traders, traderID)
logger.Infof("✓ Trader %s 已从内存中移除", traderID)
logger.Infof("✓ Trader %s removed from memory", traderID)
}
}
// LoadUserTradersFromStore 为特定用户从store加载交易员到内存
// LoadUserTradersFromStore loads traders from store for a specific user to memory
func (tm *TraderManager) LoadUserTradersFromStore(st *store.Store, userID string) error {
tm.mu.Lock()
defer tm.mu.Unlock()
// 获取指定用户的所有交易员
// Get all traders for the specified user
traders, err := st.Trader().List(userID)
if err != nil {
return fmt.Errorf("获取用户 %s 的交易员列表失败: %w", userID, err)
return fmt.Errorf("failed to get trader list for user %s: %w", userID, err)
}
logger.Infof("📋 为用户 %s 加载交易员配置: %d 个", userID, len(traders))
logger.Infof("📋 Loading trader configurations for user %s: %d traders", userID, len(traders))
// 获取AI模型和交易所列表在循环外只查询一次
// Get AI model and exchange lists (query only once outside loop)
aiModels, err := st.AIModel().List(userID)
if err != nil {
logger.Infof("⚠️ 获取用户 %s 的AI模型配置失败: %v", userID, err)
return fmt.Errorf("获取AI模型配置失败: %w", err)
logger.Infof("⚠️ Failed to get AI model config for user %s: %v", userID, err)
return fmt.Errorf("failed to get AI model config: %w", err)
}
exchanges, err := st.Exchange().List(userID)
if err != nil {
logger.Infof("⚠️ 获取用户 %s 的交易所配置失败: %v", userID, err)
return fmt.Errorf("获取交易所配置失败: %w", err)
logger.Infof("⚠️ Failed to get exchange config for user %s: %v", userID, err)
return fmt.Errorf("failed to get exchange config: %w", err)
}
// 为每个交易员加载配置
// Load configuration for each trader
for _, traderCfg := range traders {
// 检查是否已经加载过这个交易员
// Check if this trader is already loaded
if _, exists := tm.traders[traderCfg.ID]; exists {
logger.Infof("⚠️ 交易员 %s 已经加载,跳过", traderCfg.Name)
logger.Infof("⚠️ Trader %s already loaded, skipping", traderCfg.Name)
continue
}
// 从已查询的列表中查找AI模型配置
// Find AI model config from already queried list
var aiModelCfg *store.AIModel
for _, model := range aiModels {
if model.ID == traderCfg.AIModelID {
@@ -436,16 +436,16 @@ func (tm *TraderManager) LoadUserTradersFromStore(st *store.Store, userID string
}
if aiModelCfg == nil {
logger.Infof("⚠️ 交易员 %s 的AI模型 %s 不存在,跳过", traderCfg.Name, traderCfg.AIModelID)
logger.Infof("⚠️ AI model %s for trader %s does not exist, skipping", traderCfg.AIModelID, traderCfg.Name)
continue
}
if !aiModelCfg.Enabled {
logger.Infof("⚠️ 交易员 %s 的AI模型 %s 未启用,跳过", traderCfg.Name, traderCfg.AIModelID)
logger.Infof("⚠️ AI model %s for trader %s is not enabled, skipping", traderCfg.AIModelID, traderCfg.Name)
continue
}
// 从已查询的列表中查找交易所配置
// Find exchange config from already queried list
var exchangeCfg *store.Exchange
for _, exchange := range exchanges {
if exchange.ID == traderCfg.ExchangeID {
@@ -455,95 +455,95 @@ func (tm *TraderManager) LoadUserTradersFromStore(st *store.Store, userID string
}
if exchangeCfg == nil {
logger.Infof("⚠️ 交易员 %s 的交易所 %s 不存在,跳过", traderCfg.Name, traderCfg.ExchangeID)
logger.Infof("⚠️ Exchange %s for trader %s does not exist, skipping", traderCfg.ExchangeID, traderCfg.Name)
continue
}
if !exchangeCfg.Enabled {
logger.Infof("⚠️ 交易员 %s 的交易所 %s 未启用,跳过", traderCfg.Name, traderCfg.ExchangeID)
logger.Infof("⚠️ Exchange %s for trader %s is not enabled, skipping", traderCfg.ExchangeID, traderCfg.Name)
continue
}
// 使用现有的方法加载交易员
logger.Infof("📦 正在加载交易员 %s (AI模型: %s, 交易所: %s, 策略ID: %s)", traderCfg.Name, aiModelCfg.Provider, exchangeCfg.ID, traderCfg.StrategyID)
// Use existing method to load trader
logger.Infof("📦 Loading trader %s (AI Model: %s, Exchange: %s, Strategy ID: %s)", traderCfg.Name, aiModelCfg.Provider, exchangeCfg.ID, traderCfg.StrategyID)
err = tm.addTraderFromStore(traderCfg, aiModelCfg, exchangeCfg, st)
if err != nil {
logger.Infof("❌ 加载交易员 %s 失败: %v", traderCfg.Name, err)
logger.Infof("❌ Failed to load trader %s: %v", traderCfg.Name, err)
}
}
return nil
}
// LoadTradersFromStore 从store加载所有交易员到内存新版API
// LoadTradersFromStore loads all traders from store to memory (new API)
func (tm *TraderManager) LoadTradersFromStore(st *store.Store) error {
tm.mu.Lock()
defer tm.mu.Unlock()
// 获取所有用户
// Get all users
userIDs, err := st.User().GetAllIDs()
if err != nil {
return fmt.Errorf("获取用户列表失败: %w", err)
return fmt.Errorf("failed to get user list: %w", err)
}
logger.Infof("📋 发现 %d 个用户,开始加载所有交易员配置...", len(userIDs))
logger.Infof("📋 Found %d users, loading all trader configurations...", len(userIDs))
var allTraders []*store.Trader
for _, userID := range userIDs {
// 获取每个用户的交易员
// Get traders for each user
traders, err := st.Trader().List(userID)
if err != nil {
logger.Infof("⚠️ 获取用户 %s 的交易员失败: %v", userID, err)
logger.Infof("⚠️ Failed to get traders for user %s: %v", userID, err)
continue
}
logger.Infof("📋 用户 %s: %d 个交易员", userID, len(traders))
logger.Infof("📋 User %s: %d traders", userID, len(traders))
allTraders = append(allTraders, traders...)
}
logger.Infof("📋 总共加载 %d 个交易员配置", len(allTraders))
logger.Infof("📋 Total loaded trader configurations: %d", len(allTraders))
// 为每个交易员获取AI模型和交易所配置
// Get AI model and exchange configs for each trader
for _, traderCfg := range allTraders {
// 获取AI模型配置
// Get AI model config
aiModels, err := st.AIModel().List(traderCfg.UserID)
if err != nil {
logger.Infof("⚠️ 获取AI模型配置失败: %v", err)
logger.Infof("⚠️ Failed to get AI model config: %v", err)
continue
}
var aiModelCfg *store.AIModel
// 优先精确匹配 model.ID
// Prioritize exact match on model.ID
for _, model := range aiModels {
if model.ID == traderCfg.AIModelID {
aiModelCfg = model
break
}
}
// 如果没有精确匹配,尝试匹配 provider兼容旧数据
// If no exact match, try matching provider (for backward compatibility)
if aiModelCfg == nil {
for _, model := range aiModels {
if model.Provider == traderCfg.AIModelID {
aiModelCfg = model
logger.Infof("⚠️ 交易员 %s 使用旧版 provider 匹配: %s -> %s", traderCfg.Name, traderCfg.AIModelID, model.ID)
logger.Infof("⚠️ Trader %s using legacy provider match: %s -> %s", traderCfg.Name, traderCfg.AIModelID, model.ID)
break
}
}
}
if aiModelCfg == nil {
logger.Infof("⚠️ 交易员 %s 的AI模型 %s 不存在,跳过", traderCfg.Name, traderCfg.AIModelID)
logger.Infof("⚠️ AI model %s for trader %s does not exist, skipping", traderCfg.AIModelID, traderCfg.Name)
continue
}
if !aiModelCfg.Enabled {
logger.Infof("⚠️ 交易员 %s 的AI模型 %s 未启用,跳过", traderCfg.Name, traderCfg.AIModelID)
logger.Infof("⚠️ AI model %s for trader %s is not enabled, skipping", traderCfg.AIModelID, traderCfg.Name)
continue
}
// 获取交易所配置
// Get exchange config
exchanges, err := st.Exchange().List(traderCfg.UserID)
if err != nil {
logger.Infof("⚠️ 获取交易所配置失败: %v", err)
logger.Infof("⚠️ Failed to get exchange config: %v", err)
continue
}
@@ -556,51 +556,51 @@ func (tm *TraderManager) LoadTradersFromStore(st *store.Store) error {
}
if exchangeCfg == nil {
logger.Infof("⚠️ 交易员 %s 的交易所 %s 不存在,跳过", traderCfg.Name, traderCfg.ExchangeID)
logger.Infof("⚠️ Exchange %s for trader %s does not exist, skipping", traderCfg.ExchangeID, traderCfg.Name)
continue
}
if !exchangeCfg.Enabled {
logger.Infof("⚠️ 交易员 %s 的交易所 %s 未启用,跳过", traderCfg.Name, traderCfg.ExchangeID)
logger.Infof("⚠️ Exchange %s for trader %s is not enabled, skipping", traderCfg.ExchangeID, traderCfg.Name)
continue
}
// 添加到TraderManagercoinPoolURL/oiTopURL 已从策略配置中获取)
// Add to TraderManager (coinPoolURL/oiTopURL already obtained from strategy config)
err = tm.addTraderFromStore(traderCfg, aiModelCfg, exchangeCfg, st)
if err != nil {
logger.Infof("❌ 添加交易员 %s 失败: %v", traderCfg.Name, err)
logger.Infof("❌ Failed to add trader %s: %v", traderCfg.Name, err)
continue
}
}
logger.Infof("✓ 成功加载 %d 个交易员到内存", len(tm.traders))
logger.Infof("✓ Successfully loaded %d traders to memory", len(tm.traders))
return nil
}
// addTraderFromStore 内部方法从store配置添加交易员
// addTraderFromStore internal method: adds trader from store configuration
func (tm *TraderManager) addTraderFromStore(traderCfg *store.Trader, aiModelCfg *store.AIModel, exchangeCfg *store.Exchange, st *store.Store) error {
if _, exists := tm.traders[traderCfg.ID]; exists {
return fmt.Errorf("trader ID '%s' 已存在", traderCfg.ID)
return fmt.Errorf("trader ID '%s' already exists", traderCfg.ID)
}
// 加载策略配置(必须有策略)
// Load strategy config (must have strategy)
var strategyConfig *store.StrategyConfig
if traderCfg.StrategyID != "" {
strategy, err := st.Strategy().Get(traderCfg.UserID, traderCfg.StrategyID)
if err != nil {
return fmt.Errorf("交易员 %s 的策略 %s 加载失败: %w", traderCfg.Name, traderCfg.StrategyID, err)
return fmt.Errorf("failed to load strategy %s for trader %s: %w", traderCfg.StrategyID, traderCfg.Name, err)
}
// 解析 JSON 配置
// Parse JSON config
strategyConfig, err = strategy.ParseConfig()
if err != nil {
return fmt.Errorf("交易员 %s 的策略配置解析失败: %w", traderCfg.Name, err)
return fmt.Errorf("failed to parse strategy config for trader %s: %w", traderCfg.Name, err)
}
logger.Infof("✓ 交易员 %s 加载策略配置: %s", traderCfg.Name, strategy.Name)
logger.Infof("✓ Trader %s loaded strategy config: %s", traderCfg.Name, strategy.Name)
} else {
return fmt.Errorf("交易员 %s 未配置策略", traderCfg.Name)
return fmt.Errorf("trader %s has no strategy configured", traderCfg.Name)
}
// 构建AutoTraderConfigcoinPoolURL/oiTopURL 从策略配置获取,在 StrategyEngine 中使用)
// Build AutoTraderConfig (coinPoolURL/oiTopURL obtained from strategy config, used in StrategyEngine)
traderConfig := trader.AutoTraderConfig{
ID: traderCfg.ID,
Name: traderCfg.Name,
@@ -621,7 +621,7 @@ func (tm *TraderManager) addTraderFromStore(traderCfg *store.Trader, aiModelCfg
StrategyConfig: strategyConfig,
}
// 根据交易所类型设置API密钥
// Set API keys based on exchange type
switch exchangeCfg.ID {
case "binance":
traderConfig.BinanceAPIKey = exchangeCfg.APIKey
@@ -646,31 +646,31 @@ func (tm *TraderManager) addTraderFromStore(traderCfg *store.Trader, aiModelCfg
traderConfig.LighterTestnet = exchangeCfg.Testnet
}
// 根据AI模型设置API密钥
// Set API keys based on AI model
if aiModelCfg.Provider == "qwen" {
traderConfig.QwenKey = aiModelCfg.APIKey
} else if aiModelCfg.Provider == "deepseek" {
traderConfig.DeepSeekKey = aiModelCfg.APIKey
}
// 创建trader实例
// Create trader instance
at, err := trader.NewAutoTrader(traderConfig, st, traderCfg.UserID)
if err != nil {
return fmt.Errorf("创建trader失败: %w", err)
return fmt.Errorf("failed to create trader: %w", err)
}
// 设置自定义prompt如果有
// Set custom prompt (if exists)
if traderCfg.CustomPrompt != "" {
at.SetCustomPrompt(traderCfg.CustomPrompt)
at.SetOverrideBasePrompt(traderCfg.OverrideBasePrompt)
if traderCfg.OverrideBasePrompt {
logger.Infof("✓ 已设置自定义交易策略prompt (覆盖基础prompt)")
logger.Infof("✓ Set custom trading strategy prompt (overriding base prompt)")
} else {
logger.Infof("✓ 已设置自定义交易策略prompt (补充基础prompt)")
logger.Infof("✓ Set custom trading strategy prompt (supplementing base prompt)")
}
}
tm.traders[traderCfg.ID] = at
logger.Infof("✓ Trader '%s' (%s + %s) 已加载到内存", traderCfg.Name, aiModelCfg.Provider, exchangeCfg.ID)
logger.Infof("✓ Trader '%s' (%s + %s) loaded to memory", traderCfg.Name, aiModelCfg.Provider, exchangeCfg.ID)
return nil
}

View File

@@ -4,51 +4,51 @@ import (
"testing"
)
// TestRemoveTrader 测试从内存中移除trader
// TestRemoveTrader tests removing trader from memory
func TestRemoveTrader(t *testing.T) {
tm := NewTraderManager()
// 创建一个模拟的 trader 并添加到 map
// Create a mock trader and add it to map
traderID := "test-trader-123"
tm.traders[traderID] = nil // 使用 nil 作为占位符,实际测试中只需验证删除逻辑
tm.traders[traderID] = nil // Use nil as placeholder, only need to verify deletion logic in test
// 验证 trader 存在
// Verify trader exists
if _, exists := tm.traders[traderID]; !exists {
t.Fatal("trader 应该存在于 map")
t.Fatal("trader should exist in map")
}
// 调用 RemoveTrader
// Call RemoveTrader
tm.RemoveTrader(traderID)
// 验证 trader 已被移除
// Verify trader has been removed
if _, exists := tm.traders[traderID]; exists {
t.Error("trader 应该已从 map 中移除")
t.Error("trader should be removed from map")
}
}
// TestRemoveTrader_NonExistent 测试移除不存在的trader不会报错
// TestRemoveTrader_NonExistent tests that removing non-existent trader doesn't error
func TestRemoveTrader_NonExistent(t *testing.T) {
tm := NewTraderManager()
// 尝试移除不存在的 trader不应该 panic
// Trying to remove non-existent trader should not panic
defer func() {
if r := recover(); r != nil {
t.Errorf("移除不存在的 trader 不应该 panic: %v", r)
t.Errorf("removing non-existent trader should not panic: %v", r)
}
}()
tm.RemoveTrader("non-existent-trader")
}
// TestRemoveTrader_Concurrent 测试并发移除trader的安全性
// TestRemoveTrader_Concurrent tests concurrent removal of trader safety
func TestRemoveTrader_Concurrent(t *testing.T) {
tm := NewTraderManager()
traderID := "test-trader-concurrent"
// 添加 trader
// Add trader
tm.traders[traderID] = nil
// 并发调用 RemoveTrader
// Concurrently call RemoveTrader
done := make(chan bool, 10)
for i := 0; i < 10; i++ {
go func() {
@@ -57,31 +57,31 @@ func TestRemoveTrader_Concurrent(t *testing.T) {
}()
}
// 等待所有 goroutine 完成
// Wait for all goroutines to complete
for i := 0; i < 10; i++ {
<-done
}
// 验证 trader 已被移除
// Verify trader has been removed
if _, exists := tm.traders[traderID]; exists {
t.Error("trader 应该已从 map 中移除")
t.Error("trader should be removed from map")
}
}
// TestGetTrader_AfterRemove 测试移除后获取trader返回错误
// TestGetTrader_AfterRemove tests that getting trader after removal returns error
func TestGetTrader_AfterRemove(t *testing.T) {
tm := NewTraderManager()
traderID := "test-trader-get"
// 添加 trader
// Add trader
tm.traders[traderID] = nil
// 移除 trader
// Remove trader
tm.RemoveTrader(traderID)
// 尝试获取已移除的 trader
// Try to get removed trader
_, err := tm.GetTrader(traderID)
if err == nil {
t.Error("获取已移除的 trader 应该返回错误")
t.Error("getting removed trader should return error")
}
}