mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +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()
|
|
} |