fix(rotation): 修复ETF数据获取逻辑,分别获取指数raw和ETF hfq数据

问题:之前统一使用fetch_indices(adj='raw')导致ETF未使用后复权价格

修复:
- 在GlobalRotationStrategy中覆盖get_data()方法
- 指数数据:调用fetch_indices(adj='raw')获取原始价格
- ETF数据:调用fetch_etf(adj='hfq')获取后复权价格
- 确保指数信号计算和ETF收益计算使用正确的数据源

影响:回测将正确反映ETF的真实收益(包含分红再投资)
This commit is contained in:
2026-05-26 19:54:21 +08:00
parent 7fc1170964
commit d404ddee17

View File

@@ -115,6 +115,68 @@ class GlobalRotationStrategy(StrategyBase):
return list(codes)
def get_data(self) -> Dict[str, pd.DataFrame]:
"""
获取数据(分别获取指数和 ETF使用不同的复权方式
指数数据:使用 raw原始价格用于信号计算
ETF 数据:使用 hfq后复权价格用于收益计算
Returns:
数据字典 {code: DataFrame}
"""
if self._data_fetcher is None:
self._data_fetcher = self._create_data_fetcher()
# 获取信号→交易映射
signal_to_trade = self.config.asset_pools.get_signal_to_trade_mapping()
# 处理 end_date 为 None 的情况(使用今天)
from datetime import date
start = self.config.backtest.start_date
end = self.config.backtest.end_date
if end is None:
end = date.today().strftime('%Y-%m-%d')
data = {}
# 1. 获取指数数据(信号标的,使用 raw
signal_codes = set(self.config.asset_pools.get_signal_codes())
if self.use_dynamic_threshold and self.bond_code:
signal_codes.add(self.bond_code)
if signal_codes:
print(f"\n[数据] 获取 {len(signal_codes)} 只指数数据adj='raw'...")
try:
index_data = self._data_fetcher.fetch_indices(
codes=list(signal_codes),
start=start,
end=end,
adj='raw' # 指数使用原始价格
)
data.update(index_data)
print(f" ✓ 指数数据: {len(index_data)}")
except Exception as e:
print(f" ✗ 指数数据获取失败: {e}")
# 2. 获取 ETF 数据(交易标的,使用 hfq
trade_codes = list(set(signal_to_trade.values()))
if trade_codes:
print(f"\n[数据] 获取 {len(trade_codes)} 只 ETF 数据adj='hfq'...")
try:
etf_data = self._data_fetcher.fetch_etf(
codes=trade_codes,
start=start,
end=end,
adj='hfq' # ETF 使用后复权价格
)
data.update(etf_data)
print(f" ✓ ETF 数据: {len(etf_data)}")
except Exception as e:
print(f" ✗ ETF 数据获取失败: {e}")
return data
def compute_factors(self, data: Dict[str, pd.DataFrame]) -> Dict[str, pd.Series]:
"""
计算动量因子(只使用信号标的的数据)