diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py
index 1a484e6..072fdfd 100644
--- a/crypto_monitor_okx/app.py
+++ b/crypto_monitor_okx/app.py
@@ -2735,9 +2735,12 @@ def resolve_order_entry_price(order_resp, exchange_symbol, fallback_price):
def get_contract_size(exchange_symbol):
- ensure_markets_loaded()
- market = exchange.market(exchange_symbol)
- return float(market.get("contractSize") or 1)
+ try:
+ ensure_markets_loaded()
+ market = exchange.market(exchange_symbol)
+ return float(market.get("contractSize") or 1)
+ except Exception:
+ return 1.0
def parse_positive_float(value):
@@ -6945,11 +6948,14 @@ def api_account_snapshot():
except Exception:
return exchange.fetch_positions() or []
- unrealized_pnl = resolve_instance_unrealized_pnl(
- _okx_positions,
- active_pnl_rows,
- get_live_position_exchange_metrics,
- )
+ try:
+ unrealized_pnl = resolve_instance_unrealized_pnl(
+ _okx_positions,
+ active_pnl_rows,
+ get_live_position_exchange_metrics,
+ )
+ except Exception:
+ unrealized_pnl = None
options_unrealized_pnl = None
if OKX_OPTIONS_ENABLED and exchange_options.apiKey:
try:
diff --git a/lib/common/static/instance_settings_prefs.js b/lib/common/static/instance_settings_prefs.js
index f8d9d23..c5a7544 100644
--- a/lib/common/static/instance_settings_prefs.js
+++ b/lib/common/static/instance_settings_prefs.js
@@ -202,8 +202,21 @@
input = document.createElement("input");
input.id = "env-f-" + field.key;
input.type = "password";
- input.autocomplete = "off";
+ // 防止浏览器把登录密码自动填进 API Key/Secret(保存对冲开关时曾误写入密钥)
+ input.autocomplete = "new-password";
+ input.setAttribute("data-lpignore", "true");
+ input.setAttribute("data-1p-ignore", "true");
+ input.setAttribute("data-form-type", "other");
+ input.readOnly = true;
+ input.addEventListener("focus", function () {
+ input.readOnly = false;
+ });
if (field.sensitive) {
+ input.dataset.envSensitive = "1";
+ input.dataset.envDirty = "0";
+ input.addEventListener("input", function () {
+ input.dataset.envDirty = "1";
+ });
if (field.has_value) {
const cur = document.createElement("div");
cur.className = "env-sensitive-current muted";
@@ -219,6 +232,7 @@
input.placeholder = field.has_value ? "修改时填写新值,留空不修改" : "请输入";
} else {
input.type = "text";
+ input.autocomplete = "off";
input.value = field.current || field.default || "";
}
}
@@ -309,6 +323,10 @@
const values = {};
const scope = root || document;
scope.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;
diff --git a/lib/env/env_schema.py b/lib/env/env_schema.py
index 596067c..45d2625 100644
--- a/lib/env/env_schema.py
+++ b/lib/env/env_schema.py
@@ -275,6 +275,10 @@ def validate_env_updates(groups: list[dict], updates: dict[str, str]) -> tuple[d
val = str(value).strip()
if allowed[key].get("sensitive") and (val == "" or (val.startswith("****") and len(val) <= 8)):
continue
+ # API Key 被密码管理器/自动填充成登录密码时通常很短;OKX Key 一般为 36 位
+ if key.endswith("_API_KEY") and 0 < len(val) < 16:
+ errors.append(f"{key} 长度异常,疑似自动填充;留空则不修改已有密钥")
+ continue
ftype = allowed[key].get("type")
if ftype == "bool":
low = val.lower()
diff --git a/lib/instance/instance_embed_context_lib.py b/lib/instance/instance_embed_context_lib.py
index ba52a80..563467f 100644
--- a/lib/instance/instance_embed_context_lib.py
+++ b/lib/instance/instance_embed_context_lib.py
@@ -105,18 +105,21 @@ def total_funds_usdt(
options_funding_usdt: float | None = None,
options_trading_usdt: float | None = None,
) -> float | None:
- if funding_usdt is None:
+ parts = [
+ funding_usdt,
+ trading_usdt,
+ options_funding_usdc,
+ options_funding_usdt,
+ options_trading_usdc,
+ options_trading_usdt,
+ ]
+ if all(v is None for v in parts):
return None
try:
- total = float(funding_usdt) + float(trading_usdt or 0)
- if options_funding_usdc is not None:
- total += float(options_funding_usdc)
- if options_funding_usdt is not None:
- total += float(options_funding_usdt)
- if options_trading_usdc is not None:
- total += float(options_trading_usdc)
- if options_trading_usdt is not None:
- total += float(options_trading_usdt)
+ total = 0.0
+ for v in parts:
+ if v is not None:
+ total += float(v)
return round(total, 2)
except (TypeError, ValueError):
return None
diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html
index bade13c..4f0dc3c 100644
--- a/lib/instance/templates/embed_shell.html
+++ b/lib/instance/templates/embed_shell.html
@@ -108,7 +108,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
-
+