package db import ( "database/sql" "log" "os" "path/filepath" "time" _ "modernc.org/sqlite" ) // DB wraps the sql.DB connection with our schema. type DB struct { *sql.DB Path string } // Open opens or creates the SQLite database at the given path. func Open(path string) (*DB, error) { if path == "" { home, _ := os.UserHomeDir() path = filepath.Join(home, "Project", "exchange-monitor-go", "data", "trades.db") } // Ensure directory exists os.MkdirAll(filepath.Dir(path), 0755) db, err := sql.Open("sqlite", path) if err != nil { return nil, err } db.SetMaxOpenConns(1) // SQLite single-writer db.SetMaxIdleConns(1) d := &DB{DB: db, Path: path} if err := d.migrate(); err != nil { return nil, err } return d, nil } func (d *DB) migrate() error { schema := ` CREATE TABLE IF NOT EXISTS trades ( id INTEGER PRIMARY KEY AUTOINCREMENT, coin TEXT NOT NULL, direction TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'open', entry_spread REAL, exit_spread REAL, long_exchange TEXT, short_exchange TEXT, long_entry REAL, long_exit REAL, short_entry REAL, short_exit REAL, long_pnl REAL, short_pnl REAL, fee_entry REAL, fee_exit REAL, net_pnl REAL, amount_usd REAL, scale_count INTEGER DEFAULT 0, exit_reason TEXT, convergence TEXT, opened_at DATETIME NOT NULL, closed_at DATETIME ); CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, trade_id INTEGER NOT NULL REFERENCES trades(id), leg TEXT NOT NULL, type TEXT NOT NULL, exchange TEXT NOT NULL, side TEXT NOT NULL, price REAL, size REAL, fee REAL, order_id TEXT, status TEXT, created_at DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS config_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT NOT NULL, value TEXT NOT NULL, changed_at DATETIME NOT NULL ); CREATE INDEX IF NOT EXISTS idx_trades_coin ON trades(coin); CREATE INDEX IF NOT EXISTS idx_trades_status ON trades(status); CREATE INDEX IF NOT EXISTS idx_trades_opened ON trades(opened_at); CREATE INDEX IF NOT EXISTS idx_orders_trade_id ON orders(trade_id); ` _, err := d.Exec(schema) if err != nil { return err } log.Printf("[DB] SQLite ready: %s", d.Path) return nil } // Now returns the current time in UTC. func Now() time.Time { return time.Now().UTC() }