mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-10 06:20:58 +08:00
feat: add OI ranking data support and fix trader config update issues
- Add OI ranking data fetching and formatting for AI prompts - Fix trader update not saving strategy_id, ai_model_id, initial_balance - Fix AI API key not set for non-qwen/deepseek providers - Add strategy_id to trader config API response - Remove old trader from memory before reloading on update - Clean up unused useTraderActions.ts
This commit is contained in:
@@ -66,6 +66,10 @@ type CreateDebateRequest struct {
|
||||
AutoExecute bool `json:"auto_execute"`
|
||||
TraderID string `json:"trader_id"`
|
||||
Participants []ParticipantConfig `json:"participants" binding:"required,min=2"`
|
||||
// OI Ranking data options
|
||||
EnableOIRanking bool `json:"enable_oi_ranking"` // Whether to include OI ranking data
|
||||
OIRankingLimit int `json:"oi_ranking_limit"` // Number of OI ranking entries (default 10)
|
||||
OIDuration string `json:"oi_duration"` // Duration for OI data (1h, 4h, 24h, etc.)
|
||||
}
|
||||
|
||||
// ParticipantConfig represents a participant configuration
|
||||
@@ -215,6 +219,9 @@ func (h *DebateHandler) HandleCreateDebate(c *gin.Context) {
|
||||
PromptVariant: req.PromptVariant,
|
||||
AutoExecute: req.AutoExecute,
|
||||
TraderID: req.TraderID,
|
||||
EnableOIRanking: req.EnableOIRanking,
|
||||
OIRankingLimit: req.OIRankingLimit,
|
||||
OIDuration: req.OIDuration,
|
||||
}
|
||||
|
||||
if err := h.debateStore.CreateSession(session); err != nil {
|
||||
|
||||
101
api/server.go
101
api/server.go
@@ -792,19 +792,24 @@ func (s *Server) handleUpdateTrader(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
err = s.store.Trader().Update(traderRecord)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update trader: %v", err)})
|
||||
return
|
||||
}
|
||||
|
||||
// Reload traders into memory
|
||||
// Remove old trader from memory first to ensure fresh config is loaded
|
||||
s.traderManager.RemoveTrader(traderID)
|
||||
|
||||
// Reload traders into memory with fresh config
|
||||
err = s.traderManager.LoadUserTradersFromStore(s.store, userID)
|
||||
if err != nil {
|
||||
logger.Infof("⚠️ Failed to reload user traders into memory: %v", err)
|
||||
}
|
||||
|
||||
logger.Infof("✓ Trader updated successfully: %s (model: %s, exchange: %s)", req.Name, req.AIModelID, req.ExchangeID)
|
||||
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{
|
||||
"trader_id": traderID,
|
||||
@@ -854,54 +859,57 @@ func (s *Server) handleStartTrader(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
trader, err := s.traderManager.GetTrader(traderID)
|
||||
if err != nil {
|
||||
// Trader not in memory, try loading from database
|
||||
logger.Infof("🔄 Trader %s not in memory, trying to load...", traderID)
|
||||
if loadErr := s.traderManager.LoadUserTradersFromStore(s.store, userID); loadErr != nil {
|
||||
logger.Infof("❌ Failed to load user traders: %v", loadErr)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load trader: " + loadErr.Error()})
|
||||
return
|
||||
}
|
||||
// Try to get trader again
|
||||
trader, err = s.traderManager.GetTrader(traderID)
|
||||
if err != nil {
|
||||
// Check detailed reason
|
||||
fullCfg, _ := s.store.Trader().GetFullConfig(userID, traderID)
|
||||
if fullCfg != nil && fullCfg.Trader != nil {
|
||||
// Check strategy
|
||||
if fullCfg.Strategy == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader has no strategy configured, please create a strategy in Strategy Studio and associate it with the trader"})
|
||||
return
|
||||
}
|
||||
// Check AI model
|
||||
if fullCfg.AIModel == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's AI model does not exist, please check AI model configuration"})
|
||||
return
|
||||
}
|
||||
if !fullCfg.AIModel.Enabled {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's AI model is not enabled, please enable the AI model first"})
|
||||
return
|
||||
}
|
||||
// Check exchange
|
||||
if fullCfg.Exchange == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's exchange does not exist, please check exchange configuration"})
|
||||
return
|
||||
}
|
||||
if !fullCfg.Exchange.Enabled {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's exchange is not enabled, please enable the exchange first"})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Failed to load trader, please check AI model, exchange and strategy configuration"})
|
||||
// Check if trader exists in memory and if it's running
|
||||
existingTrader, _ := s.traderManager.GetTrader(traderID)
|
||||
if existingTrader != nil {
|
||||
status := existingTrader.GetStatus()
|
||||
if isRunning, ok := status["is_running"].(bool); ok && isRunning {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader is already running"})
|
||||
return
|
||||
}
|
||||
// Trader exists but is stopped - remove from memory to reload fresh config
|
||||
logger.Infof("🔄 Removing stopped trader %s from memory to reload config...", traderID)
|
||||
s.traderManager.RemoveTrader(traderID)
|
||||
}
|
||||
|
||||
// Check if trader is already running
|
||||
status := trader.GetStatus()
|
||||
if isRunning, ok := status["is_running"].(bool); ok && isRunning {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader is already running"})
|
||||
// Load trader from database (always reload to get latest config)
|
||||
logger.Infof("🔄 Loading trader %s from database...", traderID)
|
||||
if loadErr := s.traderManager.LoadUserTradersFromStore(s.store, userID); loadErr != nil {
|
||||
logger.Infof("❌ Failed to load user traders: %v", loadErr)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load trader: " + loadErr.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
trader, err := s.traderManager.GetTrader(traderID)
|
||||
if err != nil {
|
||||
// Check detailed reason
|
||||
fullCfg, _ := s.store.Trader().GetFullConfig(userID, traderID)
|
||||
if fullCfg != nil && fullCfg.Trader != nil {
|
||||
// Check strategy
|
||||
if fullCfg.Strategy == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader has no strategy configured, please create a strategy in Strategy Studio and associate it with the trader"})
|
||||
return
|
||||
}
|
||||
// Check AI model
|
||||
if fullCfg.AIModel == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's AI model does not exist, please check AI model configuration"})
|
||||
return
|
||||
}
|
||||
if !fullCfg.AIModel.Enabled {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's AI model is not enabled, please enable the AI model first"})
|
||||
return
|
||||
}
|
||||
// Check exchange
|
||||
if fullCfg.Exchange == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's exchange does not exist, please check exchange configuration"})
|
||||
return
|
||||
}
|
||||
if !fullCfg.Exchange.Enabled {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Trader's exchange is not enabled, please enable the exchange first"})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Failed to load trader, please check AI model, exchange and strategy configuration"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1730,6 +1738,7 @@ func (s *Server) handleGetTraderConfig(c *gin.Context) {
|
||||
"trader_name": traderConfig.Name,
|
||||
"ai_model": aiModelID,
|
||||
"exchange_id": traderConfig.ExchangeID,
|
||||
"strategy_id": traderConfig.StrategyID,
|
||||
"initial_balance": traderConfig.InitialBalance,
|
||||
"scan_interval_minutes": traderConfig.ScanIntervalMinutes,
|
||||
"btc_eth_leverage": traderConfig.BTCETHLeverage,
|
||||
|
||||
@@ -449,6 +449,16 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) {
|
||||
marketDataMap[coin.Symbol] = data
|
||||
}
|
||||
|
||||
// Fetch quantitative data for each candidate coin
|
||||
symbols := make([]string, 0, len(candidates))
|
||||
for _, c := range candidates {
|
||||
symbols = append(symbols, c.Symbol)
|
||||
}
|
||||
quantDataMap := engine.FetchQuantDataBatch(symbols)
|
||||
|
||||
// Fetch OI ranking data (market-wide position changes)
|
||||
oiRankingData := engine.FetchOIRankingData()
|
||||
|
||||
// Build real context (for generating User Prompt)
|
||||
testContext := &decision.Context{
|
||||
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||
@@ -468,6 +478,8 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) {
|
||||
CandidateCoins: candidates,
|
||||
PromptVariant: req.PromptVariant,
|
||||
MarketDataMap: marketDataMap,
|
||||
QuantDataMap: quantDataMap,
|
||||
OIRankingData: oiRankingData,
|
||||
}
|
||||
|
||||
// Build System Prompt
|
||||
|
||||
Reference in New Issue
Block a user