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:
@@ -5,6 +5,7 @@ ROE 因子。
|
||||
import pandas as pd
|
||||
|
||||
from factors.base import BaseFactor
|
||||
from factors.fundamental._mapping import map_fundamental_to_daily, effective_available_dates
|
||||
|
||||
|
||||
class ROEFactor(BaseFactor):
|
||||
@@ -45,28 +46,8 @@ class ROEFactor(BaseFactor):
|
||||
fina_df: pd.DataFrame,
|
||||
column: str,
|
||||
) -> pd.Series:
|
||||
"""将财务数据(季度)映射到日线索引。"""
|
||||
fina = fina_df[["end_date", column]].dropna().copy()
|
||||
fina["end_date"] = fina["end_date"].astype(str)
|
||||
fina = fina.sort_values("end_date")
|
||||
|
||||
result = pd.Series(float("nan"), index=daily_df.index)
|
||||
|
||||
if fina.empty:
|
||||
return result
|
||||
|
||||
dates = pd.to_datetime(daily_df.index, format="%Y%m%d", errors="coerce")
|
||||
fina_dates = pd.to_datetime(fina["end_date"], format="%Y%m%d", errors="coerce")
|
||||
|
||||
for i, fina_date in enumerate(fina_dates):
|
||||
mask = dates >= fina_date
|
||||
if i + 1 < len(fina_dates):
|
||||
mask &= dates < fina_dates.iloc[i + 1]
|
||||
else:
|
||||
pass # 最新一期覆盖所有后续日期
|
||||
result[mask] = fina[column].iloc[i]
|
||||
|
||||
return result.astype("float64")
|
||||
"""将财务数据(季度)映射到日线索引,按披露可用日消除前视。"""
|
||||
return map_fundamental_to_daily(daily_df, fina_df, column)
|
||||
|
||||
|
||||
class ROETTMDeltaFactor(BaseFactor):
|
||||
@@ -82,23 +63,36 @@ class ROETTMDeltaFactor(BaseFactor):
|
||||
if self._financial_df is None or self._financial_df.empty:
|
||||
return pd.Series(float("nan"), index=df.index)
|
||||
|
||||
fina = self._financial_df[["end_date", "roe"]].dropna().copy()
|
||||
fina["end_date"] = fina["end_date"].astype(str)
|
||||
fina["year"] = fina["end_date"].str[:4].astype(int)
|
||||
fina = fina.sort_values("end_date")
|
||||
if "roe" not in self._financial_df.columns or "end_date" not in self._financial_df.columns:
|
||||
return pd.Series(float("nan"), index=df.index)
|
||||
|
||||
# 基于财务帧计算披露可用日(含 ann_date 优先/法定滞后兜底)
|
||||
fina = effective_available_dates(self._financial_df)
|
||||
fina = fina[["_avail", "end_date", "roe"]].dropna(subset=["_avail", "roe"]).copy()
|
||||
if fina.empty:
|
||||
return pd.Series(float("nan"), index=df.index)
|
||||
|
||||
# 报告期月份(A 股季末:03/06/09/12)
|
||||
end_dt = pd.to_datetime(fina["end_date"].astype(str), format="%Y%m%d", errors="coerce")
|
||||
fina["report_month"] = end_dt.dt.month
|
||||
fina["report_year"] = end_dt.dt.year
|
||||
# 本年取数映射:{ (year, month): roe }
|
||||
cur_map = dict(zip(zip(fina["report_year"], fina["report_month"]), fina["roe"]))
|
||||
# 按披露可用日排序,逐期覆盖区间
|
||||
fina = fina.sort_values("_avail")
|
||||
|
||||
# 按年分组计算 YoY 差值
|
||||
roe_delta = pd.Series(float("nan"), index=df.index)
|
||||
dates = pd.to_datetime(df.index, format="%Y%m%d", errors="coerce")
|
||||
|
||||
for _, row in fina.iterrows():
|
||||
this_year = row["year"]
|
||||
prev_row = fina[fina["year"] == this_year - 1]
|
||||
if prev_row.empty:
|
||||
avail_dt = row["_avail"]
|
||||
if pd.isna(avail_dt):
|
||||
continue
|
||||
delta = row["roe"] - prev_row["roe"].iloc[-1]
|
||||
f_date = pd.to_datetime(row["end_date"], format="%Y%m%d")
|
||||
mask = dates >= f_date
|
||||
prev_roe = cur_map.get((row["report_year"] - 1, row["report_month"]))
|
||||
if prev_roe is None:
|
||||
continue
|
||||
delta = row["roe"] - prev_roe
|
||||
mask = dates >= avail_dt
|
||||
roe_delta[mask] = delta
|
||||
|
||||
return roe_delta.astype("float64")
|
||||
|
||||
Reference in New Issue
Block a user