Fix header funds flicker and unify settings page top bar.
Preserve SSR fund values when account_snapshot returns null, retry on failure, and show the same instance header panel on system settings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1039,18 +1039,23 @@ function refreshOrderDefaults(){
|
||||
}
|
||||
|
||||
function paintRealtimePnl(v){
|
||||
const pnlEl = document.getElementById("realtime-pnl");
|
||||
if(!pnlEl) return;
|
||||
const nodes = document.querySelectorAll('[data-funds-field="realtime-pnl"]');
|
||||
if(!nodes.length) return;
|
||||
if(v === null || v === undefined || Number.isNaN(Number(v))){
|
||||
pnlEl.innerText = "—";
|
||||
pnlEl.classList.remove("pnl-pos", "pnl-neg");
|
||||
nodes.forEach((pnlEl) => {
|
||||
pnlEl.innerText = "—";
|
||||
pnlEl.classList.remove("pnl-pos", "pnl-neg");
|
||||
});
|
||||
return;
|
||||
}
|
||||
const n = Number(v);
|
||||
const sign = n > 0 ? "+" : "";
|
||||
pnlEl.innerText = `${sign}${n.toFixed(2)}U`;
|
||||
pnlEl.classList.toggle("pnl-pos", n > 0);
|
||||
pnlEl.classList.toggle("pnl-neg", n < 0);
|
||||
const text = `${sign}${n.toFixed(2)}U`;
|
||||
nodes.forEach((pnlEl) => {
|
||||
pnlEl.innerText = text;
|
||||
pnlEl.classList.toggle("pnl-pos", n > 0);
|
||||
pnlEl.classList.toggle("pnl-neg", n < 0);
|
||||
});
|
||||
}
|
||||
function sumOrdersFloatPnl(orders){
|
||||
if(!orders || !orders.length) return null;
|
||||
@@ -1091,81 +1096,104 @@ function formatOptionsFundingLabel(usdc, usdt) {
|
||||
return parts.length ? parts.join(" · ") : "—";
|
||||
}
|
||||
|
||||
function setFundsFieldText(field, text){
|
||||
if(text == null || text === "") return;
|
||||
document.querySelectorAll(`[data-funds-field="${field}"]`).forEach((el) => {
|
||||
el.innerText = text;
|
||||
});
|
||||
}
|
||||
function accountSnapshotFundingMissing(data){
|
||||
if(!data || typeof data !== "object") return true;
|
||||
const hasFunding = data.funding_usdt != null && data.funding_usdt !== "";
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasTrading = data.current_capital != null && data.current_capital !== "";
|
||||
return !hasFunding && !hasTotal && !hasTrading;
|
||||
}
|
||||
let accountSnapshotRetryCount = 0;
|
||||
function applyAccountSnapshot(data){
|
||||
if(!data || typeof data !== "object") return;
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
if(data.total_funds != null && data.total_funds !== ""){
|
||||
setFundsFieldText("total-funds", `${Number(data.total_funds).toFixed(2)}U`);
|
||||
}
|
||||
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
|
||||
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
||||
}
|
||||
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);
|
||||
}
|
||||
if(data.options_trading_usdc != null && data.options_trading_usdc !== ""){
|
||||
setFundsFieldText("options-trading-usdc", `${Number(data.options_trading_usdc).toFixed(2)} USDC`);
|
||||
}
|
||||
if(typeof data.unrealized_pnl !== "undefined"){
|
||||
paintRealtimePnl(data.unrealized_pnl);
|
||||
}
|
||||
if(typeof data.available_trading_usdt !== "undefined" && data.available_trading_usdt !== null){
|
||||
latestAvailableUsdt = Number(data.available_trading_usdt);
|
||||
}
|
||||
if(data.risk_status){
|
||||
document.querySelectorAll("#account-risk-badge").forEach((badge) => {
|
||||
if(window.AccountRiskBadge){
|
||||
AccountRiskBadge.applyToElement(badge, data.risk_status);
|
||||
}else{
|
||||
const st = data.risk_status.status || "normal";
|
||||
badge.className = "risk-status-badge risk-status-" + st;
|
||||
badge.innerText = data.risk_status.status_label || "正常";
|
||||
badge.title = data.risk_status.reason || "";
|
||||
}
|
||||
});
|
||||
}
|
||||
if(data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader){
|
||||
TimeCloseUI.paintForceCloseHeader(data.force_close);
|
||||
}
|
||||
let canTradeText = "可开仓";
|
||||
if(!data.can_trade){
|
||||
const parts = [];
|
||||
if(data.risk_status && data.risk_status.can_trade === false && data.risk_status.reason){
|
||||
parts.push(data.risk_status.reason);
|
||||
}
|
||||
const ac = Number(data.active_count || 0);
|
||||
const max = Number(data.max_active_positions || {{ max_active_positions }});
|
||||
if(ac >= max) parts.push(`持仓 ${ac}/${max}`);
|
||||
const hard = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const opens = Number(data.opens_today);
|
||||
if(hard > 0 && !Number.isNaN(opens) && opens >= hard) parts.push(`本交易日开仓 ${opens}/${hard} 已达上限`);
|
||||
if(!parts.length) parts.push(`未到北京时间 {{ reset_hour }}:00`);
|
||||
else parts.push(`或未到北京时间 {{ reset_hour }}:00`);
|
||||
canTradeText = `不可开仓(${parts.join(";")})`;
|
||||
}
|
||||
const opensToday = Number(data.opens_today);
|
||||
const hardLim = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const alertLim = Number(data.daily_open_alert_threshold != null ? data.daily_open_alert_threshold : {{ daily_open_alert_threshold }});
|
||||
const openCntTxt = !Number.isNaN(opensToday)
|
||||
? `本交易日开仓 ${opensToday}${hardLim > 0 ? ` / 硬上限 ${hardLim}` : ""}(AI 提醒 ${alertLim})`
|
||||
: "";
|
||||
const tip = document.getElementById("order-rule-tip");
|
||||
const avail = (latestAvailableUsdt !== null && !Number.isNaN(latestAvailableUsdt)) ? `;交易账户可用约${latestAvailableUsdt.toFixed(2)}U` : "";
|
||||
if(tip){
|
||||
tip.innerText = `规则:最多 ${data.max_active_positions || {{ max_active_positions }}} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;${openCntTxt ? openCntTxt + ";" : ""}${canTradeText}${avail};人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1`;
|
||||
}
|
||||
}
|
||||
function refreshAccountSnapshot(opts){
|
||||
const options = opts || {};
|
||||
const qs = options.force ? "?force=1" : "";
|
||||
fetch("/api/account_snapshot" + qs).then(r=>r.json()).then(data=>{
|
||||
if (typeof data.funding_usdt !== "undefined") {
|
||||
const el = document.getElementById("total-capital");
|
||||
if(el) el.innerText = (data.funding_usdt === null || data.funding_usdt === undefined) ? "—" : `${Number(data.funding_usdt).toFixed(2)}U`;
|
||||
applyAccountSnapshot(data);
|
||||
if(accountSnapshotFundingMissing(data) && !options.force && accountSnapshotRetryCount < 3){
|
||||
accountSnapshotRetryCount += 1;
|
||||
setTimeout(() => refreshAccountSnapshot({ silent: true, force: accountSnapshotRetryCount >= 2 }), 1200 * accountSnapshotRetryCount);
|
||||
}else if(!accountSnapshotFundingMissing(data)){
|
||||
accountSnapshotRetryCount = 0;
|
||||
}
|
||||
if (typeof data.total_funds !== "undefined") {
|
||||
const el = document.getElementById("total-funds");
|
||||
if(el) el.innerText = (data.total_funds === null || data.total_funds === undefined) ? "—" : `${Number(data.total_funds).toFixed(2)}U`;
|
||||
}).catch(()=>{
|
||||
if(!options.silent && accountSnapshotRetryCount < 3){
|
||||
accountSnapshotRetryCount += 1;
|
||||
setTimeout(() => refreshAccountSnapshot({ silent: true }), 1200 * accountSnapshotRetryCount);
|
||||
}
|
||||
if (typeof data.current_capital !== "undefined") {
|
||||
const el = document.getElementById("current-capital");
|
||||
if(el) el.innerText = `${Number(data.current_capital).toFixed(2)}U`;
|
||||
}
|
||||
if (typeof data.options_funding_usdc !== "undefined" || typeof data.options_funding_usdt !== "undefined") {
|
||||
const el = document.getElementById("options-funding-usdc");
|
||||
if (el) el.innerText = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
|
||||
}
|
||||
if (typeof data.options_trading_usdc !== "undefined") {
|
||||
const el = document.getElementById("options-trading-usdc");
|
||||
if (el) el.innerText = (data.options_trading_usdc === null || data.options_trading_usdc === undefined)
|
||||
? "—" : `${Number(data.options_trading_usdc).toFixed(2)} USDC`;
|
||||
}
|
||||
if (typeof data.unrealized_pnl !== "undefined") {
|
||||
paintRealtimePnl(data.unrealized_pnl);
|
||||
}
|
||||
if (typeof data.available_trading_usdt !== "undefined" && data.available_trading_usdt !== null) {
|
||||
latestAvailableUsdt = Number(data.available_trading_usdt);
|
||||
}
|
||||
if (data.risk_status) {
|
||||
const badge = document.getElementById("account-risk-badge");
|
||||
if (badge) {
|
||||
if (window.AccountRiskBadge) {
|
||||
AccountRiskBadge.applyToElement(badge, data.risk_status);
|
||||
} else {
|
||||
const st = data.risk_status.status || "normal";
|
||||
badge.className = "risk-status-badge risk-status-" + st;
|
||||
badge.innerText = data.risk_status.status_label || "正常";
|
||||
badge.title = data.risk_status.reason || "";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader) {
|
||||
TimeCloseUI.paintForceCloseHeader(data.force_close);
|
||||
}
|
||||
let canTradeText = "可开仓";
|
||||
if (!data.can_trade) {
|
||||
const parts = [];
|
||||
if (data.risk_status && data.risk_status.can_trade === false && data.risk_status.reason) {
|
||||
parts.push(data.risk_status.reason);
|
||||
}
|
||||
const ac = Number(data.active_count || 0);
|
||||
const max = Number(data.max_active_positions || {{ max_active_positions }});
|
||||
if (ac >= max) parts.push(`持仓 ${ac}/${max}`);
|
||||
const hard = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const opens = Number(data.opens_today);
|
||||
if (hard > 0 && !Number.isNaN(opens) && opens >= hard) parts.push(`本交易日开仓 ${opens}/${hard} 已达上限`);
|
||||
if (!parts.length) parts.push(`未到北京时间 {{ reset_hour }}:00`);
|
||||
else parts.push(`或未到北京时间 {{ reset_hour }}:00`);
|
||||
canTradeText = `不可开仓(${parts.join(";")})`;
|
||||
}
|
||||
const opensToday = Number(data.opens_today);
|
||||
const hardLim = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const alertLim = Number(data.daily_open_alert_threshold != null ? data.daily_open_alert_threshold : {{ daily_open_alert_threshold }});
|
||||
const openCntTxt = !Number.isNaN(opensToday)
|
||||
? `本交易日开仓 ${opensToday}${hardLim > 0 ? ` / 硬上限 ${hardLim}` : ""}(AI 提醒 ${alertLim})`
|
||||
: "";
|
||||
const tip = document.getElementById("order-rule-tip");
|
||||
const avail = (latestAvailableUsdt !== null && !Number.isNaN(latestAvailableUsdt)) ? `;交易账户可用约${latestAvailableUsdt.toFixed(2)}U` : "";
|
||||
if(tip){
|
||||
tip.innerText = `规则:最多 ${data.max_active_positions || {{ max_active_positions }}} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;${openCntTxt ? openCntTxt + ";" : ""}${canTradeText}${avail};人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1`;
|
||||
}
|
||||
}).catch(()=>{});
|
||||
});
|
||||
}
|
||||
|
||||
const orderSymbolEl = document.getElementById("order-symbol");
|
||||
|
||||
@@ -43,9 +43,7 @@
|
||||
</nav>
|
||||
<div id="embed-flash" class="flash" style="display:none" role="status"></div>
|
||||
|
||||
{% if initial_tab != 'settings' %}
|
||||
{% include 'instance_header_panel.html' %}
|
||||
{% endif %}
|
||||
{% if initial_tab != 'settings' and include_transfer_block %}
|
||||
{% include 'instance_top_bar.html' %}
|
||||
{% endif %}
|
||||
@@ -91,6 +89,6 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
||||
<script src="/static/key_monitor_form.js?v=2"></script>
|
||||
{% include 'embed_boot_scripts.html' %}
|
||||
<script src="/static/instance_live.js?v=3"></script>
|
||||
<script src="/static/instance_embed.js?v=13"></script>
|
||||
<script src="/static/instance_embed.js?v=15"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -68,9 +68,7 @@
|
||||
</div>
|
||||
{% with msg=get_flashed_messages() %}{% if msg %}<div class="flash">{{ msg[0] }}</div>{% endif %}{% endwith %}
|
||||
|
||||
{% if page != 'settings' %}
|
||||
{% include 'instance_header_panel.html' %}
|
||||
{% endif %}
|
||||
{% if page != 'settings' and page != 'options' %}
|
||||
{% include 'instance_top_bar.html' %}
|
||||
{% endif %}
|
||||
@@ -1593,18 +1591,23 @@ function refreshOrderDefaults(){
|
||||
}
|
||||
|
||||
function paintRealtimePnl(v){
|
||||
const pnlEl = document.getElementById("realtime-pnl");
|
||||
if(!pnlEl) return;
|
||||
const nodes = document.querySelectorAll('[data-funds-field="realtime-pnl"]');
|
||||
if(!nodes.length) return;
|
||||
if(v === null || v === undefined || Number.isNaN(Number(v))){
|
||||
pnlEl.innerText = "—";
|
||||
pnlEl.classList.remove("pnl-pos", "pnl-neg");
|
||||
nodes.forEach((pnlEl) => {
|
||||
pnlEl.innerText = "—";
|
||||
pnlEl.classList.remove("pnl-pos", "pnl-neg");
|
||||
});
|
||||
return;
|
||||
}
|
||||
const n = Number(v);
|
||||
const sign = n > 0 ? "+" : "";
|
||||
pnlEl.innerText = `${sign}${n.toFixed(2)}U`;
|
||||
pnlEl.classList.toggle("pnl-pos", n > 0);
|
||||
pnlEl.classList.toggle("pnl-neg", n < 0);
|
||||
const text = `${sign}${n.toFixed(2)}U`;
|
||||
nodes.forEach((pnlEl) => {
|
||||
pnlEl.innerText = text;
|
||||
pnlEl.classList.toggle("pnl-pos", n > 0);
|
||||
pnlEl.classList.toggle("pnl-neg", n < 0);
|
||||
});
|
||||
}
|
||||
function sumOrdersFloatPnl(orders){
|
||||
if(!orders || !orders.length) return null;
|
||||
@@ -1645,87 +1648,112 @@ function formatOptionsFundingLabel(usdc, usdt) {
|
||||
return parts.length ? parts.join(" · ") : "—";
|
||||
}
|
||||
|
||||
function refreshAccountSnapshot(){
|
||||
fetch("/api/account_snapshot").then(r=>r.json()).then(data=>{
|
||||
if (typeof data.funding_usdt !== "undefined") {
|
||||
const el = document.getElementById("total-capital");
|
||||
if(el) el.innerText = (data.funding_usdt === null || data.funding_usdt === undefined) ? "—" : `${Number(data.funding_usdt).toFixed(2)}U`;
|
||||
}
|
||||
if (typeof data.total_funds !== "undefined") {
|
||||
const el = document.getElementById("total-funds");
|
||||
if(el) el.innerText = (data.total_funds === null || data.total_funds === undefined) ? "—" : `${Number(data.total_funds).toFixed(2)}U`;
|
||||
}
|
||||
if (typeof data.current_capital !== "undefined") {
|
||||
const el = document.getElementById("current-capital");
|
||||
if(el) el.innerText = `${Number(data.current_capital).toFixed(2)}U`;
|
||||
}
|
||||
if (typeof data.options_funding_usdc !== "undefined" || typeof data.options_funding_usdt !== "undefined") {
|
||||
const el = document.getElementById("options-funding-usdc");
|
||||
if (el) el.innerText = formatOptionsFundingLabel(data.options_funding_usdc, data.options_funding_usdt);
|
||||
}
|
||||
if (typeof data.options_trading_usdc !== "undefined") {
|
||||
const el = document.getElementById("options-trading-usdc");
|
||||
if (el) el.innerText = (data.options_trading_usdc === null || data.options_trading_usdc === undefined)
|
||||
? "—" : `${Number(data.options_trading_usdc).toFixed(2)} USDC`;
|
||||
}
|
||||
if (typeof data.unrealized_pnl !== "undefined") {
|
||||
paintRealtimePnl(data.unrealized_pnl);
|
||||
}
|
||||
if (typeof data.available_trading_usdt !== "undefined" && data.available_trading_usdt !== null) {
|
||||
latestAvailableUsdt = Number(data.available_trading_usdt);
|
||||
}
|
||||
if (data.risk_status) {
|
||||
const badge = document.getElementById("account-risk-badge");
|
||||
if (badge) {
|
||||
if (window.AccountRiskBadge) {
|
||||
AccountRiskBadge.applyToElement(badge, data.risk_status);
|
||||
} else {
|
||||
const st = data.risk_status.status || "normal";
|
||||
badge.className = "risk-status-badge risk-status-" + st;
|
||||
badge.innerText = data.risk_status.status_label || "正常";
|
||||
badge.title = data.risk_status.reason || "";
|
||||
}
|
||||
function setFundsFieldText(field, text){
|
||||
if(text == null || text === "") return;
|
||||
document.querySelectorAll(`[data-funds-field="${field}"]`).forEach((el) => {
|
||||
el.innerText = text;
|
||||
});
|
||||
}
|
||||
function accountSnapshotFundingMissing(data){
|
||||
if(!data || typeof data !== "object") return true;
|
||||
const hasFunding = data.funding_usdt != null && data.funding_usdt !== "";
|
||||
const hasTotal = data.total_funds != null && data.total_funds !== "";
|
||||
const hasTrading = data.current_capital != null && data.current_capital !== "";
|
||||
return !hasFunding && !hasTotal && !hasTrading;
|
||||
}
|
||||
let accountSnapshotRetryCount = 0;
|
||||
function applyAccountSnapshot(data){
|
||||
if(!data || typeof data !== "object") return;
|
||||
if(data.funding_usdt != null && data.funding_usdt !== ""){
|
||||
setFundsFieldText("total-capital", `${Number(data.funding_usdt).toFixed(2)}U`);
|
||||
}
|
||||
if(data.total_funds != null && data.total_funds !== ""){
|
||||
setFundsFieldText("total-funds", `${Number(data.total_funds).toFixed(2)}U`);
|
||||
}
|
||||
if(data.current_capital != null && data.current_capital !== "" && !Number.isNaN(Number(data.current_capital))){
|
||||
setFundsFieldText("current-capital", `${Number(data.current_capital).toFixed(2)}U`);
|
||||
}
|
||||
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);
|
||||
}
|
||||
if(data.options_trading_usdc != null && data.options_trading_usdc !== ""){
|
||||
setFundsFieldText("options-trading-usdc", `${Number(data.options_trading_usdc).toFixed(2)} USDC`);
|
||||
}
|
||||
if(typeof data.unrealized_pnl !== "undefined"){
|
||||
paintRealtimePnl(data.unrealized_pnl);
|
||||
}
|
||||
if(typeof data.available_trading_usdt !== "undefined" && data.available_trading_usdt !== null){
|
||||
latestAvailableUsdt = Number(data.available_trading_usdt);
|
||||
}
|
||||
if(data.risk_status){
|
||||
document.querySelectorAll("#account-risk-badge").forEach((badge) => {
|
||||
if(window.AccountRiskBadge){
|
||||
AccountRiskBadge.applyToElement(badge, data.risk_status);
|
||||
}else{
|
||||
const st = data.risk_status.status || "normal";
|
||||
badge.className = "risk-status-badge risk-status-" + st;
|
||||
badge.innerText = data.risk_status.status_label || "正常";
|
||||
badge.title = data.risk_status.reason || "";
|
||||
}
|
||||
});
|
||||
}
|
||||
if(data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader){
|
||||
TimeCloseUI.paintForceCloseHeader(data.force_close);
|
||||
}
|
||||
let canTradeText = "可开仓";
|
||||
if(!data.can_trade){
|
||||
const parts = [];
|
||||
if(data.risk_status && data.risk_status.can_trade === false && data.risk_status.reason){
|
||||
parts.push(data.risk_status.reason);
|
||||
}
|
||||
if (data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader) {
|
||||
TimeCloseUI.paintForceCloseHeader(data.force_close);
|
||||
if((data.active_count||0) >= (data.max_active_positions||{{ max_active_positions }})) parts.push(`持仓 ${data.active_count}/${data.max_active_positions}`);
|
||||
const hard = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const opens = Number(data.opens_today);
|
||||
if(hard > 0 && !Number.isNaN(opens) && opens >= hard) parts.push(`本交易日开仓 ${opens}/${hard} 已达上限`);
|
||||
if(data.open_guard_blocks_now) parts.push(`未到北京时间 ${data.reset_hour||{{ reset_hour }}}:00`);
|
||||
canTradeText = parts.length ? `不可开仓(${parts.join(";")})` : "不可开仓";
|
||||
}
|
||||
const opensToday = Number(data.opens_today);
|
||||
const hardLim = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const alertLim = Number(data.daily_open_alert_threshold != null ? data.daily_open_alert_threshold : {{ daily_open_alert_threshold }});
|
||||
const openCntTxt = !Number.isNaN(opensToday)
|
||||
? `本交易日开仓 ${opensToday}${hardLim > 0 ? ` / 硬上限 ${hardLim}` : ""}(AI 提醒 ${alertLim})`
|
||||
: "";
|
||||
const tip = document.getElementById("order-rule-tip");
|
||||
const avail = (latestAvailableUsdt !== null && !Number.isNaN(latestAvailableUsdt)) ? `;交易账户可用约${latestAvailableUsdt.toFixed(2)}U` : "";
|
||||
if(tip){
|
||||
tip.innerText = `规则:最多 ${data.max_active_positions || {{ max_active_positions }}} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;${openCntTxt ? openCntTxt + ";" : ""}${canTradeText}${avail};人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1`;
|
||||
}
|
||||
const allowEl = document.getElementById("allow-open-before-reset");
|
||||
const guardStatus = document.getElementById("open-guard-status");
|
||||
const resetH = data.reset_hour != null ? data.reset_hour : {{ reset_hour }};
|
||||
if(allowEl && typeof data.open_guard_enabled !== "undefined"){
|
||||
allowEl.checked = !data.open_guard_enabled;
|
||||
}
|
||||
if(guardStatus && typeof data.open_guard_enabled !== "undefined"){
|
||||
guardStatus.innerText = data.open_guard_enabled
|
||||
? `已限制:${resetH}:00 前不可开仓`
|
||||
: `已放开:${resetH}:00 前允许开仓`;
|
||||
}
|
||||
}
|
||||
function refreshAccountSnapshot(opts){
|
||||
const options = opts || {};
|
||||
const qs = options.force ? "?force=1" : "";
|
||||
fetch("/api/account_snapshot" + qs).then(r=>r.json()).then(data=>{
|
||||
applyAccountSnapshot(data);
|
||||
if(accountSnapshotFundingMissing(data) && !options.force && accountSnapshotRetryCount < 3){
|
||||
accountSnapshotRetryCount += 1;
|
||||
setTimeout(() => refreshAccountSnapshot({ silent: true, force: accountSnapshotRetryCount >= 2 }), 1200 * accountSnapshotRetryCount);
|
||||
}else if(!accountSnapshotFundingMissing(data)){
|
||||
accountSnapshotRetryCount = 0;
|
||||
}
|
||||
let canTradeText = "可开仓";
|
||||
if(!data.can_trade){
|
||||
const parts = [];
|
||||
if (data.risk_status && data.risk_status.can_trade === false && data.risk_status.reason) {
|
||||
parts.push(data.risk_status.reason);
|
||||
}
|
||||
if((data.active_count||0) >= (data.max_active_positions||{{ max_active_positions }})) parts.push(`持仓 ${data.active_count}/${data.max_active_positions}`);
|
||||
const hard = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const opens = Number(data.opens_today);
|
||||
if (hard > 0 && !Number.isNaN(opens) && opens >= hard) parts.push(`本交易日开仓 ${opens}/${hard} 已达上限`);
|
||||
if(data.open_guard_blocks_now) parts.push(`未到北京时间 ${data.reset_hour||{{ reset_hour }}}:00`);
|
||||
canTradeText = parts.length ? `不可开仓(${parts.join(";")})` : "不可开仓";
|
||||
}).catch(()=>{
|
||||
if(!options.silent && accountSnapshotRetryCount < 3){
|
||||
accountSnapshotRetryCount += 1;
|
||||
setTimeout(() => refreshAccountSnapshot({ silent: true }), 1200 * accountSnapshotRetryCount);
|
||||
}
|
||||
const opensToday = Number(data.opens_today);
|
||||
const hardLim = Number(data.daily_open_hard_limit != null ? data.daily_open_hard_limit : {{ daily_open_hard_limit }});
|
||||
const alertLim = Number(data.daily_open_alert_threshold != null ? data.daily_open_alert_threshold : {{ daily_open_alert_threshold }});
|
||||
const openCntTxt = !Number.isNaN(opensToday)
|
||||
? `本交易日开仓 ${opensToday}${hardLim > 0 ? ` / 硬上限 ${hardLim}` : ""}(AI 提醒 ${alertLim})`
|
||||
: "";
|
||||
const tip = document.getElementById("order-rule-tip");
|
||||
const avail = (latestAvailableUsdt !== null && !Number.isNaN(latestAvailableUsdt)) ? `;交易账户可用约${latestAvailableUsdt.toFixed(2)}U` : "";
|
||||
if(tip){
|
||||
tip.innerText = `规则:最多 ${data.max_active_positions || {{ max_active_positions }}} 仓;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x;${openCntTxt ? openCntTxt + ";" : ""}${canTradeText}${avail};人工开仓盈亏比不得低于 {{ manual_min_planned_rr }}:1`;
|
||||
}
|
||||
const allowEl = document.getElementById("allow-open-before-reset");
|
||||
const guardStatus = document.getElementById("open-guard-status");
|
||||
const resetH = data.reset_hour != null ? data.reset_hour : {{ reset_hour }};
|
||||
if(allowEl && typeof data.open_guard_enabled !== "undefined"){
|
||||
allowEl.checked = !data.open_guard_enabled;
|
||||
}
|
||||
if(guardStatus && typeof data.open_guard_enabled !== "undefined"){
|
||||
guardStatus.innerText = data.open_guard_enabled
|
||||
? `已限制:${resetH}:00 前不可开仓`
|
||||
: `已放开:${resetH}:00 前允许开仓`;
|
||||
}
|
||||
}).catch(()=>{});
|
||||
});
|
||||
}
|
||||
|
||||
{% if ui_open_guard_enabled %}
|
||||
@@ -1801,7 +1829,7 @@ if(window.ManualOrderRrPreview){
|
||||
|
||||
refreshAccountSnapshot();
|
||||
const settingsRefreshFunds = document.getElementById("settings-refresh-funds");
|
||||
if (settingsRefreshFunds) settingsRefreshFunds.addEventListener("click", refreshAccountSnapshot);
|
||||
if (settingsRefreshFunds) settingsRefreshFunds.addEventListener("click", function(){ refreshAccountSnapshot({ force: true }); });
|
||||
if (window.AccountRiskBadge) AccountRiskBadge.startTicker();
|
||||
const _journalFormEl = document.getElementById("journal-form");
|
||||
if(_journalFormEl){
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<span class="risk-status-badge risk-status-{{ risk_status.status|default('normal') }}" id="account-risk-badge" role="status" title="{{ risk_status.reason|default('', true) }}" data-status-label="{{ risk_status.status_label|default('正常') }}"{% if risk_status.freeze_until_ms %} data-freeze-until-ms="{{ risk_status.freeze_until_ms }}"{% endif %}>{{ risk_status.status_label|default('正常') }}</span>
|
||||
</div>
|
||||
{% include 'instance_theme_toggle.html' %}
|
||||
<button type="button" class="btn-secondary btn-sm instance-header-refresh-funds" id="settings-refresh-funds" title="从交易所拉取最新资金">刷新资金</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="instance-header-stats-wrap instance-desktop-only">
|
||||
|
||||
@@ -10,40 +10,40 @@
|
||||
</div>
|
||||
<div class="stat-strip-item stat-strip-item--primary">
|
||||
<div class="label">总交易</div>
|
||||
<div class="value" id="stat-total">{{ total }}</div>
|
||||
<div class="value" id="stat-total" data-funds-field="stat-total">{{ total }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">胜率</div>
|
||||
<div class="value" id="stat-rate">{{ rate }}%</div>
|
||||
<div class="value" id="stat-rate" data-funds-field="stat-rate">{{ rate }}%</div>
|
||||
</div>
|
||||
<div class="stat-strip-item" title="平均盈利 ÷ 平均亏损(当前列表窗口)">
|
||||
<div class="label">盈亏比</div>
|
||||
<div class="value" id="stat-pl-ratio">{% if profit_loss_ratio is not none %}{{ profit_loss_ratio }}{% else %}—{% endif %}</div>
|
||||
<div class="value" id="stat-pl-ratio" data-funds-field="stat-pl-ratio">{% if profit_loss_ratio is not none %}{{ profit_loss_ratio }}{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">总资金</div>
|
||||
<div class="value" id="total-funds">{% if total_funds is not none %}{{ funds_fmt(total_funds) }}U{% else %}—{% endif %}</div>
|
||||
<div class="value" id="total-funds" data-funds-field="total-funds">{% if total_funds is not none %}{{ funds_fmt(total_funds) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">资金账户</div>
|
||||
<div class="value" id="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div>
|
||||
<div class="value" id="total-capital" data-funds-field="total-capital">{% if funding_usdt is not none %}{{ funds_fmt(funding_usdt) }}U{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">交易账户</div>
|
||||
<div class="value" id="current-capital">{{ funds_fmt(current_capital) }}U</div>
|
||||
<div class="value" id="current-capital" data-funds-field="current-capital">{{ funds_fmt(current_capital) }}U</div>
|
||||
</div>
|
||||
{% if options_enabled %}
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权资金账户</div>
|
||||
<div class="value" id="options-funding-usdc">{{ options_funding_label(options_funding_usdc, options_funding_usdt) }}</div>
|
||||
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc, options_funding_usdt) }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权交易账户</div>
|
||||
<div class="value" id="options-trading-usdc">{% if options_trading_usdc is not none %}{{ funds_fmt(options_trading_usdc) }} USDC{% else %}—{% endif %}</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{% if options_trading_usdc is not none %}{{ funds_fmt(options_trading_usdc) }} USDC{% else %}—{% endif %}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stat-strip-item stat-strip-item--pnl">
|
||||
<div class="label">实时盈亏</div>
|
||||
<div class="value" id="realtime-pnl">—</div>
|
||||
<div class="value" id="realtime-pnl" data-funds-field="realtime-pnl">—</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
{# 系统设置:左风控说明 · 右资金划转 + 数据导出 #}
|
||||
{# 系统设置:左风控说明 · 右资金划转 + 数据导出(顶栏资金与统计见 instance_header_panel) #}
|
||||
<div class="settings-page">
|
||||
<div class="card settings-account-summary">
|
||||
<div class="settings-account-summary-head">
|
||||
<div class="instance-toolbar-status">
|
||||
<div class="exchange-tag">{{ exchange_display }}</div>
|
||||
<span class="risk-status-badge risk-status-{{ risk_status.status|default('normal') }}" role="status">{{ risk_status.status_label|default('正常') }}</span>
|
||||
</div>
|
||||
<div class="settings-account-summary-actions">
|
||||
{% include 'instance_theme_toggle.html' %}
|
||||
<button type="button" class="btn-secondary btn-sm" id="settings-refresh-funds">刷新资金</button>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'instance_header_stats.html' %}
|
||||
</div>
|
||||
<div class="settings-cards-grid">
|
||||
<div class="card settings-card settings-card--risk">
|
||||
<h2>风控说明</h2>
|
||||
|
||||
Reference in New Issue
Block a user