feat(framework_v2): 对齐 V1 配置,实现指数信号→ETF收益回测

配置对齐:
- config_simple.yaml 严格对齐 V1 config.yaml
  * 11 个标的覆盖 7 个策略分组
  * 回测区间: 2020-01-01 ~ 至今
  * 选股数量: Top-3,强制分散化
  * V3 动态阈值(短债动量参考)
  * 溢价控制启用(HK/US 10%阈值)

策略实现:
- SimpleRotationStrategy 支持 signal_source/trade_source 分离
  * get_codes() 同时获取信号和交易标的
  * compute_factors() 只使用 signal_source 计算因子
  * _execute_backtest() 使用 trade_source 计算收益
  * 支持跨市场场景(指数信号 → ETF收益)

回测验证:
- 成功运行端到端回测
- 获取 21 个标的(11 signal + 10 trade)
- 平均仓位 84.42%
- ⚠️ 已知问题: Flask API 只返回缓存数据(2026年),需修复

修复项:
- StrategyBase.run() 兼容信号矩阵(移除 'weight' 列假设)
This commit is contained in:
2026-05-24 14:58:41 +08:00
parent 43ce8056f1
commit e6657bd2cc
4 changed files with 264 additions and 52 deletions

View File

@@ -55,41 +55,45 @@ class SimpleRotationStrategy(StrategyBase):
def get_codes(self) -> list:
"""
获取标的列表
获取标的列表(信号标的 + 交易标的)
从配置的资产池中获取所有标的
返回所有需要的数据标的
- signal_source: 用于计算因子和信号
- trade_source: 用于计算收益
"""
codes = []
codes = set()
# 股票资产
if self.config.asset_pools.equity:
codes.extend(self.config.asset_pools.equity.keys())
# 添加所有信号标的
codes.update(self.config.asset_pools.get_signal_codes())
# 商品资产
if self.config.asset_pools.commodity:
codes.extend(self.config.asset_pools.commodity.keys())
# 添加所有交易标的
codes.update(self.config.asset_pools.get_trade_codes())
# 固定收益资产
if self.config.asset_pools.fixed_income:
codes.extend(self.config.asset_pools.fixed_income.keys())
return codes
return list(codes)
def compute_factors(self, data: Dict[str, pd.DataFrame]) -> Dict[str, pd.Series]:
"""
计算动量因子
计算动量因子(只使用信号标的的数据)
Args:
data: 数据字典 {code: DataFrame}
data: 数据字典 {code: DataFrame}(包含 signal_source 和 trade_source
Returns:
因子字典 {code: Series}
因子字典 {signal_source: Series}
"""
factors = {}
for code, df in data.items():
# 只使用信号标的计算因子
signal_codes = self.config.asset_pools.get_signal_codes()
for code in signal_codes:
if code not in data:
print(f" 警告: {code} 数据不存在,跳过")
continue
try:
# 计算动量得分
df = data[code]
# 计算动量得分(使用信号标的的数据)
factor_values = self.momentum.compute(df)
factors[code] = factor_values
except Exception as e:
@@ -173,18 +177,29 @@ class SimpleRotationStrategy(StrategyBase):
"""
执行回测
核心逻辑:
1. 使用 signal_source 计算信号positions 的 columns 是 signal_source
2. 使用 trade_source 计算收益(通过 signal→trade 映射)
3. T+1 执行:今天的信号明天生效
Args:
positions: 仓位 DataFrame
data: 数据字典 {code: DataFrame}
positions: 仓位 DataFramecolumns=signal_source
data: 数据字典 {code: DataFrame}(包含 signal_source 和 trade_source
Returns:
回测结果字典
"""
# 提取收盘价
# 获取信号→交易映射
signal_to_trade = self.config.asset_pools.get_signal_to_trade_mapping()
# 提取交易标的的收盘价
close_prices = {}
for code, df in data.items():
if 'close' in df.columns:
close_prices[code] = df['close']
for signal_code, trade_code in signal_to_trade.items():
if trade_code in data:
# 使用交易标的的数据计算收益
close_prices[signal_code] = data[trade_code]['close']
else:
print(f" 警告: {trade_code} 数据不存在,跳过")
close_df = pd.DataFrame(close_prices)