fix bug
This commit is contained in:
@@ -9,10 +9,9 @@ from dataclasses import asdict, dataclass, field
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
|
||||
from sdk import OrderItem, PositionItem
|
||||
from sdk import DealItem, PositionItem
|
||||
|
||||
READY, SOLD, DONE = "READY", "SOLD", "DONE"
|
||||
TERMINAL_STATUSES = {"53", "54", "56", "57"}
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
@@ -72,14 +71,14 @@ class TState:
|
||||
raise
|
||||
|
||||
def reconcile(
|
||||
self, positions: list[PositionItem], orders: list[OrderItem], today: str
|
||||
self, positions: list[PositionItem], deals: list[DealItem], today: str
|
||||
) -> None:
|
||||
"""先按实际成交记账,再接管未知持仓;不覆盖已记录的底仓成本。"""
|
||||
# 同一本地委托可能拆单;按券商订单号去重,数量齐全且全部结束才记账。
|
||||
by_id: dict[str, dict[str, OrderItem]] = {}
|
||||
for order in orders:
|
||||
if order.local_order_id:
|
||||
by_id.setdefault(order.local_order_id, {})[order.id] = order
|
||||
# 同一本地委托可能有多笔成交;按成交编号去重后合并数量和金额。
|
||||
by_id: dict[str, dict[str, DealItem]] = {}
|
||||
for deal in deals:
|
||||
if deal.local_order_id and deal.id:
|
||||
by_id.setdefault(deal.local_order_id, {})[deal.id] = deal
|
||||
for order_id, pending in list(self.pending.items()):
|
||||
side = "SELL" if pending.kind == "sell" else "BUY"
|
||||
rows = [
|
||||
@@ -88,25 +87,16 @@ class TState:
|
||||
if row.code == pending.code and row.side == side
|
||||
]
|
||||
if not rows:
|
||||
log.warning("[ZT 状态] 委托暂未查到,保留待确认:%s", order_id)
|
||||
log.warning("[ZT 状态] 成交暂未查到,保留待确认:%s", order_id)
|
||||
continue
|
||||
if sum(row.volume for row in rows) != pending.qty:
|
||||
qty = sum(row.volume for row in rows)
|
||||
# 成交未达到计划数量时继续等待,防止后续成交到达后重复记账。
|
||||
if qty != pending.qty:
|
||||
continue
|
||||
if any(row.status not in TERMINAL_STATUSES for row in rows):
|
||||
continue
|
||||
if any(
|
||||
row.status == "56" and row.traded_volume != row.volume for row in rows
|
||||
):
|
||||
continue
|
||||
qty = sum(row.traded_volume for row in rows)
|
||||
amounts = [
|
||||
(
|
||||
row.trade_amount
|
||||
if row.trade_amount > 0
|
||||
else row.trade_price * row.traded_volume
|
||||
)
|
||||
row.amount if row.amount > 0 else row.price * row.volume
|
||||
for row in rows
|
||||
if row.traded_volume > 0
|
||||
if row.volume > 0
|
||||
]
|
||||
if any(not math.isfinite(amount) or amount <= 0 for amount in amounts):
|
||||
continue
|
||||
@@ -132,7 +122,7 @@ class TState:
|
||||
item.phase = DONE if total >= item.sell_qty else SOLD
|
||||
if item.phase == DONE:
|
||||
item.trade_date = today
|
||||
# 零成交撤单也记录,保留计划、实际数量、均价和柜台终态。
|
||||
# 记录真实成交编号,重启后仍可核对本次状态变更的来源。
|
||||
self.records.append(
|
||||
{
|
||||
**asdict(pending),
|
||||
@@ -140,7 +130,7 @@ class TState:
|
||||
"filled_qty": qty,
|
||||
"filled_cost": cost,
|
||||
"amount": amount,
|
||||
"statuses": [row.status for row in rows],
|
||||
"deal_ids": [row.id for row in rows],
|
||||
}
|
||||
)
|
||||
del self.pending[order_id]
|
||||
@@ -201,19 +191,10 @@ class TState:
|
||||
if not self.path.is_file():
|
||||
return
|
||||
raw = json.loads(self.path.read_text(encoding="utf-8"))
|
||||
# 兼容原 ZT 文件,保留原底仓成本;没有额外版本字段。
|
||||
self.items = {
|
||||
code: TStateItem(**item) for code, item in raw.get("items", raw).items()
|
||||
code: TStateItem(**item) for code, item in raw["items"].items()
|
||||
}
|
||||
self.pending = {
|
||||
key: PendingOrder(**item) for key, item in raw.get("pending", {}).items()
|
||||
key: PendingOrder(**item) for key, item in raw["pending"].items()
|
||||
}
|
||||
self.records = raw.get("records", [])
|
||||
if "items" not in raw:
|
||||
# 旧记录只有提交行情价,不把它伪装成真实成交历史。
|
||||
self.records.append({"kind": "legacy_import", "items": raw})
|
||||
for item in self.items.values():
|
||||
if item.phase in {"SELLING", "BUYING", SOLD}:
|
||||
raise ValueError(
|
||||
f"[ZT 状态] {item.code} 旧做 T 轮次未结束,需先核对成交再迁移"
|
||||
)
|
||||
self.records = raw["records"]
|
||||
|
||||
Reference in New Issue
Block a user