This commit is contained in:
2026-09-12 16:25:34 +08:00
parent 7a7049ce44
commit 554dd0f4cb
13 changed files with 646 additions and 93 deletions

View File

@@ -1,6 +1,7 @@
"""SQLite 策略状态与成交存储;每个数据库仅使用一个写入者,不做数据迁移。"""
import math
import json
import logging as log
import sqlite3
from contextlib import closing
@@ -144,9 +145,11 @@ class State:
self.deals, self.deals_sys_ids = deals, set(deals)
@staticmethod
def _insert_deals(db: sqlite3.Connection, deals: list[DealItem]) -> None:
def _insert_deals(db: sqlite3.Connection, deals: list[DealItem],
existing_ids: set[str] | None = None) -> None:
"""共享事务内保存成交;空本地编号使用明确的待核对标记。"""
existing = {row[0] for row in db.execute('SELECT order_sys_id FROM deals')}
existing = existing_ids if existing_ids is not None else {
row[0] for row in db.execute('SELECT order_sys_id FROM deals')}
new_deals: dict[str, DealItem] = {}
for deal in deals:
if deal.order_sys_id not in existing:
@@ -280,13 +283,16 @@ class State:
'AND is_arch = 0', (code,),
)
def _archive_pending(self, db: sqlite3.Connection, snapshot_dedup: bool = True) -> None:
def _archive_pending(self, db: sqlite3.Connection, snapshot_dedup: bool = True,
blocked_codes: set[str] | None = None) -> None:
"""保留现有逐证券保存点ZT 增量路径禁用数量相等推断。"""
codes = db.execute(
'SELECT DISTINCT stock_code FROM deals WHERE is_arch = 0'
).fetchall()
for row in codes:
code = row['stock_code']
if blocked_codes and code in blocked_codes:
continue
db.execute('SAVEPOINT archive_stock')
try:
self._archive_stock(db, code, snapshot_dedup)
@@ -314,6 +320,11 @@ class State:
holdings = {p.stock_code: p for p in positions if p.volume > 0}
with closing(self._connect()) as db, db:
db.execute('BEGIN IMMEDIATE')
db.execute('''CREATE TABLE IF NOT EXISTS zt_rejected_deals (
stock_code TEXT NOT NULL, order_sys_id TEXT NOT NULL,
payload TEXT NOT NULL, error TEXT NOT NULL,
PRIMARY KEY (stock_code, order_sys_id)
)''')
if initialize:
if (db.execute('SELECT 1 FROM state LIMIT 1').fetchone()
or db.execute('SELECT 1 FROM deals LIMIT 1').fetchone()):
@@ -330,11 +341,13 @@ class State:
# 这些成交已包含在已核对的基准中,不再增减快照库存。
db.execute('UPDATE deals SET is_arch = 1')
else:
self._insert_deals(db, deals)
self._archive_pending(db, snapshot_dedup=False)
self._insert_account_deals(db, deals)
rejected = {row[0] for row in db.execute('SELECT stock_code FROM zt_rejected_deals')}
self._archive_pending(db, snapshot_dedup=False, blocked_codes=rejected)
state = self._read_state(db)
cached_deals = self._read_deals(db)
blocked = {d['stock_code'] for d in cached_deals.values() if d['is_arch'] != 1}
blocked.update(row[0] for row in db.execute('SELECT stock_code FROM zt_rejected_deals'))
for code in set(state) | set(holdings):
row = state.get(code, {})
recorded = row.get('base_qty', 0) + row.get('added_qty', 0)
@@ -345,3 +358,35 @@ class State:
self.blocked_codes = blocked
if blocked:
log.warning('[ZT 同步] 以下证券状态待核对,暂停交易:%s', ', '.join(sorted(blocked)))
def _insert_account_deals(self, db: sqlite3.Connection, deals: list[DealItem]) -> None:
"""仅 ZT 增量路径隔离坏成交;保留原始字段,缺席回报不会解除隔离。"""
existing = {row[0] for row in db.execute('SELECT order_sys_id FROM deals')}
for deal in deals:
if not deal.stock_code:
raise ValueError('Trade without stock code cannot be isolated')
db.execute('SAVEPOINT insert_zt_deal')
try:
if not deal.stock_code or not deal.order_sys_id:
raise ValueError('Trade code and order ID are required')
if (deal.offset_flag not in (FLAG_BUY, FLAG_SELL)
or not math.isfinite(deal.volume) or deal.volume <= 0
or int(deal.volume) != deal.volume
or not math.isfinite(deal.price) or deal.price < 0
or not math.isfinite(deal.trade_amount)
or not math.isfinite(deal.close_profit)):
raise ValueError('Invalid trade direction, volume or amount')
self._insert_deals(db, [deal], existing)
except (ValueError, TypeError, OverflowError, sqlite3.IntegrityError) as exc:
db.execute('ROLLBACK TO insert_zt_deal')
db.execute('''INSERT OR REPLACE INTO zt_rejected_deals
(stock_code, order_sys_id, payload, error) VALUES (?, ?, ?, ?)''',
(deal.stock_code, deal.order_sys_id, json.dumps(asdict(deal), ensure_ascii=False), str(exc)))
log.warning('[ZT 成交] 隔离证券=%s,委托=%s,原因=%s',
deal.stock_code, deal.order_sys_id, exc)
else:
existing.add(deal.order_sys_id)
db.execute('DELETE FROM zt_rejected_deals WHERE stock_code = ? AND order_sys_id = ?',
(deal.stock_code, deal.order_sys_id))
finally:
db.execute('RELEASE insert_zt_deal')