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:
@@ -10,6 +10,7 @@
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
@@ -21,6 +22,8 @@ import requests
|
||||
# 确保 .env 已加载
|
||||
import config.settings # noqa: F401
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NewsSource:
|
||||
"""
|
||||
@@ -87,7 +90,7 @@ class NewsSource:
|
||||
frames.append(df)
|
||||
time.sleep(self.request_delay)
|
||||
except Exception as e:
|
||||
print(" [WARN] AkShare 新闻获取失败 ({}): {}".format(ts_code, e))
|
||||
logger.warning("[news_source] AkShare 新闻获取失败 (%s): %s", ts_code, e)
|
||||
|
||||
if self.use_xwlb:
|
||||
try:
|
||||
@@ -99,7 +102,7 @@ class NewsSource:
|
||||
if not df.empty:
|
||||
frames.append(df)
|
||||
except Exception as e:
|
||||
print(" [WARN] xwlb 新闻获取失败: {}".format(e))
|
||||
logger.warning("[news_source] xwlb 新闻获取失败: %s", e)
|
||||
|
||||
if self.use_mcp:
|
||||
try:
|
||||
@@ -107,7 +110,7 @@ class NewsSource:
|
||||
if not df.empty:
|
||||
frames.append(df)
|
||||
except Exception as e:
|
||||
print(" [WARN] MCP 新闻获取失败: {}".format(e))
|
||||
logger.warning("[news_source] MCP 新闻获取失败: %s", e)
|
||||
|
||||
if not frames:
|
||||
return pd.DataFrame(columns=["date", "title", "content", "source", "url"])
|
||||
@@ -133,7 +136,8 @@ class NewsSource:
|
||||
symbol = ts_code.replace(".SZ", "").replace(".SH", "").replace(".BJ", "")
|
||||
try:
|
||||
df = ak.stock_news_em(symbol=symbol.zfill(6))
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.warning("[news_source] AkShare 新闻接口失败 (%s): %s", ts_code, e)
|
||||
return pd.DataFrame()
|
||||
|
||||
if df is None or df.empty:
|
||||
@@ -204,7 +208,8 @@ class NewsSource:
|
||||
df["url"] = ""
|
||||
return df[["date", "title", "content", "source", "url"]]
|
||||
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.warning("[news_source] xwlb 新闻获取失败: %s", e)
|
||||
return pd.DataFrame()
|
||||
|
||||
# ── MCP 数据源 ────────────────────────────────────────
|
||||
@@ -232,7 +237,8 @@ class NewsSource:
|
||||
df = pd.DataFrame(records)
|
||||
df["source"] = "mcp_trendradar"
|
||||
return df[["date", "title", "content", "source", "url"]]
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.warning("[news_source] MCP 新闻获取失败: %s", e)
|
||||
return pd.DataFrame()
|
||||
|
||||
def _mcp_initialize(self) -> str | None:
|
||||
@@ -267,10 +273,10 @@ class NewsSource:
|
||||
if session_id:
|
||||
self._mcp_session_id = session_id
|
||||
else:
|
||||
print(" [WARN] MCP initialize 未返回 session-id")
|
||||
logger.warning("[news_source] MCP initialize 未返回 session-id")
|
||||
return session_id
|
||||
except Exception as e:
|
||||
print(" [WARN] MCP 连接失败: {}".format(e))
|
||||
logger.warning("[news_source] MCP 连接失败: %s", e)
|
||||
return None
|
||||
|
||||
def _mcp_call_tool(
|
||||
@@ -299,7 +305,8 @@ class NewsSource:
|
||||
data = json.loads(line[5:].strip())
|
||||
return data.get("result", {})
|
||||
return None
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
logger.warning("[news_source] MCP call_tool 失败: %s", e)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -128,11 +128,12 @@ class SentimentEngine:
|
||||
# 5. Qwen 情绪分析(有 API key 时才执行)
|
||||
sentiment_df = self._analyze_news(news_df)
|
||||
|
||||
# 6. 计算因子
|
||||
# 6. 计算因子(对齐注册表,产出全部 4 个注册情绪因子,含 news_sent_20)
|
||||
factor_dfs = {}
|
||||
if not sentiment_df.empty:
|
||||
for factor_cls, kwargs in [
|
||||
(NewsSentimentFactor, {"window": 5, "sentiment_df": sentiment_df}),
|
||||
(NewsSentimentFactor, {"window": 20, "sentiment_df": sentiment_df}),
|
||||
(SentimentConfidenceFactor, {"window": 5, "sentiment_df": sentiment_df}),
|
||||
(SentimentMomentumFactor, {"period": 5, "sentiment_df": sentiment_df}),
|
||||
]:
|
||||
|
||||
Reference in New Issue
Block a user