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
+19 -6
View File
@@ -6,7 +6,7 @@ strategy-facing communication runs over RabbitMQ (REQ-021/022/023).
Threads (started in `run()`):
- intent RPC consumer — `trex.intent.req`, prefetch=1, FIFO, dedup via intent_log
- snapshot RPC consumer — `trex.snapshot.req` (also refreshes the watchdog)
- pause RPC consumer — `trex.pause.req` (REQ-032)
- control RPC consumer — `trex.control.req` (pause/resume/list + kill_switch/resume_kill_switch, REQ-032/043)
- execution loop — broker dispatch + poll + slot transitions + event emit
- ledger writer — ledger outbox drain (REQ-019)
- snapshot scheduler — daily portfolio-hub snapshot refresh (REQ-030)
@@ -43,15 +43,17 @@ from trex_core.adapters.portfolio import (
PostgreSQLPauseStore,
StubPortfolioRead,
)
from trex_core.adapters.portfolio.killswitch_store import PostgreSQLKillSwitchStore
from trex_core.application.broker_port import BrokerPort
from trex_core.application.execution import ExecutionService
from trex_core.application.intents import IntentService
from trex_core.application.ledger_outbox import LedgerWriter
from trex_core.application.ledger_port import LedgerPort
from trex_core.application.messaging import MessagingTransport
from trex_core.application.killswitch import KillSwitchService
from trex_core.application.messaging_handlers import (
make_control_rpc_handler,
make_intent_rpc_handler,
make_pause_rpc_handler,
make_snapshot_rpc_handler,
slot_transition_event_payload,
)
@@ -148,6 +150,13 @@ class Daemon:
# BAT-010/011: per-slot watchdog (snapshot RPC writer, safety-sell reader).
self.watchdog = WatchdogStore()
self.pause_store = PostgreSQLPauseStore(config.dsn)
# BAT-012: day-scoped kill-switch exclusion store + service.
self.killswitch_store = PostgreSQLKillSwitchStore(config.dsn)
self.kill_switch = KillSwitchService(
slot_repo=self.slot_repo,
slot_service=self.slot_service,
killswitch_store=self.killswitch_store,
)
self.portfolio_read = _build_portfolio_read(config)
self.snapshot_scheduler = SnapshotScheduler(
read_port=self.portfolio_read,
@@ -231,18 +240,22 @@ class Daemon:
pause_store=self.pause_store,
default_total_slots=self.config.total_slots,
watchdog_store=self.watchdog,
killswitch_store=self.killswitch_store,
)
transport.consume_rpc_forever(topology.SNAPSHOT_REQ_QUEUE, handler, self.stop)
def _pause_rpc_loop(self) -> None:
def _control_rpc_loop(self) -> None:
# REQ-043: operator control channel (pause/resume/list + kill_switch/resume_kill_switch).
transport = _build_transport(self.config)
self._transports.append(transport)
try:
transport.declare_topology()
except Exception: # noqa: BLE001
pass
handler = make_pause_rpc_handler(self.pause_store)
transport.consume_rpc_forever(topology.PAUSE_REQ_QUEUE, handler, self.stop)
handler = make_control_rpc_handler(
self.pause_store, kill_switch_service=self.kill_switch,
)
transport.consume_rpc_forever(topology.CONTROL_REQ_QUEUE, handler, self.stop)
def _safety_sell_loop(self) -> None:
# REQ-037/038/039: forced_close / stop-loss·take-profit / market-close.
@@ -278,7 +291,7 @@ class Daemon:
("trex-ledger", self._ledger_loop),
("trex-intent-rpc", self._intent_rpc_loop),
("trex-snapshot-rpc", self._snapshot_rpc_loop),
("trex-pause-rpc", self._pause_rpc_loop),
("trex-control-rpc", self._control_rpc_loop),
("trex-snapshot-scheduler", self._snapshot_scheduler_loop),
("trex-safety-sell", self._safety_sell_loop),
]