feat: add kill-switch control flow

This commit is contained in:
2026-06-07 14:00:20 +09:00
parent c912c08071
commit 7e23648489
30 changed files with 1424 additions and 51 deletions
@@ -0,0 +1,52 @@
"""PostgreSQL-backed day-scoped kill-switch exclusion store (REQ-044 / BAT-012).
Distinct from `PostgreSQLPauseStore`: pause is permanent (resume-until),
kill-switch exclusion is day-scoped — keyed by `(portfolio_id, trading_date)`
so the next trading day is automatically clear (reads filter by trading_date).
`portfolio_id == "*"` is the global-exclusion sentinel.
"""
from __future__ import annotations
from datetime import date
from typing import Any
from trex_core.adapters.persistence.postgresql import _require_psycopg
from trex_core.application.portfolio import GLOBAL_KILLSWITCH # noqa: F401 (re-export)
class PostgreSQLKillSwitchStore:
"""`killswitch_exclusion` table CRUD. (portfolio_id, trading_date) PK."""
def __init__(self, dsn: str, *, psycopg_module: Any = None) -> None:
self._psycopg = _require_psycopg(psycopg_module)
self._dsn = dsn
def exclude(self, portfolio_id: str, trading_date: date, *, actor: str = "kill-switch") -> None:
with self._psycopg.connect(self._dsn, autocommit=False) as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO killswitch_exclusion (portfolio_id, trading_date, actor) "
"VALUES (%s, %s, %s) "
"ON CONFLICT (portfolio_id, trading_date) DO NOTHING",
(portfolio_id, trading_date, actor),
)
conn.commit()
def clear(self, portfolio_id: str, trading_date: date) -> None:
with self._psycopg.connect(self._dsn, autocommit=False) as conn:
with conn.cursor() as cur:
cur.execute(
"DELETE FROM killswitch_exclusion "
"WHERE portfolio_id = %s AND trading_date = %s",
(portfolio_id, trading_date),
)
conn.commit()
def list_excluded(self, trading_date: date) -> set[str]:
with self._psycopg.connect(self._dsn) as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT portfolio_id FROM killswitch_exclusion WHERE trading_date = %s",
(trading_date,),
)
return {row[0] for row in cur.fetchall()}