diff --git a/tests/application/test_unmanaged_position_lifecycle.py b/tests/application/test_unmanaged_position_lifecycle.py index 3344117..eec439e 100644 --- a/tests/application/test_unmanaged_position_lifecycle.py +++ b/tests/application/test_unmanaged_position_lifecycle.py @@ -32,7 +32,7 @@ class FakeBroker: class RaisingBroker(FakeBroker): def get_balance(self): - raise BrokerBalanceError("broker_unavailable") + raise BrokerBalanceError("transport_failed") def _wire(): diff --git a/tests/conftest.py b/tests/conftest.py index cfa8acd..a3a4413 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -96,7 +96,6 @@ def pg_clean(pg_dsn: str): from trex_core.adapters.persistence.migrate import apply_migrations apply_migrations(pg_dsn) - with psycopg.connect(pg_dsn, autocommit=True) as conn: - with conn.cursor() as cur: - cur.execute(f"TRUNCATE {', '.join(_PG_TABLES)}") + with psycopg.connect(pg_dsn, autocommit=True) as conn, conn.cursor() as cur: + cur.execute(f"TRUNCATE {', '.join(_PG_TABLES)}") return pg_dsn diff --git a/tests/runtime/test_cli_operator.py b/tests/runtime/test_cli_operator.py index d87683f..b4296ae 100644 --- a/tests/runtime/test_cli_operator.py +++ b/tests/runtime/test_cli_operator.py @@ -383,10 +383,13 @@ def test_sync_requires_yes_when_not_dry_run(capsys, monkeypatch): """sync without --yes (and not dry-run) should abort.""" def _fake_sync(args, cfg, confirm): # Simulate the confirmation prompt returning False (no --yes, interactive aborts) - if not getattr(args, "dry_run", False) and not getattr(args, "yes", False): - if not confirm("Sync?"): - print("aborted") - return 1 + if ( + not getattr(args, "dry_run", False) + and not getattr(args, "yes", False) + and not confirm("Sync?") + ): + print("aborted") + return 1 return 0 monkeypatch.setattr(cli, "_cmd_sync", _fake_sync) rc = cli.main(["sync", "--portfolio-id", "PF-1"], confirm=lambda _: False) @@ -398,7 +401,7 @@ def test_sync_live_requires_live_flag(capsys, monkeypatch): monkeypatch.setattr(cli, "_broker_mode", lambda cfg: ("kis", True)) def _fake_sync(args, cfg, confirm): - broker, is_live = cli._broker_mode(cfg) + _broker, is_live = cli._broker_mode(cfg) if is_live and not getattr(args, "live", False): cli._fail("live account requires --live (with --yes) to confirm a real sync", args.json) return 2 @@ -415,7 +418,7 @@ def test_sync_fail_closed_skipped_error_exits_nonzero_without_writes( """REQ-123: a fail-closed skipped live sync is a CLI failure.""" class _Provider: def get_broker_for_read(self): - return SimpleNamespace(get_balance=lambda: []), None + return SimpleNamespace(get_balance=list), None class _WarningStore: def clear_warned(self, *_args): @@ -452,7 +455,7 @@ def test_sync_fail_closed_skipped_error_exits_nonzero_without_writes( } psycopg = types.ModuleType("psycopg") - setattr(psycopg, "connect", lambda *_args, **_kwargs: _Connection()) + psycopg.connect = lambda *_args, **_kwargs: _Connection() monkeypatch.setitem(sys.modules, "psycopg", psycopg) monkeypatch.setattr("trex_core.runtime.broker_provider.build_broker_provider", lambda _cfg: _Provider()) monkeypatch.setattr("trex_core.adapters.portfolio.reconcile_warning_store.PostgreSQLReconcileWarningStore", lambda *_args: _WarningStore()) @@ -707,10 +710,10 @@ def test_verify_cmd_broker_import_does_not_raise(monkeypatch): # Patch at module level — _cmd_verify uses local `from ... import` so we # must patch the source module, not the cli namespace. + import trex_core.adapters.persistence.postgresql as pg_mod + import trex_core.adapters.portfolio.reconcile_warning_store as rws_mod from trex_core.application.portfolio import InMemoryReconcileWarningStore from trex_core.domain.slot.in_memory_repository import InMemorySlotRepository - import trex_core.adapters.portfolio.reconcile_warning_store as rws_mod - import trex_core.adapters.persistence.postgresql as pg_mod monkeypatch.setattr(rws_mod, "PostgreSQLReconcileWarningStore", lambda dsn: InMemoryReconcileWarningStore()) diff --git a/trex_core/application/snapshot_scheduler.py b/trex_core/application/snapshot_scheduler.py index d675656..611a477 100644 --- a/trex_core/application/snapshot_scheduler.py +++ b/trex_core/application/snapshot_scheduler.py @@ -12,8 +12,8 @@ from __future__ import annotations import logging import os import threading -from datetime import date, datetime, time, timedelta, timezone -from typing import Callable, Optional +from collections.abc import Callable +from datetime import UTC, date, datetime, time, timedelta, timezone from trex_core.application.alarm_emitter import AlarmEmitter from trex_core.application.broker_port import BrokerPort @@ -23,10 +23,10 @@ from trex_core.application.portfolio import ( PortfolioHubReadPort, PortfolioHubUnavailableError, ReconcileNotificationStore, - UnmanagedPositionStore, ReconcileWarningStore, SnapshotStore, TradingContext, + UnmanagedPositionStore, ) from trex_core.application.reconciliation import reconcile_positions @@ -45,32 +45,32 @@ class SnapshotScheduler: store: SnapshotStore, broker: BrokerPort, schedule_kst: time = time(hour=7, minute=30), - clock: Optional[Callable[[], datetime]] = None, + clock: Callable[[], datetime] | None = None, slot_repo=None, slot_service=None, instance_key: str = "default", - alarm_emitter: Optional[AlarmEmitter] = None, - warn_store: Optional[ReconcileWarningStore] = None, - notification_store: Optional[ReconcileNotificationStore] = None, - unmanaged_position_store: Optional[UnmanagedPositionStore] = None, + alarm_emitter: AlarmEmitter | None = None, + warn_store: ReconcileWarningStore | None = None, + notification_store: ReconcileNotificationStore | None = None, + unmanaged_position_store: UnmanagedPositionStore | None = None, credential_fetch_enabled: bool = False, - kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = None, - credential_refresh: Optional[Callable[[], object]] = None, + kis_reinit_callback: Callable[[BrokerCredentialPayload], None] | None = None, + credential_refresh: Callable[[], object] | None = None, ) -> None: self._read = read_port self._store = store self._broker = broker self._schedule_kst = schedule_kst - self._clock = clock or (lambda: datetime.now(tz=timezone.utc)) - self._daemon_id: Optional[str] = None - self._last_fired_date: Optional[datetime] = None + self._clock = clock or (lambda: datetime.now(tz=UTC)) + self._daemon_id: str | None = None + self._last_fired_date: datetime | None = None # dcy-020 / REQ-058 (BAT-030): asset-centric slot reconcile after each # universe fetch — always on when slot_repo is wired (the reconcile is # what creates/retires the permanent asset slots). self._slot_repo = slot_repo self._slot_service = slot_service self._instance_key = instance_key - self._last_universe_size: Optional[int] = None + self._last_universe_size: int | None = None # dcy-033 / REQ-100: best-effort alarm emitter. self._alarm_emitter = alarm_emitter # BAT-050 / REQ-103: durable reconcile-warning store (Optional). @@ -84,7 +84,9 @@ class SnapshotScheduler: # dcy-036 / REQ-113 AC-2 / REQ-115 AC-1: optional callback to reinitialize # KISBroker with the fetched day-start credential. None = no reinit (paper # broker or credential_fetch_enabled=False). Set by daemon.py assembly. - self._kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = kis_reinit_callback + self._kis_reinit_callback: Callable[[BrokerCredentialPayload], None] | None = ( + kis_reinit_callback + ) # REQ-119: provider-owned refresh supersedes the legacy raw fetch hook. self._credential_refresh = credential_refresh # REQ-113: detect break-glass at init time (env var set by operator). @@ -93,8 +95,8 @@ class SnapshotScheduler: in ("1", "true", "yes") ) # REQ-115: day-scoped credential cache. None until first successful fetch. - self._cached_credential: Optional[BrokerCredentialPayload] = None - self._credential_date: Optional[date] = None + self._cached_credential: BrokerCredentialPayload | None = None + self._credential_date: date | None = None # REQ-114: entry gate. True = new entries allowed (default). # credential_fetch_enabled=False keeps this always True (no gate). self._entry_allowed: bool = True @@ -266,7 +268,7 @@ class SnapshotScheduler: try: self._credential_refresh() self._entry_allowed = True - except Exception as exc: # provider exposes only sanitized failure + except Exception as exc: # noqa: BLE001 - provider exposes only sanitized failure _log.error("DAY_START_LOAD_FAILED: broker credential refresh failed: %s (instance=%s)", exc.__class__.__name__, self._instance_key) self._entry_allowed = False diff --git a/trex_core/runtime/cli.py b/trex_core/runtime/cli.py index e4ffe68..7c4b950 100644 --- a/trex_core/runtime/cli.py +++ b/trex_core/runtime/cli.py @@ -737,12 +737,12 @@ def _build_parser() -> argparse.ArgumentParser: def _cmd_sync(args, cfg, confirm) -> int: """REQ-105 (dcy-034, BAT-051): sync slot qty to broker ground truth.""" - from trex_core.adapters.portfolio.reconcile_warning_store import ( - PostgreSQLReconcileWarningStore, - ) from trex_core.adapters.portfolio.reconcile_notification_store import ( PostgreSQLReconcileNotificationStore, ) + from trex_core.adapters.portfolio.reconcile_warning_store import ( + PostgreSQLReconcileWarningStore, + ) from trex_core.application.reconciliation import sync_slot_to_broker if not args.portfolio_id and not args.all: @@ -891,12 +891,12 @@ def _cmd_sync(args, cfg, confirm) -> int: def _cmd_verify(args, cfg) -> int: """REQ-106 (dcy-034, BAT-051): re-check C-case reconcile-warning (read-centric).""" from trex_core.adapters.persistence.postgresql import PostgreSQLSlotRepository - from trex_core.adapters.portfolio.reconcile_warning_store import ( - PostgreSQLReconcileWarningStore, - ) from trex_core.adapters.portfolio.reconcile_notification_store import ( PostgreSQLReconcileNotificationStore, ) + from trex_core.adapters.portfolio.reconcile_warning_store import ( + PostgreSQLReconcileWarningStore, + ) from trex_core.application.portfolio import SnapshotStore from trex_core.application.reconciliation import verify_slot_reconcile diff --git a/trex_core/runtime/daemon.py b/trex_core/runtime/daemon.py index b0b0f2e..e5f117e 100644 --- a/trex_core/runtime/daemon.py +++ b/trex_core/runtime/daemon.py @@ -31,11 +31,11 @@ from trex_core.adapters.messaging import ( RabbitMQTransport, topology, ) -from trex_core.adapters.persistence.migrate import apply_migrations from trex_core.adapters.persistence.account_snapshot import ( PostgreSQLAccountSnapshotStore, ) from trex_core.adapters.persistence.ledger_skip_store import PostgreSQLLedgerSkipStore +from trex_core.adapters.persistence.migrate import apply_migrations from trex_core.adapters.persistence.postgresql import ( PgStrategyStateStore, PostgreSQLIntentStore, @@ -55,16 +55,15 @@ from trex_core.adapters.portfolio.reconcile_notification_store import ( from trex_core.adapters.portfolio.unmanaged_position_store import ( PostgreSQLUnmanagedPositionStore, ) -from trex_core.application.alarm_emitter import AlarmEmitter from trex_core.application.account_snapshot import AccountSnapshotService +from trex_core.application.alarm_emitter import AlarmEmitter 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.intents import BuyIntent +from trex_core.application.intents import BuyIntent, IntentService +from trex_core.application.killswitch import KillSwitchService 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,