feat(shadow-desk): 双轨日终对账报表(影子P3前半,设计§8.2)—reconcile_report四指标(成交笔数全同/每笔价差vwap对比<10bps带符号供滑点重标/收盘持仓逐只数量/净值月偏差<0.5%)+find_dual_track_pairs自动配对(shadow策略名↔live strategy_class,live#5↔shadow#39实证)+dual_track_reconcile落库upsert;API GET /paper/reconcile(+单配对refresh);CLI python -m;supervisor auto轮询挂15:10后每日一次兜底;14新测试(符号口径SH/XSHG归一/影子拒单不计/部分成交如实报笔数异) [vps]
CI/CD / test (push) Successful in 11s
CI/CD / nas-deploy (push) Successful in 33s
CI/CD / nas-verify (push) Successful in 12s

This commit is contained in:
2026-08-14 23:18:07 +08:00
parent 2cd158286b
commit 534a06aad7
5 changed files with 706 additions and 0 deletions
+49
View File
@@ -191,3 +191,52 @@ def test_pending_endpoint(tmp_path):
assert data[0]["symbol"] == "600000"
assert data[0]["side"] == "buy"
assert data[0]["is_market"] is True
# ===== 双轨对账报表(影子 P3 前半)=====
def test_reconcile_routes(tmp_path):
"""GET /paper/reconcile 自动配对+报告;/paper/reconcile/{l}/{s} 单配对。"""
import json as _json
import sqlite3
c, token = _client(tmp_path)
db = os.path.join(str(tmp_path), "p.db")
with sqlite3.connect(db) as conn:
conn.execute(
"INSERT INTO live_accounts (id,name,account,vt_symbol,strategy_class,"
"strategy_name,status) VALUES (5,'live','66639661','pool',"
"'channel_test','portfolio_channel_test','running')")
conn.execute(
"INSERT INTO paper_accounts (id,name,strategy_type,mode,status,"
"strategies) VALUES (39,'paper','portfolio','shadow','running',"
"'[{\"name\": \"channel_test\", \"params\": {}}]')")
conn.execute(
"INSERT INTO live_trades (account_id,symbol,direction,price,volume,"
"traded_at,vt_tradeid) VALUES (5,'510300.SH','buy',4.0,1000,"
"'2026-08-15 09:35:00','t1')")
conn.execute(
"INSERT INTO paper_trades (account_id,strategy_id,datetime,symbol,"
"direction,offset,price,volume,rejected,bar_date) VALUES (39,'ct',"
"'2026-08-15 09:35:00','510300.XSHG','long','open',4.0,1000,0,"
"'2026-08-15')")
conn.commit()
# 自动配对列表(指定 date 保证命中测试数据)
r = c.get("/api/v1/paper/reconcile?date=2026-08-15", headers=_auth(token))
assert r.status_code == 200
pairs = r.json()["pairs"]
assert len(pairs) == 1
assert pairs[0]["live_account_id"] == 5
assert pairs[0]["shadow_account_id"] == 39
assert pairs[0]["report"]["trades"]["count_match"] is True
# 单配对端点
r2 = c.get("/api/v1/paper/reconcile/5/39?date=2026-08-15", headers=_auth(token))
assert r2.status_code == 200
assert r2.json()["trades"]["live_count"] == 1
# 未配对的 aid 路由不被 reconcile 吞:GET /paper/39 仍走账户详情
r3 = c.get("/api/v1/paper/39", headers=_auth(token))
assert r3.status_code == 200
assert r3.json()["mode"] == "shadow"