diff --git a/api/server.go b/api/server.go index 0cbb251f..401d9ef0 100644 --- a/api/server.go +++ b/api/server.go @@ -779,11 +779,13 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { // Set scan interval, allow updates scanIntervalMinutes := req.ScanIntervalMinutes + logger.Infof("📊 Update trader scan_interval: req=%d, existing=%d", req.ScanIntervalMinutes, existingTrader.ScanIntervalMinutes) if scanIntervalMinutes <= 0 { scanIntervalMinutes = existingTrader.ScanIntervalMinutes // Keep original value } else if scanIntervalMinutes < 3 { scanIntervalMinutes = 3 } + logger.Infof("📊 Final scan_interval_minutes: %d", scanIntervalMinutes) // Set system prompt template systemPromptTemplate := req.SystemPromptTemplate @@ -818,16 +820,26 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { IsRunning: existingTrader.IsRunning, // Keep original value } + // Check if trader was running before update (we'll restart it after) + wasRunning := false + if existingMemTrader, memErr := s.traderManager.GetTrader(traderID); memErr == nil { + status := existingMemTrader.GetStatus() + if running, ok := status["is_running"].(bool); ok && running { + wasRunning = true + logger.Infof("🔄 Trader %s was running, will restart with new config after update", traderID) + } + } + // Update database - logger.Infof("🔄 Updating trader: ID=%s, Name=%s, AIModelID=%s, StrategyID=%s, req.StrategyID=%s", - traderRecord.ID, traderRecord.Name, traderRecord.AIModelID, traderRecord.StrategyID, req.StrategyID) + logger.Infof("🔄 Updating trader: ID=%s, Name=%s, AIModelID=%s, StrategyID=%s, ScanInterval=%d min", + traderRecord.ID, traderRecord.Name, traderRecord.AIModelID, traderRecord.StrategyID, scanIntervalMinutes) err = s.store.Trader().Update(traderRecord) if err != nil { SafeInternalError(c, "Failed to update trader", err) return } - // Remove old trader from memory first to ensure fresh config is loaded + // Remove old trader from memory first (this also stops if running) s.traderManager.RemoveTrader(traderID) // Reload traders into memory with fresh config @@ -836,6 +848,18 @@ func (s *Server) handleUpdateTrader(c *gin.Context) { logger.Infof("⚠️ Failed to reload user traders into memory: %v", err) } + // If trader was running before, restart it with new config + if wasRunning { + if reloadedTrader, getErr := s.traderManager.GetTrader(traderID); getErr == nil { + go func() { + logger.Infof("▶️ Restarting trader %s with new config...", traderID) + if runErr := reloadedTrader.Run(); runErr != nil { + logger.Infof("❌ Trader %s runtime error: %v", traderID, runErr) + } + }() + } + } + logger.Infof("✓ Trader updated successfully: %s (model: %s, exchange: %s, strategy: %s)", req.Name, req.AIModelID, req.ExchangeID, strategyID) c.JSON(http.StatusOK, gin.H{ diff --git a/manager/trader_manager.go b/manager/trader_manager.go index 5f4e9c1d..8e39670e 100644 --- a/manager/trader_manager.go +++ b/manager/trader_manager.go @@ -410,11 +410,18 @@ func (tm *TraderManager) GetTopTradersData() (map[string]interface{}, error) { // RemoveTrader removes a trader from memory (does not affect database) // Used to force reload when updating trader configuration +// If the trader is running, it will be stopped first func (tm *TraderManager) RemoveTrader(traderID string) { tm.mu.Lock() defer tm.mu.Unlock() - if _, exists := tm.traders[traderID]; exists { + if t, exists := tm.traders[traderID]; exists { + // Stop the trader if it's running (this ensures the goroutine exits) + status := t.GetStatus() + if isRunning, ok := status["is_running"].(bool); ok && isRunning { + logger.Infof("⏹ Stopping trader %s before removing from memory...", traderID) + t.Stop() + } delete(tm.traders, traderID) logger.Infof("✓ Trader %s removed from memory", traderID) } @@ -664,6 +671,9 @@ func (tm *TraderManager) addTraderFromStore(traderCfg *store.Trader, aiModelCfg StrategyConfig: strategyConfig, } + logger.Infof("📊 Loading trader %s: ScanIntervalMinutes=%d (from DB), ScanInterval=%v", + traderCfg.Name, traderCfg.ScanIntervalMinutes, traderConfig.ScanInterval) + // Set API keys based on exchange type (convert EncryptedString to string) switch exchangeCfg.ExchangeType { case "binance": diff --git a/store/trader.go b/store/trader.go index 426e7488..ebe93e55 100644 --- a/store/trader.go +++ b/store/trader.go @@ -124,6 +124,9 @@ func (s *TraderStore) Update(trader *Trader) error { } if trader.ScanIntervalMinutes > 0 { updates["scan_interval_minutes"] = trader.ScanIntervalMinutes + fmt.Printf("📊 TraderStore.Update: scan_interval_minutes=%d will be saved\n", trader.ScanIntervalMinutes) + } else { + fmt.Printf("⚠️ TraderStore.Update: scan_interval_minutes=%d (<=0, NOT updating)\n", trader.ScanIntervalMinutes) } return s.db.Model(&Trader{}).