fix: preserve SOR execution venue separately
This commit is contained in:
@@ -573,6 +573,27 @@ class KISBroker:
|
||||
delta = (ordered_at - submitted_at).total_seconds()
|
||||
return -5.0 <= delta <= 120.0
|
||||
|
||||
@staticmethod
|
||||
def _execution_venue_from_row(row: dict) -> str | None:
|
||||
"""Parse broker-authoritative KRX/NXT without time inference."""
|
||||
notice_code = str(row.get("ord_exg_gb") or row.get("ORD_EXG_GB") or "").strip()
|
||||
notice_map = {"1": "KRX", "2": "NXT", "3": "KRX", "4": "NXT"}
|
||||
if notice_code in notice_map:
|
||||
return notice_map[notice_code]
|
||||
for key in (
|
||||
"excg_id_dvsn_cd",
|
||||
"excg_id_dvsn_Cd",
|
||||
"EXCG_ID_DVSN_CD",
|
||||
"execution_venue",
|
||||
):
|
||||
value = str(row.get(key) or "").strip().upper()
|
||||
if value in {"KRX", "SOR-KRX"}:
|
||||
return "KRX"
|
||||
if value in {"NXT", "SOR-NXT"}:
|
||||
return "NXT"
|
||||
# SOR is routing provenance, not an execution exchange.
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _fill_status_from_row(
|
||||
row: dict,
|
||||
@@ -581,6 +602,7 @@ class KISBroker:
|
||||
today: str,
|
||||
fallback_odno: str,
|
||||
) -> FillStatus:
|
||||
execution_venue = KISBroker._execution_venue_from_row(row)
|
||||
ord_qty = _safe_int(row.get("ord_qty")) or 0
|
||||
ccld_qty = _safe_int(row.get("tot_ccld_qty")) or 0
|
||||
rejected_qty = _safe_int(row.get("rjct_qty")) or 0
|
||||
@@ -623,6 +645,7 @@ class KISBroker:
|
||||
execution_id=execution_id,
|
||||
fee=fee,
|
||||
tax=tax,
|
||||
execution_venue=execution_venue,
|
||||
raw=row,
|
||||
)
|
||||
# 0 < ccld_qty < ord_qty
|
||||
@@ -646,6 +669,7 @@ class KISBroker:
|
||||
execution_id=execution_id,
|
||||
fee=fee,
|
||||
tax=tax,
|
||||
execution_venue=execution_venue,
|
||||
cancelled=True,
|
||||
raw=row,
|
||||
)
|
||||
@@ -653,6 +677,7 @@ class KISBroker:
|
||||
state="partially_filled",
|
||||
filled_qty=ccld_qty,
|
||||
avg_price=float(avg) if avg else None,
|
||||
execution_venue=execution_venue,
|
||||
raw=row,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ class _Order:
|
||||
self.side = req.side
|
||||
self.symbol = req.symbol
|
||||
self.qty = req.qty
|
||||
# Deterministic SOR simulation: paper routes SOR to KRX while retaining
|
||||
# req.venue on the acknowledgement as submission provenance.
|
||||
self.execution_venue = "KRX" if req.venue == "SOR" else req.venue
|
||||
self.price = float(req.limit_price) if req.limit_price is not None else 1000.0
|
||||
self.polls = 0
|
||||
self.filled = 0
|
||||
@@ -118,11 +121,13 @@ class PaperBroker:
|
||||
return FillStatus(
|
||||
state="filled", filled_qty=o.filled, avg_price=o.price,
|
||||
execution_id=execution_id, fee=fee, tax=tax,
|
||||
execution_venue=o.execution_venue,
|
||||
)
|
||||
return FillStatus(
|
||||
state="partially_filled",
|
||||
filled_qty=o.filled,
|
||||
avg_price=o.price,
|
||||
execution_venue=o.execution_venue,
|
||||
raw={"order_open_confirmed": True},
|
||||
)
|
||||
|
||||
|
||||
@@ -548,7 +548,7 @@ class _PgIntentRecord:
|
||||
_OUTBOX_COLUMNS = (
|
||||
"correlation_id, asset_id, order_id, execution_id, side, qty, price, "
|
||||
"fee, tax, executed_at, status, attempts, last_outcome, last_error, "
|
||||
"created_at, updated_at, next_retry_at, venue"
|
||||
"created_at, updated_at, next_retry_at, venue, order_venue"
|
||||
)
|
||||
|
||||
|
||||
@@ -565,8 +565,8 @@ class PostgreSQLLedgerOutbox:
|
||||
cur.execute(
|
||||
"INSERT INTO ledger_outbox "
|
||||
"(correlation_id, asset_id, order_id, execution_id, side, "
|
||||
" qty, price, fee, tax, executed_at, venue) "
|
||||
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) "
|
||||
" qty, price, fee, tax, executed_at, venue, order_venue) "
|
||||
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) "
|
||||
"ON CONFLICT (correlation_id) DO NOTHING",
|
||||
(
|
||||
trade.correlation_id,
|
||||
@@ -580,6 +580,7 @@ class PostgreSQLLedgerOutbox:
|
||||
trade.tax,
|
||||
trade.executed_at,
|
||||
trade.venue,
|
||||
trade.order_venue,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
@@ -684,6 +685,7 @@ class PostgreSQLLedgerOutbox:
|
||||
tax=f(row[8]),
|
||||
executed_at=row[9],
|
||||
venue=row[17],
|
||||
order_venue=row[18],
|
||||
)
|
||||
return LedgerWorkItem(
|
||||
correlation_id=row[0],
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
-- GitHub #61: keep the original submission venue separate from actual execution venue.
|
||||
ALTER TABLE ledger_outbox
|
||||
ADD COLUMN IF NOT EXISTS order_venue TEXT NOT NULL DEFAULT 'KRX';
|
||||
|
||||
ALTER TABLE ledger_outbox
|
||||
DROP CONSTRAINT IF EXISTS ledger_outbox_order_venue_check;
|
||||
ALTER TABLE ledger_outbox
|
||||
ADD CONSTRAINT ledger_outbox_order_venue_check
|
||||
CHECK (order_venue IN ('KRX', 'NXT', 'SOR'));
|
||||
|
||||
-- Historical rows used venue for both concepts. Preserve that provenance.
|
||||
UPDATE ledger_outbox SET order_venue = venue WHERE order_venue = 'KRX' AND venue <> 'KRX';
|
||||
Reference in New Issue
Block a user