""" ⚠️ 已废弃 — 2026-06-05 AkShare 股息率数据获取功能已归一化到 data_source.py。 如需 AkShare 日线数据,请使用: from .data_source import get_daily df = get_daily(ts_code, start, end, source='akshare') 本文件保留仅用于向后兼容,所有公开函数委托给统一数据源。 """ import pandas as pd import numpy as np from datetime import datetime, timedelta from .data_source import get_daily from .stock_utils import tscodeCheck, date_format_correction from .smoothBrush import smooth_dataframe_brush GAP_DAYS = 360 # TTM 计算窗口 def _parse_tscode(tscode: str) -> tuple: """将 tscode(如 000001.SZ)转为 akshare 格式的 symbol 和 market""" code = tscode.split('.')[0] suffix = tscode.split('.')[1].lower() market_map = {'sz': 'sz', 'sh': 'sh', 'bj': 'bj'} return code, market_map.get(suffix, suffix) def _fetch_daily_price(symbol: str, start_date: str, end_date: str) -> pd.DataFrame: """通过 akshare 获取前复权日线行情""" df = ak.stock_zh_a_hist( symbol=symbol, period='daily', start_date=start_date, end_date=end_date, adjust='qfq' ) if df.empty: return pd.DataFrame() df = df.rename(columns={ '日期': 'trade_date', '开盘': 'open', '收盘': 'close', '最高': 'high', '最低': 'low', '成交量': 'vol', '成交额': 'amount', '换手率': 'turnover_rate', }) df['trade_date'] = df['trade_date'].astype(str).str.replace('-', '') return df def _fetch_dividends(symbol: str, market: str) -> pd.DataFrame: """通过 akshare 获取历史分红记录""" try: df = ak.stock_dividend_cninfo(stock=symbol, symbol=market + symbol) except Exception: return pd.DataFrame() if df.empty: return pd.DataFrame() # 列名映射(akshare 返回中文列名) col_map = { '除权除息日': 'ex_date', '每股派息': 'cash_div_tax', } df = df.rename(columns={k: v for k, v in col_map.items() if k in df.columns}) if 'ex_date' not in df.columns or 'cash_div_tax' not in df.columns: return pd.DataFrame() df['ex_date'] = df['ex_date'].astype(str).str.replace('-', '').str[:8] df['cash_div_tax'] = pd.to_numeric(df['cash_div_tax'], errors='coerce').fillna(0) return df[['ex_date', 'cash_div_tax']] def get_akshare_dividend_yield(tscode: str, start_date: str = None, end_date: str = None) -> pd.DataFrame: """ 通过 akshare 获取股价和分红数据,计算股息率。 Args: tscode: 股票代码,如 '000001.SZ' start_date: 起始日期 yyyyMMdd end_date: 结束日期 yyyyMMdd Returns: DataFrame: ts_code, trade_date, close, cash_div_tax, cash_div_year, div_yield """ tscode = tscodeCheck(tscode) today = datetime.now().strftime('%Y%m%d') if start_date: start_date = date_format_correction(start_date) else: start_date = '20200101' if end_date: end_date = date_format_correction(end_date) else: end_date = today if end_date > today: end_date = today symbol, market = _parse_tscode(tscode) # 1. 获取日线行情(运算时起始日期往前推 GAP_DAYS) calc_start = datetime.strptime(start_date, '%Y%m%d') - timedelta(days=GAP_DAYS) calc_start_str = calc_start.strftime('%Y%m%d') df_price = _fetch_daily_price(symbol, calc_start_str, end_date) if df_price.empty: return pd.DataFrame() df_price = df_price.sort_values('trade_date').reset_index(drop=True) # 2. 获取分红数据 df_div = _fetch_dividends(symbol, market) # 3. 计算 TTM 分红序列 if df_div.empty: df_price['cash_div_tax'] = 0.0 df_price['cash_div_year'] = 0.0 else: # 将分红按 ex_date 合并到交易日历 df_div['ex_date'] = pd.to_datetime(df_div['ex_date'], format='%Y%m%d') df_price['trade_date_dt'] = pd.to_datetime(df_price['trade_date'], format='%Y%m%d') # 按日期合并 div_dict = df_div.set_index('ex_date')['cash_div_tax'].to_dict() def calc_ttm_div(trade_dt): window_start = trade_dt - timedelta(days=GAP_DAYS) total = 0.0 for ex_dt, cash in div_dict.items(): if window_start < ex_dt <= trade_dt: total += cash return total cash_div_tax_list = [] cash_div_year_list = [] for _, row in df_price.iterrows(): td = row['trade_date_dt'] cash = div_dict.get(td, 0.0) cash_div_tax_list.append(cash) cash_div_year_list.append(calc_ttm_div(td)) df_price['cash_div_tax'] = cash_div_tax_list df_price['cash_div_year'] = cash_div_year_list df_price = df_price.drop(columns=['trade_date_dt']) # 4. 毛刺平滑 df_price = smooth_dataframe_brush( df_price, target_columns=['cash_div_year'], window_size=31, threshold_factor=0.5, max_brush_length=15 ) # 5. 截取请求的时间范围 df_result = df_price[ (df_price['trade_date'] >= start_date) & (df_price['trade_date'] <= end_date) ].copy() # 6. 计算股息率 df_result['div_yield'] = 0.0 mask = (df_result['close'] > 0) & (df_result['cash_div_year'] > 0) df_result.loc[mask, 'div_yield'] = ( (df_result.loc[mask, 'cash_div_year'] / df_result.loc[mask, 'close']) * 100 ).round(4) df_result['ts_code'] = tscode final_cols = ['ts_code', 'trade_date', 'close', 'cash_div_tax', 'cash_div_year', 'div_yield'] return df_result[final_cols].reset_index(drop=True)