fix: close broker uncertainty audit gaps
This commit is contained in:
@@ -579,6 +579,7 @@ class ExecutionService:
|
||||
slot.slot_id,
|
||||
str(slot.broker_order_id),
|
||||
CancelState.UNKNOWN.value,
|
||||
only_if_unset=True,
|
||||
):
|
||||
_log.error(
|
||||
"cancel attempt reservation conflict: slot=%s broker_order_id=%s",
|
||||
@@ -719,6 +720,8 @@ class ExecutionService:
|
||||
if not slot.broker_order_id:
|
||||
# Claimed but not yet dispatched — execution loop will pick up or rollback.
|
||||
return "not_dispatched"
|
||||
if slot.cancel_state is not None:
|
||||
return "cancel_already_recorded"
|
||||
side = "buy" if slot.status is SlotState.PENDING_BUY else "sell"
|
||||
qty = int(slot.qty or 0)
|
||||
try:
|
||||
@@ -735,8 +738,9 @@ class ExecutionService:
|
||||
slot.slot_id,
|
||||
str(slot.broker_order_id),
|
||||
CancelState.UNKNOWN.value,
|
||||
only_if_unset=True,
|
||||
):
|
||||
return "cancel_state_conflict"
|
||||
return "cancel_already_recorded"
|
||||
outcome = self._broker.cancel_order(slot.broker_order_id, **cancel_kwargs)
|
||||
except Exception: # noqa: BLE001 - adapter failure becomes durable unknown
|
||||
outcome = CancelOutcome(CancelState.UNKNOWN, "adapter_exception")
|
||||
|
||||
@@ -30,6 +30,7 @@ from uuid import UUID
|
||||
|
||||
from trex_core.application.ports import IntentStore
|
||||
from trex_core.domain.slot import (
|
||||
SlotAdmissionRejectedError,
|
||||
SlotRepository,
|
||||
SlotService,
|
||||
SlotState,
|
||||
@@ -275,20 +276,24 @@ class IntentService:
|
||||
symbol = _resolve(slot)
|
||||
if not symbol:
|
||||
return IntentDecision(intent.intent_id, "rejected", None, "unknown_portfolio")
|
||||
if self._buy_guard_fn is not None:
|
||||
def _admission_precondition(_locked_slot) -> Optional[str]:
|
||||
# Runs inside SlotService's instance-scoped advisory lock. This
|
||||
# makes capacity and cached-cash reservation checks atomic with
|
||||
# the PENDING_BUY transition across same-instance daemons.
|
||||
if (
|
||||
not is_increase
|
||||
and self._max_active_slots is not None
|
||||
and self._repo.count_active(self._instance_key)
|
||||
>= self._max_active_slots
|
||||
):
|
||||
return "capacity_full"
|
||||
if self._buy_guard_fn is None:
|
||||
return None
|
||||
try:
|
||||
guard_reason = self._buy_guard_fn(intent)
|
||||
return self._buy_guard_fn(intent)
|
||||
except Exception: # noqa: BLE001 - buy guard fails closed
|
||||
guard_reason = "account_snapshot_unavailable"
|
||||
if guard_reason is not None:
|
||||
return IntentDecision(
|
||||
intent.intent_id, "rejected", None, guard_reason
|
||||
)
|
||||
# M cap applies only to NEW activations (initial buy). An increase on
|
||||
# an already-active asset does not change the active asset count.
|
||||
if (not is_increase and self._max_active_slots is not None
|
||||
and self._repo.count_active(self._instance_key) >= self._max_active_slots):
|
||||
return IntentDecision(intent.intent_id, "rejected", None, "capacity_full")
|
||||
return "account_snapshot_unavailable"
|
||||
|
||||
# dcy-022 / REQ-068: resolve effective timeout (per-intent > config default).
|
||||
effective_timeout = (
|
||||
intent.timeout_sec
|
||||
@@ -317,6 +322,11 @@ class IntentService:
|
||||
slippage_pct=intent.slippage_pct,
|
||||
venue=intent.venue,
|
||||
extra={"intent_id": intent.intent_id, "symbol": symbol},
|
||||
precondition=_admission_precondition,
|
||||
)
|
||||
except SlotAdmissionRejectedError as exc:
|
||||
return IntentDecision(
|
||||
intent.intent_id, "rejected", None, exc.reason
|
||||
)
|
||||
except SlotStateMismatchError:
|
||||
continue # slot changed under us; re-fetch and re-evaluate
|
||||
|
||||
Reference in New Issue
Block a user