"""交易模式: sim | live, 持久化到 app_runtime_settings.""" from __future__ import annotations import os from typing import Callable from lib.instance.runtime_settings_lib import runtime_get, runtime_set, with_db TRADING_MODE_KEY = "trading.mode" MODE_SIM = "sim" MODE_LIVE = "live" VALID_MODES = (MODE_SIM, MODE_LIVE) def default_trading_mode() -> str: raw = (os.getenv("SIM_DEFAULT_MODE") or "").strip().lower() if raw in VALID_MODES: return raw return MODE_SIM def peek_persisted_trading_mode(db_path: str | None = None) -> str | None: """只读 SQLite 中的 trading.mode(供 env 配置页展示当前生效值).""" import sqlite3 path = (db_path or os.getenv("DB_PATH") or "crypto.db").strip() if not path: return None if not os.path.isabs(path): path = os.path.abspath(path) if not os.path.isfile(path): return None try: conn = sqlite3.connect(path, timeout=2) try: row = conn.execute( "SELECT value FROM app_runtime_settings WHERE key=?", (TRADING_MODE_KEY,), ).fetchone() finally: conn.close() except Exception: return None if not row: return None m = str(row[0] or "").strip().lower() return m if m in VALID_MODES else None def normalize_mode(mode: str | None) -> str: m = (mode or "").strip().lower() if m in VALID_MODES: return m raise ValueError("mode 须为 sim 或 live") def get_trading_mode(get_db: Callable) -> str: def _read(conn): v = runtime_get(conn, TRADING_MODE_KEY) if v is None or str(v).strip() == "": return default_trading_mode() m = str(v).strip().lower() return m if m in VALID_MODES else default_trading_mode() return with_db(get_db, _read) def set_trading_mode(get_db: Callable, mode: str) -> str: m = normalize_mode(mode) def _write(conn): runtime_set(conn, TRADING_MODE_KEY, m) return m return with_db(get_db, _write) def is_sim_mode(get_db: Callable) -> bool: return get_trading_mode(get_db) == MODE_SIM