"""模拟盘 API 路由测试(spec §10)。""" import os from fastapi.testclient import TestClient from sanguo_api.app import create_app from sanguo_api.auth import create_token, set_jwt_config from sanguo_api.routes_paper import set_db_path def _client(tmp_path): set_jwt_config(secret="t", expire_minutes=60) db = os.path.join(str(tmp_path), "p.db") app = create_app(db_path=db) set_db_path(db) return TestClient(app), create_token("admin") def _auth(token): return {"Authorization": f"Bearer {token}"} def test_create_paper(tmp_path): c, token = _client(tmp_path) resp = c.post( "/api/v1/paper/create", json={ "symbols": ["600000"], "strategies": [{"name": "DoubleMa", "symbol": "600000", "match_session": "next_open"}], "start": "2024-01-01", "end": "2024-06-30", "initial_capital": 1_000_000, }, headers=_auth(token), ) assert resp.status_code == 200 assert "account_id" in resp.json() def test_get_paper_and_empty_trades(tmp_path): c, token = _client(tmp_path) aid = c.post( "/api/v1/paper/create", json={"symbols": ["600000"], "strategies": [{"name": "S", "symbol": "600000"}], "start": "2024-01-01", "end": "2024-06-30"}, headers=_auth(token), ).json()["account_id"] assert c.get(f"/api/v1/paper/{aid}", headers=_auth(token)).status_code == 200 assert c.get(f"/api/v1/paper/{aid}/trades", headers=_auth(token)).json() == [] assert c.get(f"/api/v1/paper/{aid}/equity", headers=_auth(token)).json() == [] def test_get_paper_404(tmp_path): c, token = _client(tmp_path) assert c.get("/api/v1/paper/999", headers=_auth(token)).status_code == 404 def test_unauthorized_401(tmp_path): c, _ = _client(tmp_path) assert c.get("/api/v1/paper/1").status_code == 401 def test_strategy_summary_aggregation(tmp_path): """C-S2 归因:分策略成交/拒单/费用聚合(spec §7)。""" from sanguo_trader.persistence import init_db, save_account, save_trade c, token = _client(tmp_path) db = os.path.join(str(tmp_path), "p.db") aid = c.post( "/api/v1/paper/create", json={"symbols": ["600000"], "strategies": [{"name": "S", "symbol": "600000"}], "start": "2024-01-01", "end": "2024-06-30"}, headers=_auth(token), ).json()["account_id"] # 造假 2 笔成交 + 1 拒单(s1),1 成交(s2) save_trade(db, aid, {"strategy_id": "s1", "symbol": "600000", "price": 10, "volume": 100, "commission": 5, "transfer_fee": 0.02}) save_trade(db, aid, {"strategy_id": "s1", "symbol": "600000", "price": 11, "volume": 100, "commission": 5, "stamp_duty": 0.55, "transfer_fee": 0.02}) save_trade(db, aid, {"strategy_id": "s1", "symbol": "300750"}, rejected=True, reject_reason="limit_up_locked") save_trade(db, aid, {"strategy_id": "s2", "symbol": "000001", "price": 15, "volume": 100, "commission": 5}) resp = c.get(f"/api/v1/paper/{aid}/strategies", headers=_auth(token)) assert resp.status_code == 200 summary = {s["strategy_id"]: s for s in resp.json()} assert summary["s1"]["filled"] == 2 assert summary["s1"]["rejected"] == 1 assert summary["s2"]["filled"] == 1 def test_positions_endpoint(tmp_path): """Phase 3c:当前持仓快照({symbol:{volume,frozen,avg_price}} → list)。""" from sanguo_trader.persistence import save_positions c, token = _client(tmp_path) db = os.path.join(str(tmp_path), "p.db") aid = c.post( "/api/v1/paper/create", json={"symbols": ["600000"], "strategies": [{"name": "S", "symbol": "600000"}], "start": "2024-01-01", "end": "2024-06-30"}, headers=_auth(token), ).json()["account_id"] save_positions(db, aid, "account", {"600000": {"volume": 100, "frozen": 0, "avg_price": 10.5}}, "2024-01-15") resp = c.get(f"/api/v1/paper/{aid}/positions", headers=_auth(token)) assert resp.status_code == 200 data = resp.json() assert len(data) == 1 assert data[0]["symbol"] == "600000" assert data[0]["volume"] == 100 assert data[0]["frozen"] == 0 assert data[0]["avg_price"] == 10.5 def test_pending_endpoint(tmp_path): """Phase 3c:跨日 pending 订单(C-S3)。""" from sanguo_trader.persistence import save_pending_orders c, token = _client(tmp_path) db = os.path.join(str(tmp_path), "p.db") aid = c.post( "/api/v1/paper/create", json={"symbols": ["600000"], "strategies": [{"name": "S", "symbol": "600000"}], "start": "2024-01-01", "end": "2024-06-30"}, headers=_auth(token), ).json()["account_id"] save_pending_orders(db, aid, [ {"strategy_id": "s1", "symbol": "600000", "side": "buy", "price": 10.0, "volume": 100, "is_market": True, "match_session": "next_open", "listing_days": 0}, ]) resp = c.get(f"/api/v1/paper/{aid}/pending", headers=_auth(token)) assert resp.status_code == 200 data = resp.json() assert len(data) == 1 assert data[0]["symbol"] == "600000" assert data[0]["side"] == "buy" assert data[0]["is_market"] is True