Shorten force-close window to 5m and grey-out open during blocks.

Unify Gate/OKX/Binance: disable the open button with a side note during force-close, cooloff, and daily freeze, and enforce the same gate server-side.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-06 08:38:00 +08:00
parent 7352d10254
commit 7f22bffbc6
23 changed files with 403 additions and 42 deletions
+2
View File
@@ -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
+34 -6
View File
@@ -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,