fix: 修复回测日期对齐问题,优化收益率计算
- 使用对齐后的index_close数据计算日收益率 - 添加日期对齐逻辑确保信号和收益率数据一致 - 修复pivot重复索引问题,使用pivot_table - 修复tushare期货接口调用(futures_daily -> fut_daily) 回测结果: - 最终净值: 0.9435 - 累计收益: -5.65% - 信号日期: 2302天
This commit is contained in:
@@ -200,7 +200,7 @@ class HybridDataSource:
|
|||||||
etf_data_list.append(etf_data[['code', 'close']])
|
etf_data_list.append(etf_data[['code', 'close']])
|
||||||
|
|
||||||
price_count = len(etf_data)
|
price_count = len(etf_data)
|
||||||
nav_count = len(etf_nav) if etf_nav else 0
|
nav_count = len(etf_nav) if etf_nav is not None else 0
|
||||||
|
|
||||||
print(f"✓ 价格{price_count}条 净值{nav_count}条")
|
print(f"✓ 价格{price_count}条 净值{nav_count}条")
|
||||||
else:
|
else:
|
||||||
@@ -214,17 +214,32 @@ class HybridDataSource:
|
|||||||
index_data = None
|
index_data = None
|
||||||
if index_data_list:
|
if index_data_list:
|
||||||
index_data = pd.concat(index_data_list)
|
index_data = pd.concat(index_data_list)
|
||||||
index_data = index_data.pivot(columns='code', values='close')
|
if 'code' in index_data.columns and 'close' in index_data.columns:
|
||||||
|
index_data = index_data.reset_index()
|
||||||
|
if 'index' in index_data.columns:
|
||||||
|
index_data = index_data.rename(columns={'index': 'date'})
|
||||||
|
index_data['date'] = pd.to_datetime(index_data['date']).dt.normalize()
|
||||||
|
index_data = index_data.pivot_table(index='date', columns='code', values='close')
|
||||||
|
|
||||||
etf_data = None
|
etf_data = None
|
||||||
if etf_data_list:
|
if etf_data_list:
|
||||||
etf_data = pd.concat(etf_data_list)
|
etf_data = pd.concat(etf_data_list)
|
||||||
etf_data = etf_data.pivot(columns='code', values='close')
|
if 'code' in etf_data.columns and 'close' in etf_data.columns:
|
||||||
|
etf_data = etf_data.reset_index()
|
||||||
|
if 'index' in etf_data.columns:
|
||||||
|
etf_data = etf_data.rename(columns={'index': 'date'})
|
||||||
|
etf_data['date'] = pd.to_datetime(etf_data['date']).dt.normalize()
|
||||||
|
etf_data = etf_data.pivot_table(index='date', columns='code', values='close')
|
||||||
|
|
||||||
etf_nav_data = None
|
etf_nav_data = None
|
||||||
if etf_nav_data_list:
|
if etf_nav_data_list:
|
||||||
etf_nav_data = pd.concat(etf_nav_data_list)
|
etf_nav_data = pd.concat(etf_nav_data_list)
|
||||||
etf_nav_data = etf_nav_data.pivot(columns='code', values='nav')
|
if 'code' in etf_nav_data.columns and 'nav' in etf_nav_data.columns:
|
||||||
|
etf_nav_data = etf_nav_data.reset_index()
|
||||||
|
if 'index' in etf_nav_data.columns:
|
||||||
|
etf_nav_data = etf_nav_data.rename(columns={'index': 'date'})
|
||||||
|
etf_nav_data['date'] = pd.to_datetime(etf_nav_data['date']).dt.normalize()
|
||||||
|
etf_nav_data = etf_nav_data.pivot_table(index='date', columns='code', values='nav')
|
||||||
|
|
||||||
# 基准数据
|
# 基准数据
|
||||||
benchmark_data = self._tushare.fetch_index(benchmark_code, start_date, end_date)
|
benchmark_data = self._tushare.fetch_index(benchmark_code, start_date, end_date)
|
||||||
|
|||||||
@@ -104,13 +104,14 @@ class TushareSource:
|
|||||||
original_proxy = self._clear_proxy()
|
original_proxy = self._clear_proxy()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import tushare as ts
|
||||||
pro = self._get_pro_api()
|
pro = self._get_pro_api()
|
||||||
|
|
||||||
df = pro.futures_daily(
|
# 使用 fut_daily 接口
|
||||||
|
df = pro.fut_daily(
|
||||||
ts_code=code,
|
ts_code=code,
|
||||||
start_date=start_date.replace("-", ""),
|
start_date=start_date.replace("-", ""),
|
||||||
end_date=end_date.replace("-", ""),
|
end_date=end_date.replace("-", "")
|
||||||
exchange=''
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if df is None or len(df) == 0:
|
if df is None or len(df) == 0:
|
||||||
|
|||||||
@@ -197,15 +197,34 @@ class RotationStrategy(StrategyBase):
|
|||||||
|
|
||||||
# 4. 执行回测
|
# 4. 执行回测
|
||||||
print("\n执行回测...")
|
print("\n执行回测...")
|
||||||
|
|
||||||
|
# 使用对齐后的指数收盘价数据获取日期基准
|
||||||
|
index_close = data.get('index_close')
|
||||||
|
|
||||||
|
# 计算日收益率(使用对齐后的收盘价数据)
|
||||||
|
if index_close is not None and not index_close.empty:
|
||||||
|
returns_df = index_close.pct_change()
|
||||||
|
returns_df.columns = [f'日收益率_{col}' for col in returns_df.columns]
|
||||||
|
else:
|
||||||
|
# 回退到原始数据
|
||||||
returns_data = {}
|
returns_data = {}
|
||||||
first_code = valid_codes[0]
|
|
||||||
for code in valid_codes:
|
for code in valid_codes:
|
||||||
|
if code in index_data:
|
||||||
df = index_data[code]
|
df = index_data[code]
|
||||||
returns_data[f'日收益率_{code}'] = df['close'].pct_change()
|
returns_data[f'日收益率_{code}'] = df['close'].pct_change()
|
||||||
|
|
||||||
returns_df = pd.DataFrame(returns_data)
|
returns_df = pd.DataFrame(returns_data)
|
||||||
|
|
||||||
|
if valid_codes:
|
||||||
|
first_code = valid_codes[0]
|
||||||
returns_df.index = index_data[first_code].index
|
returns_df.index = index_data[first_code].index
|
||||||
|
|
||||||
|
# 确保信号和收益率数据日期对齐
|
||||||
|
common_dates = signals.index.intersection(returns_df.index)
|
||||||
|
signals = signals.loc[common_dates]
|
||||||
|
returns_df = returns_df.loc[common_dates]
|
||||||
|
|
||||||
|
print(f" 对齐后日期: {len(common_dates)} 天")
|
||||||
|
|
||||||
executor = BacktestExecutor(
|
executor = BacktestExecutor(
|
||||||
initial_capital=100000,
|
initial_capital=100000,
|
||||||
trade_cost=self.trade_cost,
|
trade_cost=self.trade_cost,
|
||||||
|
|||||||
Reference in New Issue
Block a user