From a7a4a6915339eeec839cbe34664c70574eb10a3b Mon Sep 17 00:00:00 2001 From: aszerW Date: Tue, 12 May 2026 00:12:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9B=9E=E6=B5=8B?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E5=AF=B9=E9=BD=90=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=94=B6=E7=9B=8A=E7=8E=87=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用对齐后的index_close数据计算日收益率 - 添加日期对齐逻辑确保信号和收益率数据一致 - 修复pivot重复索引问题,使用pivot_table - 修复tushare期货接口调用(futures_daily -> fut_daily) 回测结果: - 最终净值: 0.9435 - 累计收益: -5.65% - 信号日期: 2302天 --- datasource/hybrid_source.py | 23 +++++++++++++++++++---- datasource/tushare_source.py | 7 ++++--- strategies/rotation/strategy.py | 33 ++++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/datasource/hybrid_source.py b/datasource/hybrid_source.py index 0be74d0..be411a7 100644 --- a/datasource/hybrid_source.py +++ b/datasource/hybrid_source.py @@ -200,7 +200,7 @@ class HybridDataSource: etf_data_list.append(etf_data[['code', 'close']]) 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}条") else: @@ -214,17 +214,32 @@ class HybridDataSource: index_data = None if 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 if 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 if 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) diff --git a/datasource/tushare_source.py b/datasource/tushare_source.py index 71551c4..0ba9ce4 100644 --- a/datasource/tushare_source.py +++ b/datasource/tushare_source.py @@ -104,13 +104,14 @@ class TushareSource: original_proxy = self._clear_proxy() try: + import tushare as ts pro = self._get_pro_api() - df = pro.futures_daily( + # 使用 fut_daily 接口 + df = pro.fut_daily( ts_code=code, start_date=start_date.replace("-", ""), - end_date=end_date.replace("-", ""), - exchange='' + end_date=end_date.replace("-", "") ) if df is None or len(df) == 0: diff --git a/strategies/rotation/strategy.py b/strategies/rotation/strategy.py index ad86c44..a475e4e 100644 --- a/strategies/rotation/strategy.py +++ b/strategies/rotation/strategy.py @@ -197,14 +197,33 @@ class RotationStrategy(StrategyBase): # 4. 执行回测 print("\n执行回测...") - returns_data = {} - first_code = valid_codes[0] - for code in valid_codes: - df = index_data[code] - returns_data[f'日收益率_{code}'] = df['close'].pct_change() - returns_df = pd.DataFrame(returns_data) - returns_df.index = index_data[first_code].index + # 使用对齐后的指数收盘价数据获取日期基准 + 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 = {} + for code in valid_codes: + if code in index_data: + df = index_data[code] + returns_data[f'日收益率_{code}'] = df['close'].pct_change() + returns_df = pd.DataFrame(returns_data) + + if valid_codes: + first_code = valid_codes[0] + 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( initial_capital=100000,