fix: persist uncertain cancel outcomes
This commit is contained in:
@@ -37,7 +37,8 @@ _SLOT_COLUMNS = (
|
||||
"order_id, strategy_id, broker_order_id, portfolio_id, "
|
||||
"sell_strategy_id, forced_close_at, stop_loss_price, take_profit_price, "
|
||||
"created_at, updated_at, instance_key, retired_at, "
|
||||
"timeout_sec, slippage_pct, occupancy_seq, venue"
|
||||
"timeout_sec, slippage_pct, occupancy_seq, venue, "
|
||||
"cancel_state, cancel_requested_at"
|
||||
)
|
||||
|
||||
|
||||
@@ -216,7 +217,8 @@ class PostgreSQLSlotRepository:
|
||||
"broker_order_id=%s, portfolio_id=%s, sell_strategy_id=%s, "
|
||||
"forced_close_at=%s, stop_loss_price=%s, take_profit_price=%s, "
|
||||
"instance_key=%s, retired_at=%s, timeout_sec=%s, slippage_pct=%s, "
|
||||
"occupancy_seq=%s, venue=%s, updated_at=%s WHERE slot_id=%s",
|
||||
"occupancy_seq=%s, venue=%s, cancel_state=%s, "
|
||||
"cancel_requested_at=%s, updated_at=%s WHERE slot_id=%s",
|
||||
(
|
||||
slot.status.value,
|
||||
slot.symbol,
|
||||
@@ -237,6 +239,8 @@ class PostgreSQLSlotRepository:
|
||||
slot.slippage_pct,
|
||||
slot.occupancy_seq,
|
||||
slot.venue,
|
||||
slot.cancel_state,
|
||||
slot.cancel_requested_at,
|
||||
slot.updated_at or datetime.now(tz=timezone.utc),
|
||||
slot.slot_id,
|
||||
),
|
||||
@@ -332,6 +336,26 @@ class PostgreSQLSlotRepository:
|
||||
conn.commit()
|
||||
return ok
|
||||
|
||||
def mark_cancel_outcome(
|
||||
self,
|
||||
slot_id: UUID,
|
||||
broker_order_id: str,
|
||||
state: str,
|
||||
) -> bool:
|
||||
now = datetime.now(tz=timezone.utc)
|
||||
with self._psycopg.connect(self._dsn, autocommit=False) as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"UPDATE daily_slot SET cancel_state=%s, "
|
||||
"cancel_requested_at=%s, updated_at=%s "
|
||||
"WHERE slot_id=%s AND broker_order_id=%s "
|
||||
"AND status IN ('pending_buy','pending_sell')",
|
||||
(state, now, now, slot_id, broker_order_id),
|
||||
)
|
||||
ok = cur.rowcount == 1
|
||||
conn.commit()
|
||||
return ok
|
||||
|
||||
def list_dispatched_pending(self, instance_key: str = "default") -> list[Slot]:
|
||||
with self._psycopg.connect(self._dsn) as conn:
|
||||
with conn.cursor() as cur:
|
||||
@@ -347,7 +371,8 @@ class PostgreSQLSlotRepository:
|
||||
with self._psycopg.connect(self._dsn, autocommit=False) as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"UPDATE daily_slot SET broker_order_id=NULL, updated_at=%s WHERE slot_id=%s",
|
||||
"UPDATE daily_slot SET broker_order_id=NULL, cancel_state=NULL, "
|
||||
"cancel_requested_at=NULL, updated_at=%s WHERE slot_id=%s",
|
||||
(datetime.now(tz=timezone.utc), slot_id),
|
||||
)
|
||||
conn.commit()
|
||||
@@ -374,7 +399,8 @@ class PostgreSQLSlotRepository:
|
||||
# forced_close_at(11) stop_loss_price(12) take_profit_price(13)
|
||||
# created_at(14) updated_at(15) instance_key(16) retired_at(17)
|
||||
# timeout_sec(18) slippage_pct(19) [dcy-022/REQ-068]
|
||||
# occupancy_seq(20) [dcy-028/REQ-081], venue(21)
|
||||
# occupancy_seq(20) [dcy-028/REQ-081], venue(21),
|
||||
# cancel_state(22), cancel_requested_at(23)
|
||||
return Slot(
|
||||
slot_id=row[0] if isinstance(row[0], UUID) else UUID(str(row[0])),
|
||||
status=SlotState(row[1]),
|
||||
@@ -398,6 +424,8 @@ class PostgreSQLSlotRepository:
|
||||
slippage_pct=f(row[19]),
|
||||
occupancy_seq=int(row[20]) if len(row) > 20 and row[20] is not None else 0,
|
||||
venue=str(row[21]) if len(row) > 21 and row[21] is not None else "KRX",
|
||||
cancel_state=str(row[22]) if len(row) > 22 and row[22] is not None else None,
|
||||
cancel_requested_at=row[23] if len(row) > 23 else None,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
-- dcy-041 / GitHub #44: cancellation request acceptance is distinct from
|
||||
-- final order cancellation. Persist the immediate result to prevent blind
|
||||
-- automatic retries across execution ticks and daemon restarts.
|
||||
ALTER TABLE daily_slot
|
||||
ADD COLUMN IF NOT EXISTS cancel_state TEXT;
|
||||
|
||||
ALTER TABLE daily_slot
|
||||
ADD COLUMN IF NOT EXISTS cancel_requested_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE daily_slot
|
||||
DROP CONSTRAINT IF EXISTS daily_slot_cancel_state_check;
|
||||
|
||||
ALTER TABLE daily_slot
|
||||
ADD CONSTRAINT daily_slot_cancel_state_check
|
||||
CHECK (
|
||||
cancel_state IS NULL
|
||||
OR cancel_state IN ('requested', 'rejected', 'unknown')
|
||||
);
|
||||
Reference in New Issue
Block a user