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:
@@ -121,6 +121,11 @@ from lib.trade.time_close_lib import (
|
||||
time_close_label,
|
||||
time_close_settings_from_row,
|
||||
)
|
||||
from lib.trade.force_close_lib import (
|
||||
apply_force_close_to_payload,
|
||||
enrich_orders_force_close,
|
||||
force_close_template_context,
|
||||
)
|
||||
from lib.trade.manual_sltp_lib import (
|
||||
normalize_open_sltp_mode,
|
||||
resolve_entrust_sltp_prices,
|
||||
@@ -6910,6 +6915,12 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
raw_order_list = conn.execute("SELECT * FROM order_monitors WHERE status='active'").fetchall()
|
||||
for o in raw_order_list:
|
||||
order_list.append(enrich_order_item(row_to_dict(o), current_capital))
|
||||
enrich_orders_force_close(
|
||||
order_list,
|
||||
FORCE_CLOSE_ENABLED,
|
||||
FORCE_CLOSE_BJ_HOUR,
|
||||
now_ms=int(app_now().timestamp() * 1000),
|
||||
)
|
||||
exchange_pnl_sync = {}
|
||||
if exchange_private_api_configured() and not request_is_hub_soft_nav() and embed_mode not in (
|
||||
"fragment",
|
||||
@@ -7062,6 +7073,11 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
kline_timeframe=KLINE_TIMEFRAME,
|
||||
exchange_pnl_sync=exchange_pnl_sync,
|
||||
**strategy_extra,
|
||||
**force_close_template_context(
|
||||
FORCE_CLOSE_ENABLED,
|
||||
FORCE_CLOSE_BJ_HOUR,
|
||||
now_ms=int(app_now().timestamp() * 1000),
|
||||
),
|
||||
**embed_context_extras("gate"),
|
||||
)
|
||||
if embed_mode == "fragment":
|
||||
@@ -7159,6 +7175,11 @@ def api_account_snapshot():
|
||||
"manual_min_planned_rr": MANUAL_MIN_PLANNED_RR,
|
||||
"trading_day": trading_day,
|
||||
"risk_status": risk_status,
|
||||
**force_close_template_context(
|
||||
FORCE_CLOSE_ENABLED,
|
||||
FORCE_CLOSE_BJ_HOUR,
|
||||
now_ms=int(now.timestamp() * 1000),
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
@@ -7425,6 +7446,11 @@ def api_price_snapshot():
|
||||
funds_decimals=FUNDS_DECIMALS,
|
||||
)
|
||||
apply_time_close_to_payload(payload, r)
|
||||
apply_force_close_to_payload(
|
||||
payload,
|
||||
enabled=FORCE_CLOSE_ENABLED,
|
||||
bj_hour=FORCE_CLOSE_BJ_HOUR,
|
||||
)
|
||||
payload["opened_at"] = r["opened_at"] if "opened_at" in r.keys() else None
|
||||
open_ms = r["opened_at_ms"] if "opened_at_ms" in r.keys() else None
|
||||
payload["opened_at_ms"] = int(open_ms) if open_ms not in (None, "") else None
|
||||
@@ -7460,6 +7486,10 @@ def api_price_snapshot():
|
||||
"order_prices": order_prices,
|
||||
"position_marks": position_marks,
|
||||
"positions_raw_count": len(all_swap_positions),
|
||||
**force_close_template_context(
|
||||
FORCE_CLOSE_ENABLED,
|
||||
FORCE_CLOSE_BJ_HOUR,
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -277,6 +277,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="暗色主题">
|
||||
@@ -413,6 +414,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">
|
||||
@@ -755,7 +757,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>
|
||||
@@ -1637,6 +1639,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){
|
||||
@@ -1772,6 +1777,9 @@ function refreshAccountSnapshot(){
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader) {
|
||||
TimeCloseUI.paintForceCloseHeader(data.force_close);
|
||||
}
|
||||
let canTradeText = "可开仓";
|
||||
if (!data.can_trade) {
|
||||
const parts = [];
|
||||
@@ -1944,6 +1952,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}`);
|
||||
@@ -1993,6 +2004,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));
|
||||
|
||||
Reference in New Issue
Block a user