refactor: standardize code comments

This commit is contained in:
tinkle-community
2025-12-08 01:40:48 +08:00
parent 0636ced476
commit a12c0ae8c9
103 changed files with 5466 additions and 5468 deletions

View File

@@ -9,14 +9,14 @@ import (
"time"
)
// AIModelStore AI模型存储
// AIModelStore AI model storage
type AIModelStore struct {
db *sql.DB
encryptFunc func(string) string
decryptFunc func(string) string
}
// AIModel AI模型配置
// AIModel AI model configuration
type AIModel struct {
ID string `json:"id"`
UserID string `json:"user_id"`
@@ -49,7 +49,7 @@ func (s *AIModelStore) initTables() error {
return err
}
// 触发器
// Trigger
_, err = s.db.Exec(`
CREATE TRIGGER IF NOT EXISTS update_ai_models_updated_at
AFTER UPDATE ON ai_models
@@ -61,7 +61,7 @@ func (s *AIModelStore) initTables() error {
return err
}
// 向后兼容:添加可能缺失的列
// Backward compatibility: add potentially missing columns
s.db.Exec(`ALTER TABLE ai_models ADD COLUMN custom_api_url TEXT DEFAULT ''`)
s.db.Exec(`ALTER TABLE ai_models ADD COLUMN custom_model_name TEXT DEFAULT ''`)
@@ -82,7 +82,7 @@ func (s *AIModelStore) initDefaultData() error {
VALUES (?, 'default', ?, ?, 0)
`, model.id, model.name, model.provider)
if err != nil {
return fmt.Errorf("初始化AI模型失败: %w", err)
return fmt.Errorf("failed to initialize AI model: %w", err)
}
}
return nil
@@ -102,7 +102,7 @@ func (s *AIModelStore) decrypt(encrypted string) string {
return encrypted
}
// List 获取用户的AI模型列表
// List retrieves user's AI model list
func (s *AIModelStore) List(userID string) ([]*AIModel, error) {
rows, err := s.db.Query(`
SELECT id, user_id, name, provider, enabled, api_key,
@@ -136,10 +136,10 @@ func (s *AIModelStore) List(userID string) ([]*AIModel, error) {
return models, nil
}
// Get 获取单个AI模型
// Get retrieves a single AI model
func (s *AIModelStore) Get(userID, modelID string) (*AIModel, error) {
if modelID == "" {
return nil, fmt.Errorf("模型ID不能为空")
return nil, fmt.Errorf("model ID cannot be empty")
}
candidates := []string{}
@@ -178,7 +178,7 @@ func (s *AIModelStore) Get(userID, modelID string) (*AIModel, error) {
return nil, sql.ErrNoRows
}
// GetDefault 获取默认启用的AI模型
// GetDefault retrieves the default enabled AI model
func (s *AIModelStore) GetDefault(userID string) (*AIModel, error) {
if userID == "" {
userID = "default"
@@ -193,7 +193,7 @@ func (s *AIModelStore) GetDefault(userID string) (*AIModel, error) {
if userID != "default" {
return s.firstEnabled("default")
}
return nil, fmt.Errorf("请先在系统中配置可用的AI模型")
return nil, fmt.Errorf("please configure an available AI model in the system first")
}
func (s *AIModelStore) firstEnabled(userID string) (*AIModel, error) {
@@ -218,9 +218,9 @@ func (s *AIModelStore) firstEnabled(userID string) (*AIModel, error) {
return &model, nil
}
// Update 更新AI模型不存在则创建
// Update updates AI model, creates if not exists
func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPIURL, customModelName string) error {
// 先尝试精确匹配ID
// Try exact ID match first
var existingID string
err := s.db.QueryRow(`SELECT id FROM ai_models WHERE user_id = ? AND id = ? LIMIT 1`, userID, id).Scan(&existingID)
if err == nil {
@@ -232,11 +232,11 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
return err
}
// 尝试兼容旧逻辑将id作为provider查找
// Try legacy logic compatibility: use id as provider to search
provider := id
err = s.db.QueryRow(`SELECT id FROM ai_models WHERE user_id = ? AND provider = ? LIMIT 1`, userID, provider).Scan(&existingID)
if err == nil {
logger.Warnf("⚠️ 使用旧版 provider 匹配更新模型: %s -> %s", provider, existingID)
logger.Warnf("⚠️ Using legacy provider matching to update model: %s -> %s", provider, existingID)
encryptedAPIKey := s.encrypt(apiKey)
_, err = s.db.Exec(`
UPDATE ai_models SET enabled = ?, api_key = ?, custom_api_url = ?, custom_model_name = ?, updated_at = datetime('now')
@@ -245,7 +245,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
return err
}
// 创建新记录
// Create new record
if provider == id && (provider == "deepseek" || provider == "qwen") {
provider = id
} else {
@@ -274,7 +274,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
newModelID = fmt.Sprintf("%s_%s", userID, provider)
}
logger.Infof("✓ 创建新的 AI 模型配置: ID=%s, Provider=%s, Name=%s", newModelID, provider, name)
logger.Infof("✓ Creating new AI model configuration: ID=%s, Provider=%s, Name=%s", newModelID, provider, name)
encryptedAPIKey := s.encrypt(apiKey)
_, err = s.db.Exec(`
INSERT INTO ai_models (id, user_id, name, provider, enabled, api_key, custom_api_url, custom_model_name, created_at, updated_at)
@@ -283,7 +283,7 @@ func (s *AIModelStore) Update(userID, id string, enabled bool, apiKey, customAPI
return err
}
// Create 创建AI模型
// Create creates an AI model
func (s *AIModelStore) Create(userID, id, name, provider string, enabled bool, apiKey, customAPIURL string) error {
_, err := s.db.Exec(`
INSERT OR IGNORE INTO ai_models (id, user_id, name, provider, enabled, api_key, custom_api_url)

View File

@@ -7,12 +7,12 @@ import (
"time"
)
// BacktestStore 回测数据存储
// BacktestStore backtest data storage
type BacktestStore struct {
db *sql.DB
}
// RunState 回测状态
// RunState backtest state
type RunState string
const (
@@ -23,7 +23,7 @@ const (
RunStateFailed RunState = "failed"
)
// RunMetadata 回测元数据
// RunMetadata backtest metadata
type RunMetadata struct {
RunID string `json:"run_id"`
UserID string `json:"user_id"`
@@ -36,7 +36,7 @@ type RunMetadata struct {
UpdatedAt time.Time `json:"updated_at"`
}
// RunSummary 回测摘要
// RunSummary backtest summary
type RunSummary struct {
SymbolCount int `json:"symbol_count"`
DecisionTF string `json:"decision_tf"`
@@ -48,7 +48,7 @@ type RunSummary struct {
LiquidationNote string `json:"liquidation_note"`
}
// EquityPoint 权益点
// EquityPoint equity point
type EquityPoint struct {
Timestamp int64 `json:"timestamp"`
Equity float64 `json:"equity"`
@@ -59,7 +59,7 @@ type EquityPoint struct {
Cycle int `json:"cycle"`
}
// TradeEvent 交易事件
// TradeEvent trade event
type TradeEvent struct {
Timestamp int64 `json:"timestamp"`
Symbol string `json:"symbol"`
@@ -78,7 +78,7 @@ type TradeEvent struct {
Note string `json:"note"`
}
// RunIndexEntry 回测索引条目
// RunIndexEntry backtest index entry
type RunIndexEntry struct {
RunID string `json:"run_id"`
State string `json:"state"`
@@ -92,10 +92,10 @@ type RunIndexEntry struct {
UpdatedAtISO string `json:"updated_at"`
}
// initTables 初始化回测相关表
// initTables initializes backtest related tables
func (s *BacktestStore) initTables() error {
queries := []string{
// 回测运行主表
// Backtest runs main table
`CREATE TABLE IF NOT EXISTS backtest_runs (
run_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL DEFAULT '',
@@ -120,7 +120,7 @@ func (s *BacktestStore) initTables() error {
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
// 回测检查点
// Backtest checkpoints
`CREATE TABLE IF NOT EXISTS backtest_checkpoints (
run_id TEXT PRIMARY KEY,
payload BLOB NOT NULL,
@@ -128,7 +128,7 @@ func (s *BacktestStore) initTables() error {
FOREIGN KEY (run_id) REFERENCES backtest_runs(run_id) ON DELETE CASCADE
)`,
// 回测权益曲线
// Backtest equity curve
`CREATE TABLE IF NOT EXISTS backtest_equity (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
@@ -142,7 +142,7 @@ func (s *BacktestStore) initTables() error {
FOREIGN KEY (run_id) REFERENCES backtest_runs(run_id) ON DELETE CASCADE
)`,
// 回测交易记录
// Backtest trade records
`CREATE TABLE IF NOT EXISTS backtest_trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
@@ -164,7 +164,7 @@ func (s *BacktestStore) initTables() error {
FOREIGN KEY (run_id) REFERENCES backtest_runs(run_id) ON DELETE CASCADE
)`,
// 回测指标
// Backtest metrics
`CREATE TABLE IF NOT EXISTS backtest_metrics (
run_id TEXT PRIMARY KEY,
payload BLOB NOT NULL,
@@ -172,7 +172,7 @@ func (s *BacktestStore) initTables() error {
FOREIGN KEY (run_id) REFERENCES backtest_runs(run_id) ON DELETE CASCADE
)`,
// 回测决策日志
// Backtest decision logs
`CREATE TABLE IF NOT EXISTS backtest_decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
@@ -182,7 +182,7 @@ func (s *BacktestStore) initTables() error {
FOREIGN KEY (run_id) REFERENCES backtest_runs(run_id) ON DELETE CASCADE
)`,
// 索引
// Indexes
`CREATE INDEX IF NOT EXISTS idx_backtest_runs_state ON backtest_runs(state, updated_at)`,
`CREATE INDEX IF NOT EXISTS idx_backtest_equity_run_ts ON backtest_equity(run_id, ts)`,
`CREATE INDEX IF NOT EXISTS idx_backtest_trades_run_ts ON backtest_trades(run_id, ts)`,
@@ -191,11 +191,11 @@ func (s *BacktestStore) initTables() error {
for _, query := range queries {
if _, err := s.db.Exec(query); err != nil {
return fmt.Errorf("执行SQL失败: %w", err)
return fmt.Errorf("failed to execute SQL: %w", err)
}
}
// 添加可能缺失的列(向后兼容)
// Add potentially missing columns (backward compatibility)
s.addColumnIfNotExists("backtest_runs", "label", "TEXT DEFAULT ''")
s.addColumnIfNotExists("backtest_runs", "last_error", "TEXT DEFAULT ''")
s.addColumnIfNotExists("backtest_trades", "leverage", "INTEGER DEFAULT 0")
@@ -219,14 +219,14 @@ func (s *BacktestStore) addColumnIfNotExists(table, column, definition string) {
continue
}
if name == column {
return // 列已存在
return // Column already exists
}
}
s.db.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s", table, column, definition))
}
// SaveCheckpoint 保存检查点
// SaveCheckpoint saves checkpoint
func (s *BacktestStore) SaveCheckpoint(runID string, payload []byte) error {
_, err := s.db.Exec(`
INSERT INTO backtest_checkpoints (run_id, payload, updated_at)
@@ -236,14 +236,14 @@ func (s *BacktestStore) SaveCheckpoint(runID string, payload []byte) error {
return err
}
// LoadCheckpoint 加载检查点
// LoadCheckpoint loads checkpoint
func (s *BacktestStore) LoadCheckpoint(runID string) ([]byte, error) {
var payload []byte
err := s.db.QueryRow(`SELECT payload FROM backtest_checkpoints WHERE run_id = ?`, runID).Scan(&payload)
return payload, err
}
// SaveRunMetadata 保存运行元数据
// SaveRunMetadata saves run metadata
func (s *BacktestStore) SaveRunMetadata(meta *RunMetadata) error {
created := meta.CreatedAt.UTC().Format(time.RFC3339)
updated := meta.UpdatedAt.UTC().Format(time.RFC3339)
@@ -270,7 +270,7 @@ func (s *BacktestStore) SaveRunMetadata(meta *RunMetadata) error {
return err
}
// LoadRunMetadata 加载运行元数据
// LoadRunMetadata loads run metadata
func (s *BacktestStore) LoadRunMetadata(runID string) (*RunMetadata, error) {
var (
userID string
@@ -326,7 +326,7 @@ func (s *BacktestStore) LoadRunMetadata(runID string) (*RunMetadata, error) {
return meta, nil
}
// ListRunIDs 列出所有运行ID
// ListRunIDs lists all run IDs
func (s *BacktestStore) ListRunIDs() ([]string, error) {
rows, err := s.db.Query(`SELECT run_id FROM backtest_runs ORDER BY datetime(updated_at) DESC`)
if err != nil {
@@ -345,7 +345,7 @@ func (s *BacktestStore) ListRunIDs() ([]string, error) {
return ids, rows.Err()
}
// AppendEquityPoint 添加权益点
// AppendEquityPoint appends equity point
func (s *BacktestStore) AppendEquityPoint(runID string, point EquityPoint) error {
_, err := s.db.Exec(`
INSERT INTO backtest_equity (run_id, ts, equity, available, pnl, pnl_pct, dd_pct, cycle)
@@ -355,7 +355,7 @@ func (s *BacktestStore) AppendEquityPoint(runID string, point EquityPoint) error
return err
}
// LoadEquityPoints 加载权益点
// LoadEquityPoints loads equity points
func (s *BacktestStore) LoadEquityPoints(runID string) ([]EquityPoint, error) {
rows, err := s.db.Query(`
SELECT ts, equity, available, pnl, pnl_pct, dd_pct, cycle
@@ -378,7 +378,7 @@ func (s *BacktestStore) LoadEquityPoints(runID string) ([]EquityPoint, error) {
return points, rows.Err()
}
// AppendTradeEvent 添加交易事件
// AppendTradeEvent appends trade event
func (s *BacktestStore) AppendTradeEvent(runID string, event TradeEvent) error {
_, err := s.db.Exec(`
INSERT INTO backtest_trades (run_id, ts, symbol, action, side, qty, price, fee,
@@ -391,7 +391,7 @@ func (s *BacktestStore) AppendTradeEvent(runID string, event TradeEvent) error {
return err
}
// LoadTradeEvents 加载交易事件
// LoadTradeEvents loads trade events
func (s *BacktestStore) LoadTradeEvents(runID string) ([]TradeEvent, error) {
rows, err := s.db.Query(`
SELECT ts, symbol, action, side, qty, price, fee, slippage, order_value,
@@ -417,7 +417,7 @@ func (s *BacktestStore) LoadTradeEvents(runID string) ([]TradeEvent, error) {
return events, rows.Err()
}
// SaveMetrics 保存指标
// SaveMetrics saves metrics
func (s *BacktestStore) SaveMetrics(runID string, payload []byte) error {
_, err := s.db.Exec(`
INSERT INTO backtest_metrics (run_id, payload, updated_at)
@@ -427,14 +427,14 @@ func (s *BacktestStore) SaveMetrics(runID string, payload []byte) error {
return err
}
// LoadMetrics 加载指标
// LoadMetrics loads metrics
func (s *BacktestStore) LoadMetrics(runID string) ([]byte, error) {
var payload []byte
err := s.db.QueryRow(`SELECT payload FROM backtest_metrics WHERE run_id = ?`, runID).Scan(&payload)
return payload, err
}
// SaveDecisionRecord 保存决策记录
// SaveDecisionRecord saves decision record
func (s *BacktestStore) SaveDecisionRecord(runID string, cycle int, payload []byte) error {
_, err := s.db.Exec(`
INSERT INTO backtest_decisions (run_id, cycle, payload)
@@ -443,7 +443,7 @@ func (s *BacktestStore) SaveDecisionRecord(runID string, cycle int, payload []by
return err
}
// LoadDecisionRecords 加载决策记录
// LoadDecisionRecords loads decision records
func (s *BacktestStore) LoadDecisionRecords(runID string, limit, offset int) ([]json.RawMessage, error) {
rows, err := s.db.Query(`
SELECT payload FROM backtest_decisions
@@ -467,7 +467,7 @@ func (s *BacktestStore) LoadDecisionRecords(runID string, limit, offset int) ([]
return records, rows.Err()
}
// LoadLatestDecision 加载最新决策
// LoadLatestDecision loads latest decision
func (s *BacktestStore) LoadLatestDecision(runID string, cycle int) ([]byte, error) {
var query string
var args []interface{}
@@ -485,7 +485,7 @@ func (s *BacktestStore) LoadLatestDecision(runID string, cycle int) ([]byte, err
return payload, err
}
// UpdateProgress 更新进度
// UpdateProgress updates progress
func (s *BacktestStore) UpdateProgress(runID string, progressPct, equity float64, barIndex int, liquidated bool) error {
_, err := s.db.Exec(`
UPDATE backtest_runs
@@ -495,7 +495,7 @@ func (s *BacktestStore) UpdateProgress(runID string, progressPct, equity float64
return err
}
// ListIndexEntries 列出索引条目
// ListIndexEntries lists index entries
func (s *BacktestStore) ListIndexEntries() ([]RunIndexEntry, error) {
rows, err := s.db.Query(`
SELECT run_id, state, symbol_count, decision_tf, equity_last, max_drawdown_pct,
@@ -524,7 +524,7 @@ func (s *BacktestStore) ListIndexEntries() ([]RunIndexEntry, error) {
entry.UpdatedAtISO = updatedISO
entry.Symbols = make([]string, 0, symbolCnt)
// 尝试从配置中提取更多信息
// Try to extract more information from config
if len(cfgJSON) > 0 {
var cfg struct {
Symbols []string `json:"symbols"`
@@ -543,13 +543,13 @@ func (s *BacktestStore) ListIndexEntries() ([]RunIndexEntry, error) {
return entries, rows.Err()
}
// DeleteRun 删除运行
// DeleteRun deletes run
func (s *BacktestStore) DeleteRun(runID string) error {
_, err := s.db.Exec(`DELETE FROM backtest_runs WHERE run_id = ?`, runID)
return err
}
// SaveConfig 保存配置
// SaveConfig saves config
func (s *BacktestStore) SaveConfig(runID, userID, template, customPrompt, provider, model string, override bool, configJSON []byte) error {
now := time.Now().UTC().Format(time.RFC3339)
if userID == "" {
@@ -575,7 +575,7 @@ func (s *BacktestStore) SaveConfig(runID, userID, template, customPrompt, provid
return err
}
// LoadConfig 加载配置
// LoadConfig loads config
func (s *BacktestStore) LoadConfig(runID string) ([]byte, error) {
var payload []byte
err := s.db.QueryRow(`SELECT config_json FROM backtest_runs WHERE run_id = ?`, runID).Scan(&payload)

View File

@@ -7,12 +7,12 @@ import (
"time"
)
// DecisionStore 决策日志存储
// DecisionStore decision log storage
type DecisionStore struct {
db *sql.DB
}
// DecisionRecord 决策记录
// DecisionRecord decision record
type DecisionRecord struct {
ID int64 `json:"id"`
TraderID string `json:"trader_id"`
@@ -32,7 +32,7 @@ type DecisionRecord struct {
Decisions []DecisionAction `json:"decisions"`
}
// AccountSnapshot 账户状态快照
// AccountSnapshot account state snapshot
type AccountSnapshot struct {
TotalBalance float64 `json:"total_balance"`
AvailableBalance float64 `json:"available_balance"`
@@ -42,7 +42,7 @@ type AccountSnapshot struct {
InitialBalance float64 `json:"initial_balance"`
}
// PositionSnapshot 持仓快照
// PositionSnapshot position snapshot
type PositionSnapshot struct {
Symbol string `json:"symbol"`
Side string `json:"side"`
@@ -54,8 +54,8 @@ type PositionSnapshot struct {
LiquidationPrice float64 `json:"liquidation_price"`
}
// DecisionAction 决策动作
type DecisionAction struct {
// DecisionAction decision action
type DecisionAction struct{
Action string `json:"action"`
Symbol string `json:"symbol"`
Quantity float64 `json:"quantity"`
@@ -67,7 +67,7 @@ type DecisionAction struct {
Error string `json:"error"`
}
// Statistics 统计信息
// Statistics statistics information
type Statistics struct {
TotalCycles int `json:"total_cycles"`
SuccessfulCycles int `json:"successful_cycles"`
@@ -76,11 +76,11 @@ type Statistics struct {
TotalClosePositions int `json:"total_close_positions"`
}
// initTables 初始化 AI 决策日志表
// 注意:账户净值曲线数据已迁移到 trader_equity_snapshots 表(由 EquityStore 管理)
// initTables initializes AI decision log tables
// Note: Account equity curve data has been migrated to trader_equity_snapshots table (managed by EquityStore)
func (s *DecisionStore) initTables() error {
queries := []string{
// AI 决策日志表(记录 AI 的输入输出、思维链等)
// AI decision log table (records AI input/output, chain of thought, etc.)
`CREATE TABLE IF NOT EXISTS decision_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trader_id TEXT NOT NULL,
@@ -97,21 +97,21 @@ func (s *DecisionStore) initTables() error {
ai_request_duration_ms INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
// 索引
// Indexes
`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)`,
}
for _, query := range queries {
if _, err := s.db.Exec(query); err != nil {
return fmt.Errorf("执行SQL失败: %w", err)
return fmt.Errorf("failed to execute SQL: %w", err)
}
}
return nil
}
// LogDecision 记录决策(仅保存 AI 决策日志,净值曲线已迁移到 equity 表)
// LogDecision logs decision (only saves AI decision log, equity curve has been migrated to equity table)
func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
if record.Timestamp.IsZero() {
record.Timestamp = time.Now().UTC()
@@ -119,11 +119,11 @@ func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
record.Timestamp = record.Timestamp.UTC()
}
// 序列化候选币种和执行日志为 JSON
// Serialize candidate coins and execution log to JSON
candidateCoinsJSON, _ := json.Marshal(record.CandidateCoins)
executionLogJSON, _ := json.Marshal(record.ExecutionLog)
// 插入决策记录主表(仅保存 AI 决策相关内容)
// Insert decision record main table (only save AI decision related content)
result, err := s.db.Exec(`
INSERT INTO decision_records (
trader_id, cycle_number, timestamp, system_prompt, input_prompt,
@@ -137,19 +137,19 @@ func (s *DecisionStore) LogDecision(record *DecisionRecord) error {
record.Success, record.ErrorMessage, record.AIRequestDurationMs,
)
if err != nil {
return fmt.Errorf("插入决策记录失败: %w", err)
return fmt.Errorf("failed to insert decision record: %w", err)
}
decisionID, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("获取决策ID失败: %w", err)
return fmt.Errorf("failed to get decision ID: %w", err)
}
record.ID = decisionID
return nil
}
// GetLatestRecords 获取指定交易员最近N条记录按时间正序从旧到新
// GetLatestRecords gets the latest N records for specified trader (sorted by time in ascending order: old to new)
func (s *DecisionStore) GetLatestRecords(traderID string, n int) ([]*DecisionRecord, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, cycle_number, timestamp, system_prompt, input_prompt,
@@ -161,7 +161,7 @@ func (s *DecisionStore) GetLatestRecords(traderID string, n int) ([]*DecisionRec
LIMIT ?
`, traderID, n)
if err != nil {
return nil, fmt.Errorf("查询决策记录失败: %w", err)
return nil, fmt.Errorf("failed to query decision records: %w", err)
}
defer rows.Close()
@@ -174,12 +174,12 @@ func (s *DecisionStore) GetLatestRecords(traderID string, n int) ([]*DecisionRec
records = append(records, record)
}
// 填充关联数据
// Fill associated data
for _, record := range records {
s.fillRecordDetails(record)
}
// 反转数组,让时间从旧到新排列
// Reverse array to sort time from old to new
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
records[i], records[j] = records[j], records[i]
}
@@ -187,7 +187,7 @@ func (s *DecisionStore) GetLatestRecords(traderID string, n int) ([]*DecisionRec
return records, nil
}
// GetAllLatestRecords 获取所有交易员最近N条记录
// GetAllLatestRecords gets the latest N records for all traders
func (s *DecisionStore) GetAllLatestRecords(n int) ([]*DecisionRecord, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, cycle_number, timestamp, system_prompt, input_prompt,
@@ -198,7 +198,7 @@ func (s *DecisionStore) GetAllLatestRecords(n int) ([]*DecisionRecord, error) {
LIMIT ?
`, n)
if err != nil {
return nil, fmt.Errorf("查询决策记录失败: %w", err)
return nil, fmt.Errorf("failed to query decision records: %w", err)
}
defer rows.Close()
@@ -211,7 +211,7 @@ func (s *DecisionStore) GetAllLatestRecords(n int) ([]*DecisionRecord, error) {
records = append(records, record)
}
// 反转数组
// Reverse array
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
records[i], records[j] = records[j], records[i]
}
@@ -219,7 +219,7 @@ func (s *DecisionStore) GetAllLatestRecords(n int) ([]*DecisionRecord, error) {
return records, nil
}
// GetRecordsByDate 获取指定交易员指定日期的所有记录
// GetRecordsByDate gets all records for a specified trader on a specified date
func (s *DecisionStore) GetRecordsByDate(traderID string, date time.Time) ([]*DecisionRecord, error) {
dateStr := date.Format("2006-01-02")
@@ -232,7 +232,7 @@ func (s *DecisionStore) GetRecordsByDate(traderID string, date time.Time) ([]*De
ORDER BY timestamp ASC
`, traderID, dateStr)
if err != nil {
return nil, fmt.Errorf("查询决策记录失败: %w", err)
return nil, fmt.Errorf("failed to query decision records: %w", err)
}
defer rows.Close()
@@ -248,7 +248,7 @@ func (s *DecisionStore) GetRecordsByDate(traderID string, date time.Time) ([]*De
return records, nil
}
// CleanOldRecords 清理N天前的旧记录
// CleanOldRecords cleans old records from N days ago
func (s *DecisionStore) CleanOldRecords(traderID string, days int) (int64, error) {
cutoffTime := time.Now().AddDate(0, 0, -days).Format(time.RFC3339)
@@ -257,13 +257,13 @@ func (s *DecisionStore) CleanOldRecords(traderID string, days int) (int64, error
WHERE trader_id = ? AND timestamp < ?
`, traderID, cutoffTime)
if err != nil {
return 0, fmt.Errorf("清理旧记录失败: %w", err)
return 0, fmt.Errorf("failed to clean old records: %w", err)
}
return result.RowsAffected()
}
// GetStatistics 获取指定交易员的统计信息
// GetStatistics gets statistics information for specified trader
func (s *DecisionStore) GetStatistics(traderID string) (*Statistics, error) {
stats := &Statistics{}
@@ -271,24 +271,24 @@ func (s *DecisionStore) GetStatistics(traderID string) (*Statistics, error) {
SELECT COUNT(*) FROM decision_records WHERE trader_id = ?
`, traderID).Scan(&stats.TotalCycles)
if err != nil {
return nil, fmt.Errorf("查询总周期数失败: %w", err)
return nil, fmt.Errorf("failed to query total cycles: %w", err)
}
err = s.db.QueryRow(`
SELECT COUNT(*) FROM decision_records WHERE trader_id = ? AND success = 1
`, traderID).Scan(&stats.SuccessfulCycles)
if err != nil {
return nil, fmt.Errorf("查询成功周期数失败: %w", err)
return nil, fmt.Errorf("failed to query successful cycles: %w", err)
}
stats.FailedCycles = stats.TotalCycles - stats.SuccessfulCycles
// trader_orders 表统计开仓次数
// Count open positions from trader_orders table
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)
// trader_orders 表统计平仓次数
// Count close positions from trader_orders table
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')
@@ -297,7 +297,7 @@ func (s *DecisionStore) GetStatistics(traderID string) (*Statistics, error) {
return stats, nil
}
// GetAllStatistics 获取所有交易员的统计信息
// GetAllStatistics gets statistics information for all traders
func (s *DecisionStore) GetAllStatistics() (*Statistics, error) {
stats := &Statistics{}
@@ -305,7 +305,7 @@ 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 表统计
// Count from trader_orders table
s.db.QueryRow(`
SELECT COUNT(*) FROM trader_orders
WHERE status = 'FILLED' AND action IN ('open_long', 'open_short')
@@ -319,7 +319,7 @@ func (s *DecisionStore) GetAllStatistics() (*Statistics, error) {
return stats, nil
}
// GetLastCycleNumber 获取指定交易员的最后周期编号
// GetLastCycleNumber gets the last cycle number for specified trader
func (s *DecisionStore) GetLastCycleNumber(traderID string) (int, error) {
var cycleNumber int
err := s.db.QueryRow(`
@@ -331,7 +331,7 @@ func (s *DecisionStore) GetLastCycleNumber(traderID string) (int, error) {
return cycleNumber, nil
}
// scanDecisionRecord 从行中扫描决策记录
// scanDecisionRecord scans decision record from row
func (s *DecisionStore) scanDecisionRecord(rows *sql.Rows) (*DecisionRecord, error) {
var record DecisionRecord
var timestampStr string
@@ -354,11 +354,11 @@ func (s *DecisionStore) scanDecisionRecord(rows *sql.Rows) (*DecisionRecord, err
return &record, nil
}
// fillRecordDetails 填充决策记录的关联数据(旧的关联表已删除,此函数保留用于兼容性)
// 注意:账户快照、持仓快照、决策动作等数据已不再存储在 decision 相关表中
// - 净值数据请使用 EquityStore.GetLatest()
// - 订单数据请使用 OrderStore
// fillRecordDetails fills associated data for decision record (old associated tables removed, this function kept for compatibility)
// Note: Account snapshot, position snapshot, decision action data are no longer stored in decision related tables
// - For equity data use EquityStore.GetLatest()
// - For order data use OrderStore
func (s *DecisionStore) fillRecordDetails(record *DecisionRecord) {
// 旧的关联表已删除,不再需要填充
// AccountState, Positions, Decisions 字段将保持为零值
// Old associated tables removed, no longer need to fill
// AccountState, Positions, Decisions fields will remain at zero values
}

View File

@@ -6,27 +6,27 @@ import (
"time"
)
// EquityStore 账户净值存储(用于绘制收益率曲线)
// EquityStore account equity storage (for plotting return curves)
type EquityStore struct {
db *sql.DB
}
// EquitySnapshot 净值快照
// EquitySnapshot equity snapshot
type EquitySnapshot struct {
ID int64 `json:"id"`
TraderID string `json:"trader_id"`
Timestamp time.Time `json:"timestamp"`
TotalEquity float64 `json:"total_equity"` // 账户净值 (余额 + 未实现盈亏)
Balance float64 `json:"balance"` // 账户余额
UnrealizedPnL float64 `json:"unrealized_pnl"` // 未实现盈亏
PositionCount int `json:"position_count"` // 持仓数量
MarginUsedPct float64 `json:"margin_used_pct"` // 保证金使用率
TotalEquity float64 `json:"total_equity"` // Account equity (balance + unrealized PnL)
Balance float64 `json:"balance"` // Account balance
UnrealizedPnL float64 `json:"unrealized_pnl"` // Unrealized profit and loss
PositionCount int `json:"position_count"` // Position count
MarginUsedPct float64 `json:"margin_used_pct"` // Margin usage percentage
}
// initTables 初始化净值表
// initTables initializes equity tables
func (s *EquityStore) initTables() error {
queries := []string{
// 净值快照表 - 专门用于收益率曲线
// Equity snapshot table - specifically for return curves
`CREATE TABLE IF NOT EXISTS trader_equity_snapshots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trader_id TEXT NOT NULL,
@@ -38,21 +38,21 @@ func (s *EquityStore) initTables() error {
margin_used_pct REAL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
// 索引
// Indexes
`CREATE INDEX IF NOT EXISTS idx_equity_trader_time ON trader_equity_snapshots(trader_id, timestamp DESC)`,
`CREATE INDEX IF NOT EXISTS idx_equity_timestamp ON trader_equity_snapshots(timestamp DESC)`,
}
for _, query := range queries {
if _, err := s.db.Exec(query); err != nil {
return fmt.Errorf("执行SQL失败: %w", err)
return fmt.Errorf("failed to execute SQL: %w", err)
}
}
return nil
}
// Save 保存净值快照
// Save saves equity snapshot
func (s *EquityStore) Save(snapshot *EquitySnapshot) error {
if snapshot.Timestamp.IsZero() {
snapshot.Timestamp = time.Now().UTC()
@@ -75,7 +75,7 @@ func (s *EquityStore) Save(snapshot *EquitySnapshot) error {
snapshot.MarginUsedPct,
)
if err != nil {
return fmt.Errorf("保存净值快照失败: %w", err)
return fmt.Errorf("failed to save equity snapshot: %w", err)
}
id, _ := result.LastInsertId()
@@ -83,7 +83,7 @@ func (s *EquityStore) Save(snapshot *EquitySnapshot) error {
return nil
}
// GetLatest 获取指定交易员最近N条净值记录按时间正序从旧到新
// GetLatest gets the latest N equity records for specified trader (sorted in ascending chronological order: old to new)
func (s *EquityStore) GetLatest(traderID string, limit int) ([]*EquitySnapshot, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, timestamp, total_equity, balance,
@@ -94,7 +94,7 @@ func (s *EquityStore) GetLatest(traderID string, limit int) ([]*EquitySnapshot,
LIMIT ?
`, traderID, limit)
if err != nil {
return nil, fmt.Errorf("查询净值记录失败: %w", err)
return nil, fmt.Errorf("failed to query equity records: %w", err)
}
defer rows.Close()
@@ -113,7 +113,7 @@ func (s *EquityStore) GetLatest(traderID string, limit int) ([]*EquitySnapshot,
snapshots = append(snapshots, snap)
}
// 反转数组,让时间从旧到新排列(适合绘制曲线)
// Reverse the array to sort time from old to new (suitable for plotting curves)
for i, j := 0, len(snapshots)-1; i < j; i, j = i+1, j-1 {
snapshots[i], snapshots[j] = snapshots[j], snapshots[i]
}
@@ -121,7 +121,7 @@ func (s *EquityStore) GetLatest(traderID string, limit int) ([]*EquitySnapshot,
return snapshots, nil
}
// GetByTimeRange 获取指定时间范围内的净值记录
// GetByTimeRange gets equity records within specified time range
func (s *EquityStore) GetByTimeRange(traderID string, start, end time.Time) ([]*EquitySnapshot, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, timestamp, total_equity, balance,
@@ -131,7 +131,7 @@ func (s *EquityStore) GetByTimeRange(traderID string, start, end time.Time) ([]*
ORDER BY timestamp ASC
`, traderID, start.Format(time.RFC3339), end.Format(time.RFC3339))
if err != nil {
return nil, fmt.Errorf("查询净值记录失败: %w", err)
return nil, fmt.Errorf("failed to query equity records: %w", err)
}
defer rows.Close()
@@ -153,7 +153,7 @@ func (s *EquityStore) GetByTimeRange(traderID string, start, end time.Time) ([]*
return snapshots, nil
}
// GetAllTradersLatest 获取所有交易员的最新净值(用于排行榜)
// GetAllTradersLatest gets latest equity for all traders (for leaderboards)
func (s *EquityStore) GetAllTradersLatest() (map[string]*EquitySnapshot, error) {
rows, err := s.db.Query(`
SELECT e.id, e.trader_id, e.timestamp, e.total_equity, e.balance,
@@ -166,7 +166,7 @@ func (s *EquityStore) GetAllTradersLatest() (map[string]*EquitySnapshot, error)
) latest ON e.trader_id = latest.trader_id AND e.timestamp = latest.max_ts
`)
if err != nil {
return nil, fmt.Errorf("查询最新净值失败: %w", err)
return nil, fmt.Errorf("failed to query latest equity: %w", err)
}
defer rows.Close()
@@ -188,7 +188,7 @@ func (s *EquityStore) GetAllTradersLatest() (map[string]*EquitySnapshot, error)
return result, nil
}
// CleanOldRecords 清理N天前的旧记录
// CleanOldRecords cleans old records from N days ago
func (s *EquityStore) CleanOldRecords(traderID string, days int) (int64, error) {
cutoffTime := time.Now().AddDate(0, 0, -days).Format(time.RFC3339)
@@ -197,13 +197,13 @@ func (s *EquityStore) CleanOldRecords(traderID string, days int) (int64, error)
WHERE trader_id = ? AND timestamp < ?
`, traderID, cutoffTime)
if err != nil {
return 0, fmt.Errorf("清理旧记录失败: %w", err)
return 0, fmt.Errorf("failed to clean old records: %w", err)
}
return result.RowsAffected()
}
// GetCount 获取指定交易员的记录数
// GetCount gets record count for specified trader
func (s *EquityStore) GetCount(traderID string) (int, error) {
var count int
err := s.db.QueryRow(`
@@ -212,26 +212,26 @@ func (s *EquityStore) GetCount(traderID string) (int, error) {
return count, err
}
// MigrateFromDecision 从旧的 decision_account_snapshots 迁移数据
// MigrateFromDecision migrates data from old decision_account_snapshots table
func (s *EquityStore) MigrateFromDecision() (int64, error) {
// 检查是否需要迁移(新表是否为空)
// Check if migration is needed (whether new table is empty)
var count int
s.db.QueryRow(`SELECT COUNT(*) FROM trader_equity_snapshots`).Scan(&count)
if count > 0 {
return 0, nil // 已有数据,跳过迁移
return 0, nil // Already has data, skip migration
}
// 检查旧表是否存在
// Check if old table exists
var tableName string
err := s.db.QueryRow(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='decision_account_snapshots'
`).Scan(&tableName)
if err != nil {
return 0, nil // 旧表不存在,跳过
return 0, nil // Old table doesn't exist, skip
}
// 迁移数据:从 decision_records + decision_account_snapshots 联合查询
// Migrate data: join query from decision_records + decision_account_snapshots
result, err := s.db.Exec(`
INSERT INTO trader_equity_snapshots (
trader_id, timestamp, total_equity, balance,
@@ -250,7 +250,7 @@ func (s *EquityStore) MigrateFromDecision() (int64, error) {
ORDER BY dr.timestamp ASC
`)
if err != nil {
return 0, fmt.Errorf("迁移数据失败: %w", err)
return 0, fmt.Errorf("failed to migrate data: %w", err)
}
return result.RowsAffected()

View File

@@ -8,14 +8,14 @@ import (
"time"
)
// ExchangeStore 交易所存储
// ExchangeStore exchange storage
type ExchangeStore struct {
db *sql.DB
encryptFunc func(string) string
decryptFunc func(string) string
}
// Exchange 交易所配置
// Exchange exchange configuration
type Exchange struct {
ID string `json:"id"`
UserID string `json:"user_id"`
@@ -24,7 +24,7 @@ type Exchange struct {
Enabled bool `json:"enabled"`
APIKey string `json:"apiKey"`
SecretKey string `json:"secretKey"`
Passphrase string `json:"passphrase"` // OKX专用
Passphrase string `json:"passphrase"` // OKX-specific
Testnet bool `json:"testnet"`
HyperliquidWalletAddr string `json:"hyperliquidWalletAddr"`
AsterUser string `json:"asterUser"`
@@ -65,10 +65,10 @@ func (s *ExchangeStore) initTables() error {
return err
}
// 迁移:添加 passphrase 列(如果不存在)
// Migration: add passphrase column (if not exists)
s.db.Exec(`ALTER TABLE exchanges ADD COLUMN passphrase TEXT DEFAULT ''`)
// 触发器
// Trigger
_, err = s.db.Exec(`
CREATE TRIGGER IF NOT EXISTS update_exchanges_updated_at
AFTER UPDATE ON exchanges
@@ -97,7 +97,7 @@ func (s *ExchangeStore) initDefaultData() error {
VALUES (?, 'default', ?, ?, 0)
`, exchange.id, exchange.name, exchange.typ)
if err != nil {
return fmt.Errorf("初始化交易所失败: %w", err)
return fmt.Errorf("failed to initialize exchange: %w", err)
}
}
return nil
@@ -117,7 +117,7 @@ func (s *ExchangeStore) decrypt(encrypted string) string {
return encrypted
}
// EnsureUserExchanges 确保用户有所有支持的交易所记录
// EnsureUserExchanges ensures user has records for all supported exchanges
func (s *ExchangeStore) EnsureUserExchanges(userID string) error {
exchanges := []struct {
id, name, typ string
@@ -136,17 +136,17 @@ func (s *ExchangeStore) EnsureUserExchanges(userID string) error {
VALUES (?, ?, ?, ?, 0)
`, exchange.id, userID, exchange.name, exchange.typ)
if err != nil {
return fmt.Errorf("确保用户交易所失败: %w", err)
return fmt.Errorf("failed to ensure user exchanges: %w", err)
}
}
return nil
}
// List 获取用户的交易所列表
// List gets user's exchange list
func (s *ExchangeStore) List(userID string) ([]*Exchange, error) {
// 确保用户有所有支持的交易所记录
// Ensure user has records for all supported exchanges
if err := s.EnsureUserExchanges(userID); err != nil {
logger.Debugf("⚠️ 确保用户交易所记录失败: %v", err)
logger.Debugf("Warning: failed to ensure user exchange records: %v", err)
}
rows, err := s.db.Query(`
@@ -194,7 +194,7 @@ func (s *ExchangeStore) List(userID string) ([]*Exchange, error) {
return exchanges, nil
}
// Update 更新交易所配置
// Update updates exchange configuration
func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey, secretKey, passphrase string, testnet bool,
hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey, lighterWalletAddr, lighterPrivateKey, lighterApiKeyPrivateKey string) error {
@@ -246,7 +246,7 @@ func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey, secretKe
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
// 创建新记录type 使用交易所 ID 以便后续正确识别
// Create new record, use exchange ID as type for correct identification
var name, typ string
switch id {
case "binance":
@@ -278,7 +278,7 @@ func (s *ExchangeStore) Update(userID, id string, enabled bool, apiKey, secretKe
return nil
}
// Create 创建交易所配置
// Create creates exchange configuration
func (s *ExchangeStore) Create(userID, id, name, typ string, enabled bool, apiKey, secretKey string, testnet bool,
hyperliquidWalletAddr, asterUser, asterSigner, asterPrivateKey string) error {
_, err := s.db.Exec(`

View File

@@ -7,73 +7,73 @@ import (
"time"
)
// TraderOrder 交易员订单记录
// TraderOrder trader order record
type TraderOrder struct {
ID int64 `json:"id"`
TraderID string `json:"trader_id"` // 交易员ID
OrderID string `json:"order_id"` // 交易所订单ID
ClientOrderID string `json:"client_order_id"` // 客户端订单ID
Symbol string `json:"symbol"` // 交易对
TraderID string `json:"trader_id"` // Trader ID
OrderID string `json:"order_id"` // Exchange order ID
ClientOrderID string `json:"client_order_id"` // Client order ID
Symbol string `json:"symbol"` // Trading pair
Side string `json:"side"` // BUY/SELL
PositionSide string `json:"position_side"` // LONG/SHORT/BOTH
Action string `json:"action"` // open_long/close_long/open_short/close_short
OrderType string `json:"order_type"` // MARKET/LIMIT
Quantity float64 `json:"quantity"` // 订单数量
Price float64 `json:"price"` // 订单价格
AvgPrice float64 `json:"avg_price"` // 实际成交均价
ExecutedQty float64 `json:"executed_qty"` // 已成交数量
Leverage int `json:"leverage"` // 杠杆倍数
Quantity float64 `json:"quantity"` // Order quantity
Price float64 `json:"price"` // Order price
AvgPrice float64 `json:"avg_price"` // Actual average execution price
ExecutedQty float64 `json:"executed_qty"` // Executed quantity
Leverage int `json:"leverage"` // Leverage multiplier
Status string `json:"status"` // NEW/FILLED/CANCELED/EXPIRED
Fee float64 `json:"fee"` // 手续费
FeeAsset string `json:"fee_asset"` // 手续费资产
RealizedPnL float64 `json:"realized_pnl"` // 已实现盈亏(平仓时)
EntryPrice float64 `json:"entry_price"` // 开仓价(平仓时记录)
Fee float64 `json:"fee"` // Fee
FeeAsset string `json:"fee_asset"` // Fee asset
RealizedPnL float64 `json:"realized_pnl"` // Realized PnL (when closing)
EntryPrice float64 `json:"entry_price"` // Entry price (recorded when closing)
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
FilledAt time.Time `json:"filled_at"` // 成交时间
FilledAt time.Time `json:"filled_at"` // Filled time
}
// TraderStats 交易统计指标
// TraderStats trading statistics metrics
type TraderStats struct {
TotalTrades int `json:"total_trades"` // 总交易数(已平仓)
WinTrades int `json:"win_trades"` // 盈利交易数
LossTrades int `json:"loss_trades"` // 亏损交易数
WinRate float64 `json:"win_rate"` // 胜率 (%)
ProfitFactor float64 `json:"profit_factor"` // 盈亏比
SharpeRatio float64 `json:"sharpe_ratio"` // 夏普比
TotalPnL float64 `json:"total_pnl"` // 总盈亏
TotalFee float64 `json:"total_fee"` // 总手续费
AvgWin float64 `json:"avg_win"` // 平均盈利
AvgLoss float64 `json:"avg_loss"` // 平均亏损
MaxDrawdownPct float64 `json:"max_drawdown_pct"` // 最大回撤 (%)
TotalTrades int `json:"total_trades"` // Total trades (closed)
WinTrades int `json:"win_trades"` // Winning trades
LossTrades int `json:"loss_trades"` // Losing trades
WinRate float64 `json:"win_rate"` // Win rate (%)
ProfitFactor float64 `json:"profit_factor"` // Profit factor
SharpeRatio float64 `json:"sharpe_ratio"` // Sharpe ratio
TotalPnL float64 `json:"total_pnl"` // Total PnL
TotalFee float64 `json:"total_fee"` // Total fees
AvgWin float64 `json:"avg_win"` // Average win
AvgLoss float64 `json:"avg_loss"` // Average loss
MaxDrawdownPct float64 `json:"max_drawdown_pct"` // Max drawdown (%)
}
// CompletedOrder 已完成订单用于AI输入
// CompletedOrder completed order (for AI input)
type CompletedOrder struct {
Symbol string `json:"symbol"` // 交易对
Symbol string `json:"symbol"` // Trading pair
Action string `json:"action"` // close_long/close_short
Side string `json:"side"` // long/short
Quantity float64 `json:"quantity"` // 数量
EntryPrice float64 `json:"entry_price"` // 开仓价
ExitPrice float64 `json:"exit_price"` // 平仓价
RealizedPnL float64 `json:"realized_pnl"` // 已实现盈亏
PnLPct float64 `json:"pnl_pct"` // 盈亏百分比
Fee float64 `json:"fee"` // 手续费
Leverage int `json:"leverage"` // 杠杆
FilledAt time.Time `json:"filled_at"` // 成交时间
Quantity float64 `json:"quantity"` // Quantity
EntryPrice float64 `json:"entry_price"` // Entry price
ExitPrice float64 `json:"exit_price"` // Exit price
RealizedPnL float64 `json:"realized_pnl"` // Realized PnL
PnLPct float64 `json:"pnl_pct"` // PnL percentage
Fee float64 `json:"fee"` // Fee
Leverage int `json:"leverage"` // Leverage
FilledAt time.Time `json:"filled_at"` // Filled time
}
// OrderStore 订单存储
// OrderStore order storage
type OrderStore struct {
db *sql.DB
}
// NewOrderStore 创建订单存储实例
// NewOrderStore creates order storage instance
func NewOrderStore(db *sql.DB) *OrderStore {
return &OrderStore{db: db}
}
// InitTables 初始化订单表
// InitTables initializes order tables
func (s *OrderStore) InitTables() error {
_, err := s.db.Exec(`
CREATE TABLE IF NOT EXISTS trader_orders (
@@ -103,10 +103,10 @@ func (s *OrderStore) InitTables() error {
)
`)
if err != nil {
return fmt.Errorf("创建trader_orders表失败: %w", err)
return fmt.Errorf("failed to create trader_orders table: %w", err)
}
// 创建索引
// Create indexes
indices := []string{
`CREATE INDEX IF NOT EXISTS idx_trader_orders_trader ON trader_orders(trader_id)`,
`CREATE INDEX IF NOT EXISTS idx_trader_orders_status ON trader_orders(trader_id, status)`,
@@ -115,14 +115,14 @@ func (s *OrderStore) InitTables() error {
}
for _, idx := range indices {
if _, err := s.db.Exec(idx); err != nil {
return fmt.Errorf("创建索引失败: %w", err)
return fmt.Errorf("failed to create index: %w", err)
}
}
return nil
}
// Create 创建订单记录
// Create creates order record
func (s *OrderStore) Create(order *TraderOrder) error {
now := time.Now().Format(time.RFC3339)
result, err := s.db.Exec(`
@@ -140,7 +140,7 @@ func (s *OrderStore) Create(order *TraderOrder) error {
order.RealizedPnL, order.EntryPrice, now, now,
)
if err != nil {
return fmt.Errorf("创建订单记录失败: %w", err)
return fmt.Errorf("failed to create order record: %w", err)
}
id, _ := result.LastInsertId()
@@ -148,7 +148,7 @@ func (s *OrderStore) Create(order *TraderOrder) error {
return nil
}
// Update 更新订单记录
// Update updates order record
func (s *OrderStore) Update(order *TraderOrder) error {
now := time.Now().Format(time.RFC3339)
filledAt := ""
@@ -167,12 +167,12 @@ func (s *OrderStore) Update(order *TraderOrder) error {
order.TraderID, order.OrderID,
)
if err != nil {
return fmt.Errorf("更新订单记录失败: %w", err)
return fmt.Errorf("failed to update order record: %w", err)
}
return nil
}
// GetByOrderID 根据订单ID获取订单
// GetByOrderID gets order by order ID
func (s *OrderStore) GetByOrderID(traderID, orderID string) (*TraderOrder, error) {
var order TraderOrder
var createdAt, updatedAt, filledAt sql.NullString
@@ -208,9 +208,9 @@ func (s *OrderStore) GetByOrderID(traderID, orderID string) (*TraderOrder, error
return &order, nil
}
// GetLatestOpenOrder 获取某币种最近的开仓订单(用于计算平仓盈亏)
// GetLatestOpenOrder gets the latest open order for a symbol (for calculating close PnL)
func (s *OrderStore) GetLatestOpenOrder(traderID, symbol, side string) (*TraderOrder, error) {
// side: long -> open_long, short -> open_short
// side: long -> find open_long, short -> find open_short
action := "open_long"
if side == "short" {
action = "open_short"
@@ -252,7 +252,7 @@ func (s *OrderStore) GetLatestOpenOrder(traderID, symbol, side string) (*TraderO
return &order, nil
}
// GetRecentCompletedOrders 获取最近已完成的平仓订单
// GetRecentCompletedOrders gets recent completed close orders
func (s *OrderStore) GetRecentCompletedOrders(traderID string, limit int) ([]CompletedOrder, error) {
rows, err := s.db.Query(`
SELECT symbol, action, side, executed_qty, entry_price, avg_price,
@@ -264,7 +264,7 @@ func (s *OrderStore) GetRecentCompletedOrders(traderID string, limit int) ([]Com
LIMIT ?
`, traderID, limit)
if err != nil {
return nil, fmt.Errorf("查询已完成订单失败: %w", err)
return nil, fmt.Errorf("failed to query completed orders: %w", err)
}
defer rows.Close()
@@ -282,7 +282,7 @@ func (s *OrderStore) GetRecentCompletedOrders(traderID string, limit int) ([]Com
continue
}
// 根据action推断side
// Infer side from action
if o.Action == "close_long" {
o.Side = "long"
} else if o.Action == "close_short" {
@@ -291,7 +291,7 @@ func (s *OrderStore) GetRecentCompletedOrders(traderID string, limit int) ([]Com
o.Side = side.String
}
// 计算盈亏百分比
// Calculate PnL percentage
if o.EntryPrice > 0 {
if o.Side == "long" {
o.PnLPct = (o.ExitPrice - o.EntryPrice) / o.EntryPrice * 100 * float64(o.Leverage)
@@ -310,11 +310,11 @@ func (s *OrderStore) GetRecentCompletedOrders(traderID string, limit int) ([]Com
return orders, nil
}
// GetTraderStats 获取交易统计指标
// GetTraderStats gets trading statistics metrics
func (s *OrderStore) GetTraderStats(traderID string) (*TraderStats, error) {
stats := &TraderStats{}
// 查询所有已完成的平仓订单
// Query all completed close orders
rows, err := s.db.Query(`
SELECT realized_pnl, fee, filled_at
FROM trader_orders
@@ -323,7 +323,7 @@ func (s *OrderStore) GetTraderStats(traderID string) (*TraderStats, error) {
ORDER BY filled_at ASC
`, traderID)
if err != nil {
return nil, fmt.Errorf("查询订单统计失败: %w", err)
return nil, fmt.Errorf("failed to query order statistics: %w", err)
}
defer rows.Close()
@@ -351,17 +351,17 @@ func (s *OrderStore) GetTraderStats(traderID string) (*TraderStats, error) {
}
}
// 计算胜率
// Calculate win rate
if stats.TotalTrades > 0 {
stats.WinRate = float64(stats.WinTrades) / float64(stats.TotalTrades) * 100
}
// 计算盈亏比
// Calculate profit factor
if totalLoss > 0 {
stats.ProfitFactor = totalWin / totalLoss
}
// 计算平均盈亏
// Calculate average win/loss
if stats.WinTrades > 0 {
stats.AvgWin = totalWin / float64(stats.WinTrades)
}
@@ -369,12 +369,12 @@ func (s *OrderStore) GetTraderStats(traderID string) (*TraderStats, error) {
stats.AvgLoss = totalLoss / float64(stats.LossTrades)
}
// 计算夏普比(使用盈亏序列)
// Calculate Sharpe ratio (using PnL sequence)
if len(pnls) > 1 {
stats.SharpeRatio = calculateSharpeRatio(pnls)
}
// 计算最大回撤
// Calculate max drawdown
if len(pnls) > 0 {
stats.MaxDrawdownPct = calculateMaxDrawdown(pnls)
}
@@ -382,20 +382,20 @@ func (s *OrderStore) GetTraderStats(traderID string) (*TraderStats, error) {
return stats, nil
}
// calculateSharpeRatio 计算夏普比
// calculateSharpeRatio calculates Sharpe ratio
func calculateSharpeRatio(pnls []float64) float64 {
if len(pnls) < 2 {
return 0
}
// 计算平均收益
// Calculate average return
var sum float64
for _, pnl := range pnls {
sum += pnl
}
mean := sum / float64(len(pnls))
// 计算标准差
// Calculate standard deviation
var variance float64
for _, pnl := range pnls {
variance += (pnl - mean) * (pnl - mean)
@@ -406,17 +406,17 @@ func calculateSharpeRatio(pnls []float64) float64 {
return 0
}
// 夏普比 = 平均收益 / 标准差
// Sharpe ratio = average return / standard deviation
return mean / stdDev
}
// calculateMaxDrawdown 计算最大回撤
// calculateMaxDrawdown calculates max drawdown
func calculateMaxDrawdown(pnls []float64) float64 {
if len(pnls) == 0 {
return 0
}
// 计算累计权益曲线
// Calculate cumulative equity curve
var cumulative float64
var peak float64
var maxDD float64
@@ -437,7 +437,7 @@ func calculateMaxDrawdown(pnls []float64) float64 {
return maxDD
}
// GetPendingOrders 获取未成交的订单(用于轮询)
// GetPendingOrders gets pending orders (for polling)
func (s *OrderStore) GetPendingOrders(traderID string) ([]*TraderOrder, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, order_id, client_order_id, symbol, side, position_side,
@@ -449,14 +449,14 @@ func (s *OrderStore) GetPendingOrders(traderID string) ([]*TraderOrder, error) {
ORDER BY created_at ASC
`, traderID)
if err != nil {
return nil, fmt.Errorf("查询未成交订单失败: %w", err)
return nil, fmt.Errorf("failed to query pending orders: %w", err)
}
defer rows.Close()
return s.scanOrders(rows)
}
// GetAllPendingOrders 获取所有未成交的订单(用于全局同步)
// GetAllPendingOrders gets all pending orders (for global sync)
func (s *OrderStore) GetAllPendingOrders() ([]*TraderOrder, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, order_id, client_order_id, symbol, side, position_side,
@@ -468,14 +468,14 @@ func (s *OrderStore) GetAllPendingOrders() ([]*TraderOrder, error) {
ORDER BY trader_id, created_at ASC
`)
if err != nil {
return nil, fmt.Errorf("查询未成交订单失败: %w", err)
return nil, fmt.Errorf("failed to query pending orders: %w", err)
}
defer rows.Close()
return s.scanOrders(rows)
}
// scanOrders 扫描订单行到结构体
// scanOrders scans order rows to structs
func (s *OrderStore) scanOrders(rows *sql.Rows) ([]*TraderOrder, error) {
var orders []*TraderOrder
for rows.Next() {

View File

@@ -7,40 +7,40 @@ import (
"time"
)
// TraderPosition 仓位记录(完整的开平仓追踪)
// TraderPosition position record (complete open/close position tracking)
type TraderPosition struct {
ID int64 `json:"id"`
TraderID string `json:"trader_id"`
ExchangeID string `json:"exchange_id"` // 交易所ID: binance/bybit/hyperliquid/aster/lighter
ExchangeID string `json:"exchange_id"` // Exchange ID: binance/bybit/hyperliquid/aster/lighter
Symbol string `json:"symbol"`
Side string `json:"side"` // LONG/SHORT
Quantity float64 `json:"quantity"` // 开仓数量
EntryPrice float64 `json:"entry_price"` // 开仓均价
EntryOrderID string `json:"entry_order_id"` // 开仓订单ID
EntryTime time.Time `json:"entry_time"` // 开仓时间
ExitPrice float64 `json:"exit_price"` // 平仓均价
ExitOrderID string `json:"exit_order_id"` // 平仓订单ID
ExitTime *time.Time `json:"exit_time"` // 平仓时间
RealizedPnL float64 `json:"realized_pnl"` // 已实现盈亏
Fee float64 `json:"fee"` // 手续费
Leverage int `json:"leverage"` // 杠杆倍数
Quantity float64 `json:"quantity"` // Opening quantity
EntryPrice float64 `json:"entry_price"` // Entry price
EntryOrderID string `json:"entry_order_id"` // Entry order ID
EntryTime time.Time `json:"entry_time"` // Entry time
ExitPrice float64 `json:"exit_price"` // Exit price
ExitOrderID string `json:"exit_order_id"` // Exit order ID
ExitTime *time.Time `json:"exit_time"` // Exit time
RealizedPnL float64 `json:"realized_pnl"` // Realized profit and loss
Fee float64 `json:"fee"` // Fee
Leverage int `json:"leverage"` // Leverage multiplier
Status string `json:"status"` // OPEN/CLOSED
CloseReason string `json:"close_reason"` // 平仓原因: ai_decision/manual/stop_loss/take_profit
CloseReason string `json:"close_reason"` // Close reason: ai_decision/manual/stop_loss/take_profit
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// PositionStore 仓位存储
// PositionStore position storage
type PositionStore struct {
db *sql.DB
}
// NewPositionStore 创建仓位存储实例
// NewPositionStore creates position storage instance
func NewPositionStore(db *sql.DB) *PositionStore {
return &PositionStore{db: db}
}
// InitTables 初始化仓位表
// InitTables initializes position tables
func (s *PositionStore) InitTables() error {
_, err := s.db.Exec(`
CREATE TABLE IF NOT EXISTS trader_positions (
@@ -66,14 +66,14 @@ func (s *PositionStore) InitTables() error {
)
`)
if err != nil {
return fmt.Errorf("创建trader_positions表失败: %w", err)
return fmt.Errorf("failed to create trader_positions table: %w", err)
}
// 迁移:为现有表添加 exchange_id 列(如果不存在)
// 必须在创建索引之前执行!
// Migration: add exchange_id column to existing table (if not exists)
// Must be executed before creating indexes!
s.db.Exec(`ALTER TABLE trader_positions ADD COLUMN exchange_id TEXT NOT NULL DEFAULT ''`)
// 创建索引(在迁移之后)
// Create indexes (after migration)
indices := []string{
`CREATE INDEX IF NOT EXISTS idx_positions_trader ON trader_positions(trader_id)`,
`CREATE INDEX IF NOT EXISTS idx_positions_exchange ON trader_positions(exchange_id)`,
@@ -84,14 +84,14 @@ func (s *PositionStore) InitTables() error {
}
for _, idx := range indices {
if _, err := s.db.Exec(idx); err != nil {
return fmt.Errorf("创建索引失败: %w", err)
return fmt.Errorf("failed to create index: %w", err)
}
}
return nil
}
// Create 创建仓位记录(开仓时调用)
// Create creates position record (called when opening position)
func (s *PositionStore) Create(pos *TraderPosition) error {
now := time.Now()
pos.CreatedAt = now
@@ -109,7 +109,7 @@ func (s *PositionStore) Create(pos *TraderPosition) error {
pos.Status, now.Format(time.RFC3339), now.Format(time.RFC3339),
)
if err != nil {
return fmt.Errorf("创建仓位记录失败: %w", err)
return fmt.Errorf("failed to create position record: %w", err)
}
id, _ := result.LastInsertId()
@@ -117,7 +117,7 @@ func (s *PositionStore) Create(pos *TraderPosition) error {
return nil
}
// ClosePosition 平仓(更新仓位记录)
// ClosePosition closes position (updates position record)
func (s *PositionStore) ClosePosition(id int64, exitPrice float64, exitOrderID string, realizedPnL float64, fee float64, closeReason string) error {
now := time.Now()
_, err := s.db.Exec(`
@@ -131,12 +131,12 @@ func (s *PositionStore) ClosePosition(id int64, exitPrice float64, exitOrderID s
realizedPnL, fee, closeReason, now.Format(time.RFC3339), id,
)
if err != nil {
return fmt.Errorf("更新仓位记录失败: %w", err)
return fmt.Errorf("failed to update position record: %w", err)
}
return nil
}
// GetOpenPositions 获取所有未平仓位
// GetOpenPositions gets all open positions
func (s *PositionStore) GetOpenPositions(traderID string) ([]*TraderPosition, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, exchange_id, symbol, side, quantity, entry_price, entry_order_id,
@@ -147,14 +147,14 @@ func (s *PositionStore) GetOpenPositions(traderID string) ([]*TraderPosition, er
ORDER BY entry_time DESC
`, traderID)
if err != nil {
return nil, fmt.Errorf("查询未平仓位失败: %w", err)
return nil, fmt.Errorf("failed to query open positions: %w", err)
}
defer rows.Close()
return s.scanPositions(rows)
}
// GetOpenPositionBySymbol 获取指定币种方向的未平仓位
// GetOpenPositionBySymbol gets open position for specified symbol and direction
func (s *PositionStore) GetOpenPositionBySymbol(traderID, symbol, side string) (*TraderPosition, error) {
var pos TraderPosition
var entryTime, exitTime, createdAt, updatedAt sql.NullString
@@ -183,7 +183,7 @@ func (s *PositionStore) GetOpenPositionBySymbol(traderID, symbol, side string) (
return &pos, nil
}
// GetClosedPositions 获取已平仓位(历史记录)
// GetClosedPositions gets closed positions (historical records)
func (s *PositionStore) GetClosedPositions(traderID string, limit int) ([]*TraderPosition, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, exchange_id, symbol, side, quantity, entry_price, entry_order_id,
@@ -195,14 +195,14 @@ func (s *PositionStore) GetClosedPositions(traderID string, limit int) ([]*Trade
LIMIT ?
`, traderID, limit)
if err != nil {
return nil, fmt.Errorf("查询已平仓位失败: %w", err)
return nil, fmt.Errorf("failed to query closed positions: %w", err)
}
defer rows.Close()
return s.scanPositions(rows)
}
// GetAllOpenPositions 获取所有trader的未平仓位用于全局同步
// GetAllOpenPositions gets all traders' open positions (for global sync)
func (s *PositionStore) GetAllOpenPositions() ([]*TraderPosition, error) {
rows, err := s.db.Query(`
SELECT id, trader_id, exchange_id, symbol, side, quantity, entry_price, entry_order_id,
@@ -213,18 +213,18 @@ func (s *PositionStore) GetAllOpenPositions() ([]*TraderPosition, error) {
ORDER BY trader_id, entry_time DESC
`)
if err != nil {
return nil, fmt.Errorf("查询所有未平仓位失败: %w", err)
return nil, fmt.Errorf("failed to query all open positions: %w", err)
}
defer rows.Close()
return s.scanPositions(rows)
}
// GetPositionStats 获取仓位统计(简单版)
// GetPositionStats gets position statistics (simplified version)
func (s *PositionStore) GetPositionStats(traderID string) (map[string]interface{}, error) {
stats := make(map[string]interface{})
// 总交易数
// Total trades
var totalTrades, winTrades int
var totalPnL, totalFee float64
@@ -254,11 +254,11 @@ func (s *PositionStore) GetPositionStats(traderID string) (map[string]interface{
return stats, nil
}
// GetFullStats 获取完整的交易统计(与 TraderStats 兼容)
// GetFullStats gets complete trading statistics (compatible with TraderStats)
func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, error) {
stats := &TraderStats{}
// 查询所有已平仓位
// Query all closed positions
rows, err := s.db.Query(`
SELECT realized_pnl, fee, exit_time
FROM trader_positions
@@ -266,7 +266,7 @@ func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, error) {
ORDER BY exit_time ASC
`, traderID)
if err != nil {
return nil, fmt.Errorf("查询仓位统计失败: %w", err)
return nil, fmt.Errorf("failed to query position statistics: %w", err)
}
defer rows.Close()
@@ -290,21 +290,21 @@ func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, error) {
totalWin += pnl
} else if pnl < 0 {
stats.LossTrades++
totalLoss += -pnl // 转为正数
totalLoss += -pnl // Convert to positive
}
}
// 计算胜率
// Calculate win rate
if stats.TotalTrades > 0 {
stats.WinRate = float64(stats.WinTrades) / float64(stats.TotalTrades) * 100
}
// 计算盈亏比
// Calculate profit factor
if totalLoss > 0 {
stats.ProfitFactor = totalWin / totalLoss
}
// 计算平均盈亏
// Calculate average profit/loss
if stats.WinTrades > 0 {
stats.AvgWin = totalWin / float64(stats.WinTrades)
}
@@ -312,12 +312,12 @@ func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, error) {
stats.AvgLoss = totalLoss / float64(stats.LossTrades)
}
// 计算夏普比
// Calculate Sharpe ratio
if len(pnls) > 1 {
stats.SharpeRatio = calculateSharpeRatioFromPnls(pnls)
}
// 计算最大回撤
// Calculate maximum drawdown
if len(pnls) > 0 {
stats.MaxDrawdownPct = calculateMaxDrawdownFromPnls(pnls)
}
@@ -325,7 +325,7 @@ func (s *PositionStore) GetFullStats(traderID string) (*TraderStats, error) {
return stats, nil
}
// RecentTrade 最近的交易记录用于AI输入
// RecentTrade recent trade record (for AI input)
type RecentTrade struct {
Symbol string `json:"symbol"`
Side string `json:"side"` // long/short
@@ -336,7 +336,7 @@ type RecentTrade struct {
ExitTime string `json:"exit_time"`
}
// GetRecentTrades 获取最近的已平仓交易
// GetRecentTrades gets recent closed trades
func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]RecentTrade, error) {
rows, err := s.db.Query(`
SELECT symbol, side, entry_price, exit_price, realized_pnl, leverage, exit_time
@@ -346,7 +346,7 @@ func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]RecentTra
LIMIT ?
`, traderID, limit)
if err != nil {
return nil, fmt.Errorf("查询最近交易失败: %w", err)
return nil, fmt.Errorf("failed to query recent trades: %w", err)
}
defer rows.Close()
@@ -361,14 +361,14 @@ func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]RecentTra
continue
}
// 转换 side 格式
// Convert side format
if t.Side == "LONG" {
t.Side = "long"
} else if t.Side == "SHORT" {
t.Side = "short"
}
// 计算盈亏百分比
// Calculate profit/loss percentage
if t.EntryPrice > 0 {
if t.Side == "long" {
t.PnLPct = (t.ExitPrice - t.EntryPrice) / t.EntryPrice * 100 * float64(leverage)
@@ -377,7 +377,7 @@ func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]RecentTra
}
}
// 格式化时间
// Format time
if exitTime.Valid {
if parsed, err := time.Parse(time.RFC3339, exitTime.String); err == nil {
t.ExitTime = parsed.Format("01-02 15:04")
@@ -390,7 +390,7 @@ func (s *PositionStore) GetRecentTrades(traderID string, limit int) ([]RecentTra
return trades, nil
}
// calculateSharpeRatioFromPnls 计算夏普比
// calculateSharpeRatioFromPnls calculates Sharpe ratio
func calculateSharpeRatioFromPnls(pnls []float64) float64 {
if len(pnls) < 2 {
return 0
@@ -415,7 +415,7 @@ func calculateSharpeRatioFromPnls(pnls []float64) float64 {
return mean / stdDev
}
// calculateMaxDrawdownFromPnls 计算最大回撤
// calculateMaxDrawdownFromPnls calculates maximum drawdown
func calculateMaxDrawdownFromPnls(pnls []float64) float64 {
if len(pnls) == 0 {
return 0
@@ -438,7 +438,7 @@ func calculateMaxDrawdownFromPnls(pnls []float64) float64 {
return maxDD
}
// scanPositions 扫描仓位行到结构体
// scanPositions scans position rows into structs
func (s *PositionStore) scanPositions(rows *sql.Rows) ([]*TraderPosition, error) {
var positions []*TraderPosition
for rows.Next() {
@@ -462,7 +462,7 @@ func (s *PositionStore) scanPositions(rows *sql.Rows) ([]*TraderPosition, error)
return positions, nil
}
// parsePositionTimes 解析时间字段
// parsePositionTimes parses time fields
func (s *PositionStore) parsePositionTimes(pos *TraderPosition, entryTime, exitTime, createdAt, updatedAt sql.NullString) {
if entryTime.Valid {
pos.EntryTime, _ = time.Parse(time.RFC3339, entryTime.String)

View File

@@ -1,5 +1,5 @@
// Package store 提供统一的数据库存储层
// 所有数据库操作都应该通过这个包进行
// Package store provides unified database storage layer
// All database operations should go through this package
package store
import (
@@ -11,11 +11,11 @@ import (
_ "modernc.org/sqlite"
)
// Store 统一的数据存储接口
// Store unified data storage interface
type Store struct {
db *sql.DB
// 子存储(延迟初始化)
// Sub-stores (lazy initialization)
user *UserStore
aiModel *AIModelStore
exchange *ExchangeStore
@@ -27,80 +27,80 @@ type Store struct {
strategy *StrategyStore
equity *EquityStore
// 加密函数
// Encryption functions
encryptFunc func(string) string
decryptFunc func(string) string
mu sync.RWMutex
}
// New 创建新的 Store 实例
// New creates new Store instance
func New(dbPath string) (*Store, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, fmt.Errorf("打开数据库失败: %w", err)
return nil, fmt.Errorf("failed to open database: %w", err)
}
// SQLite 配置
// SQLite configuration
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
// 启用外键约束
// Enable foreign key constraints
if _, err := db.Exec(`PRAGMA foreign_keys = ON`); err != nil {
db.Close()
return nil, fmt.Errorf("启用外键失败: %w", err)
return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
}
// 使用 DELETE 模式(传统模式)以确保 Docker bind mount 兼容性
// 注意WAL 模式在 macOS Docker 下会导致数据同步问题
// Use DELETE mode (traditional mode) to ensure Docker bind mount compatibility
// Note: WAL mode causes data sync issues on macOS Docker
if _, err := db.Exec("PRAGMA journal_mode=DELETE"); err != nil {
db.Close()
return nil, fmt.Errorf("设置journal_mode失败: %w", err)
return nil, fmt.Errorf("failed to set journal_mode: %w", err)
}
// 设置 synchronous=FULL
// Set synchronous=FULL
if _, err := db.Exec("PRAGMA synchronous=FULL"); err != nil {
db.Close()
return nil, fmt.Errorf("设置synchronous失败: %w", err)
return nil, fmt.Errorf("failed to set synchronous: %w", err)
}
// 设置 busy_timeout
// Set busy_timeout
if _, err := db.Exec("PRAGMA busy_timeout = 5000"); err != nil {
db.Close()
return nil, fmt.Errorf("设置busy_timeout失败: %w", err)
return nil, fmt.Errorf("failed to set busy_timeout: %w", err)
}
s := &Store{db: db}
// 初始化所有表结构
// Initialize all table structures
if err := s.initTables(); err != nil {
db.Close()
return nil, fmt.Errorf("初始化表结构失败: %w", err)
return nil, fmt.Errorf("failed to initialize table structure: %w", err)
}
// 初始化默认数据
// Initialize default data
if err := s.initDefaultData(); err != nil {
db.Close()
return nil, fmt.Errorf("初始化默认数据失败: %w", err)
return nil, fmt.Errorf("failed to initialize default data: %w", err)
}
logger.Info("✅ 数据库已启用 DELETE 模式和 FULL 同步")
logger.Info("✅ Database enabled DELETE mode and FULL sync")
return s, nil
}
// NewFromDB 从现有数据库连接创建 Store
// NewFromDB creates Store from existing database connection
func NewFromDB(db *sql.DB) *Store {
return &Store{db: db}
}
// SetCryptoFuncs 设置加密解密函数
// SetCryptoFuncs sets encryption/decryption functions
func (s *Store) SetCryptoFuncs(encrypt, decrypt func(string) string) {
s.mu.Lock()
defer s.mu.Unlock()
s.encryptFunc = encrypt
s.decryptFunc = decrypt
// 更新已初始化的子存储
// Update already initialized sub-stores
if s.aiModel != nil {
s.aiModel.encryptFunc = encrypt
s.aiModel.decryptFunc = decrypt
@@ -114,43 +114,43 @@ func (s *Store) SetCryptoFuncs(encrypt, decrypt func(string) string) {
}
}
// initTables 初始化所有数据库表
// initTables initializes all database tables
func (s *Store) initTables() error {
// 按依赖顺序初始化
// Initialize in dependency order
if err := s.User().initTables(); err != nil {
return fmt.Errorf("初始化用户表失败: %w", err)
return fmt.Errorf("failed to initialize user tables: %w", err)
}
if err := s.AIModel().initTables(); err != nil {
return fmt.Errorf("初始化AI模型表失败: %w", err)
return fmt.Errorf("failed to initialize AI model tables: %w", err)
}
if err := s.Exchange().initTables(); err != nil {
return fmt.Errorf("初始化交易所表失败: %w", err)
return fmt.Errorf("failed to initialize exchange tables: %w", err)
}
if err := s.Trader().initTables(); err != nil {
return fmt.Errorf("初始化交易员表失败: %w", err)
return fmt.Errorf("failed to initialize trader tables: %w", err)
}
if err := s.Decision().initTables(); err != nil {
return fmt.Errorf("初始化决策日志表失败: %w", err)
return fmt.Errorf("failed to initialize decision log tables: %w", err)
}
if err := s.Backtest().initTables(); err != nil {
return fmt.Errorf("初始化回测表失败: %w", err)
return fmt.Errorf("failed to initialize backtest tables: %w", err)
}
if err := s.Order().InitTables(); err != nil {
return fmt.Errorf("初始化订单表失败: %w", err)
return fmt.Errorf("failed to initialize order tables: %w", err)
}
if err := s.Position().InitTables(); err != nil {
return fmt.Errorf("初始化仓位表失败: %w", err)
return fmt.Errorf("failed to initialize position tables: %w", err)
}
if err := s.Strategy().initTables(); err != nil {
return fmt.Errorf("初始化策略表失败: %w", err)
return fmt.Errorf("failed to initialize strategy tables: %w", err)
}
if err := s.Equity().initTables(); err != nil {
return fmt.Errorf("初始化净值表失败: %w", err)
return fmt.Errorf("failed to initialize equity tables: %w", err)
}
return nil
}
// initDefaultData 初始化默认数据
// initDefaultData initializes default data
func (s *Store) initDefaultData() error {
if err := s.AIModel().initDefaultData(); err != nil {
return err
@@ -161,16 +161,16 @@ func (s *Store) initDefaultData() error {
if err := s.Strategy().initDefaultData(); err != nil {
return err
}
// 迁移旧的 decision_account_snapshots 数据到新的 trader_equity_snapshots
// Migrate old decision_account_snapshots data to new trader_equity_snapshots table
if migrated, err := s.Equity().MigrateFromDecision(); err != nil {
logger.Warnf("迁移净值数据失败: %v", err)
logger.Warnf("failed to migrate equity data: %v", err)
} else if migrated > 0 {
logger.Infof("✅ 已迁移 %d 条净值数据到新表", migrated)
logger.Infof("✅ Migrated %d equity records to new table", migrated)
}
return nil
}
// User 获取用户存储
// User gets user storage
func (s *Store) User() *UserStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -180,7 +180,7 @@ func (s *Store) User() *UserStore {
return s.user
}
// AIModel 获取AI模型存储
// AIModel gets AI model storage
func (s *Store) AIModel() *AIModelStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -194,7 +194,7 @@ func (s *Store) AIModel() *AIModelStore {
return s.aiModel
}
// Exchange 获取交易所存储
// Exchange gets exchange storage
func (s *Store) Exchange() *ExchangeStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -208,7 +208,7 @@ func (s *Store) Exchange() *ExchangeStore {
return s.exchange
}
// Trader 获取交易员存储
// Trader gets trader storage
func (s *Store) Trader() *TraderStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -221,7 +221,7 @@ func (s *Store) Trader() *TraderStore {
return s.trader
}
// Decision 获取决策日志存储
// Decision gets decision log storage
func (s *Store) Decision() *DecisionStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -231,7 +231,7 @@ func (s *Store) Decision() *DecisionStore {
return s.decision
}
// Backtest 获取回测数据存储
// Backtest gets backtest data storage
func (s *Store) Backtest() *BacktestStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -241,7 +241,7 @@ func (s *Store) Backtest() *BacktestStore {
return s.backtest
}
// Order 获取订单存储
// Order gets order storage
func (s *Store) Order() *OrderStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -251,7 +251,7 @@ func (s *Store) Order() *OrderStore {
return s.order
}
// Position 获取仓位存储
// Position gets position storage
func (s *Store) Position() *PositionStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -261,7 +261,7 @@ func (s *Store) Position() *PositionStore {
return s.position
}
// Strategy 获取策略存储
// Strategy gets strategy storage
func (s *Store) Strategy() *StrategyStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -271,7 +271,7 @@ func (s *Store) Strategy() *StrategyStore {
return s.strategy
}
// Equity 获取净值存储
// Equity gets equity storage
func (s *Store) Equity() *EquityStore {
s.mu.Lock()
defer s.mu.Unlock()
@@ -281,22 +281,22 @@ func (s *Store) Equity() *EquityStore {
return s.equity
}
// Close 关闭数据库连接
// Close closes database connection
func (s *Store) Close() error {
return s.db.Close()
}
// DB 获取底层数据库连接(仅用于兼容旧代码,逐步废弃)
// Deprecated: 使用 Store 的方法代替
// DB gets underlying database connection (for legacy code compatibility, gradually deprecated)
// Deprecated: use Store methods instead
func (s *Store) DB() *sql.DB {
return s.db
}
// Transaction 执行事务
// Transaction executes transaction
func (s *Store) Transaction(fn func(tx *sql.Tx) error) error {
tx, err := s.db.Begin()
if err != nil {
return fmt.Errorf("开始事务失败: %w", err)
return fmt.Errorf("failed to begin transaction: %w", err)
}
if err := fn(tx); err != nil {
@@ -305,7 +305,7 @@ func (s *Store) Transaction(fn func(tx *sql.Tx) error) error {
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("提交事务失败: %w", err)
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}

View File

@@ -7,139 +7,139 @@ import (
"time"
)
// StrategyStore 策略存储
// StrategyStore strategy storage
type StrategyStore struct {
db *sql.DB
}
// Strategy 策略配置
// Strategy strategy configuration
type Strategy struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Description string `json:"description"`
IsActive bool `json:"is_active"` // 是否激活(一个用户只能有一个激活的策略)
IsDefault bool `json:"is_default"` // 是否为系统默认策略
Config string `json:"config"` // JSON 格式的策略配置
IsActive bool `json:"is_active"` // whether it is active (a user can only have one active strategy)
IsDefault bool `json:"is_default"` // whether it is a system default strategy
Config string `json:"config"` // strategy configuration in JSON format
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// StrategyConfig 策略配置详情JSON 结构)
// StrategyConfig strategy configuration details (JSON structure)
type StrategyConfig struct {
// 币种来源配置
// coin source configuration
CoinSource CoinSourceConfig `json:"coin_source"`
// 量化数据配置
// quantitative data configuration
Indicators IndicatorConfig `json:"indicators"`
// 自定义 Prompt附加在最后
// custom prompt (appended at the end)
CustomPrompt string `json:"custom_prompt,omitempty"`
// 风险控制配置
// risk control configuration
RiskControl RiskControlConfig `json:"risk_control"`
// System Prompt 可编辑部分
// editable sections of System Prompt
PromptSections PromptSectionsConfig `json:"prompt_sections,omitempty"`
}
// PromptSectionsConfig System Prompt 可编辑部分
// PromptSectionsConfig editable sections of System Prompt
type PromptSectionsConfig struct {
// 角色定义(标题+描述)
// role definition (title + description)
RoleDefinition string `json:"role_definition,omitempty"`
// 交易频率认知
// trading frequency awareness
TradingFrequency string `json:"trading_frequency,omitempty"`
// 开仓标准
// entry standards
EntryStandards string `json:"entry_standards,omitempty"`
// 决策流程
// decision process
DecisionProcess string `json:"decision_process,omitempty"`
}
// CoinSourceConfig 币种来源配置
// CoinSourceConfig coin source configuration
type CoinSourceConfig struct {
// 来源类型: "static" | "coinpool" | "oi_top" | "mixed"
// source type: "static" | "coinpool" | "oi_top" | "mixed"
SourceType string `json:"source_type"`
// 静态币种列表(当 source_type = "static" 时使用)
// static coin list (used when source_type = "static")
StaticCoins []string `json:"static_coins,omitempty"`
// 是否使用 AI500 币种池
// whether to use AI500 coin pool
UseCoinPool bool `json:"use_coin_pool"`
// AI500 币种池最大数量
// AI500 coin pool maximum count
CoinPoolLimit int `json:"coin_pool_limit,omitempty"`
// AI500 币种池 API URL策略级别配置
// AI500 coin pool API URL (strategy-level configuration)
CoinPoolAPIURL string `json:"coin_pool_api_url,omitempty"`
// 是否使用 OI Top
// whether to use OI Top
UseOITop bool `json:"use_oi_top"`
// OI Top 最大数量
// OI Top maximum count
OITopLimit int `json:"oi_top_limit,omitempty"`
// OI Top API URL(策略级别配置)
// OI Top API URL (strategy-level configuration)
OITopAPIURL string `json:"oi_top_api_url,omitempty"`
}
// IndicatorConfig 指标配置
// IndicatorConfig indicator configuration
type IndicatorConfig struct {
// K线配置
// K-line configuration
Klines KlineConfig `json:"klines"`
// 技术指标开关
// technical indicator switches
EnableEMA bool `json:"enable_ema"`
EnableMACD bool `json:"enable_macd"`
EnableRSI bool `json:"enable_rsi"`
EnableATR bool `json:"enable_atr"`
EnableVolume bool `json:"enable_volume"`
EnableOI bool `json:"enable_oi"` // 持仓量
EnableFundingRate bool `json:"enable_funding_rate"` // 资金费率
// EMA 周期配置
EMAPeriods []int `json:"ema_periods,omitempty"` // 默认 [20, 50]
// RSI 周期配置
RSIPeriods []int `json:"rsi_periods,omitempty"` // 默认 [7, 14]
// ATR 周期配置
ATRPeriods []int `json:"atr_periods,omitempty"` // 默认 [14]
// 外部数据源
EnableOI bool `json:"enable_oi"` // open interest
EnableFundingRate bool `json:"enable_funding_rate"` // funding rate
// EMA period configuration
EMAPeriods []int `json:"ema_periods,omitempty"` // default [20, 50]
// RSI period configuration
RSIPeriods []int `json:"rsi_periods,omitempty"` // default [7, 14]
// ATR period configuration
ATRPeriods []int `json:"atr_periods,omitempty"` // default [14]
// external data sources
ExternalDataSources []ExternalDataSource `json:"external_data_sources,omitempty"`
// 量化数据源(资金流向、持仓变化、价格变化)
EnableQuantData bool `json:"enable_quant_data"` // 是否启用量化数据
QuantDataAPIURL string `json:"quant_data_api_url,omitempty"` // 量化数据 API 地址
// quantitative data sources (capital flow, position changes, price changes)
EnableQuantData bool `json:"enable_quant_data"` // whether to enable quantitative data
QuantDataAPIURL string `json:"quant_data_api_url,omitempty"` // quantitative data API address
}
// KlineConfig K线配置
// KlineConfig K-line configuration
type KlineConfig struct {
// 主时间周期: "1m", "3m", "5m", "15m", "1h", "4h"
// primary timeframe: "1m", "3m", "5m", "15m", "1h", "4h"
PrimaryTimeframe string `json:"primary_timeframe"`
// 主时间周期 K 线数量
// primary timeframe K-line count
PrimaryCount int `json:"primary_count"`
// 长周期时间框架
// longer timeframe
LongerTimeframe string `json:"longer_timeframe,omitempty"`
// 长周期 K 线数量
// longer timeframe K-line count
LongerCount int `json:"longer_count,omitempty"`
// 是否启用多时间框架分析
// whether to enable multi-timeframe analysis
EnableMultiTimeframe bool `json:"enable_multi_timeframe"`
// 选中的时间周期列表(新增:支持多周期选择)
// selected timeframe list (new: supports multi-timeframe selection)
SelectedTimeframes []string `json:"selected_timeframes,omitempty"`
}
// ExternalDataSource 外部数据源配置
// ExternalDataSource external data source configuration
type ExternalDataSource struct {
Name string `json:"name"` // 数据源名称
Type string `json:"type"` // 类型: "api" | "webhook"
Name string `json:"name"` // data source name
Type string `json:"type"` // type: "api" | "webhook"
URL string `json:"url"` // API URL
Method string `json:"method"` // HTTP 方法
Method string `json:"method"` // HTTP method
Headers map[string]string `json:"headers,omitempty"`
DataPath string `json:"data_path,omitempty"` // JSON 数据路径
RefreshSecs int `json:"refresh_secs,omitempty"` // 刷新间隔(秒)
DataPath string `json:"data_path,omitempty"` // JSON data path
RefreshSecs int `json:"refresh_secs,omitempty"` // refresh interval (seconds)
}
// RiskControlConfig 风险控制配置
// RiskControlConfig risk control configuration
type RiskControlConfig struct {
// 最大持仓数量
// maximum number of positions
MaxPositions int `json:"max_positions"`
// BTC/ETH 最大杠杆
// BTC/ETH maximum leverage
BTCETHMaxLeverage int `json:"btc_eth_max_leverage"`
// 山寨币最大杠杆
// altcoin maximum leverage
AltcoinMaxLeverage int `json:"altcoin_max_leverage"`
// 最小风险回报比
// minimum risk-reward ratio
MinRiskRewardRatio float64 `json:"min_risk_reward_ratio"`
// 最大保证金使用率
// maximum margin usage
MaxMarginUsage float64 `json:"max_margin_usage"`
// 单币种最大仓位比例(相对账户净值)
// maximum position ratio per coin (relative to account equity)
MaxPositionRatio float64 `json:"max_position_ratio"`
// 最小开仓金额(USDT
// minimum position size (USDT)
MinPositionSize float64 `json:"min_position_size"`
// 最小信心度
// minimum confidence level
MinConfidence int `json:"min_confidence"`
}
@@ -161,11 +161,11 @@ func (s *StrategyStore) initTables() error {
return err
}
// 创建索引
// create indexes
_, _ = s.db.Exec(`CREATE INDEX IF NOT EXISTS idx_strategies_user_id ON strategies(user_id)`)
_, _ = s.db.Exec(`CREATE INDEX IF NOT EXISTS idx_strategies_is_active ON strategies(is_active)`)
// 触发器:更新时自动更新 updated_at
// trigger: automatically update updated_at on update
_, err = s.db.Exec(`
CREATE TRIGGER IF NOT EXISTS update_strategies_updated_at
AFTER UPDATE ON strategies
@@ -178,14 +178,14 @@ func (s *StrategyStore) initTables() error {
}
func (s *StrategyStore) initDefaultData() error {
// 检查是否已有默认策略
// check if default strategy already exists
var count int
s.db.QueryRow(`SELECT COUNT(*) FROM strategies WHERE is_default = 1`).Scan(&count)
if count > 0 {
return nil
}
// 创建系统默认策略
// create system default strategy
defaultConfig := StrategyConfig{
CoinSource: CoinSourceConfig{
SourceType: "coinpool",
@@ -228,23 +228,23 @@ func (s *StrategyStore) initDefaultData() error {
MinConfidence: 75,
},
PromptSections: PromptSectionsConfig{
RoleDefinition: `# 你是专业的加密货币交易AI
RoleDefinition: `# You are a professional cryptocurrency trading AI
你的任务是根据提供的市场数据做出交易决策。你是一位经验丰富的量化交易员,擅长技术分析和风险管理。`,
TradingFrequency: `# ⏱️ 交易频率认知
Your task is to make trading decisions based on the provided market data. You are an experienced quantitative trader skilled in technical analysis and risk management.`,
TradingFrequency: `# ⏱️ Trading Frequency Awareness
- 优秀交易员每天2-4笔 ≈ 每小时0.1-0.2笔
- 每小时>2笔 = 过度交易
- 单笔持仓时间≥30-60分钟
如果你发现自己每个周期都在交易 → 标准过低;若持仓<30分钟就平仓 → 过于急躁。`,
EntryStandards: `# 🎯 开仓标准(严格)
- Excellent trader: 2-4 trades per day ≈ 0.1-0.2 trades per hour
- >2 trades per hour = overtrading
- Single position holding time ≥ 30-60 minutes
If you find yourself trading every cycle → standards are too low; if closing positions in <30 minutes → too impulsive.`,
EntryStandards: `# 🎯 Entry Standards (Strict)
只在多重信号共振时开仓。自由运用任何有效的分析方法,避免单一指标、信号矛盾、横盘震荡、刚平仓即重启等低质量行为。`,
DecisionProcess: `# 📋 决策流程
Only enter positions when multiple signals resonate. Freely use any effective analysis methods, avoid low-quality behaviors such as single indicators, contradictory signals, sideways oscillation, or immediately restarting after closing positions.`,
DecisionProcess: `# 📋 Decision Process
1. 检查持仓 → 是否该止盈/止损
2. 扫描候选币 + 多时间框 → 是否存在强信号
3. 先写思维链,再输出结构化JSON`,
1. Check positions → whether to take profit/stop loss
2. Scan candidate coins + multi-timeframe → whether strong signals exist
3. Write chain of thought first, then output structured JSON`,
},
}
@@ -252,13 +252,13 @@ func (s *StrategyStore) initDefaultData() error {
_, err := s.db.Exec(`
INSERT INTO strategies (id, user_id, name, description, is_active, is_default, config)
VALUES ('default', 'system', '默认山寨策略', '系统默认的山寨币交易策略,使用 AI500 币种池,包含完整的技术指标', 0, 1, ?)
VALUES ('default', 'system', 'Default Altcoin Strategy', 'System default altcoin trading strategy, uses AI500 coin pool, includes complete technical indicators', 0, 1, ?)
`, string(configJSON))
return err
}
// Create 创建策略
// Create create a strategy
func (s *StrategyStore) Create(strategy *Strategy) error {
_, err := s.db.Exec(`
INSERT INTO strategies (id, user_id, name, description, is_active, is_default, config)
@@ -267,7 +267,7 @@ func (s *StrategyStore) Create(strategy *Strategy) error {
return err
}
// Update 更新策略
// Update update a strategy
func (s *StrategyStore) Update(strategy *Strategy) error {
_, err := s.db.Exec(`
UPDATE strategies SET
@@ -277,22 +277,22 @@ func (s *StrategyStore) Update(strategy *Strategy) error {
return err
}
// Delete 删除策略
// Delete delete a strategy
func (s *StrategyStore) Delete(userID, id string) error {
// 不允许删除系统默认策略
// do not allow deleting system default strategy
var isDefault bool
s.db.QueryRow(`SELECT is_default FROM strategies WHERE id = ?`, id).Scan(&isDefault)
if isDefault {
return fmt.Errorf("不能删除系统默认策略")
return fmt.Errorf("cannot delete system default strategy")
}
_, err := s.db.Exec(`DELETE FROM strategies WHERE id = ? AND user_id = ?`, id, userID)
return err
}
// List 获取用户的策略列表
// List get user's strategy list
func (s *StrategyStore) List(userID string) ([]*Strategy, error) {
// 获取用户自己的策略 + 系统默认策略
// get user's own strategies + system default strategy
rows, err := s.db.Query(`
SELECT id, user_id, name, description, is_active, is_default, config, created_at, updated_at
FROM strategies
@@ -323,7 +323,7 @@ func (s *StrategyStore) List(userID string) ([]*Strategy, error) {
return strategies, nil
}
// Get 获取单个策略
// Get get a single strategy
func (s *StrategyStore) Get(userID, id string) (*Strategy, error) {
var st Strategy
var createdAt, updatedAt string
@@ -344,7 +344,7 @@ func (s *StrategyStore) Get(userID, id string) (*Strategy, error) {
return &st, nil
}
// GetActive 获取用户当前激活的策略
// GetActive get user's currently active strategy
func (s *StrategyStore) GetActive(userID string) (*Strategy, error) {
var st Strategy
var createdAt, updatedAt string
@@ -358,7 +358,7 @@ func (s *StrategyStore) GetActive(userID string) (*Strategy, error) {
&createdAt, &updatedAt,
)
if err == sql.ErrNoRows {
// 没有激活的策略,返回系统默认策略
// no active strategy, return system default strategy
return s.GetDefault()
}
if err != nil {
@@ -369,7 +369,7 @@ func (s *StrategyStore) GetActive(userID string) (*Strategy, error) {
return &st, nil
}
// GetDefault 获取系统默认策略
// GetDefault get system default strategy
func (s *StrategyStore) GetDefault() (*Strategy, error) {
var st Strategy
var createdAt, updatedAt string
@@ -391,22 +391,22 @@ func (s *StrategyStore) GetDefault() (*Strategy, error) {
return &st, nil
}
// SetActive 设置激活策略(会先取消其他策略的激活状态)
// SetActive set active strategy (will first deactivate other strategies)
func (s *StrategyStore) SetActive(userID, strategyID string) error {
// 开启事务
// begin transaction
tx, err := s.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// 先取消该用户所有策略的激活状态
// first deactivate all strategies for the user
_, err = tx.Exec(`UPDATE strategies SET is_active = 0 WHERE user_id = ?`, userID)
if err != nil {
return err
}
// 激活指定策略
// activate specified strategy
_, err = tx.Exec(`UPDATE strategies SET is_active = 1 WHERE id = ? AND (user_id = ? OR is_default = 1)`, strategyID, userID)
if err != nil {
return err
@@ -415,20 +415,20 @@ func (s *StrategyStore) SetActive(userID, strategyID string) error {
return tx.Commit()
}
// Duplicate 复制策略(用于基于默认策略创建自定义策略)
// Duplicate duplicate a strategy (used to create custom strategy based on default strategy)
func (s *StrategyStore) Duplicate(userID, sourceID, newID, newName string) error {
// 获取源策略
// get source strategy
source, err := s.Get(userID, sourceID)
if err != nil {
return fmt.Errorf("获取源策略失败: %w", err)
return fmt.Errorf("failed to get source strategy: %w", err)
}
// 创建新策略
// create new strategy
newStrategy := &Strategy{
ID: newID,
UserID: userID,
Name: newName,
Description: "基于 [" + source.Name + "] 创建",
Description: "Created based on [" + source.Name + "]",
IsActive: false,
IsDefault: false,
Config: source.Config,
@@ -437,20 +437,20 @@ func (s *StrategyStore) Duplicate(userID, sourceID, newID, newName string) error
return s.Create(newStrategy)
}
// ParseConfig 解析策略配置 JSON
// ParseConfig parse strategy configuration JSON
func (s *Strategy) ParseConfig() (*StrategyConfig, error) {
var config StrategyConfig
if err := json.Unmarshal([]byte(s.Config), &config); err != nil {
return nil, fmt.Errorf("解析策略配置失败: %w", err)
return nil, fmt.Errorf("failed to parse strategy configuration: %w", err)
}
return &config, nil
}
// SetConfig 设置策略配置
// SetConfig set strategy configuration
func (s *Strategy) SetConfig(config *StrategyConfig) error {
data, err := json.Marshal(config)
if err != nil {
return fmt.Errorf("序列化策略配置失败: %w", err)
return fmt.Errorf("failed to serialize strategy configuration: %w", err)
}
s.Config = string(data)
return nil

View File

@@ -5,20 +5,20 @@ import (
"time"
)
// TraderStore 交易员存储
// TraderStore trader storage
type TraderStore struct {
db *sql.DB
decryptFunc func(string) string
}
// Trader 交易员配置
// Trader trader configuration
type Trader struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
AIModelID string `json:"ai_model_id"`
ExchangeID string `json:"exchange_id"`
StrategyID string `json:"strategy_id"` // 关联策略ID
StrategyID string `json:"strategy_id"` // Associated strategy ID
InitialBalance float64 `json:"initial_balance"`
ScanIntervalMinutes int `json:"scan_interval_minutes"`
IsRunning bool `json:"is_running"`
@@ -26,7 +26,7 @@ type Trader struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// 以下字段已废弃,保留用于向后兼容,新交易员应使用 StrategyID
// Following fields are deprecated, kept for backward compatibility, new traders should use StrategyID
BTCETHLeverage int `json:"btc_eth_leverage,omitempty"`
AltcoinLeverage int `json:"altcoin_leverage,omitempty"`
TradingSymbols string `json:"trading_symbols,omitempty"`
@@ -37,12 +37,12 @@ type Trader struct {
SystemPromptTemplate string `json:"system_prompt_template,omitempty"`
}
// TraderFullConfig 交易员完整配置包含AI模型、交易所和策略
// TraderFullConfig trader full configuration (includes AI model, exchange and strategy)
type TraderFullConfig struct {
Trader *Trader
AIModel *AIModel
Exchange *Exchange
Strategy *Strategy // 关联的策略配置
Strategy *Strategy // Associated strategy configuration
}
func (s *TraderStore) initTables() error {
@@ -74,7 +74,7 @@ func (s *TraderStore) initTables() error {
return err
}
// 触发器
// Trigger
_, err = s.db.Exec(`
CREATE TRIGGER IF NOT EXISTS update_traders_updated_at
AFTER UPDATE ON traders
@@ -86,7 +86,7 @@ func (s *TraderStore) initTables() error {
return err
}
// 向后兼容
// Backward compatibility
alterQueries := []string{
`ALTER TABLE traders ADD COLUMN custom_prompt TEXT DEFAULT ''`,
`ALTER TABLE traders ADD COLUMN override_base_prompt BOOLEAN DEFAULT 0`,
@@ -113,7 +113,7 @@ func (s *TraderStore) decrypt(encrypted string) string {
return encrypted
}
// Create 创建交易员
// Create creates trader
func (s *TraderStore) Create(trader *Trader) error {
_, err := s.db.Exec(`
INSERT INTO traders (id, user_id, name, ai_model_id, exchange_id, strategy_id, initial_balance,
@@ -128,7 +128,7 @@ func (s *TraderStore) Create(trader *Trader) error {
return err
}
// List 获取用户的交易员列表
// List gets user's trader list
func (s *TraderStore) List(userID string) ([]*Trader, error) {
rows, err := s.db.Query(`
SELECT id, user_id, name, ai_model_id, exchange_id, COALESCE(strategy_id, ''),
@@ -165,13 +165,13 @@ func (s *TraderStore) List(userID string) ([]*Trader, error) {
return traders, nil
}
// UpdateStatus 更新交易员运行状态
// UpdateStatus updates trader running status
func (s *TraderStore) UpdateStatus(userID, id string, isRunning bool) error {
_, err := s.db.Exec(`UPDATE traders SET is_running = ? WHERE id = ? AND user_id = ?`, isRunning, id, userID)
return err
}
// Update 更新交易员配置
// Update updates trader configuration
func (s *TraderStore) Update(trader *Trader) error {
_, err := s.db.Exec(`
UPDATE traders SET
@@ -184,26 +184,26 @@ func (s *TraderStore) Update(trader *Trader) error {
return err
}
// UpdateInitialBalance 更新初始余额
// UpdateInitialBalance updates initial balance
func (s *TraderStore) UpdateInitialBalance(userID, id string, newBalance float64) error {
_, err := s.db.Exec(`UPDATE traders SET initial_balance = ? WHERE id = ? AND user_id = ?`, newBalance, id, userID)
return err
}
// UpdateCustomPrompt 更新自定义提示词
// UpdateCustomPrompt updates custom prompt
func (s *TraderStore) UpdateCustomPrompt(userID, id string, customPrompt string, overrideBase bool) error {
_, err := s.db.Exec(`UPDATE traders SET custom_prompt = ?, override_base_prompt = ? WHERE id = ? AND user_id = ?`,
customPrompt, overrideBase, id, userID)
return err
}
// Delete 删除交易员
// Delete deletes trader
func (s *TraderStore) Delete(userID, id string) error {
_, err := s.db.Exec(`DELETE FROM traders WHERE id = ? AND user_id = ?`, id, userID)
return err
}
// GetFullConfig 获取交易员完整配置
// GetFullConfig gets trader full configuration
func (s *TraderStore) GetFullConfig(userID, traderID string) (*TraderFullConfig, error) {
var trader Trader
var aiModel AIModel
@@ -255,7 +255,7 @@ func (s *TraderStore) GetFullConfig(userID, traderID string) (*TraderFullConfig,
exchange.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", exchangeCreatedAt)
exchange.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", exchangeUpdatedAt)
// 解密
// Decrypt
aiModel.APIKey = s.decrypt(aiModel.APIKey)
exchange.APIKey = s.decrypt(exchange.APIKey)
exchange.SecretKey = s.decrypt(exchange.SecretKey)
@@ -264,12 +264,12 @@ func (s *TraderStore) GetFullConfig(userID, traderID string) (*TraderFullConfig,
exchange.LighterPrivateKey = s.decrypt(exchange.LighterPrivateKey)
exchange.LighterAPIKeyPrivateKey = s.decrypt(exchange.LighterAPIKeyPrivateKey)
// 加载关联的策略
// Load associated strategy
var strategy *Strategy
if trader.StrategyID != "" {
strategy, _ = s.getStrategyByID(userID, trader.StrategyID)
}
// 如果没有关联策略,获取用户的激活策略或默认策略
// If no associated strategy, get user's active strategy or default strategy
if strategy == nil {
strategy, _ = s.getActiveOrDefaultStrategy(userID)
}
@@ -282,7 +282,7 @@ func (s *TraderStore) GetFullConfig(userID, traderID string) (*TraderFullConfig,
}, nil
}
// getStrategyByID 内部方法根据ID获取策略
// getStrategyByID internal method: gets strategy by ID
func (s *TraderStore) getStrategyByID(userID, strategyID string) (*Strategy, error) {
var strategy Strategy
var createdAt, updatedAt string
@@ -301,12 +301,12 @@ func (s *TraderStore) getStrategyByID(userID, strategyID string) (*Strategy, err
return &strategy, nil
}
// getActiveOrDefaultStrategy 内部方法:获取用户激活的策略或系统默认策略
// getActiveOrDefaultStrategy internal method: gets user's active strategy or system default strategy
func (s *TraderStore) getActiveOrDefaultStrategy(userID string) (*Strategy, error) {
var strategy Strategy
var createdAt, updatedAt string
// 先尝试获取用户激活的策略
// First try to get user's active strategy
err := s.db.QueryRow(`
SELECT id, user_id, name, description, is_active, is_default, config, created_at, updated_at
FROM strategies WHERE user_id = ? AND is_active = 1
@@ -320,7 +320,7 @@ func (s *TraderStore) getActiveOrDefaultStrategy(userID string) (*Strategy, erro
return &strategy, nil
}
// 回退到系统默认策略
// Fallback to system default strategy
err = s.db.QueryRow(`
SELECT id, user_id, name, description, is_active, is_default, config, created_at, updated_at
FROM strategies WHERE is_default = 1 LIMIT 1
@@ -336,7 +336,7 @@ func (s *TraderStore) getActiveOrDefaultStrategy(userID string) (*Strategy, erro
return &strategy, nil
}
// ListAll 获取所有用户的交易员列表
// ListAll gets all users' trader list
func (s *TraderStore) ListAll() ([]*Trader, error) {
rows, err := s.db.Query(`
SELECT id, user_id, name, ai_model_id, exchange_id, COALESCE(strategy_id, ''),

View File

@@ -7,12 +7,12 @@ import (
"time"
)
// UserStore 用户存储
// UserStore user storage
type UserStore struct {
db *sql.DB
}
// User 用户
// User user
type User struct {
ID string `json:"id"`
Email string `json:"email"`
@@ -23,7 +23,7 @@ type User struct {
UpdatedAt time.Time `json:"updated_at"`
}
// GenerateOTPSecret 生成OTP密钥
// GenerateOTPSecret generates OTP secret
func GenerateOTPSecret() (string, error) {
secret := make([]byte, 20)
_, err := rand.Read(secret)
@@ -49,7 +49,7 @@ func (s *UserStore) initTables() error {
return err
}
// 触发器
// Trigger
_, err = s.db.Exec(`
CREATE TRIGGER IF NOT EXISTS update_users_updated_at
AFTER UPDATE ON users
@@ -64,7 +64,7 @@ func (s *UserStore) initTables() error {
return nil
}
// Create 创建用户
// Create creates user
func (s *UserStore) Create(user *User) error {
_, err := s.db.Exec(`
INSERT INTO users (id, email, password_hash, otp_secret, otp_verified)
@@ -73,7 +73,7 @@ func (s *UserStore) Create(user *User) error {
return err
}
// GetByEmail 通过邮箱获取用户
// GetByEmail gets user by email
func (s *UserStore) GetByEmail(email string) (*User, error) {
var user User
var createdAt, updatedAt string
@@ -92,7 +92,7 @@ func (s *UserStore) GetByEmail(email string) (*User, error) {
return &user, nil
}
// GetByID 通过ID获取用户
// GetByID gets user by ID
func (s *UserStore) GetByID(userID string) (*User, error) {
var user User
var createdAt, updatedAt string
@@ -111,7 +111,7 @@ func (s *UserStore) GetByID(userID string) (*User, error) {
return &user, nil
}
// GetAllIDs 获取所有用户ID
// GetAllIDs gets all user IDs
func (s *UserStore) GetAllIDs() ([]string, error) {
rows, err := s.db.Query(`SELECT id FROM users ORDER BY id`)
if err != nil {
@@ -130,13 +130,13 @@ func (s *UserStore) GetAllIDs() ([]string, error) {
return userIDs, nil
}
// UpdateOTPVerified 更新OTP验证状态
// UpdateOTPVerified updates OTP verification status
func (s *UserStore) UpdateOTPVerified(userID string, verified bool) error {
_, err := s.db.Exec(`UPDATE users SET otp_verified = ? WHERE id = ?`, verified, userID)
return err
}
// UpdatePassword 更新密码
// UpdatePassword updates password
func (s *UserStore) UpdatePassword(userID, passwordHash string) error {
_, err := s.db.Exec(`
UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
@@ -144,7 +144,7 @@ func (s *UserStore) UpdatePassword(userID, passwordHash string) error {
return err
}
// EnsureAdmin 确保admin用户存在
// EnsureAdmin ensures admin user exists
func (s *UserStore) EnsureAdmin() error {
var count int
err := s.db.QueryRow(`SELECT COUNT(*) FROM users WHERE id = 'admin'`).Scan(&count)