fix: auto-restart trader on config update and add scan interval debug logs

- RemoveTrader now stops running trader before removing from memory
- handleUpdateTrader auto-restarts trader if it was running before update
- Add debug logs to trace scan_interval_minutes through update/save/load flow
This commit is contained in:
tinkle-community
2026-01-04 01:27:30 +08:00
parent 0275e23b7e
commit bdfd8dc0d0
3 changed files with 41 additions and 4 deletions
+27 -3
View File
@@ -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{
+11 -1
View File
@@ -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":
+3
View File
@@ -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{}).