diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py
index 473ad55..25196be 100644
--- a/crypto_monitor_okx/app.py
+++ b/crypto_monitor_okx/app.py
@@ -2526,6 +2526,10 @@ def friendly_okx_error(err, available_usdt=None):
friendly_exchange_error = friendly_okx_error
+def invalidate_account_balance_cache() -> None:
+ ACCOUNT_BALANCE_CACHE["updated_at"] = 0
+
+
def get_exchange_capitals(force=False):
ok_live, _ = ensure_okx_live_ready()
if not ok_live:
@@ -9286,6 +9290,13 @@ def manual_transfer():
conn.commit()
conn.close()
if ok:
+ invalidate_account_balance_cache()
+ try:
+ from lib.instance.instance_live_push_lib import notify_instance_balance_changed
+
+ notify_instance_balance_changed()
+ except Exception:
+ pass
flash(f"手动划转成功:{amount}U {from_account}->{to_account}")
else:
flash(f"手动划转失败:{msg}")
diff --git a/lib/common/static/instance_live.js b/lib/common/static/instance_live.js
index b5d1125..b948542 100644
--- a/lib/common/static/instance_live.js
+++ b/lib/common/static/instance_live.js
@@ -32,12 +32,13 @@
}
}
- function scheduleRefresh() {
+ function scheduleRefresh(opts) {
if (refreshTimer) return;
+ const options = opts || {};
refreshTimer = setTimeout(function () {
refreshTimer = null;
if (document.hidden) return;
- refreshTabData(currentTab(), { silent: true });
+ refreshTabData(currentTab(), { silent: true, force: !!options.force });
}, 80);
}
@@ -52,7 +53,7 @@
}
if (ver === localLiveVersion) return;
localLiveVersion = ver;
- scheduleRefresh();
+ scheduleRefresh({ force: reason === "balance" });
}
function closeLiveStream() {
diff --git a/lib/common/static/options_settings.js b/lib/common/static/options_settings.js
index e89b497..5464b88 100644
--- a/lib/common/static/options_settings.js
+++ b/lib/common/static/options_settings.js
@@ -9,6 +9,14 @@
return r.json();
}
+ function refreshFundsAfterMutation() {
+ if (typeof refreshAccountSnapshot !== "function") return;
+ refreshAccountSnapshot({ force: true });
+ setTimeout(function () {
+ refreshAccountSnapshot({ force: true, silent: true });
+ }, 1500);
+ }
+
function setMsg(id, text, isErr) {
const el = document.getElementById(id);
if (!el) return;
@@ -50,7 +58,7 @@
}),
});
setMsg("opt-set-swap-msg", d.ok ? "兑换单已提交" : (d.msg || "失败"), !d.ok);
- if (d.ok && typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
+ if (d.ok && typeof refreshAccountSnapshot === "function") refreshFundsAfterMutation();
}
const swapBtn = document.getElementById("opt-set-swap-btn");
@@ -96,7 +104,7 @@
}),
});
setMsg("opt-set-int-msg", d.ok ? "划转成功" : (d.msg || "失败"), !d.ok);
- if (d.ok && typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
+ if (d.ok && typeof refreshAccountSnapshot === "function") refreshFundsAfterMutation();
}
const intBtn = document.getElementById("opt-set-int-btn");
@@ -143,7 +151,7 @@
}),
});
setMsg("opt-set-cross-msg", d.ok ? "划转成功" : (d.msg || "失败"), !d.ok);
- if (d.ok && typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
+ if (d.ok && typeof refreshAccountSnapshot === "function") refreshFundsAfterMutation();
}
const crossBtn = document.getElementById("opt-set-cross-btn");
diff --git a/lib/exchange/okx_options_lib.py b/lib/exchange/okx_options_lib.py
index 9445876..9de50d7 100644
--- a/lib/exchange/okx_options_lib.py
+++ b/lib/exchange/okx_options_lib.py
@@ -26,6 +26,11 @@ _OKX_OPTION_ERR_ZH: dict[str, str] = {
_OPTIONS_BALANCE_CACHE: dict[str, Any] = {"updated_at": 0.0, "data": None}
+def invalidate_options_balance_cache() -> None:
+ _OPTIONS_BALANCE_CACHE["updated_at"] = 0.0
+ _OPTIONS_BALANCE_CACHE["data"] = None
+
+
def _okx_trade_error_message(exc: BaseException | None = None, resp: Any = None) -> str:
row: dict[str, Any] | None = None
if isinstance(resp, dict):
diff --git a/lib/instance/instance_live_push_lib.py b/lib/instance/instance_live_push_lib.py
index 2baa2d8..ca93a67 100644
--- a/lib/instance/instance_live_push_lib.py
+++ b/lib/instance/instance_live_push_lib.py
@@ -100,6 +100,11 @@ class InstanceLivePush:
instance_live_push = InstanceLivePush()
+def notify_instance_balance_changed() -> int:
+ """划转/兑换后通知 embed 壳拉最新资金快照."""
+ return instance_live_push.tick("balance")
+
+
def register_instance_live_routes(app: Flask, login_required: Callable) -> None:
instance_live_push.start()
diff --git a/lib/instance/templates/embed_boot_scripts.html b/lib/instance/templates/embed_boot_scripts.html
index 3d8ea40..990312b 100644
--- a/lib/instance/templates/embed_boot_scripts.html
+++ b/lib/instance/templates/embed_boot_scripts.html
@@ -1166,11 +1166,11 @@ function applyAccountSnapshot(data){
}
if(data.options_funding_usdc != null || data.options_funding_usdt != null){
const optFunding = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
- if(optFunding !== "—") setFundsFieldText("options-funding-usdc", optFunding);
+ setFundsFieldText("options-funding-usdc", optFunding);
}
if(data.options_trading_usdc != null || data.options_trading_usdt != null){
const optTrading = formatOptionsFundingLabel(data.options_trading_usdc, data.options_trading_usdt);
- if(optTrading !== "—") setFundsFieldText("options-trading-usdc", optTrading);
+ setFundsFieldText("options-trading-usdc", optTrading);
}
if(typeof data.unrealized_pnl !== "undefined"){
updateRealtimePnl(data.unrealized_pnl);
diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html
index ea23b29..c88b071 100644
--- a/lib/instance/templates/embed_shell.html
+++ b/lib/instance/templates/embed_shell.html
@@ -106,7 +106,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
-
+