From e3bf196a59f7a0220f913c55f3bfa2dd0a1ab6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Tue, 30 Jun 2026 23:53:57 +0800 Subject: [PATCH] fix: ALTER TABLE migrations must run before CREATE TABLE + currency validation in write_holdings_batch --- mofin_db.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/mofin_db.py b/mofin_db.py index 2f97574..2b8fff9 100644 --- a/mofin_db.py +++ b/mofin_db.py @@ -370,6 +370,32 @@ def init_all_tables(conn: sqlite3.Connection): conn.execute("ALTER TABLE signal_news ADD COLUMN source TEXT DEFAULT 'trend'") except sqlite3.OperationalError: pass + + # ── 币种约束迁移(2026-06-30)──────────────────────────────── + _currency_migrations = [ + ("holdings", ["price REAL", "market_value REAL", "change_pct REAL", + "currency TEXT NOT NULL DEFAULT 'CNY'"]), + ("holding_strategies", ["name TEXT", "price REAL", "cost REAL", "shares INTEGER DEFAULT 0", + "currency TEXT NOT NULL DEFAULT 'CNY'", + "action TEXT", "timing_signal TEXT", "rr_ratio REAL", + "tech_snapshot TEXT", "stock_category TEXT", + "sector_context TEXT", "status TEXT DEFAULT 'active'", + "trigger_json TEXT", "changelog_json TEXT", + "updated_at TEXT"]), + ("portfolio_summary", ["total_mv REAL", "frozen_cash REAL DEFAULT 0", + "currency TEXT NOT NULL DEFAULT 'CNY'"]), + ("watchlist_stocks", ["price REAL", "entry_low REAL", "entry_high REAL", + "stop_loss REAL", "currency TEXT NOT NULL DEFAULT 'CNY'", + "source TEXT", "source_detail TEXT", "notes TEXT", + "added_by TEXT", "analysis_json TEXT"]), + ] + for table, columns in _currency_migrations: + for col_def in columns: + col_name = col_def.split()[0] + try: + conn.execute(f"ALTER TABLE {table} ADD COLUMN {col_def}") + except sqlite3.OperationalError: + pass # column already exists conn.commit() @@ -875,6 +901,9 @@ def write_holdings_batch(conn, holdings: list[dict]) -> tuple[bool, str]: """批量写入持仓(替代 portfolio.json holdings[])""" try: for h in holdings: + currency = str(h.get('currency', 'CNY')).upper() + if currency not in ('CNY', 'HKD'): + return False, f"非法币种: {currency}(必须 CNY 或 HKD)" conn.execute(""" INSERT INTO holdings (code, name, shares, cost, price, market_value, change_pct, currency, position_pct, added_at, is_active)