diff --git a/lib/common/static/instance_settings_prefs.js b/lib/common/static/instance_settings_prefs.js
index e43676d..804985b 100644
--- a/lib/common/static/instance_settings_prefs.js
+++ b/lib/common/static/instance_settings_prefs.js
@@ -416,6 +416,41 @@
}
}
+ function collectSettingsExchangeApiValues() {
+ const panel = document.getElementById("settings-exchange-api-panel");
+ const values = {};
+ if (!panel) return values;
+ panel.querySelectorAll(".env-field-input[data-env-key]").forEach((el) => {
+ if (el.dataset.envSensitive === "1" && el.dataset.envDirty !== "1") {
+ return;
+ }
+ values[el.dataset.envKey] = el.value;
+ });
+ return values;
+ }
+
+ async function saveSettingsExchangeApi(restartAfter) {
+ const status = document.getElementById("settings-exchange-api-status");
+ setStatus(status, "保存中…");
+ try {
+ const data = await fetchJson("/api/settings/env", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ values: collectSettingsExchangeApiValues() }),
+ });
+ const needRestart = restartAfter || data.restart_required;
+ if (needRestart) {
+ setStatus(status, "已保存,正在重启实例…");
+ await restartInstance();
+ setStatus(status, "保存并重启完成,请刷新页面");
+ } else {
+ setStatus(status, "已保存(即时生效项已应用)");
+ }
+ } catch (e) {
+ setStatus(status, e.message || "保存失败", true);
+ }
+ }
+
function installDelegatedHandlers() {
if (document.documentElement.dataset.prefsDelegateBound === "1") return;
document.documentElement.dataset.prefsDelegateBound = "1";
@@ -445,6 +480,16 @@
if (target.closest("#pwd-save-btn")) {
ev.preventDefault();
void savePassword();
+ return;
+ }
+ if (target.closest("#settings-exchange-api-save")) {
+ ev.preventDefault();
+ void saveSettingsExchangeApi(false);
+ return;
+ }
+ if (target.closest("#settings-exchange-api-save-restart")) {
+ ev.preventDefault();
+ void saveSettingsExchangeApi(true);
}
});
}
@@ -462,6 +507,8 @@
bindClickOnce("env-config-save-restart", () => saveEnvConfig(true));
bindClickOnce("env-config-reload", () => loadEnvConfig(true));
bindClickOnce("pwd-save-btn", savePassword);
+ bindClickOnce("settings-exchange-api-save", () => saveSettingsExchangeApi(false));
+ bindClickOnce("settings-exchange-api-save-restart", () => saveSettingsExchangeApi(true));
}
function initPage() {
diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py
index 182dfd0..f5bf5b3 100644
--- a/lib/env/env_ui_manifest.py
+++ b/lib/env/env_ui_manifest.py
@@ -220,6 +220,27 @@ def ui_allowed_keys(exchange_key: str) -> frozenset[str]:
return frozenset(keys)
+def build_exchange_api_group(
+ exchange_key: str,
+ example_path: str,
+ env_path: str,
+) -> dict[str, Any]:
+ """系统设置「交易所 API」专用:仅交易所与实盘字段."""
+ schema = _schema_field_map(example_path)
+ values = env_get_all(read_env_lines(env_path))
+ ex = (exchange_key or "").strip().lower()
+ live_fields = _EXCHANGE_LIVE_FIELDS.get(ex, _EXCHANGE_LIVE_FIELDS["okx"])
+ fields = [
+ _build_field(key, label, note, schema, values)
+ for key, label, note in live_fields
+ ]
+ return {
+ "title": "交易所 API",
+ "fields": fields,
+ "has_restart": any(f.get("restart_required") for f in fields),
+ }
+
+
def build_env_ui_payload(
exchange_key: str,
example_path: str,
diff --git a/lib/instance/instance_display_prefs_lib.py b/lib/instance/instance_display_prefs_lib.py
index f75b985..d0aa105 100644
--- a/lib/instance/instance_display_prefs_lib.py
+++ b/lib/instance/instance_display_prefs_lib.py
@@ -21,6 +21,7 @@ DEFAULT_INSTANCE_DISPLAY: dict[str, bool] = {
"show_settings_transfer": True,
"show_settings_export": True,
"show_settings_password": True,
+ "show_settings_exchange_api": True,
"show_settings_options_swap": True,
"show_settings_options_transfer": True,
}
@@ -39,6 +40,7 @@ DISPLAY_LABELS: dict[str, str] = {
"show_settings_transfer": "资金划转",
"show_settings_export": "数据导出",
"show_settings_password": "账户密码修改",
+ "show_settings_exchange_api": "交易所 API",
"show_settings_options_swap": "期权币种兑换",
"show_settings_options_transfer": "期权资金划转",
}
@@ -118,6 +120,7 @@ def display_meta_for_ui() -> list[dict[str, Any]]:
"show_nav_hedge_plan",
]
settings_keys = [
+ "show_settings_exchange_api",
"show_settings_transfer",
"show_settings_export",
"show_settings_password",
diff --git a/lib/instance/instance_settings_lib.py b/lib/instance/instance_settings_lib.py
index eea0f6b..239c33b 100644
--- a/lib/instance/instance_settings_lib.py
+++ b/lib/instance/instance_settings_lib.py
@@ -187,6 +187,8 @@ def build_settings_tabs(display: dict[str, Any] | None, instance_settings: dict[
disp = display or {}
inst = instance_settings or {}
tabs: list[dict[str, str]] = [{"key": "nav", "title": "导航显示"}]
+ if disp.get("show_settings_exchange_api", True):
+ tabs.append({"key": "exchange_api", "title": "交易所 API"})
if disp.get("show_settings_password", True):
tabs.append({"key": "password", "title": "账户密码"})
if inst.get("show_transfer") and disp.get("show_settings_transfer", True):
@@ -205,13 +207,21 @@ def settings_page_context(page: str, *, instance_base_dir: str | None = None, **
if p not in ("settings", "risk_policy", "env_config"):
return {}
display = kwargs.pop("display", None)
+ exchange_key = str(kwargs.get("exchange_key") or "")
ctx: dict[str, Any] = {"instance_settings": build_instance_settings_view(**kwargs)}
if p == "settings":
ctx["settings_tabs"] = build_settings_tabs(display, ctx["instance_settings"])
+ if instance_base_dir:
+ from lib.env.env_ui_manifest import build_exchange_api_group
+
+ env_path = os.path.join(instance_base_dir, ".env")
+ example_path = os.path.join(instance_base_dir, ".env.example")
+ ctx["exchange_api_group"] = build_exchange_api_group(
+ exchange_key, example_path, env_path
+ )
if p == "env_config" and instance_base_dir:
from lib.env.env_ui_manifest import build_env_ui_payload
- exchange_key = str(kwargs.get("exchange_key") or "")
env_path = os.path.join(instance_base_dir, ".env")
example_path = os.path.join(instance_base_dir, ".env.example")
ctx["env_config_groups"] = build_env_ui_payload(exchange_key, example_path, env_path)
diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html
index 02df580..85545ca 100644
--- a/lib/instance/templates/embed_shell.html
+++ b/lib/instance/templates/embed_shell.html
@@ -115,7 +115,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
-
+