Expose sim/live matching mode in env config UI.
Saving SIM_DEFAULT_MODE also switches runtime trading.mode immediately. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+2
-2
@@ -72,8 +72,8 @@ TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true
|
||||
LIVE_TRADING_ENABLED=true
|
||||
|
||||
# =============================================================================
|
||||
# 模拟资金(本地 SQLite 钱包; 系统设置可切换 模拟资金/实盘)
|
||||
# 交易模式默认由系统设置(runtime trading.mode)切换; 也可启动时指定默认
|
||||
# 模拟资金(本地 SQLite 钱包; env「撮合模式」或系统设置可切换 sim/live)
|
||||
# sim=本地模拟资金撮合; live=实盘账户
|
||||
# =============================================================================
|
||||
SIM_DEFAULT_MODE=sim
|
||||
SIM_INITIAL_EQUITY_USDT=10000
|
||||
|
||||
Vendored
+5
@@ -99,6 +99,7 @@ HOT_RELOAD_EXACT = frozenset({
|
||||
"OKX_OPTIONS_TRADE_BUDGET_USDC",
|
||||
"OKX_OPTIONS_BUDGET_BUFFER",
|
||||
"OKX_TRADE_MODE",
|
||||
"SIM_DEFAULT_MODE",
|
||||
"MAX_ACTIVE_HEDGE_PLANS",
|
||||
"HEDGE_PLAN_LIVE_ORDER",
|
||||
"HEDGE_PLAN_OPTION_PRIMARY",
|
||||
@@ -159,6 +160,10 @@ SELECT_OPTIONS: dict[str, tuple[tuple[str, str], ...]] = {
|
||||
("perp_options", "永期对冲"),
|
||||
("options_options", "期期对冲"),
|
||||
),
|
||||
"SIM_DEFAULT_MODE": (
|
||||
("sim", "模拟(sim)"),
|
||||
("live", "实盘(live)"),
|
||||
),
|
||||
"HEDGE_PLAN_OPTION_PRIMARY": (
|
||||
("true", "以期权为主"),
|
||||
("false", "保险模式"),
|
||||
|
||||
Vendored
+17
-1
@@ -19,7 +19,12 @@ from lib.env.env_schema import (
|
||||
# 各所「交易所与实盘」字段(顺序即页面顺序)
|
||||
_EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
"okx": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
(
|
||||
"SIM_DEFAULT_MODE",
|
||||
"撮合模式",
|
||||
"sim=本地模拟资金(公开行情撮合,不下真实单); live=实盘账户.保存后立即切换",
|
||||
),
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "仅 live 模式下生效;关闭时即使 live 也不向交易所发单"),
|
||||
("OKX_API_KEY", "API Key", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_SECRET", "API Secret", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_PASSPHRASE", "API Passphrase", "OKX 必填"),
|
||||
@@ -291,6 +296,7 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
|
||||
"OKX_OPTIONS_MAX_DTE_DAYS": "2",
|
||||
"OKX_OPTIONS_MAX_ACTIVE_POSITIONS": "0",
|
||||
"OKX_TRADE_MODE": "options",
|
||||
"SIM_DEFAULT_MODE": "sim",
|
||||
"MAX_ACTIVE_HEDGE_PLANS": "1",
|
||||
"HEDGE_PLAN_OO_CLOSE_MODE_ENABLED": "true",
|
||||
"HEDGE_PLAN_OO_BIAS_SPLIT_BY": "budget",
|
||||
@@ -303,6 +309,16 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
|
||||
|
||||
|
||||
def _effective_env_value(key: str, file_values: dict[str, str], schema_default: str = "") -> str:
|
||||
if key == "SIM_DEFAULT_MODE":
|
||||
# 展示当前生效撮合模式(运行时 trading.mode 优先)
|
||||
try:
|
||||
from lib.sim.mode_lib import peek_persisted_trading_mode
|
||||
|
||||
cur = peek_persisted_trading_mode()
|
||||
if cur:
|
||||
return cur
|
||||
except Exception:
|
||||
pass
|
||||
if key == "OKX_TRADE_MODE":
|
||||
# 展示值必须与运行时 get_okx_trade_mode() 一致,避免未写入时默认 options 静默改模式
|
||||
file_val = str(file_values.get(key) or "").strip() if key in file_values else ""
|
||||
|
||||
@@ -51,12 +51,26 @@ def apply_env_reload(env_path: str, get_db: Callable, changed_keys: list[str], g
|
||||
field_map[field["key"]] = field
|
||||
for key in changed_keys:
|
||||
meta = field_map.get(key) or {}
|
||||
if meta.get("hot_reload") and not meta.get("restart_required"):
|
||||
# SIM_DEFAULT_MODE 不在 example schema 的 hot 列表时仍应热切
|
||||
is_hot = bool(meta.get("hot_reload") and not meta.get("restart_required"))
|
||||
if key == "SIM_DEFAULT_MODE":
|
||||
is_hot = True
|
||||
if is_hot:
|
||||
val = os.getenv(key)
|
||||
if val is not None:
|
||||
hot[key] = val
|
||||
if hot:
|
||||
set_config_overrides(get_db, hot)
|
||||
# 撮合模式:写入 .env 的同时切换运行时 trading.mode(sim|live)
|
||||
if "SIM_DEFAULT_MODE" in changed_keys:
|
||||
try:
|
||||
from lib.sim.mode_lib import set_trading_mode
|
||||
|
||||
raw = (os.getenv("SIM_DEFAULT_MODE") or "").strip().lower()
|
||||
if raw in ("sim", "live"):
|
||||
set_trading_mode(get_db, raw)
|
||||
except Exception:
|
||||
pass
|
||||
from lib.env.env_schema import updates_need_restart
|
||||
|
||||
return {"restart_required": updates_need_restart(groups, changed_keys)}
|
||||
|
||||
@@ -20,6 +20,34 @@ def default_trading_mode() -> str:
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user