fix(risk): stop stale 4h cooloff after 1h journal expires

Anchor last_close on journal save, ignore leftover stored until when 1h window ended, and clear expired cooloff on trading-day rollover.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-18 22:04:14 +08:00
parent 9c778e0232
commit c73944581c
3 changed files with 105 additions and 31 deletions
+60
View File
@@ -236,6 +236,66 @@ class AccountRiskLibTests(unittest.TestCase):
self.assertEqual(st["status"], STATUS_FREEZE_1H)
self.assertAlmostEqual(st["freeze_remaining_sec"], 54 * 60, delta=3)
def test_stale_4h_ignored_after_1h_journal_expired(self):
"""复盘已降为 1h 且窗口结束后,不应再读库内旧 4h until。"""
conn = _mem_conn()
close_at = datetime(2026, 6, 18, 17, 56, 0)
now = datetime(2026, 6, 18, 21, 50, 0)
close_ms = _local_ms(close_at)
stale_4h_until = close_ms + 4 * 3600 * 1000
conn.execute(
"""UPDATE account_risk_state SET
trading_day='2026-06-18',
manual_close_count=1,
cooloff_until_ms=?,
cooloff_hours=1,
last_close_at_ms=?,
daily_frozen=0
WHERE id=1""",
(stale_4h_until, close_ms),
)
st = compute_account_risk_status(conn, trading_day="2026-06-18", now=now)
self.assertEqual(st["status"], STATUS_NORMAL)
self.assertTrue(st["can_trade"])
def test_active_4h_countdown_matches_tier(self):
conn = _mem_conn()
close_at = datetime(2026, 6, 18, 21, 46, 0)
now = datetime(2026, 6, 18, 21, 52, 0)
close_ms = _local_ms(close_at)
on_user_initiated_close(
conn,
source=CLOSE_SOURCE_USER_INSTANCE,
closed_at_ms=close_ms,
trading_day="2026-06-18",
now=close_at,
)
st = compute_account_risk_status(conn, trading_day="2026-06-18", now=now)
self.assertEqual(st["status"], STATUS_FREEZE_4H)
self.assertAlmostEqual(st["freeze_remaining_sec"], 3 * 3600 + 54 * 60, delta=5)
def test_trading_day_reset_clears_expired_stale_cooloff(self):
conn = _mem_conn()
close_at = datetime(2026, 6, 18, 17, 56, 0)
close_ms = _local_ms(close_at)
stale_4h_until = close_ms + 4 * 3600 * 1000
conn.execute(
"""UPDATE account_risk_state SET
trading_day='2026-06-18',
manual_close_count=1,
cooloff_until_ms=?,
cooloff_hours=1,
last_close_at_ms=?,
daily_frozen=0
WHERE id=1""",
(stale_4h_until, close_ms),
)
next_day = datetime(2026, 6, 19, 9, 0, 0)
st = compute_account_risk_status(conn, trading_day="2026-06-19", now=next_day)
self.assertEqual(st["status"], STATUS_NORMAL)
row = conn.execute("SELECT cooloff_until_ms FROM account_risk_state WHERE id=1").fetchone()
self.assertIsNone(row["cooloff_until_ms"])
def test_legacy_naive_utc_ms_countdown_normalized(self):
conn = _mem_conn()
now = datetime(2026, 6, 14, 12, 0, 0)