feat(strategy): #77灯=列表入口×N徽标+#78实盘服务端兜底名——#77:策略库四状态灯点击一律进该类型列表(按实例过滤;空列表→新建页带实例),灯带×N徽标=该类型运行总数(enriched新增counts:backtest任务表/replay回放账户/paper_live实走+影子/live实盘,查询失败退化无徽标),删「回测/模拟/实盘历史」三按钮(灯即入口);#78:live/create名称空或默认值(live-600000等)→服务端兜底{实例名}_v{YYYYMMDD}{minor}同实例同日递增,前端autoName失败被吞也拿到正确名;+2回归测试(counts四桶/兜底名递增);docs补北京迁移注记;929绿+build绿 [vps]
CI/CD / test (push) Successful in 15s
CI/CD / nas-deploy (push) Successful in 33s
CI/CD / nas-verify (push) Successful in 16s

This commit is contained in:
2026-08-16 13:33:49 +08:00
parent 214608506a
commit 28374c341b
6 changed files with 176 additions and 80 deletions
+54
View File
@@ -169,3 +169,57 @@ def test_update_instance_run_event_kinds(tmp_path, monkeypatch):
assert inst["last_return"] == 0.15
# 不存在的档案静默丢弃
assert not instance_store.update_instance_run(999, "backtest", "done")
def test_enriched_counts_per_kind(tmp_path, monkeypatch):
"""#77 灯徽标:enriched 每实例带 counts{backtest,replay,paper_live,live}。"""
c, token = _setup(tmp_path, monkeypatch)
iid = instance_store.create_instance({
"code_file": "double_ma.py", "name": "双均线·浦发", "type": "cta",
"params": {"fast_window": 10}, "symbol_or_pool": "600000", "interval": "d",
})
# 实走 ×1paper_accounts 按 mode 分桶;paper_live=实走+影子不分状态)
r = c.post("/api/v1/paper/create",
json={**_LIVE_BODY, "mode": "live", "instance_id": iid},
headers=_auth(token))
assert r.status_code == 200
# 回放 ×1:直插行(create 走后台回放线程+区间校验,测试不必绕)
from sanguo_api.routes_paper import PaperCreateRequest, StrategyCfg, _db_path as _pdb
from sanguo_trader.persistence import save_account
rep = PaperCreateRequest(
mode="replay", symbols=["600000"],
strategies=[StrategyCfg(name="DoubleMaStrategy", symbol="600000")],
start="2026-01-01", end="2026-12-31", name="回放",
)
save_account(_pdb["path"], {**rep.model_dump(), "instance_id": iid})
# 实盘 ×1
r = c.post("/api/v1/live/create", json={
"name": "L1", "account": "123", "strategy_name": "dm1", "instance_id": iid,
}, headers=_auth(token))
assert r.status_code == 200
ent = [i for i in c.get("/api/v1/strategy/instances/enriched",
headers=_auth(token)).json()["instances"]
if i["id"] == iid][0]
assert ent["counts"] == {"backtest": 0, "replay": 1, "paper_live": 1, "live": 1}
def test_live_create_default_name_fallback(tmp_path, monkeypatch):
"""#78live/create 名称空/默认值 → 服务端兜底 {实例名}_v{YYYYMMDD}{minor}"""
import time as _time
c, token = _setup(tmp_path, monkeypatch)
iid = instance_store.create_instance({
"code_file": "double_ma.py", "name": "全天候·测试", "type": "cta",
"params": {"fast_window": 10}, "symbol_or_pool": "600000", "interval": "d",
})
body = {"account": "123", "strategy_name": "dm1", "instance_id": iid}
a1 = c.post("/api/v1/live/create", json=body, headers=_auth(token)).json()["account_id"]
# 前端默认占位名(live-600000)同样触发兜底
a2 = c.post("/api/v1/live/create", json={**body, "name": "live-600000"},
headers=_auth(token)).json()["account_id"]
accs = {a["id"]: a["name"]
for a in c.get("/api/v1/live", headers=_auth(token)).json()["accounts"]}
today = _time.strftime("%Y%m%d")
assert accs[a1] == f"全天候·测试_v{today}0"
assert accs[a2] == f"全天候·测试_v{today}1"