diff --git a/crypto_monitor_binance/.env.example b/crypto_monitor_binance/.env.example index e85fa87..b085a57 100644 --- a/crypto_monitor_binance/.env.example +++ b/crypto_monitor_binance/.env.example @@ -190,6 +190,8 @@ AUTO_TRANSFER_BJ_HOUR=8 FORCE_CLOSE_BJ_HOUR=0 # 是否启用强制清仓(默认关闭,true 才会在整点执行) FORCE_CLOSE_ENABLED=false +# 强制清仓执行窗口(分钟,默认 5;该窗口内禁止开仓) +FORCE_CLOSE_GRACE_MINUTES=5 # 推送与AI超时(秒) WECHAT_TIMEOUT_SECONDS=10 diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py index 5d5f57b..961f582 100644 --- a/crypto_monitor_binance/app.py +++ b/crypto_monitor_binance/app.py @@ -3307,6 +3307,7 @@ def resolve_capital_base_for_key_open(conn, trading_day, live_capital): def precheck_risk(conn, symbol, direction): now = app_now() from lib.trade.account_risk_lib import account_risk_blocks_trading + from lib.trade.force_close_lib import force_close_blocks_new_open ok_risk, risk_reason = account_risk_blocks_trading( conn, @@ -3316,6 +3317,13 @@ def precheck_risk(conn, symbol, direction): ) if not ok_risk: return False, risk_reason + fc_block, fc_note = force_close_blocks_new_open( + FORCE_CLOSE_ENABLED, + FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + ) + if fc_block: + return False, fc_note or "强制清仓窗口内暂不可开仓" if not trading_day_reset_allows_new_open(now): return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" from lib.trade.account_risk_lib import position_limit_reached @@ -6930,8 +6938,10 @@ def force_close_before_reset(): if not FORCE_CLOSE_ENABLED: return now = app_now() - # 每天北京时间指定整点小时内执行一次性兜底清仓(默认 00:xx) - if now.hour != FORCE_CLOSE_BJ_HOUR: + # 每天北京时间指定整点起 FORCE_CLOSE_GRACE_MINUTES 分钟内执行兜底清仓 + from lib.trade.force_close_lib import is_force_close_executing + + if not is_force_close_executing(FORCE_CLOSE_BJ_HOUR, now_ms=int(now.timestamp() * 1000)): return conn = get_db() rows = conn.execute("SELECT * FROM order_monitors WHERE status='active'").fetchall() @@ -7303,14 +7313,22 @@ def render_main_page(page="trade", embed_mode=None): position_limit_count = count_position_limit_active_monitors(conn) opens_today = count_opens_for_trading_day(conn, trading_day) risk_status = hub_account_risk_status(conn) - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] key_rule_ctx = key_monitor_rule_template_context( kline_timeframe=KLINE_TIMEFRAME, key_breakout_amp_min_pct=KEY_BREAKOUT_AMP_MIN_PCT, @@ -7377,6 +7395,7 @@ def render_main_page(page="trade", embed_mode=None): price_refresh_seconds=PRICE_REFRESH_SECONDS, active_count=position_limit_count, can_trade=can_trade, + open_block_note=open_block_note, opens_today=opens_today, daily_open_hard_limit=DAILY_OPEN_HARD_LIMIT, daily_open_alert_threshold=DAILY_OPEN_ALERT_THRESHOLD, @@ -7564,14 +7583,22 @@ def api_account_snapshot(): header_trade_stats = header_trade_stats_for_window(conn, _list_window_from_request(), APP_TZ) conn.close() - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] available_trading_usdt = get_available_trading_usdt() unrealized_pnl = None @@ -7597,6 +7624,7 @@ def api_account_snapshot(): "active_count": position_limit_count, "max_active_positions": MAX_ACTIVE_POSITIONS, "can_trade": can_trade, + "open_block_note": open_block_note, "opens_today": opens_today, "daily_open_hard_limit": DAILY_OPEN_HARD_LIMIT, "daily_open_alert_threshold": DAILY_OPEN_ALERT_THRESHOLD, diff --git a/crypto_monitor_gate/.env.example b/crypto_monitor_gate/.env.example index 49b0744..220a2c3 100644 --- a/crypto_monitor_gate/.env.example +++ b/crypto_monitor_gate/.env.example @@ -192,6 +192,8 @@ AUTO_TRANSFER_BJ_HOUR=8 FORCE_CLOSE_BJ_HOUR=0 # 是否启用强制清仓(默认关闭,true 才会在整点执行) FORCE_CLOSE_ENABLED=false +# 强制清仓执行窗口(分钟,默认 5;该窗口内禁止开仓) +FORCE_CLOSE_GRACE_MINUTES=5 # 推送与AI超时(秒) WECHAT_TIMEOUT_SECONDS=10 diff --git a/crypto_monitor_gate/app.py b/crypto_monitor_gate/app.py index a00dfd7..6f18812 100644 --- a/crypto_monitor_gate/app.py +++ b/crypto_monitor_gate/app.py @@ -2971,6 +2971,7 @@ def resolve_capital_base_for_key_open(conn, trading_day, live_capital): def precheck_risk(conn, symbol, direction): now = app_now() from lib.trade.account_risk_lib import account_risk_blocks_trading + from lib.trade.force_close_lib import force_close_blocks_new_open ok_risk, risk_reason = account_risk_blocks_trading( conn, @@ -2980,6 +2981,13 @@ def precheck_risk(conn, symbol, direction): ) if not ok_risk: return False, risk_reason + fc_block, fc_note = force_close_blocks_new_open( + FORCE_CLOSE_ENABLED, + FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + ) + if fc_block: + return False, fc_note or "强制清仓窗口内暂不可开仓" if not trading_day_reset_allows_new_open(now): return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" from lib.trade.account_risk_lib import position_limit_reached @@ -6553,8 +6561,10 @@ def force_close_before_reset(): if not FORCE_CLOSE_ENABLED: return now = app_now() - # 每天北京时间指定整点小时内执行一次性兜底清仓(默认 00:xx) - if now.hour != FORCE_CLOSE_BJ_HOUR: + # 每天北京时间指定整点起 FORCE_CLOSE_GRACE_MINUTES 分钟内执行兜底清仓 + from lib.trade.force_close_lib import is_force_close_executing + + if not is_force_close_executing(FORCE_CLOSE_BJ_HOUR, now_ms=int(now.timestamp() * 1000)): return conn = get_db() rows = conn.execute("SELECT * FROM order_monitors WHERE status='active'").fetchall() @@ -7075,14 +7085,22 @@ def render_main_page(page="trade", embed_mode=None): position_limit_count = count_position_limit_active_monitors(conn) opens_today = count_opens_for_trading_day(conn, trading_day) risk_status = hub_account_risk_status(conn) - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] key_rule_ctx = key_monitor_rule_template_context( kline_timeframe=KLINE_TIMEFRAME, key_breakout_amp_min_pct=KEY_BREAKOUT_AMP_MIN_PCT, @@ -7146,6 +7164,7 @@ def render_main_page(page="trade", embed_mode=None): price_refresh_seconds=PRICE_REFRESH_SECONDS, active_count=position_limit_count, can_trade=can_trade, + open_block_note=open_block_note, opens_today=opens_today, daily_open_hard_limit=DAILY_OPEN_HARD_LIMIT, daily_open_alert_threshold=DAILY_OPEN_ALERT_THRESHOLD, @@ -7354,14 +7373,22 @@ def api_account_snapshot(): header_trade_stats = header_trade_stats_for_window(conn, _list_window_from_request(), APP_TZ) conn.close() - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] available_trading_usdt = get_available_trading_usdt() unrealized_pnl = None @@ -7390,6 +7417,7 @@ def api_account_snapshot(): "active_count": position_limit_count, "max_active_positions": MAX_ACTIVE_POSITIONS, "can_trade": can_trade, + "open_block_note": open_block_note, "opens_today": opens_today, "daily_open_hard_limit": DAILY_OPEN_HARD_LIMIT, "daily_open_alert_threshold": DAILY_OPEN_ALERT_THRESHOLD, diff --git a/crypto_monitor_okx/.env.example b/crypto_monitor_okx/.env.example index a38d5ef..2cd4ee2 100644 --- a/crypto_monitor_okx/.env.example +++ b/crypto_monitor_okx/.env.example @@ -262,6 +262,8 @@ AUTO_TRANSFER_BJ_HOUR=8 FORCE_CLOSE_BJ_HOUR=0 # 是否启用强制清仓(默认关闭,true 才会在整点执行) FORCE_CLOSE_ENABLED=false +# 强制清仓执行窗口(分钟,默认 5;该窗口内禁止开仓) +FORCE_CLOSE_GRACE_MINUTES=5 # 推送与AI超时(秒) WECHAT_TIMEOUT_SECONDS=10 diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 7c58d3e..5270451 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -2729,6 +2729,7 @@ def trading_day_reset_allows_new_open(now, conn=None): def precheck_risk(conn, symbol, direction): now = app_now() from lib.trade.account_risk_lib import account_risk_blocks_trading + from lib.trade.force_close_lib import force_close_blocks_new_open ok_risk, risk_reason = account_risk_blocks_trading( conn, @@ -2738,6 +2739,13 @@ def precheck_risk(conn, symbol, direction): ) if not ok_risk: return False, risk_reason + fc_block, fc_note = force_close_blocks_new_open( + FORCE_CLOSE_ENABLED, + FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + ) + if fc_block: + return False, fc_note or "强制清仓窗口内暂不可开仓" if not trading_day_reset_allows_new_open(now): return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓" from lib.trade.account_risk_lib import position_limit_reached @@ -6399,8 +6407,10 @@ def force_close_before_reset(): if not FORCE_CLOSE_ENABLED: return now = app_now() - # 每天北京时间指定整点小时内执行一次性兜底清仓(默认 00:xx) - if now.hour != FORCE_CLOSE_BJ_HOUR: + # 每天北京时间指定整点起 FORCE_CLOSE_GRACE_MINUTES 分钟内执行兜底清仓 + from lib.trade.force_close_lib import is_force_close_executing + + if not is_force_close_executing(FORCE_CLOSE_BJ_HOUR, now_ms=int(now.timestamp() * 1000)): return conn = get_db() rows = conn.execute("SELECT * FROM order_monitors WHERE status='active'").fetchall() @@ -6700,14 +6710,22 @@ def render_main_page(page="trade", embed_mode=None): open_guard_blocks_now = open_guard_enabled and now.hour < TRADING_DAY_RESET_HOUR opens_today = count_opens_for_trading_day(conn, trading_day) risk_status = hub_account_risk_status(conn) - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now, conn), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] key_rule_ctx = {} if page in ("key_monitor", "trade") or page in ( "strategy", @@ -6794,6 +6812,7 @@ def render_main_page(page="trade", embed_mode=None): price_refresh_seconds=PRICE_REFRESH_SECONDS, active_count=position_limit_count, can_trade=can_trade, + open_block_note=open_block_note, opens_today=opens_today, daily_open_hard_limit=DAILY_OPEN_HARD_LIMIT, daily_open_alert_threshold=DAILY_OPEN_ALERT_THRESHOLD, @@ -7050,14 +7069,22 @@ def api_account_snapshot(): header_trade_stats = header_trade_stats_for_window(conn, _list_window_from_request(), APP_TZ) conn.close() open_guard_blocks_now = open_guard_enabled and now.hour < TRADING_DAY_RESET_HOUR - can_trade = can_trade_new_open( + from lib.trade.open_trade_gate_lib import resolve_manual_open_gate + + _open_gate = resolve_manual_open_gate( time_allows=trading_day_reset_allows_new_open(now), active_count=position_limit_count, max_active_positions=MAX_ACTIVE_POSITIONS, opens_today=opens_today, hard_limit=DAILY_OPEN_HARD_LIMIT, - extra_blocks=not risk_status.get("can_trade", True), + risk_status=risk_status, + force_close_enabled=FORCE_CLOSE_ENABLED, + force_close_bj_hour=FORCE_CLOSE_BJ_HOUR, + now_ms=int(now.timestamp() * 1000), + reset_hour=TRADING_DAY_RESET_HOUR, ) + can_trade = _open_gate["can_trade"] + open_block_note = _open_gate["open_block_note"] available_trading_usdt = get_available_trading_usdt() unrealized_pnl = None @@ -7120,6 +7147,7 @@ def api_account_snapshot(): "active_count": position_limit_count, "max_active_positions": MAX_ACTIVE_POSITIONS, "can_trade": can_trade, + "open_block_note": open_block_note, "opens_today": opens_today, "daily_open_hard_limit": DAILY_OPEN_HARD_LIMIT, "daily_open_alert_threshold": DAILY_OPEN_ALERT_THRESHOLD, diff --git a/lib/common/static/form_submit_guard.js b/lib/common/static/form_submit_guard.js index 25b56e0..e84807c 100644 --- a/lib/common/static/form_submit_guard.js +++ b/lib/common/static/form_submit_guard.js @@ -35,7 +35,8 @@ delete form.dataset.submitGuard; form.classList.remove("is-form-submitting"); submitButtons(form).forEach(function (btn) { - btn.disabled = false; + // 风控灰显(开仓门禁)保持禁用 + btn.disabled = btn.classList.contains("is-blocked"); var orig = btn.dataset.submitGuardOrig; if (orig !== undefined) { if (btn.tagName === "BUTTON") btn.textContent = orig; diff --git a/lib/common/static/instance_page.css b/lib/common/static/instance_page.css index ebeaea6..c0db29f 100644 --- a/lib/common/static/instance_page.css +++ b/lib/common/static/instance_page.css @@ -36,8 +36,21 @@ .order-monitor-form .om-check{display:inline-flex;align-items:center;gap:5px;font-size:.82rem;color:#cfd3ef;cursor:pointer;user-select:none} .order-monitor-form .om-time-close{display:inline-flex;align-items:center;gap:6px;font-size:.82rem;color:#cfd3ef} .order-monitor-form .om-time-close select{width:auto;min-width:4.2rem;max-width:5.5rem;padding:6px 8px} - .order-monitor-form .om-row-action{padding-top:2px} + .order-monitor-form .om-row-action{padding-top:2px;display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px} .order-monitor-form .om-submit{min-width:11rem;padding:10px 18px;font-weight:600} + .order-monitor-form .om-submit.is-blocked, + .order-monitor-form .om-submit:disabled{ + opacity:.45; + cursor:not-allowed; + filter:grayscale(.35); + pointer-events:none; + } + .order-monitor-form .om-open-block-note{ + color:var(--danger,#ff7b7b); + font-size:13px; + line-height:1.4; + max-width:min(28rem,100%); + } .order-plan-preview{display:flex;gap:18px;flex-wrap:wrap;align-items:center;margin:4px 0 10px;padding:10px 12px;background:#151a28;border:1px solid #2a3150;border-radius:8px;font-size:.85rem} #add-order-form #sltp-mode{min-width:12.5rem;max-width:16rem;width:auto} .order-preview-risk{color:#ff6b6b} diff --git a/lib/common/static/open_submit_gate.js b/lib/common/static/open_submit_gate.js new file mode 100644 index 0000000..aaf7bdf --- /dev/null +++ b/lib/common/static/open_submit_gate.js @@ -0,0 +1,55 @@ +/** + * 实盘下单监控:开仓按钮灰显 + 旁注(强制清仓/冷静期/日冻结等). + */ +(function (global) { + function apply(data) { + const d = data || {}; + const btn = + document.getElementById("om-submit-btn") || + document.querySelector("#add-order-form button.om-submit"); + const noteEl = document.getElementById("om-open-block-note"); + if (!btn && !noteEl) return; + + const canTrade = d.can_trade !== false; + let note = (d.open_block_note || "").trim(); + const fc = d.force_close || {}; + const rs = d.risk_status || {}; + if (!note && fc.enabled && fc.executing) { + const grace = fc.grace_minutes != null ? fc.grace_minutes : 5; + note = + "强制清仓窗口内(北京时间 " + + (fc.hour_label || "--:--") + + " 起 " + + grace + + " 分钟),暂不可开仓"; + } + if (!note && rs.can_trade === false && rs.reason) { + note = String(rs.reason); + } + if (!note && !canTrade) { + note = "当前不可开仓"; + } + + if (btn) { + btn.disabled = !canTrade; + btn.classList.toggle("is-blocked", !canTrade); + btn.setAttribute("aria-disabled", canTrade ? "false" : "true"); + if (!canTrade) { + btn.title = note || "当前不可开仓"; + } else { + btn.removeAttribute("title"); + } + } + if (noteEl) { + if (!canTrade && note) { + noteEl.hidden = false; + noteEl.textContent = note; + } else { + noteEl.hidden = true; + noteEl.textContent = ""; + } + } + } + + global.OpenSubmitGate = { apply: apply }; +})(window); diff --git a/lib/env/env_schema.py b/lib/env/env_schema.py index a2f43f0..2375aa9 100644 --- a/lib/env/env_schema.py +++ b/lib/env/env_schema.py @@ -76,6 +76,7 @@ HOT_RELOAD_EXACT = frozenset({ "TRANSFER_CCY", "FORCE_CLOSE_ENABLED", "FORCE_CLOSE_BJ_HOUR", + "FORCE_CLOSE_GRACE_MINUTES", "BTC_LEVERAGE", "ALT_LEVERAGE", "DAILY_START_CAPITAL", diff --git a/lib/env/env_ui_manifest.py b/lib/env/env_ui_manifest.py index 7125b1c..9dff06e 100644 --- a/lib/env/env_ui_manifest.py +++ b/lib/env/env_ui_manifest.py @@ -83,6 +83,7 @@ _SHARED_SECTIONS: list[dict[str, Any]] = [ ("KEY_AUTO_MIN_PLANNED_RR", "关键位最低盈亏比", "自动单计划 RR 须严格大于该值,默认 1.5"), ("FORCE_CLOSE_ENABLED", "强制清仓开关", ""), ("FORCE_CLOSE_BJ_HOUR", "强制清仓整点(北京)", ""), + ("FORCE_CLOSE_GRACE_MINUTES", "强制清仓窗口(分钟)", "默认 5;整点起该分钟内执行并禁止开仓"), ], }, { diff --git a/lib/instance/instance_settings_lib.py b/lib/instance/instance_settings_lib.py index 017b301..5a3e12b 100644 --- a/lib/instance/instance_settings_lib.py +++ b/lib/instance/instance_settings_lib.py @@ -152,7 +152,10 @@ def build_instance_settings_view( "title": "整点强制清仓", "rows": [ _row("强制清仓", _on_off(force_close_on)), - _row("执行时刻", f"北京时间 {force_close_hour}:00 起 15 分钟内"), + _row( + "执行时刻", + f"北京时间 {force_close_hour}:00 起 {_env_int('FORCE_CLOSE_GRACE_MINUTES', 5)} 分钟内", + ), ], } ) diff --git a/lib/instance/templates/embed_boot_scripts.html b/lib/instance/templates/embed_boot_scripts.html index 1d02345..96cb39f 100644 --- a/lib/instance/templates/embed_boot_scripts.html +++ b/lib/instance/templates/embed_boot_scripts.html @@ -1230,11 +1230,15 @@ function applyAccountSnapshot(data){ if(data.force_close && window.TimeCloseUI && TimeCloseUI.paintForceCloseHeader){ TimeCloseUI.paintForceCloseHeader(data.force_close); } + if(window.OpenSubmitGate) OpenSubmitGate.apply(data); let canTradeText = "可开仓"; if(!data.can_trade){ const parts = []; + if(data.open_block_note) parts.push(data.open_block_note); if(data.risk_status && data.risk_status.can_trade === false && data.risk_status.reason){ - parts.push(data.risk_status.reason); + if(!data.open_block_note || data.open_block_note.indexOf(data.risk_status.reason) < 0){ + parts.push(data.risk_status.reason); + } } const ac = Number(data.active_count || 0); const max = Number(data.max_active_positions || {{ max_active_positions }}); @@ -1242,9 +1246,8 @@ function applyAccountSnapshot(data){ 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(";")})`; + 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 }}); diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html index b25fa33..498f3a5 100644 --- a/lib/instance/templates/embed_shell.html +++ b/lib/instance/templates/embed_shell.html @@ -10,6 +10,7 @@ +
FORCE_CLOSE_ENABLED=false);开启后按 FORCE_CLOSE_BJ_HOUR 在北京时间该整点起 FORCE_CLOSE_GRACE_MINUTES(默认 5)分钟内清掉 active 监控仓。
+ {% endif %}
+ 冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓。
+ FORCE_CLOSE_ENABLED=false);开启后按 FORCE_CLOSE_BJ_HOUR 在北京时间该整点小时内清掉 active 监控仓。
+ 当前已关闭(FORCE_CLOSE_ENABLED=false);开启后按 FORCE_CLOSE_BJ_HOUR 在北京时间该整点起 FORCE_CLOSE_GRACE_MINUTES(默认 5)分钟内清掉 active 监控仓。
{% endif %}
- 仅影响本系统监控中的仓;交易所裸仓且无本地监控时不会被此逻辑平掉。
+ 冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓;交易所裸仓且无本地监控时不会被此逻辑平掉。
FORCE_CLOSE_ENABLED=false);开启后按 FORCE_CLOSE_BJ_HOUR 在北京时间该整点起 FORCE_CLOSE_GRACE_MINUTES(默认 5)分钟内清掉 active 监控仓。
+ {% endif %}
+ 冷静期/日冻结期间开仓按钮同样灰显不可点。仅影响本系统监控中的仓。
+