mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
refactor: simplify config and remove unused database tables
- Remove system_config, beta_codes, signal_source tables and related code - Simplify config.go to only read from .env (APIServerPort, JWTSecret, RegistrationEnabled) - Remove GetCustomCoins, use all USDT perpetual contracts for WSMonitor - Add trader_equity_snapshots table for equity tracking - Remove signal source modal from frontend AITradersPage - Fix WSMonitor nil panic by restoring initialization in main.go
This commit is contained in:
+25
-191
@@ -76,10 +76,11 @@ type Statistics struct {
|
||||
TotalClosePositions int `json:"total_close_positions"`
|
||||
}
|
||||
|
||||
// initTables 初始化决策相关表
|
||||
// initTables 初始化 AI 决策日志表
|
||||
// 注意:账户净值曲线数据已迁移到 trader_equity_snapshots 表(由 EquityStore 管理)
|
||||
func (s *DecisionStore) initTables() error {
|
||||
queries := []string{
|
||||
// 决策记录主表
|
||||
// AI 决策日志表(记录 AI 的输入输出、思维链等)
|
||||
`CREATE TABLE IF NOT EXISTS decision_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
trader_id TEXT NOT NULL,
|
||||
@@ -96,58 +97,9 @@ func (s *DecisionStore) initTables() error {
|
||||
ai_request_duration_ms INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
|
||||
// 账户状态快照表
|
||||
`CREATE TABLE IF NOT EXISTS decision_account_snapshots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
decision_id INTEGER NOT NULL,
|
||||
total_balance REAL DEFAULT 0,
|
||||
available_balance REAL DEFAULT 0,
|
||||
total_unrealized_profit REAL DEFAULT 0,
|
||||
position_count INTEGER DEFAULT 0,
|
||||
margin_used_pct REAL DEFAULT 0,
|
||||
initial_balance REAL DEFAULT 0,
|
||||
FOREIGN KEY (decision_id) REFERENCES decision_records(id) ON DELETE CASCADE
|
||||
)`,
|
||||
|
||||
// 持仓快照表
|
||||
`CREATE TABLE IF NOT EXISTS decision_position_snapshots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
decision_id INTEGER NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
side TEXT DEFAULT '',
|
||||
position_amt REAL DEFAULT 0,
|
||||
entry_price REAL DEFAULT 0,
|
||||
mark_price REAL DEFAULT 0,
|
||||
unrealized_profit REAL DEFAULT 0,
|
||||
leverage REAL DEFAULT 0,
|
||||
liquidation_price REAL DEFAULT 0,
|
||||
FOREIGN KEY (decision_id) REFERENCES decision_records(id) ON DELETE CASCADE
|
||||
)`,
|
||||
|
||||
// 决策动作表(订单详情)
|
||||
`CREATE TABLE IF NOT EXISTS decision_actions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
decision_id INTEGER NOT NULL,
|
||||
trader_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
quantity REAL DEFAULT 0,
|
||||
leverage INTEGER DEFAULT 0,
|
||||
price REAL DEFAULT 0,
|
||||
order_id INTEGER DEFAULT 0,
|
||||
timestamp DATETIME NOT NULL,
|
||||
success BOOLEAN DEFAULT 0,
|
||||
error TEXT DEFAULT '',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (decision_id) REFERENCES decision_records(id) ON DELETE CASCADE
|
||||
)`,
|
||||
|
||||
// 索引
|
||||
`CREATE INDEX IF NOT EXISTS idx_decision_records_trader_time ON decision_records(trader_id, timestamp DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_decision_records_timestamp ON decision_records(timestamp DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_decision_actions_trader ON decision_actions(trader_id, timestamp DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_decision_actions_symbol ON decision_actions(symbol, timestamp DESC)`,
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
@@ -159,7 +111,7 @@ func (s *DecisionStore) initTables() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogDecision 记录决策
|
||||
// LogDecision 记录决策(仅保存 AI 决策日志,净值曲线已迁移到 equity 表)
|
||||
func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
|
||||
if record.Timestamp.IsZero() {
|
||||
record.Timestamp = time.Now().UTC()
|
||||
@@ -167,19 +119,12 @@ func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
|
||||
record.Timestamp = record.Timestamp.UTC()
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("开始事务失败: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// 序列化候选币种和执行日志为 JSON
|
||||
candidateCoinsJSON, _ := json.Marshal(record.CandidateCoins)
|
||||
executionLogJSON, _ := json.Marshal(record.ExecutionLog)
|
||||
|
||||
// 插入决策记录主表
|
||||
result, err := tx.Exec(`
|
||||
// 插入决策记录主表(仅保存 AI 决策相关内容)
|
||||
result, err := s.db.Exec(`
|
||||
INSERT INTO decision_records (
|
||||
trader_id, cycle_number, timestamp, system_prompt, input_prompt,
|
||||
cot_trace, decision_json, candidate_coins, execution_log,
|
||||
@@ -201,63 +146,6 @@ func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
|
||||
}
|
||||
record.ID = decisionID
|
||||
|
||||
// 插入账户状态快照
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO decision_account_snapshots (
|
||||
decision_id, total_balance, available_balance, total_unrealized_profit,
|
||||
position_count, margin_used_pct, initial_balance
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
decisionID, record.AccountState.TotalBalance, record.AccountState.AvailableBalance,
|
||||
record.AccountState.TotalUnrealizedProfit, record.AccountState.PositionCount,
|
||||
record.AccountState.MarginUsedPct, record.AccountState.InitialBalance,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("插入账户快照失败: %w", err)
|
||||
}
|
||||
|
||||
// 插入持仓快照
|
||||
for _, pos := range record.Positions {
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO decision_position_snapshots (
|
||||
decision_id, symbol, side, position_amt, entry_price,
|
||||
mark_price, unrealized_profit, leverage, liquidation_price
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
decisionID, pos.Symbol, pos.Side, pos.PositionAmt, pos.EntryPrice,
|
||||
pos.MarkPrice, pos.UnrealizedProfit, pos.Leverage, pos.LiquidationPrice,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("插入持仓快照失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 插入决策动作(订单详情)
|
||||
for _, action := range record.Decisions {
|
||||
actionTimestamp := action.Timestamp
|
||||
if actionTimestamp.IsZero() {
|
||||
actionTimestamp = record.Timestamp
|
||||
}
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO decision_actions (
|
||||
decision_id, trader_id, action, symbol, quantity, leverage,
|
||||
price, order_id, timestamp, success, error
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
decisionID, record.TraderID, action.Action, action.Symbol, action.Quantity,
|
||||
action.Leverage, action.Price, action.OrderID,
|
||||
actionTimestamp.Format(time.RFC3339), action.Success, action.Error,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("插入决策动作失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("提交事务失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -394,21 +282,17 @@ func (s *DecisionStore) GetStatistics(traderID string) (*Statistics, error) {
|
||||
}
|
||||
stats.FailedCycles = stats.TotalCycles - stats.SuccessfulCycles
|
||||
|
||||
err = s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM decision_actions
|
||||
WHERE trader_id = ? AND success = 1 AND action IN ('open_long', 'open_short')
|
||||
// 从 trader_orders 表统计开仓次数
|
||||
s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM trader_orders
|
||||
WHERE trader_id = ? AND status = 'FILLED' AND action IN ('open_long', 'open_short')
|
||||
`, traderID).Scan(&stats.TotalOpenPositions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询开仓次数失败: %w", err)
|
||||
}
|
||||
|
||||
err = s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM decision_actions
|
||||
WHERE trader_id = ? AND success = 1 AND action IN ('close_long', 'close_short', 'auto_close_long', 'auto_close_short')
|
||||
// 从 trader_orders 表统计平仓次数
|
||||
s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM trader_orders
|
||||
WHERE trader_id = ? AND status = 'FILLED' AND action IN ('close_long', 'close_short', 'auto_close_long', 'auto_close_short')
|
||||
`, traderID).Scan(&stats.TotalClosePositions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询平仓次数失败: %w", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
@@ -421,14 +305,15 @@ func (s *DecisionStore) GetAllStatistics() (*Statistics, error) {
|
||||
s.db.QueryRow(`SELECT COUNT(*) FROM decision_records WHERE success = 1`).Scan(&stats.SuccessfulCycles)
|
||||
stats.FailedCycles = stats.TotalCycles - stats.SuccessfulCycles
|
||||
|
||||
// 从 trader_orders 表统计
|
||||
s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM decision_actions
|
||||
WHERE success = 1 AND action IN ('open_long', 'open_short')
|
||||
SELECT COUNT(*) FROM trader_orders
|
||||
WHERE status = 'FILLED' AND action IN ('open_long', 'open_short')
|
||||
`).Scan(&stats.TotalOpenPositions)
|
||||
|
||||
s.db.QueryRow(`
|
||||
SELECT COUNT(*) FROM decision_actions
|
||||
WHERE success = 1 AND action IN ('close_long', 'close_short', 'auto_close_long', 'auto_close_short')
|
||||
SELECT COUNT(*) FROM trader_orders
|
||||
WHERE status = 'FILLED' AND action IN ('close_long', 'close_short', 'auto_close_long', 'auto_close_short')
|
||||
`).Scan(&stats.TotalClosePositions)
|
||||
|
||||
return stats, nil
|
||||
@@ -469,62 +354,11 @@ func (s *DecisionStore) scanDecisionRecord(rows *sql.Rows) (*DecisionRecord, err
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// fillRecordDetails 填充决策记录的关联数据
|
||||
// fillRecordDetails 填充决策记录的关联数据(旧的关联表已删除,此函数保留用于兼容性)
|
||||
// 注意:账户快照、持仓快照、决策动作等数据已不再存储在 decision 相关表中
|
||||
// - 净值数据请使用 EquityStore.GetLatest()
|
||||
// - 订单数据请使用 OrderStore
|
||||
func (s *DecisionStore) fillRecordDetails(record *DecisionRecord) {
|
||||
// 查询账户状态
|
||||
s.db.QueryRow(`
|
||||
SELECT total_balance, available_balance, total_unrealized_profit,
|
||||
position_count, margin_used_pct, initial_balance
|
||||
FROM decision_account_snapshots
|
||||
WHERE decision_id = ?
|
||||
`, record.ID).Scan(
|
||||
&record.AccountState.TotalBalance,
|
||||
&record.AccountState.AvailableBalance,
|
||||
&record.AccountState.TotalUnrealizedProfit,
|
||||
&record.AccountState.PositionCount,
|
||||
&record.AccountState.MarginUsedPct,
|
||||
&record.AccountState.InitialBalance,
|
||||
)
|
||||
|
||||
// 查询持仓快照
|
||||
posRows, err := s.db.Query(`
|
||||
SELECT symbol, side, position_amt, entry_price, mark_price,
|
||||
unrealized_profit, leverage, liquidation_price
|
||||
FROM decision_position_snapshots
|
||||
WHERE decision_id = ?
|
||||
`, record.ID)
|
||||
if err == nil {
|
||||
defer posRows.Close()
|
||||
for posRows.Next() {
|
||||
var pos PositionSnapshot
|
||||
posRows.Scan(
|
||||
&pos.Symbol, &pos.Side, &pos.PositionAmt, &pos.EntryPrice,
|
||||
&pos.MarkPrice, &pos.UnrealizedProfit, &pos.Leverage,
|
||||
&pos.LiquidationPrice,
|
||||
)
|
||||
record.Positions = append(record.Positions, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询决策动作
|
||||
actionRows, err := s.db.Query(`
|
||||
SELECT action, symbol, quantity, leverage, price, order_id,
|
||||
timestamp, success, error
|
||||
FROM decision_actions
|
||||
WHERE decision_id = ?
|
||||
`, record.ID)
|
||||
if err == nil {
|
||||
defer actionRows.Close()
|
||||
for actionRows.Next() {
|
||||
var action DecisionAction
|
||||
var timestampStr string
|
||||
actionRows.Scan(
|
||||
&action.Action, &action.Symbol, &action.Quantity,
|
||||
&action.Leverage, &action.Price, &action.OrderID,
|
||||
×tampStr, &action.Success, &action.Error,
|
||||
)
|
||||
action.Timestamp, _ = time.Parse(time.RFC3339, timestampStr)
|
||||
record.Decisions = append(record.Decisions, action)
|
||||
}
|
||||
}
|
||||
// 旧的关联表已删除,不再需要填充
|
||||
// AccountState, Positions, Decisions 字段将保持为零值
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user