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:
tinkle-community
2025-12-12 16:08:21 +08:00
parent f5ae22d85c
commit cc390706d0
16 changed files with 476 additions and 716 deletions

View File

@@ -65,6 +65,10 @@ type DebateSession struct {
FinalDecisions []*DebateDecision `json:"final_decisions,omitempty"` // Multi-coin decisions
AutoExecute bool `json:"auto_execute"`
TraderID string `json:"trader_id,omitempty"` // Trader to use for auto-execute
// 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.)
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
@@ -239,6 +243,9 @@ func (s *DebateStore) InitSchema() error {
`ALTER TABLE debate_sessions ADD COLUMN interval_minutes INTEGER DEFAULT 5`,
`ALTER TABLE debate_sessions ADD COLUMN prompt_variant TEXT DEFAULT 'balanced'`,
`ALTER TABLE debate_sessions ADD COLUMN trader_id TEXT`,
`ALTER TABLE debate_sessions ADD COLUMN enable_oi_ranking BOOLEAN DEFAULT 0`,
`ALTER TABLE debate_sessions ADD COLUMN oi_ranking_limit INTEGER DEFAULT 10`,
`ALTER TABLE debate_sessions ADD COLUMN oi_duration TEXT DEFAULT '1h'`,
`ALTER TABLE debate_votes ADD COLUMN leverage INTEGER DEFAULT 5`,
`ALTER TABLE debate_votes ADD COLUMN position_pct REAL DEFAULT 0.2`,
`ALTER TABLE debate_votes ADD COLUMN stop_loss_pct REAL DEFAULT 0.03`,
@@ -266,15 +273,22 @@ func (s *DebateStore) CreateSession(session *DebateSession) error {
if session.PromptVariant == "" {
session.PromptVariant = "balanced"
}
if session.OIRankingLimit == 0 {
session.OIRankingLimit = 10
}
if session.OIDuration == "" {
session.OIDuration = "1h"
}
session.CreatedAt = time.Now()
session.UpdatedAt = time.Now()
_, err := s.db.Exec(`
INSERT INTO debate_sessions (id, user_id, name, strategy_id, status, symbol, max_rounds, current_round, interval_minutes, prompt_variant, auto_execute, trader_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
INSERT INTO debate_sessions (id, user_id, name, strategy_id, status, symbol, max_rounds, current_round, interval_minutes, prompt_variant, auto_execute, trader_id, enable_oi_ranking, oi_ranking_limit, oi_duration, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
session.ID, session.UserID, session.Name, session.StrategyID, session.Status,
session.Symbol, session.MaxRounds, session.CurrentRound, session.IntervalMinutes, session.PromptVariant,
session.AutoExecute, session.TraderID, session.CreatedAt, session.UpdatedAt,
session.AutoExecute, session.TraderID, session.EnableOIRanking, session.OIRankingLimit, session.OIDuration,
session.CreatedAt, session.UpdatedAt,
)
return err
}
@@ -286,17 +300,22 @@ func (s *DebateStore) GetSession(id string) (*DebateSession, error) {
var traderID sql.NullString
var intervalMinutes sql.NullInt64
var promptVariant sql.NullString
var enableOIRanking sql.NullBool
var oiRankingLimit sql.NullInt64
var oiDuration sql.NullString
// Try new schema first
err := s.db.QueryRow(`
SELECT id, user_id, name, strategy_id, status, symbol, max_rounds, current_round,
interval_minutes, prompt_variant, final_decision, auto_execute, trader_id, created_at, updated_at
interval_minutes, prompt_variant, final_decision, auto_execute, trader_id,
enable_oi_ranking, oi_ranking_limit, oi_duration, created_at, updated_at
FROM debate_sessions WHERE id = ?`, id,
).Scan(
&session.ID, &session.UserID, &session.Name, &session.StrategyID,
&session.Status, &session.Symbol, &session.MaxRounds, &session.CurrentRound,
&intervalMinutes, &promptVariant,
&finalDecisionJSON, &session.AutoExecute, &traderID, &session.CreatedAt, &session.UpdatedAt,
&finalDecisionJSON, &session.AutoExecute, &traderID,
&enableOIRanking, &oiRankingLimit, &oiDuration, &session.CreatedAt, &session.UpdatedAt,
)
// Fallback to basic schema if new columns don't exist
@@ -316,6 +335,8 @@ func (s *DebateStore) GetSession(id string) (*DebateSession, error) {
// Set defaults for new fields
session.IntervalMinutes = 5
session.PromptVariant = "balanced"
session.OIRankingLimit = 10
session.OIDuration = "1h"
} else {
// Set defaults for nullable fields
session.IntervalMinutes = 5
@@ -329,6 +350,17 @@ func (s *DebateStore) GetSession(id string) (*DebateSession, error) {
if traderID.Valid {
session.TraderID = traderID.String
}
if enableOIRanking.Valid {
session.EnableOIRanking = enableOIRanking.Bool
}
session.OIRankingLimit = 10
if oiRankingLimit.Valid {
session.OIRankingLimit = int(oiRankingLimit.Int64)
}
session.OIDuration = "1h"
if oiDuration.Valid {
session.OIDuration = oiDuration.String
}
}
if finalDecisionJSON.Valid && finalDecisionJSON.String != "" {

View File

@@ -98,6 +98,11 @@ type IndicatorConfig struct {
QuantDataAPIURL string `json:"quant_data_api_url,omitempty"` // quantitative data API address
EnableQuantOI bool `json:"enable_quant_oi"` // whether to show OI data
EnableQuantNetflow bool `json:"enable_quant_netflow"` // whether to show Netflow data
// OI ranking data (market-wide open interest increase/decrease rankings)
EnableOIRanking bool `json:"enable_oi_ranking"` // whether to enable OI ranking data
OIRankingAPIURL string `json:"oi_ranking_api_url,omitempty"` // OI ranking API base URL
OIRankingDuration string `json:"oi_ranking_duration,omitempty"` // duration: 1h, 4h, 24h
OIRankingLimit int `json:"oi_ranking_limit,omitempty"` // number of entries (default 10)
}
// KlineConfig K-line configuration
@@ -246,6 +251,11 @@ func GetDefaultStrategyConfig(lang string) StrategyConfig {
QuantDataAPIURL: "http://nofxaios.com:30006/api/coin/{symbol}?include=netflow,oi,price&auth=cm_568c67eae410d912c54c",
EnableQuantOI: true,
EnableQuantNetflow: true,
// OI ranking data - market-wide OI increase/decrease rankings
EnableOIRanking: true,
OIRankingAPIURL: "http://nofxaios.com:30006",
OIRankingDuration: "1h",
OIRankingLimit: 10,
},
RiskControl: RiskControlConfig{
MaxPositions: 3, // Max 3 coins simultaneously (CODE ENFORCED)

View File

@@ -2,6 +2,7 @@ package store
import (
"database/sql"
"fmt"
"strings"
"time"
)
@@ -262,14 +263,25 @@ func (s *TraderStore) UpdateShowInCompetition(userID, id string, showInCompetiti
// Update updates trader configuration
func (s *TraderStore) Update(trader *Trader) error {
fmt.Printf("📝 TraderStore.Update: ID=%s, Name=%s, AIModelID=%s, StrategyID=%s\n",
trader.ID, trader.Name, trader.AIModelID, trader.StrategyID)
_, err := s.db.Exec(`
UPDATE traders SET
name = ?, ai_model_id = ?, exchange_id = ?, strategy_id = ?,
scan_interval_minutes = ?, is_cross_margin = ?, show_in_competition = ?,
name = ?,
ai_model_id = ?,
exchange_id = ?,
strategy_id = ?,
initial_balance = CASE WHEN ? > 0 THEN ? ELSE initial_balance END,
scan_interval_minutes = CASE WHEN ? > 0 THEN ? ELSE scan_interval_minutes END,
is_cross_margin = ?,
show_in_competition = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ? AND user_id = ?
`, trader.Name, trader.AIModelID, trader.ExchangeID, trader.StrategyID,
trader.ScanIntervalMinutes, trader.IsCrossMargin, trader.ShowInCompetition, trader.ID, trader.UserID)
trader.InitialBalance, trader.InitialBalance,
trader.ScanIntervalMinutes, trader.ScanIntervalMinutes,
trader.IsCrossMargin, trader.ShowInCompetition,
trader.ID, trader.UserID)
return err
}