refactor(paper): 影子升级为第三种运行模式(回放/实走/影子,用户拍板CTA和组合都可走影子): 模式卡三选一替代组合卡内撮合引擎单选; engine由mode推导(shadow→shadow否则eod_replay,CTA/组合统一); 日终20:30 job只遍历mode=live天然隔离影子账户; 列表徽标mode=shadow亮'影子'+组合live标'日终'; 2新测试 [vps]
CI/CD / test (push) Successful in 13s
CI/CD / nas-deploy (push) Successful in 27s
CI/CD / nas-verify (push) Successful in 9s

This commit is contained in:
2026-08-14 12:55:26 +08:00
parent 8e7e7282de
commit 6220fc5943
4 changed files with 64 additions and 33 deletions
+42
View File
@@ -37,6 +37,48 @@ def test_create_paper(tmp_path):
assert "account_id" in resp.json()
def test_create_shadow_mode(tmp_path):
"""影子=第三种运行模式(CTA/组合都可):engine 由 mode 推导,不被日终 job 结算。"""
c, token = _client(tmp_path)
# CTA 影子
r1 = c.post("/api/v1/paper/create", json={
"mode": "shadow",
"symbols": ["600000"],
"strategies": [{"name": "DoubleMa", "symbol": "600000"}],
"start": "2024-01-01", "end": "2024-06-30",
}, headers=_auth(token))
assert r1.status_code == 200
# 组合影子:mode=shadow → engine=shadow
r2 = c.post("/api/v1/paper/create", json={
"mode": "shadow", "strategy_type": "portfolio",
"symbols": ["hs300_subset"],
"strategies": [{"name": "all_weather", "symbol": "hs300_subset"}],
"start": "2024-01-01", "end": "2024-12-31",
"pool": "hs300_subset",
}, headers=_auth(token))
assert r2.status_code == 200
lst = c.get("/api/v1/paper", headers=_auth(token)).json()
items = lst if isinstance(lst, list) else lst.get("accounts", lst.get("papers", []))
by_id = {a["id"]: a for a in items}
cta = by_id[r1.json()["account_id"]]
assert cta["mode"] == "shadow"
assert cta["engine"] == "shadow"
pf = by_id[r2.json()["account_id"]]
assert pf["mode"] == "shadow" and pf["engine"] == "shadow"
def test_create_portfolio_rejects_replay(tmp_path):
c, token = _client(tmp_path)
resp = c.post("/api/v1/paper/create", json={
"mode": "replay", "strategy_type": "portfolio",
"symbols": ["hs300_subset"],
"strategies": [{"name": "all_weather", "symbol": "hs300_subset"}],
"start": "2024-01-01", "end": "2024-12-31",
}, headers=_auth(token))
assert resp.status_code == 400
assert "组合回测" in resp.json()["detail"]
def test_get_paper_and_empty_trades(tmp_path):
c, token = _client(tmp_path)
aid = c.post(