update etc/*

This commit is contained in:
2026-09-07 18:18:00 +08:00
parent e320de3241
commit 8eb44440d3
24 changed files with 217 additions and 52 deletions

View File

@@ -60,6 +60,7 @@ CREATE INDEX IF NOT EXISTS idx_deals_date_time ON deals (trade_date);
# SQL is assembled once; order_local_id is derived from the SDK remark property.
POSITION_FIELDS = tuple(field.name for field in fields(PositionItem))
DEAL_FIELDS = tuple(field.name for field in fields(DealItem)) + ('order_local_id',)
POSITION_DEFAULTS = asdict(PositionItem())
POSITION_UPSERT = (
f"INSERT INTO positions ({', '.join(POSITION_FIELDS)}) "
f"VALUES ({', '.join(':' + key for key in POSITION_FIELDS)}) "
@@ -98,20 +99,17 @@ class OrderBook:
self.positions = positions
self.deals = deals
self.deals_sys_ids = set(deals)
self._deal_count = len(deals)
@staticmethod
def _insert_deals(db: sqlite3.Connection, deals: list[dict]) -> None:
db.executemany(DEAL_INSERT, deals)
@staticmethod
def deal_record(deal: DealItem) -> dict:
if not deal.order_sys_id or deal.volume <= 0:
raise ValueError('System order ID and positive volume are required')
if not deal.local_order_id:
raise ValueError('Local order ID is required')
row = asdict(deal)
row['order_local_id'] = deal.local_order_id
row['order_local_id'] = deal.get_local_order_id()
if not row['order_local_id']:
raise ValueError('Local order ID is required')
if any(isinstance(value, float) and not math.isfinite(value) for value in row.values()):
raise ValueError('Numeric values must be finite')
amount = deal.trade_amount if deal.trade_amount > 0 else deal.price * deal.volume
@@ -125,35 +123,38 @@ class OrderBook:
return row
def sync_deals(self, deals: list[DealItem]) -> None:
new_deals = []
seen = self.deals_sys_ids.copy()
new_deals = {}
for deal in deals:
if deal.order_sys_id in seen:
continue
new_deals.append(self.deal_record(deal))
seen.add(deal.order_sys_id)
if deal.order_sys_id not in self.deals_sys_ids and deal.order_sys_id not in new_deals:
new_deals[deal.order_sys_id] = self.deal_record(deal)
if not new_deals:
return
with closing(self._connect()) as db, db:
self._insert_deals(db, new_deals)
db.executemany(DEAL_INSERT, new_deals.values())
self.load()
def sync_positions(self, positions: list[PositionItem]) -> None:
"""Replace the complete position snapshot, retaining IDs for existing stocks."""
self.save({item.stock_code: asdict(item) for item in positions}, list(self.deals.values()))
self.save({item.stock_code: asdict(item) for item in positions})
def save(self, items: dict, deals: list[dict]) -> None:
if len(deals) < self._deal_count:
raise ValueError('Execution history is append-only')
new_deals = deals[self._deal_count:]
positions = [{**asdict(PositionItem()), **item} for item in items.values()]
def save(self, items: dict, deals: list[dict] | None = None) -> None:
"""保存持仓快照,可同时追加成交;省略 deals 时仅更新持仓。"""
new_deals = []
if deals is not None:
if len(deals) < len(self.deals):
raise ValueError('Execution history is append-only')
new_deals = deals[len(self.deals):]
positions = [{**POSITION_DEFAULTS, **item} for item in items.values()]
for row in [*positions, *new_deals]:
if any(isinstance(value, float) and not math.isfinite(value) for value in row.values()):
raise ValueError('Numeric values must be finite')
with closing(self._connect()) as db, db:
for row in db.execute('SELECT stock_code FROM positions').fetchall():
if row['stock_code'] not in items:
db.execute('DELETE FROM positions WHERE stock_code = ?', (row['stock_code'],))
removed = [
(row['stock_code'],)
for row in db.execute('SELECT stock_code FROM positions')
if row['stock_code'] not in items
]
db.executemany('DELETE FROM positions WHERE stock_code = ?', removed)
db.executemany(POSITION_UPSERT, positions)
self._insert_deals(db, new_deals)
db.executemany(DEAL_INSERT, new_deals)
self.load()