diff --git a/datasource/universal_fetcher.py b/datasource/universal_fetcher.py index b25bb24..15c1e2f 100644 --- a/datasource/universal_fetcher.py +++ b/datasource/universal_fetcher.py @@ -236,8 +236,13 @@ class UniversalDataFetcher: 溢价率 = (ETF收盘价 - ETF净值) / ETF净值 - 关键:QDII基金净值T+1披露,需要用T日收盘价配T-1日净值 - 集思录做法:价格日期配前一日净值(净值日期滞后一天) + 关键:不同QDII基金净值披露规则不同 + - 部分基金净值当天披露(如日经ETF):价格日期=净值日期 + - 部分基金净值T+1披露(如纳指ETF):价格日期配T-1日净值 + + 集思录做法:根据基金特性选择匹配方式 + - 如果有当天净值数据,优先使用当天净值 + - 如果当天净值不存在,使用T-1日净值 Args: price_df: ETF价格数据(索引为日期) @@ -255,22 +260,41 @@ class UniversalDataFetcher: if nav_index.has_duplicates: nav_df = nav_df[~nav_df.index.duplicated(keep='last')] - # QDII净值T+1披露:T日收盘价配T-1日净值 - # 将净值索引后移一天,使价格日期与前一日净值对齐 + # 优先尝试使用当天净值(如日经ETF) + same_day_dates = price_df.index.intersection(nav_df.index) + + # 对于没有当天净值的日期,使用T-1日净值(如纳指ETF) nav_df_shifted = nav_df.copy() nav_df_shifted.index = nav_df_shifted.index + pd.Timedelta(days=1) + shifted_dates = price_df.index.intersection(nav_df_shifted.index) - # 找到对齐后的共同日期(价格日期) - common_dates = price_df.index.intersection(nav_df_shifted.index) + # 排除已有当天净值的日期 + t1_dates = shifted_dates.difference(same_day_dates) - if len(common_dates) == 0: + premium_data = {} + + # 使用当天净值计算 + if len(same_day_dates) > 0: + close_same = price_df.loc[same_day_dates, 'close'] + nav_same = nav_df.loc[same_day_dates, 'nav'] + for date in same_day_dates: + if pd.notna(close_same.loc[date]) and pd.notna(nav_same.loc[date]): + premium_data[date] = (close_same.loc[date] - nav_same.loc[date]) / nav_same.loc[date] + + # 使用T-1日净值计算(仅用于没有当天净值的日期) + if len(t1_dates) > 0: + close_t1 = price_df.loc[t1_dates, 'close'] + nav_t1 = nav_df_shifted.loc[t1_dates, 'nav'] + for date in t1_dates: + if pd.notna(close_t1.loc[date]) and pd.notna(nav_t1.loc[date]): + premium_data[date] = (close_t1.loc[date] - nav_t1.loc[date]) / nav_t1.loc[date] + + if len(premium_data) == 0: return None - # 计算溢价率:T日收盘价 / T-1日净值(已后移到T日索引) - close_prices = price_df.loc[common_dates, 'close'] - nav_values = nav_df_shifted.loc[common_dates, 'nav'] - - premium = (close_prices - nav_values) / nav_values + # 构建Series并按日期排序 + premium = pd.Series(premium_data) + premium = premium.sort_index() premium = premium.dropna() return premium