feat: show force-close badge and countdown when FORCE_CLOSE is enabled

Add shared lib/UI for midnight force-close indicator on instance and hub pages, and document Gate intraday 0-point exit alignment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-06 02:17:47 +08:00
parent 7fb87d6030
commit a268a93027
22 changed files with 974 additions and 18 deletions
@@ -944,6 +944,9 @@ function refreshPriceSnapshot(){
if(data.updated_at && updatedEl){
updatedEl.innerText = data.updated_at;
}
if(data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader){
TimeCloseUI.paintForceCloseHeader(data.force_close);
}
(data.key_prices || []).forEach(k=>{
const pEl = document.getElementById(`key-price-${k.id}`);
if(pEl){
@@ -1015,6 +1018,7 @@ function refreshPriceSnapshot(){
if(o.exchange_tpsl) paintExchangeTpslRow(o.id, o.exchange_tpsl);
paintPlanTpslDisplay(o.id, o);
if(window.TimeCloseUI) TimeCloseUI.paintOrderTimeClose(o);
if(window.TimeCloseUI) TimeCloseUI.paintOrderForceClose(o);
});
}).catch(()=>{});
}
@@ -1074,6 +1078,9 @@ function refreshAccountSnapshot(){
}
}
}
if (data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader) {
TimeCloseUI.paintForceCloseHeader(data.force_close);
}
let canTradeText = "可开仓";
if (!data.can_trade) {
const parts = [];
@@ -1247,6 +1254,9 @@ function refreshPriceSnapshotConditional(){
fetch("/api/price_snapshot").then(r=>r.json()).then(data=>{
const updatedEl = document.getElementById("price-last-updated");
if(data.updated_at && updatedEl) updatedEl.innerText = data.updated_at;
if(data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader){
TimeCloseUI.paintForceCloseHeader(data.force_close);
}
if(page === "key_monitor"){
(data.key_prices || []).forEach(k=>{
const pEl = document.getElementById(`key-price-${k.id}`);
@@ -1296,6 +1306,7 @@ function refreshPriceSnapshotConditional(){
paintExchangeTpslRow(o.id, o.exchange_tpsl || {});
paintPlanTpslDisplay(o.id, o);
if(window.TimeCloseUI) TimeCloseUI.paintOrderTimeClose(o);
if(window.TimeCloseUI) TimeCloseUI.paintOrderForceClose(o);
const holdEl = document.getElementById(`order-hold-duration-${o.id}`);
if(holdEl && o.opened_at_ms != null && o.opened_at_ms !== ""){
holdEl.setAttribute("data-order-opened-ms", String(o.opened_at_ms));
@@ -95,6 +95,7 @@
· <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
</span>
{% endif %}
{% include 'force_close_order_badge.html' %}
<span class="pos-side-badge {{ 'pos-side-long' if o.direction == 'long' else 'pos-side-short' }}">{{ '做多' if o.direction == 'long' else '做空' }}</span>
</div>
<div class="pos-head-actions">
+2 -1
View File
@@ -31,6 +31,7 @@
{% if trade_policy.badge_text %}
<span class="trade-policy-badge" title="账户交易限制(.env">{{ trade_policy.badge_text }}</span>
{% endif %}
{% include 'force_close_header_badge.html' %}
<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 class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
@@ -118,7 +119,7 @@
<script src="/static/instance_ui.js?v=6"></script>
<script src="/static/journal_upload_slots.js?v=3"></script>
<script src="/static/instance_records_mobile.js?v=2"></script>
<script src="/static/time_close_ui.js?v=2"></script>
<script src="/static/time_close_ui.js?v=3"></script>
<script src="/static/ai_review_render.js?v=2"></script>
<script src="/static/form_submit_guard.js?v=2"></script>
<script src="/static/order_entry_model.js?v=3"></script>
@@ -0,0 +1,8 @@
{% if force_close.enabled %}
<span class="force-close-badge" id="force-close-header-badge" role="status"
title="北京时间 {{ force_close.hour_label }} 整点未平仓将市价强制清仓(result=强制清仓)"
data-force-close-at-ms="{{ force_close.next_at_ms or '' }}"
data-force-close-active="{{ '1' if force_close.active else '0' }}">
{{ force_close.label }} 已开启 · <span class="force-close-header-cd">{{ force_close.countdown or '--:--:--' }}</span>
</span>
{% endif %}
@@ -0,0 +1,8 @@
{% if force_close.enabled %}
<span class="pos-symbol-force-close pos-force-close-meta" id="order-force-close-wrap-{{ o.id }}"
data-force-close-at-ms="{{ o.force_close_at_ms or force_close.next_at_ms or '' }}"
data-force-close-active="{{ '1' if (o.force_close_active or force_close.active) else '0' }}">
<span class="pos-force-close-label">{{ o.force_close_label or force_close.label }}</span>
· <span class="pos-force-close-cd" id="order-force-close-cd-{{ o.id }}">{{ o.force_close_countdown or force_close.countdown or '--:--:--' }}</span>
</span>
{% endif %}