3a0e75fdc1
- strategy_registry 枚举 vnpy_ctastrategy 策略(兜底 STRATEGY_NAMES)
- /strategy/list、/strategy/{name}/params
- /task/{id}/equity-curve、/daily-pnl、/trades(BacktestResult JSON 化)
- /kline(read_db_daily 历史 K 线)
- 9 tests passed(4 strategy_registry + 5 routes)
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""Tests for backtest UI support endpoints (S1.4).
|
|
|
|
Uses a FakeOrch returning a BacktestResult with equity_curve/trades so we can
|
|
assert the strategy/equity-curve/daily-pnl/trades endpoints without a real
|
|
orchestrator or DB.
|
|
"""
|
|
import pytest
|
|
import pandas as pd
|
|
from fastapi.testclient import TestClient
|
|
|
|
from sanguo_api.app import create_app
|
|
from sanguo_api.routes import set_orchestrator
|
|
from sanguo_api.auth import hash_password
|
|
from sanguo_backtest.result_store import BacktestResult
|
|
|
|
|
|
class FakeOrch:
|
|
def __init__(self, result):
|
|
self._r = result
|
|
|
|
def get_result(self, task_id):
|
|
return self._r
|
|
|
|
|
|
def _result() -> BacktestResult:
|
|
return BacktestResult(
|
|
task_id="cta_t", type="cta", status="done", strategy="DoubleMaStrategy",
|
|
symbol="600000", params={"fast_window": 10}, start="2024-01-01", end="2024-06-30",
|
|
statistics={"total_return": 0.1, "sharpe_ratio": 1.2},
|
|
equity_curve=pd.DataFrame([
|
|
{"date": "2024-01-01", "balance": 1_000_000.0},
|
|
{"date": "2024-01-02", "balance": 1_010_000.0},
|
|
{"date": "2024-01-03", "balance": 1_005_000.0},
|
|
]),
|
|
trades=pd.DataFrame([
|
|
{"datetime": "2024-01-02", "direction": "多", "offset": "开",
|
|
"price": 10.5, "volume": 100, "vt_symbol": "600000.SSE"},
|
|
]),
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def client() -> TestClient:
|
|
app = create_app(
|
|
db_path="/tmp/test_bt_routes.db",
|
|
auth_config={
|
|
"username": "admin",
|
|
"password_hash": hash_password("admin"),
|
|
"jwt_secret": "test-secret",
|
|
"expire_minutes": 60,
|
|
},
|
|
max_workers=1,
|
|
)
|
|
set_orchestrator(FakeOrch(_result()))
|
|
return TestClient(app)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def token(client) -> str:
|
|
r = client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin"})
|
|
assert r.status_code == 200
|
|
return r.json()["token"]
|
|
|
|
|
|
def test_endpoints_require_auth(client):
|
|
assert client.get("/api/v1/task/t/equity-curve").status_code == 401
|
|
assert client.get("/api/v1/strategy/list").status_code == 401
|
|
|
|
|
|
def test_strategy_list_and_params(client, token):
|
|
h = {"Authorization": f"Bearer {token}"}
|
|
r = client.get("/api/v1/strategy/list", headers=h)
|
|
assert r.status_code == 200
|
|
assert "strategies" in r.json()
|
|
r2 = client.get("/api/v1/strategy/DoubleMaStrategy/params", headers=h)
|
|
assert r2.status_code == 200
|
|
assert "parameters" in r2.json()
|
|
|
|
|
|
def test_equity_curve(client, token):
|
|
h = {"Authorization": f"Bearer {token}"}
|
|
eq = client.get("/api/v1/task/t/equity-curve", headers=h).json()
|
|
assert len(eq["equity_curve"]) == 3
|
|
assert eq["equity_curve"][1]["balance"] == 1_010_000.0
|
|
|
|
|
|
def test_daily_pnl(client, token):
|
|
h = {"Authorization": f"Bearer {token}"}
|
|
pnl = client.get("/api/v1/task/t/daily-pnl", headers=h).json()
|
|
assert len(pnl["daily_pnl"]) == 3
|
|
# day 0: no prior → 0.0; day 1: +10000; day 2: -5000
|
|
assert pnl["daily_pnl"][0]["pnl"] == 0.0
|
|
assert pnl["daily_pnl"][1]["pnl"] == 10_000.0
|
|
assert pnl["daily_pnl"][2]["pnl"] == -5_000.0
|
|
|
|
|
|
def test_trades(client, token):
|
|
h = {"Authorization": f"Bearer {token}"}
|
|
tr = client.get("/api/v1/task/t/trades", headers=h).json()
|
|
assert len(tr["trades"]) == 1
|
|
assert tr["trades"][0]["price"] == 10.5
|