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]
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user