feat: 量化引擎加固 — 新增测试 + 数据/因子/回测层优化
- 新增 finance/tests/ 6 个测试套件(agents/backtest/dao_upsert/factors/features/fundamental_lookahead) - 数据层: data_manager / dao 优化,新增 upsert 逻辑 - 因子层: 基本面因子抽象定位 _mapping、ROE/PE/PB 重构 - 回测层: vectorbt/engine 大改动(251 行),report 增强 - ML 层: features/backtest_integration 特征工程与回测优化 - CLI: agent_cli 重构 - config/settings 扩充配置项
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Agent 层回归测试:RiskAgent 配置集中可用性 + Orchestrator 每日流程步骤隔离。
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
from agents.risk_agent import RiskAgent
|
||||
from agents.orchestrator import AgentOrchestrator, TOTAL_STEPS
|
||||
|
||||
|
||||
def _flat_dm():
|
||||
"""给出平淡行情 → 风险 low。"""
|
||||
class _DM:
|
||||
def get_daily(self, code):
|
||||
idx = pd.date_range('2025-01-01', periods=300, freq='B').strftime('%Y%m%d')
|
||||
close = np.linspace(100, 100 + 0.03 * 300, 300) + np.random.default_rng(0).normal(0, 0.2, 300)
|
||||
return pd.DataFrame({'trade_date': idx, 'close': close})
|
||||
return _DM()
|
||||
|
||||
|
||||
def _hot_dm():
|
||||
class _DM:
|
||||
def get_daily(self, code):
|
||||
idx = pd.date_range('2025-01-01', periods=300, freq='B').strftime('%Y%m%d')
|
||||
close = 100 * np.exp(np.cumsum(np.random.default_rng(1).normal(0, 0.03, 300)))
|
||||
return pd.DataFrame({'trade_date': idx, 'close': close})
|
||||
return _DM()
|
||||
|
||||
|
||||
def test_risk_config_low_level():
|
||||
r = RiskAgent(dm=_flat_dm()).execute()
|
||||
assert r['risk_level'] in ('low', 'medium', 'high')
|
||||
assert r['target_exposure'] in (0.85, 0.60, 0.30)
|
||||
|
||||
|
||||
def test_risk_config_high_level():
|
||||
r = RiskAgent(dm=_hot_dm()).execute()
|
||||
assert r['risk_level'] in ('high', 'medium')
|
||||
|
||||
|
||||
class _FailAgent:
|
||||
def execute(self, **kw):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
|
||||
class _OkAgent:
|
||||
def execute(self, **kw):
|
||||
return {"report_path": "/tmp/x.md", "risk_level": "low",
|
||||
"target_exposure": 0.8, "top_picks": [], "date": "20260601"}
|
||||
|
||||
|
||||
class _FakeDM:
|
||||
def get_stock_list(self):
|
||||
return pd.DataFrame(index=['000001.SZ', '600519.SH'])
|
||||
def sync_daily(self, code):
|
||||
return 5
|
||||
|
||||
|
||||
class _FakeSent:
|
||||
def get_scope_stocks(self):
|
||||
return ["000001.SZ", "600519.SH"]
|
||||
def compute(self, *a, **k):
|
||||
return pd.DataFrame({"news_sent_5": [0.1, 0.2]})
|
||||
|
||||
|
||||
def test_run_daily_steps_isolated():
|
||||
"""risk/selection 抛错被隔离,report/sentiment/sync 仍成功。"""
|
||||
with patch('database.dao.get_latest_trade_date', side_effect=lambda c: "20260530"):
|
||||
orch = AgentOrchestrator(dm=_FakeDM(), sent=_FakeSent())
|
||||
orch.agents = {"risk": _FailAgent(), "selection": _FailAgent(),
|
||||
"report": _OkAgent(), "research": _OkAgent()}
|
||||
res = orch.run_daily(date="20260601")
|
||||
assert "risk" in res and isinstance(res["risk"], dict) and "error" in res["risk"]
|
||||
assert "selection" in res and isinstance(res["selection"], dict) and "error" in res["selection"]
|
||||
# 失败不中断:report/sentiment 仍成功
|
||||
assert "report" in res and "error" not in res["report"]
|
||||
assert isinstance(res.get("sentiment"), pd.DataFrame)
|
||||
|
||||
|
||||
def test_orchestrator_total_steps_is_5():
|
||||
assert TOTAL_STEPS == 5
|
||||
Reference in New Issue
Block a user