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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user