mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
390 lines
11 KiB
Go
390 lines
11 KiB
Go
package config
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"time"
|
||
|
||
_ "github.com/mattn/go-sqlite3"
|
||
)
|
||
|
||
// Database 配置数据库
|
||
type Database struct {
|
||
db *sql.DB
|
||
}
|
||
|
||
// NewDatabase 创建配置数据库
|
||
func NewDatabase(dbPath string) (*Database, error) {
|
||
db, err := sql.Open("sqlite3", dbPath)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("打开数据库失败: %w", err)
|
||
}
|
||
|
||
database := &Database{db: db}
|
||
if err := database.createTables(); err != nil {
|
||
return nil, fmt.Errorf("创建表失败: %w", err)
|
||
}
|
||
|
||
if err := database.initDefaultData(); err != nil {
|
||
return nil, fmt.Errorf("初始化默认数据失败: %w", err)
|
||
}
|
||
|
||
return database, nil
|
||
}
|
||
|
||
// createTables 创建数据库表
|
||
func (d *Database) createTables() error {
|
||
queries := []string{
|
||
// AI模型配置表
|
||
`CREATE TABLE IF NOT EXISTS ai_models (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
provider TEXT NOT NULL,
|
||
enabled BOOLEAN DEFAULT 0,
|
||
api_key TEXT DEFAULT '',
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)`,
|
||
|
||
// 交易所配置表
|
||
`CREATE TABLE IF NOT EXISTS exchanges (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
type TEXT NOT NULL, -- 'cex' or 'dex'
|
||
enabled BOOLEAN DEFAULT 0,
|
||
api_key TEXT DEFAULT '',
|
||
secret_key TEXT DEFAULT '',
|
||
testnet BOOLEAN DEFAULT 0,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)`,
|
||
|
||
// 交易员配置表
|
||
`CREATE TABLE IF NOT EXISTS traders (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
ai_model_id TEXT NOT NULL,
|
||
exchange_id TEXT NOT NULL,
|
||
initial_balance REAL NOT NULL,
|
||
scan_interval_minutes INTEGER DEFAULT 3,
|
||
is_running BOOLEAN DEFAULT 0,
|
||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (ai_model_id) REFERENCES ai_models(id),
|
||
FOREIGN KEY (exchange_id) REFERENCES exchanges(id)
|
||
)`,
|
||
|
||
// 系统配置表
|
||
`CREATE TABLE IF NOT EXISTS system_config (
|
||
key TEXT PRIMARY KEY,
|
||
value TEXT NOT NULL,
|
||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)`,
|
||
|
||
// 触发器:自动更新 updated_at
|
||
`CREATE TRIGGER IF NOT EXISTS update_ai_models_updated_at
|
||
AFTER UPDATE ON ai_models
|
||
BEGIN
|
||
UPDATE ai_models SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||
END`,
|
||
|
||
`CREATE TRIGGER IF NOT EXISTS update_exchanges_updated_at
|
||
AFTER UPDATE ON exchanges
|
||
BEGIN
|
||
UPDATE exchanges SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||
END`,
|
||
|
||
`CREATE TRIGGER IF NOT EXISTS update_traders_updated_at
|
||
AFTER UPDATE ON traders
|
||
BEGIN
|
||
UPDATE traders SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
|
||
END`,
|
||
|
||
`CREATE TRIGGER IF NOT EXISTS update_system_config_updated_at
|
||
AFTER UPDATE ON system_config
|
||
BEGIN
|
||
UPDATE system_config SET updated_at = CURRENT_TIMESTAMP WHERE key = NEW.key;
|
||
END`,
|
||
}
|
||
|
||
for _, query := range queries {
|
||
if _, err := d.db.Exec(query); err != nil {
|
||
return fmt.Errorf("执行SQL失败 [%s]: %w", query, err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// initDefaultData 初始化默认数据
|
||
func (d *Database) initDefaultData() error {
|
||
// 初始化AI模型
|
||
aiModels := []struct {
|
||
id, name, provider string
|
||
}{
|
||
{"deepseek", "DeepSeek", "deepseek"},
|
||
{"qwen", "Qwen", "qwen"},
|
||
}
|
||
|
||
for _, model := range aiModels {
|
||
_, err := d.db.Exec(`
|
||
INSERT OR IGNORE INTO ai_models (id, name, provider, enabled)
|
||
VALUES (?, ?, ?, 0)
|
||
`, model.id, model.name, model.provider)
|
||
if err != nil {
|
||
return fmt.Errorf("初始化AI模型失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 初始化交易所
|
||
exchanges := []struct {
|
||
id, name, typ string
|
||
}{
|
||
{"binance", "Binance", "cex"},
|
||
{"hyperliquid", "Hyperliquid", "dex"},
|
||
}
|
||
|
||
for _, exchange := range exchanges {
|
||
_, err := d.db.Exec(`
|
||
INSERT OR IGNORE INTO exchanges (id, name, type, enabled)
|
||
VALUES (?, ?, ?, 0)
|
||
`, exchange.id, exchange.name, exchange.typ)
|
||
if err != nil {
|
||
return fmt.Errorf("初始化交易所失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 初始化系统配置
|
||
systemConfigs := map[string]string{
|
||
"api_server_port": "8081",
|
||
"use_default_coins": "true",
|
||
"coin_pool_api_url": "",
|
||
"oi_top_api_url": "",
|
||
"max_daily_loss": "10.0",
|
||
"max_drawdown": "20.0",
|
||
"stop_trading_minutes": "60",
|
||
}
|
||
|
||
for key, value := range systemConfigs {
|
||
_, err := d.db.Exec(`
|
||
INSERT OR IGNORE INTO system_config (key, value)
|
||
VALUES (?, ?)
|
||
`, key, value)
|
||
if err != nil {
|
||
return fmt.Errorf("初始化系统配置失败: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// AIModelConfig AI模型配置
|
||
type AIModelConfig struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Provider string `json:"provider"`
|
||
Enabled bool `json:"enabled"`
|
||
APIKey string `json:"apiKey"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// ExchangeConfig 交易所配置
|
||
type ExchangeConfig struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Type string `json:"type"`
|
||
Enabled bool `json:"enabled"`
|
||
APIKey string `json:"apiKey"`
|
||
SecretKey string `json:"secretKey"`
|
||
Testnet bool `json:"testnet"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// TraderConfig 交易员配置
|
||
type TraderConfig struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
AIModelID string `json:"ai_model_id"`
|
||
ExchangeID string `json:"exchange_id"`
|
||
InitialBalance float64 `json:"initial_balance"`
|
||
ScanIntervalMinutes int `json:"scan_interval_minutes"`
|
||
IsRunning bool `json:"is_running"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|
||
|
||
// GetAIModels 获取所有AI模型配置
|
||
func (d *Database) GetAIModels() ([]*AIModelConfig, error) {
|
||
rows, err := d.db.Query(`
|
||
SELECT id, name, provider, enabled, api_key, created_at, updated_at
|
||
FROM ai_models ORDER BY id
|
||
`)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var models []*AIModelConfig
|
||
for rows.Next() {
|
||
var model AIModelConfig
|
||
err := rows.Scan(
|
||
&model.ID, &model.Name, &model.Provider,
|
||
&model.Enabled, &model.APIKey,
|
||
&model.CreatedAt, &model.UpdatedAt,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
models = append(models, &model)
|
||
}
|
||
|
||
return models, nil
|
||
}
|
||
|
||
// UpdateAIModel 更新AI模型配置
|
||
func (d *Database) UpdateAIModel(id string, enabled bool, apiKey string) error {
|
||
_, err := d.db.Exec(`
|
||
UPDATE ai_models SET enabled = ?, api_key = ? WHERE id = ?
|
||
`, enabled, apiKey, id)
|
||
return err
|
||
}
|
||
|
||
// GetExchanges 获取所有交易所配置
|
||
func (d *Database) GetExchanges() ([]*ExchangeConfig, error) {
|
||
rows, err := d.db.Query(`
|
||
SELECT id, name, type, enabled, api_key, secret_key, testnet, created_at, updated_at
|
||
FROM exchanges ORDER BY id
|
||
`)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var exchanges []*ExchangeConfig
|
||
for rows.Next() {
|
||
var exchange ExchangeConfig
|
||
err := rows.Scan(
|
||
&exchange.ID, &exchange.Name, &exchange.Type,
|
||
&exchange.Enabled, &exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
|
||
&exchange.CreatedAt, &exchange.UpdatedAt,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
exchanges = append(exchanges, &exchange)
|
||
}
|
||
|
||
return exchanges, nil
|
||
}
|
||
|
||
// UpdateExchange 更新交易所配置
|
||
func (d *Database) UpdateExchange(id string, enabled bool, apiKey, secretKey string, testnet bool) error {
|
||
_, err := d.db.Exec(`
|
||
UPDATE exchanges SET enabled = ?, api_key = ?, secret_key = ?, testnet = ? WHERE id = ?
|
||
`, enabled, apiKey, secretKey, testnet, id)
|
||
return err
|
||
}
|
||
|
||
// CreateTrader 创建交易员
|
||
func (d *Database) CreateTrader(trader *TraderConfig) error {
|
||
_, err := d.db.Exec(`
|
||
INSERT INTO traders (id, name, ai_model_id, exchange_id, initial_balance, scan_interval_minutes, is_running)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||
`, trader.ID, trader.Name, trader.AIModelID, trader.ExchangeID, trader.InitialBalance, trader.ScanIntervalMinutes, trader.IsRunning)
|
||
return err
|
||
}
|
||
|
||
// GetTraders 获取所有交易员
|
||
func (d *Database) GetTraders() ([]*TraderConfig, error) {
|
||
rows, err := d.db.Query(`
|
||
SELECT id, name, ai_model_id, exchange_id, initial_balance, scan_interval_minutes, is_running, created_at, updated_at
|
||
FROM traders ORDER BY created_at DESC
|
||
`)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var traders []*TraderConfig
|
||
for rows.Next() {
|
||
var trader TraderConfig
|
||
err := rows.Scan(
|
||
&trader.ID, &trader.Name, &trader.AIModelID, &trader.ExchangeID,
|
||
&trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning,
|
||
&trader.CreatedAt, &trader.UpdatedAt,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
traders = append(traders, &trader)
|
||
}
|
||
|
||
return traders, nil
|
||
}
|
||
|
||
// UpdateTraderStatus 更新交易员状态
|
||
func (d *Database) UpdateTraderStatus(id string, isRunning bool) error {
|
||
_, err := d.db.Exec(`UPDATE traders SET is_running = ? WHERE id = ?`, isRunning, id)
|
||
return err
|
||
}
|
||
|
||
// DeleteTrader 删除交易员
|
||
func (d *Database) DeleteTrader(id string) error {
|
||
_, err := d.db.Exec(`DELETE FROM traders WHERE id = ?`, id)
|
||
return err
|
||
}
|
||
|
||
// GetTraderConfig 获取交易员完整配置(包含AI模型和交易所信息)
|
||
func (d *Database) GetTraderConfig(traderID string) (*TraderConfig, *AIModelConfig, *ExchangeConfig, error) {
|
||
var trader TraderConfig
|
||
var aiModel AIModelConfig
|
||
var exchange ExchangeConfig
|
||
|
||
err := d.db.QueryRow(`
|
||
SELECT
|
||
t.id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, t.created_at, t.updated_at,
|
||
a.id, a.name, a.provider, a.enabled, a.api_key, a.created_at, a.updated_at,
|
||
e.id, e.name, e.type, e.enabled, e.api_key, e.secret_key, e.testnet, e.created_at, e.updated_at
|
||
FROM traders t
|
||
JOIN ai_models a ON t.ai_model_id = a.id
|
||
JOIN exchanges e ON t.exchange_id = e.id
|
||
WHERE t.id = ?
|
||
`, traderID).Scan(
|
||
&trader.ID, &trader.Name, &trader.AIModelID, &trader.ExchangeID,
|
||
&trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning,
|
||
&trader.CreatedAt, &trader.UpdatedAt,
|
||
&aiModel.ID, &aiModel.Name, &aiModel.Provider, &aiModel.Enabled, &aiModel.APIKey,
|
||
&aiModel.CreatedAt, &aiModel.UpdatedAt,
|
||
&exchange.ID, &exchange.Name, &exchange.Type, &exchange.Enabled,
|
||
&exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
|
||
&exchange.CreatedAt, &exchange.UpdatedAt,
|
||
)
|
||
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
|
||
return &trader, &aiModel, &exchange, nil
|
||
}
|
||
|
||
// GetSystemConfig 获取系统配置
|
||
func (d *Database) GetSystemConfig(key string) (string, error) {
|
||
var value string
|
||
err := d.db.QueryRow(`SELECT value FROM system_config WHERE key = ?`, key).Scan(&value)
|
||
return value, err
|
||
}
|
||
|
||
// SetSystemConfig 设置系统配置
|
||
func (d *Database) SetSystemConfig(key, value string) error {
|
||
_, err := d.db.Exec(`
|
||
INSERT OR REPLACE INTO system_config (key, value) VALUES (?, ?)
|
||
`, key, value)
|
||
return err
|
||
}
|
||
|
||
// Close 关闭数据库连接
|
||
func (d *Database) Close() error {
|
||
return d.db.Close()
|
||
} |