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:
@@ -49,7 +49,8 @@ def test_next_force_close_after_trigger_same_day():
|
||||
|
||||
|
||||
def test_executing_window_and_countdown():
|
||||
now = _ms(2026, 7, 6, 0, 14)
|
||||
# 默认宽限 5 分钟:00:04 仍在执行窗
|
||||
now = _ms(2026, 7, 6, 0, 4)
|
||||
assert is_force_close_executing(0, now_ms=now, tz_name="Asia/Shanghai")
|
||||
assert is_force_close_active_hour(0, now_ms=now, tz_name="Asia/Shanghai")
|
||||
state = build_force_close_state(
|
||||
@@ -57,17 +58,21 @@ def test_executing_window_and_countdown():
|
||||
)
|
||||
assert state["enabled"] is True
|
||||
assert state["active"] is True
|
||||
assert state["executing"] is True
|
||||
assert state["grace_minutes"] == 5
|
||||
assert state["countdown"] == "执行中"
|
||||
assert state["next_at_ms"] == _ms(2026, 7, 7, 0, 0)
|
||||
|
||||
|
||||
def test_not_executing_after_grace_without_positions():
|
||||
now = _ms(2026, 7, 7, 0, 18)
|
||||
# 默认宽限 5 分钟:00:05 起已离开执行窗
|
||||
now = _ms(2026, 7, 7, 0, 5)
|
||||
assert not is_force_close_executing(0, now_ms=now, tz_name="Asia/Shanghai")
|
||||
state = build_force_close_state(
|
||||
True, 0, now_ms=now, tz_name="Asia/Shanghai", has_active_positions=False
|
||||
)
|
||||
assert state["active"] is False
|
||||
assert state["executing"] is False
|
||||
assert state["countdown"] != "执行中"
|
||||
assert state["next_at_ms"] == _ms(2026, 7, 8, 0, 0)
|
||||
|
||||
@@ -85,6 +90,8 @@ def test_format_countdown():
|
||||
|
||||
def test_infer_force_close_from_closed_at():
|
||||
assert is_close_at_force_close_window("2026-07-07 00:00", 0)
|
||||
assert is_close_at_force_close_window("2026-07-07 00:04", 0)
|
||||
assert not is_close_at_force_close_window("2026-07-07 00:05", 0)
|
||||
assert infer_force_close_result("2026-07-07 00:00", enabled=True, bj_hour=0) == "强制清仓"
|
||||
assert infer_force_close_result("2026-07-07 00:20", enabled=True, bj_hour=0) is None
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from lib.trade.open_trade_gate_lib import resolve_manual_open_gate
|
||||
|
||||
|
||||
def _base(**kwargs):
|
||||
cfg = dict(
|
||||
time_allows=True,
|
||||
active_count=0,
|
||||
max_active_positions=1,
|
||||
opens_today=0,
|
||||
hard_limit=10,
|
||||
risk_status={"can_trade": True, "reason": ""},
|
||||
force_close_enabled=False,
|
||||
force_close_bj_hour=0,
|
||||
now_ms=None,
|
||||
reset_hour=8,
|
||||
)
|
||||
cfg.update(kwargs)
|
||||
return resolve_manual_open_gate(**cfg)
|
||||
|
||||
|
||||
def test_open_when_clear():
|
||||
g = _base()
|
||||
assert g["can_trade"] is True
|
||||
assert g["open_block_note"] == ""
|
||||
|
||||
|
||||
def test_block_cooloff_note():
|
||||
g = _base(risk_status={"can_trade": False, "reason": "账户冷静期中"})
|
||||
assert g["can_trade"] is False
|
||||
assert "冷静期" in g["open_block_note"]
|
||||
|
||||
|
||||
def test_block_force_close_window():
|
||||
# 2026-07-07 00:02 Asia/Shanghai
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
now_ms = int(datetime(2026, 7, 7, 0, 2, tzinfo=ZoneInfo("Asia/Shanghai")).timestamp() * 1000)
|
||||
g = _base(force_close_enabled=True, force_close_bj_hour=0, now_ms=now_ms)
|
||||
assert g["can_trade"] is False
|
||||
assert g["force_close_blocks"] is True
|
||||
assert "强制清仓" in g["open_block_note"]
|
||||
assert "5 分钟" in g["open_block_note"]
|
||||
Reference in New Issue
Block a user