Fence unmanaged lifecycle by snapshot time
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
84d81658a1
commit
8b1de8e26a
@@ -625,6 +625,18 @@ class InMemoryUnmanagedPositionStore:
|
||||
self._states: dict[tuple[str, str], dict] = {}
|
||||
self._audit: list[dict] = []
|
||||
|
||||
@staticmethod
|
||||
def _latest_snapshot_at(state: dict) -> datetime:
|
||||
resolved_at = state.get("resolved_at")
|
||||
last_seen_at = state.get("last_seen_at")
|
||||
if isinstance(resolved_at, datetime) and (
|
||||
not isinstance(last_seen_at, datetime) or resolved_at >= last_seen_at
|
||||
):
|
||||
return resolved_at
|
||||
if isinstance(last_seen_at, datetime):
|
||||
return last_seen_at
|
||||
return state.get("updated_at") or state.get("created_at") or datetime.min.replace(tzinfo=UTC)
|
||||
|
||||
def observe(
|
||||
self,
|
||||
*,
|
||||
@@ -642,7 +654,21 @@ class InMemoryUnmanagedPositionStore:
|
||||
preserved = {} if existing is None else {
|
||||
"created_at": existing.get("created_at", now),
|
||||
}
|
||||
if existing is not None:
|
||||
latest_snapshot_at = self._latest_snapshot_at(existing)
|
||||
if now < latest_snapshot_at:
|
||||
return {
|
||||
"transition": "stale",
|
||||
"previous_quantity": previous_quantity,
|
||||
"state": dict(existing),
|
||||
}
|
||||
if existing is None or existing.get("lifecycle_state") == "resolved":
|
||||
if existing is not None and now == self._latest_snapshot_at(existing):
|
||||
return {
|
||||
"transition": "stale",
|
||||
"previous_quantity": previous_quantity,
|
||||
"state": dict(existing),
|
||||
}
|
||||
transition = "detected"
|
||||
state = {
|
||||
"instance_key": instance_key,
|
||||
@@ -666,6 +692,12 @@ class InMemoryUnmanagedPositionStore:
|
||||
}
|
||||
self._states[key] = state
|
||||
elif int(existing["quantity"]) != int(quantity):
|
||||
if now <= existing["last_seen_at"]:
|
||||
return {
|
||||
"transition": "stale",
|
||||
"previous_quantity": previous_quantity,
|
||||
"state": dict(existing),
|
||||
}
|
||||
transition = "quantity_changed"
|
||||
existing["quantity"] = quantity
|
||||
existing["last_seen_at"] = now
|
||||
@@ -676,8 +708,9 @@ class InMemoryUnmanagedPositionStore:
|
||||
state = existing
|
||||
else:
|
||||
transition = "unchanged"
|
||||
existing["last_seen_at"] = now
|
||||
existing["updated_at"] = now
|
||||
if now > existing["last_seen_at"]:
|
||||
existing["last_seen_at"] = now
|
||||
existing["updated_at"] = now
|
||||
state = existing
|
||||
if transition != "unchanged":
|
||||
self._audit.append(
|
||||
@@ -722,6 +755,8 @@ class InMemoryUnmanagedPositionStore:
|
||||
continue
|
||||
if state["symbol"] in observed_symbols:
|
||||
continue
|
||||
if now <= state["last_seen_at"]:
|
||||
continue
|
||||
state["lifecycle_state"] = "resolved"
|
||||
state["resolved_at"] = now
|
||||
state["updated_at"] = now
|
||||
|
||||
@@ -29,6 +29,7 @@ import hashlib
|
||||
import logging
|
||||
import math
|
||||
import re
|
||||
from datetime import UTC, datetime
|
||||
from typing import cast
|
||||
|
||||
from trex_core.application.alarm_emitter import AlarmEmitter
|
||||
@@ -407,6 +408,7 @@ def reconcile_core_holdings(
|
||||
Returns {key: 'error'|'qty_warning'|'ok'|'unmanaged_position'|
|
||||
'managed_non_holding_divergence'} for telemetry.
|
||||
"""
|
||||
snapshot_observed_at = datetime.now(tz=UTC)
|
||||
broker_qty: dict[str, int] = {
|
||||
item.symbol: int(item.hldg_qty) for item in broker.get_balance()
|
||||
}
|
||||
@@ -507,6 +509,7 @@ def reconcile_core_holdings(
|
||||
instance_key=instance_key,
|
||||
symbol=sym,
|
||||
quantity=bq,
|
||||
observed_at=snapshot_observed_at,
|
||||
)
|
||||
except Exception: # noqa: BLE001
|
||||
_log.exception(
|
||||
@@ -555,6 +558,7 @@ def reconcile_core_holdings(
|
||||
for state in unmanaged_position_store.resolve_absent(
|
||||
instance_key=instance_key,
|
||||
observed_symbols=active_unmanaged_symbols,
|
||||
observed_at=snapshot_observed_at,
|
||||
):
|
||||
_log.info(
|
||||
"unmanaged position resolved: symbol=%s qty=%d instance_key=%s",
|
||||
|
||||
Reference in New Issue
Block a user