Show okx_sim/okx_live and gate live API fields by matching mode.
In sim, hide LIVE/API keys and expose simulated fund settings in env UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5046,11 +5046,18 @@ def render_main_page(page="options", embed_mode=None):
|
||||
options_funding_usdt = None
|
||||
options_trading_usdt = None
|
||||
_sim_mode_for_header = False
|
||||
_exchange_display_for_header = EXCHANGE_DISPLAY_NAME
|
||||
try:
|
||||
from lib.sim.mode_lib import exchange_mode_label as _exchange_mode_label
|
||||
from lib.sim.mode_lib import is_sim_mode as _is_sim_mode_fn
|
||||
|
||||
_sim_mode_for_header = bool(_is_sim_mode_fn(get_db))
|
||||
_exchange_display_for_header = _exchange_mode_label(
|
||||
is_sim=_sim_mode_for_header, exchange_key="okx"
|
||||
)
|
||||
except Exception:
|
||||
_sim_mode_for_header = False
|
||||
_exchange_display_for_header = EXCHANGE_DISPLAY_NAME
|
||||
if (
|
||||
OKX_OPTIONS_ENABLED
|
||||
and embed_mode != "fragment"
|
||||
@@ -5258,7 +5265,7 @@ def render_main_page(page="options", embed_mode=None):
|
||||
key_rule_ctx=key_rule_ctx,
|
||||
funds_fmt=format_funds_u,
|
||||
options_funding_label=options_funding_label,
|
||||
exchange_display=EXCHANGE_DISPLAY_NAME,
|
||||
exchange_display=_exchange_display_for_header,
|
||||
options_enabled=OKX_OPTIONS_ENABLED,
|
||||
trading_mode=("sim" if _sim_mode_for_header else "live"),
|
||||
is_sim_mode=_sim_mode_for_header,
|
||||
@@ -5532,10 +5539,18 @@ def api_account_snapshot():
|
||||
except Exception:
|
||||
options_unrealized_pnl = None
|
||||
_show_perp_funds = show_perp_funds_enabled(exchange_key="okx")
|
||||
try:
|
||||
from lib.sim.mode_lib import exchange_mode_label as _ex_mode_label
|
||||
from lib.sim.mode_lib import is_sim_mode as _is_sim
|
||||
|
||||
_snapshot_mode_label = _ex_mode_label(is_sim=_is_sim(get_db), exchange_key="okx")
|
||||
except Exception:
|
||||
_snapshot_mode_label = EXCHANGE_DISPLAY_NAME
|
||||
return jsonify({
|
||||
"funding_usdt": funding_usdt,
|
||||
"current_capital": current_capital,
|
||||
"show_perp_funds": _show_perp_funds,
|
||||
"exchange_mode_label": _snapshot_mode_label,
|
||||
"options_funding_usdc": options_funding_usdc,
|
||||
"options_funding_usdt": options_funding_usdt,
|
||||
"options_trading_usdc": options_trading_usdc,
|
||||
|
||||
@@ -386,28 +386,61 @@
|
||||
}
|
||||
|
||||
function bindTradeModeAutoRefresh(body) {
|
||||
const modeSel = body.querySelector('.env-field-input[data-env-key="OKX_TRADE_MODE"]');
|
||||
if (!modeSel || modeSel.dataset.modeRefreshBound === "1") return;
|
||||
modeSel.dataset.modeRefreshBound = "1";
|
||||
bindEnvSelectAutoRefresh(
|
||||
body,
|
||||
"OKX_TRADE_MODE",
|
||||
"切换交易模式并刷新配置…",
|
||||
"交易模式已切换为当前选项,配置区已刷新",
|
||||
"modeRefreshBound",
|
||||
true
|
||||
);
|
||||
bindEnvSelectAutoRefresh(
|
||||
body,
|
||||
"SIM_DEFAULT_MODE",
|
||||
"切换撮合模式并刷新配置…",
|
||||
"撮合模式已切换,配置区已刷新(sim 隐藏 API / live 显示实盘项)",
|
||||
"simModeRefreshBound",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function bindEnvSelectAutoRefresh(body, key, pendingMsg, okMsg, flag, jumpModeSection) {
|
||||
const modeSel = body && body.querySelector('.env-field-input[data-env-key="' + key + '"]');
|
||||
if (!modeSel || modeSel.dataset[flag] === "1") return;
|
||||
modeSel.dataset[flag] = "1";
|
||||
modeSel.addEventListener("change", async () => {
|
||||
const status = document.getElementById("env-config-status");
|
||||
const nextMode = modeSel.value;
|
||||
setStatus(status, "切换交易模式并刷新配置…");
|
||||
setStatus(status, pendingMsg);
|
||||
try {
|
||||
const payload = { values: {} };
|
||||
payload.values[key] = nextMode;
|
||||
await fetchJson("/api/settings/env", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ values: { OKX_TRADE_MODE: nextMode } }),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
await loadEnvConfig(true);
|
||||
const page = envConfigRoot() || document.querySelector(".env-config-page");
|
||||
const newBody = page && page.querySelector("#env-config-body");
|
||||
const idx = newBody && newBody.dataset.envModeSectionIdx;
|
||||
if (idx != null) {
|
||||
const radio = document.getElementById("env-sec-" + idx);
|
||||
if (radio) radio.checked = true;
|
||||
if (jumpModeSection) {
|
||||
const page = envConfigRoot() || document.querySelector(".env-config-page");
|
||||
const newBody = page && page.querySelector("#env-config-body");
|
||||
const idx = newBody && newBody.dataset.envModeSectionIdx;
|
||||
if (idx != null) {
|
||||
const radio = document.getElementById("env-sec-" + idx);
|
||||
if (radio) radio.checked = true;
|
||||
}
|
||||
}
|
||||
setStatus(status, okMsg);
|
||||
if (typeof global.refreshAccountSnapshot === "function") {
|
||||
global.refreshAccountSnapshot({ force: true, silent: true });
|
||||
}
|
||||
// 顶栏交易所名:okx_sim / okx_live
|
||||
const label = nextMode === "sim" ? "okx_sim" : nextMode === "live" ? "okx_live" : "";
|
||||
if (label) {
|
||||
document.querySelectorAll('[data-funds-field="exchange-mode-label"]').forEach((el) => {
|
||||
el.textContent = label;
|
||||
});
|
||||
}
|
||||
setStatus(status, "交易模式已切换为当前选项,配置区已刷新");
|
||||
} catch (e) {
|
||||
setStatus(status, e.message || "切换失败", true);
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -100,6 +100,9 @@ HOT_RELOAD_EXACT = frozenset({
|
||||
"OKX_OPTIONS_BUDGET_BUFFER",
|
||||
"OKX_TRADE_MODE",
|
||||
"SIM_DEFAULT_MODE",
|
||||
"SIM_INITIAL_EQUITY_USDT",
|
||||
"SIM_INITIAL_USDC",
|
||||
"SIM_FEE_RATE",
|
||||
"MAX_ACTIVE_HEDGE_PLANS",
|
||||
"HEDGE_PLAN_LIVE_ORDER",
|
||||
"HEDGE_PLAN_OPTION_PRIMARY",
|
||||
|
||||
Vendored
+69
-4
@@ -17,12 +17,42 @@ from lib.env.env_schema import (
|
||||
)
|
||||
|
||||
# 各所「交易所与实盘」字段(顺序即页面顺序)
|
||||
_OKX_LIVE_ONLY_KEYS = frozenset(
|
||||
{
|
||||
"LIVE_TRADING_ENABLED",
|
||||
"OKX_API_KEY",
|
||||
"OKX_API_SECRET",
|
||||
"OKX_API_PASSPHRASE",
|
||||
}
|
||||
)
|
||||
|
||||
_SIM_FUNDS_SECTION: dict[str, Any] = {
|
||||
"title": "模拟资金",
|
||||
"fields": [
|
||||
(
|
||||
"SIM_INITIAL_EQUITY_USDT",
|
||||
"初始权益 USDT",
|
||||
"重置模拟钱包时写入资金账户 USDT;改完需在系统设置「模拟资金」点重置才生效",
|
||||
),
|
||||
(
|
||||
"SIM_INITIAL_USDC",
|
||||
"初始 USDC",
|
||||
"重置时写入期权侧 USDC;改完需重置才生效",
|
||||
),
|
||||
(
|
||||
"SIM_FEE_RATE",
|
||||
"模拟手续费率",
|
||||
"如 0.0005=万五;撮合立即按此费率扣费",
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
_EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
"okx": [
|
||||
(
|
||||
"SIM_DEFAULT_MODE",
|
||||
"撮合模式",
|
||||
"sim=本地模拟资金(公开行情撮合,不下真实单); live=实盘账户.保存后立即切换",
|
||||
"sim=本地模拟资金; live=实盘.保存后立即切换;sim 下隐藏 API/实盘开关",
|
||||
),
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "仅 live 模式下生效;关闭时即使 live 也不向交易所发单"),
|
||||
("OKX_API_KEY", "API Key", "账户 API(永续+期权共用)"),
|
||||
@@ -372,15 +402,41 @@ def _hedge_fields_for_mode(mode: str) -> list[tuple[str, str, str]]:
|
||||
return []
|
||||
|
||||
|
||||
def _trading_mode_for_env_ui() -> str:
|
||||
try:
|
||||
from lib.sim.mode_lib import peek_persisted_trading_mode, default_trading_mode
|
||||
|
||||
return peek_persisted_trading_mode() or default_trading_mode()
|
||||
except Exception:
|
||||
return "sim"
|
||||
|
||||
|
||||
def _okx_exchange_fields_for_trading_mode(trading_mode: str) -> list[tuple[str, str, str]]:
|
||||
fields = list(_EXCHANGE_LIVE_FIELDS["okx"])
|
||||
tm = (trading_mode or "").strip().lower()
|
||||
if tm == "sim":
|
||||
return [f for f in fields if f[0] not in _OKX_LIVE_ONLY_KEYS]
|
||||
return fields
|
||||
|
||||
|
||||
def ui_sections_for_exchange(
|
||||
exchange_key: str,
|
||||
*,
|
||||
mode: str | None = None,
|
||||
trading_mode: str | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
ex = (exchange_key or "").strip().lower()
|
||||
sections: list[dict[str, Any]] = []
|
||||
live_fields = _EXCHANGE_LIVE_FIELDS.get(ex, _EXCHANGE_LIVE_FIELDS["okx"])
|
||||
tm = (trading_mode or "").strip().lower()
|
||||
if not tm:
|
||||
tm = _trading_mode_for_env_ui() if ex == "okx" else "live"
|
||||
if ex == "okx":
|
||||
live_fields = _okx_exchange_fields_for_trading_mode(tm)
|
||||
else:
|
||||
live_fields = _EXCHANGE_LIVE_FIELDS.get(ex, _EXCHANGE_LIVE_FIELDS["okx"])
|
||||
sections.append({"title": "交易所与实盘", "fields": live_fields})
|
||||
if ex == "okx" and tm == "sim":
|
||||
sections.append(_SIM_FUNDS_SECTION)
|
||||
sections.extend(_SHARED_SECTIONS)
|
||||
if ex in _MODE_SECTION.get("exchanges", frozenset()):
|
||||
from lib.hedge_plan.okx_trade_mode_lib import normalize_okx_trade_mode
|
||||
@@ -406,7 +462,11 @@ def ui_allowed_keys(exchange_key: str) -> frozenset[str]:
|
||||
ex = (exchange_key or "").strip().lower()
|
||||
if ex == "okx":
|
||||
keys.add("OKX_TRADE_MODE")
|
||||
# 允许写入遗留键,避免旧自动化/手改失败;页面不再展示
|
||||
keys.add("SIM_DEFAULT_MODE")
|
||||
# 切模式后同请求可能带上对侧字段,始终放行
|
||||
keys.update(_OKX_LIVE_ONLY_KEYS)
|
||||
for item in _SIM_FUNDS_SECTION["fields"]:
|
||||
keys.add(item[0])
|
||||
for item in _HEDGE_PLAN_SECTION["fields"]:
|
||||
keys.add(item[0])
|
||||
for item in _OPTIONS_SECTION["fields"]:
|
||||
@@ -422,9 +482,14 @@ def build_env_ui_payload(
|
||||
schema = _schema_field_map(example_path)
|
||||
env_lines = read_env_lines(env_path)
|
||||
values = env_get_all(env_lines)
|
||||
trading_mode = ""
|
||||
if (exchange_key or "").strip().lower() == "okx":
|
||||
trading_mode = _effective_env_value("SIM_DEFAULT_MODE", values, "sim") or _trading_mode_for_env_ui()
|
||||
groups: list[dict[str, Any]] = []
|
||||
for sec in ui_sections_for_exchange(
|
||||
exchange_key, mode=values.get("OKX_TRADE_MODE") or ""
|
||||
exchange_key,
|
||||
mode=values.get("OKX_TRADE_MODE") or "",
|
||||
trading_mode=trading_mode,
|
||||
):
|
||||
fields = [
|
||||
_build_field(key, label, note, schema, values)
|
||||
|
||||
@@ -1178,6 +1178,9 @@ function applyAccountSnapshot(data){
|
||||
if(typeof data.show_perp_funds !== "undefined"){
|
||||
applyPerpFundsVisibility(data.show_perp_funds);
|
||||
}
|
||||
if(data.exchange_mode_label){
|
||||
setFundsFieldText("exchange-mode-label", data.exchange_mode_label);
|
||||
}
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
||||
<script>
|
||||
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=21"></script>
|
||||
<script src="/static/instance_settings_prefs.js?v=22"></script>
|
||||
<script src="/static/instance_live.js?v=7"></script>
|
||||
<script src="/static/instance_embed.js?v=31"></script>
|
||||
<script src="/static/instance_mobile_nav.js?v=2"></script>
|
||||
|
||||
@@ -1444,6 +1444,9 @@ function applyAccountSnapshot(data){
|
||||
if(typeof data.show_perp_funds !== "undefined"){
|
||||
applyPerpFundsVisibility(data.show_perp_funds);
|
||||
}
|
||||
if(data.exchange_mode_label){
|
||||
setFundsFieldText("exchange-mode-label", data.exchange_mode_label);
|
||||
}
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
@@ -1829,6 +1832,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=21"></script>
|
||||
<script src="/static/instance_settings_prefs.js?v=22"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="instance-header-stats{% if options_enabled %} instance-header-stats--options{% endif %}">
|
||||
<div class="stat-strip-item stat-strip-item--primary">
|
||||
<div class="label">交易所</div>
|
||||
<div class="value">{{ exchange_display }}{% if is_sim_mode|default(false) %} <span class="sim-mode-badge" title="本地模拟资金撮合,不下真实交易所单">模拟</span>{% endif %}</div>
|
||||
<div class="value" id="exchange-mode-label" data-funds-field="exchange-mode-label">{{ exchange_display }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">交易日</div>
|
||||
|
||||
@@ -78,3 +78,11 @@ def set_trading_mode(get_db: Callable, mode: str) -> str:
|
||||
|
||||
def is_sim_mode(get_db: Callable) -> bool:
|
||||
return get_trading_mode(get_db) == MODE_SIM
|
||||
|
||||
|
||||
def exchange_mode_label(*, is_sim: bool | None = None, get_db: Callable | None = None, exchange_key: str = "okx") -> str:
|
||||
"""顶栏交易所展示: okx_sim / okx_live."""
|
||||
ex = (exchange_key or "okx").strip().lower() or "okx"
|
||||
if is_sim is None and callable(get_db):
|
||||
is_sim = is_sim_mode(get_db)
|
||||
return f"{ex}_sim" if bool(is_sim) else f"{ex}_live"
|
||||
|
||||
Reference in New Issue
Block a user