- 新增 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 扩充配置项
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
"""
|
|
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 |