fix: persist uncertain cancel outcomes
This commit is contained in:
@@ -24,6 +24,8 @@ import httpx
|
||||
from trex_core.application.broker_port import (
|
||||
BalanceItem,
|
||||
BrokerBalanceError,
|
||||
CancelOutcome,
|
||||
CancelState,
|
||||
FillStatus,
|
||||
OrderAck,
|
||||
OrderRequest,
|
||||
@@ -756,7 +758,7 @@ class KISBroker:
|
||||
|
||||
def cancel_order(
|
||||
self, broker_order_id: str, *, side: str, symbol: str, qty: int, venue: str = "KRX"
|
||||
) -> bool:
|
||||
) -> CancelOutcome:
|
||||
"""Cancel a previously submitted order (REQ-118).
|
||||
|
||||
venue: exchange venue of the original order. Must match the EXCG_ID_DVSN_CD
|
||||
@@ -764,7 +766,10 @@ class KISBroker:
|
||||
Real-account NXT cancel (EXCG=NXT) confirmed rt_cd=0 on 2026-07-06.
|
||||
"""
|
||||
if broker_order_id.startswith(_SUBMISSION_UNKNOWN_PREFIX):
|
||||
return False
|
||||
return CancelOutcome(
|
||||
CancelState.UNKNOWN,
|
||||
"submission_outcome_unknown",
|
||||
)
|
||||
cano, product = self._account()
|
||||
parts = broker_order_id.split(":")
|
||||
orgno, odno = (parts[0], parts[1]) if len(parts) == 2 else ("", parts[-1])
|
||||
@@ -789,6 +794,22 @@ class KISBroker:
|
||||
json=body,
|
||||
timeout=self._timeout,
|
||||
)
|
||||
return str(resp.json().get("rt_cd")) == "0"
|
||||
except (httpx.HTTPError, ValueError):
|
||||
return False
|
||||
except httpx.HTTPError:
|
||||
return CancelOutcome(CancelState.UNKNOWN, "transport_failed")
|
||||
if getattr(resp, "status_code", 200) >= 500:
|
||||
return CancelOutcome(CancelState.UNKNOWN, "broker_unavailable")
|
||||
try:
|
||||
payload = resp.json()
|
||||
except ValueError:
|
||||
return CancelOutcome(CancelState.UNKNOWN, "malformed_response")
|
||||
rt_cd = payload.get("rt_cd")
|
||||
if str(rt_cd) == "0":
|
||||
return CancelOutcome(CancelState.REQUESTED)
|
||||
if rt_cd is None:
|
||||
if getattr(resp, "status_code", 200) >= 400:
|
||||
return CancelOutcome(CancelState.REJECTED, "http_rejected")
|
||||
return CancelOutcome(CancelState.UNKNOWN, "missing_result_code")
|
||||
return CancelOutcome(
|
||||
CancelState.REJECTED,
|
||||
str(payload.get("msg_cd") or "broker_rejected"),
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ from threading import Lock
|
||||
from trex_core.application.broker_port import (
|
||||
BalanceItem,
|
||||
BrokerBalanceError,
|
||||
CancelOutcome,
|
||||
FillStatus,
|
||||
OrderAck,
|
||||
OrderRequest,
|
||||
@@ -146,7 +147,7 @@ class OwnershipAwareBroker:
|
||||
symbol: str,
|
||||
qty: int,
|
||||
venue: str = "KRX",
|
||||
) -> bool:
|
||||
) -> CancelOutcome:
|
||||
return self._delegate.cancel_order(
|
||||
broker_order_id,
|
||||
side=side,
|
||||
|
||||
@@ -14,6 +14,8 @@ from typing import Optional
|
||||
from trex_core.adapters.broker.seq_store import SeqStore
|
||||
from trex_core.application.broker_port import (
|
||||
BalanceItem,
|
||||
CancelOutcome,
|
||||
CancelState,
|
||||
FillStatus,
|
||||
OrderAck,
|
||||
OrderRequest,
|
||||
@@ -105,16 +107,24 @@ class PaperBroker:
|
||||
)
|
||||
return FillStatus(state="partially_filled", filled_qty=o.filled, avg_price=o.price)
|
||||
|
||||
def cancel_order(self, broker_order_id: str, *, side: str, symbol: str, qty: int, venue: str = "KRX") -> bool:
|
||||
def cancel_order(
|
||||
self,
|
||||
broker_order_id: str,
|
||||
*,
|
||||
side: str,
|
||||
symbol: str,
|
||||
qty: int,
|
||||
venue: str = "KRX",
|
||||
) -> CancelOutcome:
|
||||
"""Cancel a paper order. venue is accepted for BrokerPort protocol conformance
|
||||
(REQ-118) but ignored — paper has no exchange concept. Existing behavior preserved.
|
||||
"""
|
||||
with self._lock:
|
||||
o = self._orders.get(broker_order_id)
|
||||
if o is None or o.filled >= o.qty:
|
||||
return False
|
||||
return CancelOutcome(CancelState.REJECTED, "order_not_cancellable")
|
||||
o.cancelled = True
|
||||
return True
|
||||
return CancelOutcome(CancelState.REQUESTED)
|
||||
|
||||
def get_balance(self) -> list[BalanceItem]:
|
||||
"""Deterministic per-symbol fill aggregate (REQ-031 AC-3).
|
||||
|
||||
Reference in New Issue
Block a user