/~learn/s/3b98cf4f
> For Claude: Use ${SUPERPOWERS_SKILLS_ROOT}/skills/collaboration/executing-plans/SKILL.md to implement this plan task-by-task.
Goal: 从「相关性分散」「回撤画像」「估值锚」三个维度增强截面选基策略的持有信念,降低心理波动。
Architecture: 分三步独立实现,互不干扰,可单独验证。方案C修改分散化过滤逻辑(贪心低相关选择),方案A新增独立分析模块(回撤统计+推送展示),方案B新增估值因子(PE分位数/股息率/ROE趋势)并集成到多因子选择器中。
Tech Stack: Python 3.12, pandas, numpy, 现有 Factor/ETFSelector 框架
---
Files:
adata/aohan/quant/selection/factor/diversification.py
Step 1: 实现 dedup_by_correlation 方法
在 DiversificationFilter 类中添加方法,从候选池中贪心选择低相关品种:
关键逻辑:
correlation_threshold 作为构造参数,默认 0.7。
def dedup_by_correlation(self, ranked: pd.Series, snapshot: Dict[str, pd.DataFrame],
top_n: int = 10, correlation_threshold: float = 0.7,
candidate_multiplier: int = 2) -> pd.Series:
Step 2: 修改 apply 方法,接收 snapshot 参数
在 apply() 方法中,在 enforce_category_limit 之后增加相关性筛选:
def apply(self, ranked: pd.Series, top_n: int = 8,
max_same_category: int = 4,
snapshot: Dict[str, pd.DataFrame] = None,
correlation_threshold: float = 0.7) -> pd.Series:
deduped = self.dedup_by_index(ranked)
constrained = self.enforce_category_limit(deduped, top_n=top_n,
max_same_category=max_same_category)
if snapshot is not None:
constrained = self.dedup_by_correlation(constrained, snapshot,
top_n=top_n,
correlation_threshold=correlation_threshold)
return constrained
Step 3: 修改 engine.py 中调用 apply 的地方
在 ETFSelectionEngine.run_selection() 中,传入 snapshot:
ranked = self.diversification_filter.apply(
ranked, top_n=top_n,
max_same_category=self.max_same_category,
snapshot=snapshot,
correlation_threshold=0.7
)
Step 4: 在推送消息中展示相关性信息
在 generate_push_message() 中,如果启用了相关性筛选,推送消息中显示「组合平均相关性」和「最大相关性对」信息。
---
Files:
adata/aohan/quant/selection/analysis/__init__.pyadata/aohan/quant/selection/analysis/drawdown_profile.pyadata/aohan/quant/selection/engine.py (集成到推送消息)
Step 1: 创建 drawdown_profile.py
核心类 DrawdownProfiler:
class DrawdownProfiler:
def __init__(self):
self._history: List[Dict] = [] # 每次推送记录的回撤快照
def compute_drawdown_metrics(self, equity_curve: pd.Series) -> Dict:
"""从净值曲线计算当前回撤指标"""
# 返回: current_dd, dd_duration_weeks, peak_value, peak_date
def analyze_historical_drawdowns(self, equity_curve: pd.Series) -> Dict:
"""分析历史所有回撤事件"""
# 返回: dd_events 列表,每个包含 depth, duration, recovery_time
# 返回: stats 字典,包含 avg_depth, avg_duration, max_duration, percentile_90
def generate_profile_message(self, equity_curve: pd.Series, current_dd: float) -> str:
"""生成回撤画像文本,用于推送消息"""
Step 2: 在 engine.py 中集成
在 generate_push_message() 中,如果 _last_snapshot 可用:
DrawdownProfiler 计算当前回撤和历史统计推送格式示例:
📉 回撤画像
────────────────────────────────────────────────
当前回撤: -8.2%(已持续 3 周)
净值高点: 1.152(2026-05-09)
历史同类回撤 (-8%~-12%): 共 17 次
平均恢复: 5.2 周 | 最长恢复: 14 周 | 90%在 8 周内恢复
→ 当前处于正常波动范围,无需恐慌
---
Files:
adata/aohan/quant/selection/factor/valuation_factor.pyadata/aohan/quant/selection/selector/multi_factor_selector.pyadata/aohan/quant/selection/engine.py (可选集成)
Step 1: 创建 valuation_factor.py
class ValuationFactor(Factor):
"""估值锚因子:PE分位数越低越好,ROE趋势越好越好"""
def __init__(self, lookback_years: int = 3, name: str = 'valuation'):
self._name = name
self.lookback_years = lookback_years
def calculate(self, data_dict, date) -> pd.Series:
# 对每只ETF:
# 1. 获取指数PE历史(优先 adata API,降级 tushare)
# 2. 计算当前PE在近3年的分位数(越低越好)
# 3. 结合股息率
# 4. 结合ROE趋势
# 5. 综合评分 0-1
注意:ETF 的 PE 数据需要通过底层指数获取,而非 ETF 本身。需要新增数据源支持。如果数据不可用,返回 0.5(中性)。
Step 2: 在 MultiFactorETFSelector 中注册
新增工厂方法 valuation_aware() 或在 adaptive() 中集成(牛市低权重、熊市高权重)。
Step 3: 因子权重自适应
在 _get_regime_weights 中,牛市时给估值因子较低权重(不因估值低错失行情),熊市时给较高权重(找安全边际)。
---
按改动独立性和风险排序:
每个方案完成后进行一次端到端验证。