Files
trex-core/trex_core/application/ledger_skip.py
T

99 lines
2.6 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from uuid import UUID
from trex_core.application.alarm_emitter import AlarmEmitter
@dataclass(frozen=True)
class LedgerSkipRecord:
skip_key: str
instance_key: str
slot_id: UUID
portfolio_id: str | None
symbol: str | None
order_id: str | None
execution_id: str | None
side: str
qty: float
price: float
executed_at: datetime
order_venue: str
execution_venue: str
skip_reason: str
status: str
resolution_reason: str | None
resolved_at: datetime | None
created_at: datetime
updated_at: datetime
def make_ledger_skip_key(
*,
instance_key: str,
slot_id: UUID,
side: str,
order_id: str | None,
execution_id: str | None,
skip_reason: str,
) -> str:
return ":".join(
[
"ledger-skip",
instance_key,
str(slot_id),
side,
order_id or "-",
execution_id or "-",
skip_reason,
]
)
def ledger_skip_alarm_fields(record: LedgerSkipRecord) -> dict[str, object]:
return {
"skip_key": record.skip_key,
"instance_key": record.instance_key,
"slot_id": str(record.slot_id),
"portfolio_id": record.portfolio_id,
"symbol": record.symbol,
"order_id": record.order_id,
"execution_id": record.execution_id,
"side": record.side,
"qty": record.qty,
"price": record.price,
"executed_at": record.executed_at.isoformat(),
"order_venue": record.order_venue,
"execution_venue": record.execution_venue,
"skip_reason": record.skip_reason,
"status": record.status,
"resolution_reason": record.resolution_reason,
"resolved_at": record.resolved_at.isoformat() if record.resolved_at else None,
"created_at": record.created_at.isoformat(),
"updated_at": record.updated_at.isoformat(),
}
def publish_ledger_skip_alarm(
alarm_emitter: AlarmEmitter | None,
record: LedgerSkipRecord,
*,
resolved: bool,
) -> None:
if alarm_emitter is None:
return
code = "ledger_skip_resolved" if resolved else "ledger_skip_unresolved"
title = (
f"원장 skip 해소: slot={record.slot_id} reason={record.resolution_reason or 'resolved'}"
if resolved
else f"원장 skip 발생: slot={record.slot_id} reason={record.skip_reason}"
)
alarm_emitter.publish_alarm(
code,
title,
fields=ledger_skip_alarm_fields(record),
severity="info" if resolved else "critical",
)