Files
simonandClaude Opus 4.7 271a9343a5 Initial commit: cc-cursor 全链路量化研究平台
7 Sprints 全部完成:
  Sprint 0: 基础设施 (DataManager + MariaDB)
  Sprint 1: 因子引擎 (34因子/12分类)
  Sprint 2: VectorBT 回测 (5策略+截面)
  Sprint 3: Optuna 优化 (+Walk-Forward)
  Sprint 4: ML 模型 (LightGBM+CatBoost)
  Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐)
  Sprint 6: Agent 系统 (4Agent+日报.md/.html)

生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping,
  save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复,
  RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4,
  CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化,
  indexDatas API修正

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:59:05 +08:00

135 lines
6.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pandas as pd
import numpy as np
from datetime import datetime
try:
# 尝试相对导入(作为包的一部分)
from .config import TS_TOKEN, START_DATE, END_DATE, PRECISION_CONFIG
from .stock_utils import *
except (ImportError, SystemError):
# 失败则使用绝对导入(直接运行脚本)
from config import TS_TOKEN, START_DATE, END_DATE, PRECISION_CONFIG
from stock_utils import *
'''
给定个股代码和起止日期:
1. 调用tushare接口查询个股分红数据,接口文档:https://tushare.pro/document/2?doc_id=103 查询日期范围内的所有分红记录
2. 返回调整后的分红记录,包含以下字段:
- ts_code: 股票代码
- end_date: 分红年度
- ann_date: 公告日期
- ex_date: 除权除息日
- cash_div_tax: 每股现金分红(含税)
- div_proc: 实施进度,仅筛选 div_proc='实施'的记录
3. 调用tushare 的trade_cal接口,查询起止日期内的所有交易日
4. 建一个新的df,在起止日期内填充,规则:
- 下列运算过程中将起始时间往前推 gap_days,赋值360天。运算结束后,截取起止时间内的数据
- trade_date: 以交易日历为准,填充所有交易日。
- 填入当ex_date=trade_date的日期,填入:ex_date,cash_div_tax,其余日期ex_date留空,cash_div_tax 置 0
- cash_div_year:逐行计算填入,以trade_date往前计算过去gap_days天内的cash_div_tax之和
5. 调用tushare的个股日线行情接口:https://tushare.pro/document/2?doc_id=27,查询起止日期内的个股日线行情数据,包含以下字段:
- ts_code: 股票代码
- trade_date: 交易日期
- close: 收盘价
6. 将分红数据和日线行情数据按交易日期合并,得到最终结果,包含以下字段:
- ts_code: 股票代码
- trade_date: 交易日期
- close: 收盘价
- cach_div_tax: 每股现金分红(含税)
- cach_div_year: 每股现金TTM年度分红(含税)
- div_yield: 股息率,计算公式为 (cach_div_year / close) * 100,保留PRECISION_CONFIG位小数,如果cash_div_year为0则div_yield也为0
7. 返回最终结果的DataFrame
'''
# 初始化tushare
from .data_source import get_tushare_pro
pro = get_tushare_pro()
def get_dividend_yield(ts_code, start_date, end_date):
# 检查日期范围是否超过当前日期,若超过则调整为当前日期
today = datetime.now().strftime("%Y%m%d")
if end_date > today:
end_date = today
if start_date > today:
start_date = today
gap_days = 360 # 定义TTM计算窗口期为360天
# 1. 获取分红数据:查询指定股票的分红信息
df_div = pro.dividend(ts_code=ts_code, fields='ts_code,end_date,ann_date,ex_date,cash_div_tax,div_proc')
# 筛选实施状态的分红记录
df_div = df_div[df_div['div_proc'] == '实施']
print(f"分红原始数据:\n {df_div.to_string()} ")
# 2. 获取交易日历:查询指定日期范围内的开盘日
df_cal = pro.trade_cal(exchange='', start_date=start_date, end_date=end_date, is_open='1')
trade_dates = df_cal['cal_date'].tolist()
# 3. 创建基础DataFrame:将计算窗口期向前扩展gap_days天
extended_start = pd.to_datetime(start_date) - pd.Timedelta(days=gap_days)
extended_start_str = extended_start.strftime('%Y%m%d')
# 获取扩展后的交易日历
df_cal_ext = pro.trade_cal(exchange='', start_date=extended_start_str, end_date=end_date, is_open='1')
df_base = pd.DataFrame({'trade_date': df_cal_ext['cal_date']})
# 4. 合并分红数据:将分红信息按除权除息日合并到基础交易日历
df_base = df_base.merge(df_div[['ex_date', 'cash_div_tax']],
left_on='trade_date', right_on='ex_date', how='left')
# 填充空值:无分红日期现金分红设为0
df_base['cash_div_tax'] = df_base['cash_div_tax'].fillna(0)
# 5. 计算TTM年度分红:滚动计算过去gap_days天的现金分红总和
# 修正滚动窗口计算:使用固定窗口大小,确保在窗口期内正确累加
df_base['cash_div_year'] = df_base['cash_div_tax'].rolling(window=gap_days, min_periods=0).sum()
print(f"分红填充数据S1\n {df_base.to_string()} ")
# 6. 截取指定日期范围:保留原始查询日期范围内的数据
df_base = df_base[df_base['trade_date'] >= start_date]
print(f"分红填充数据S2\n {df_base.to_string()} ")
# 7. 获取日线行情数据:查询指定股票的日线收盘价
df_daily = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date,
fields='ts_code,trade_date,close')
# 8. 合并数据:将行情数据与分红数据按交易日合并
result = df_base.merge(df_daily, on='trade_date', how='left')
# 填充股票代码:确保所有行都有股票代码
result['ts_code'] = result['ts_code'].fillna(ts_code)
# 9. 计算股息率:当TTM分红>0时计算(分红/收盘价)*100,否则为0
result['div_yield'] = np.where(
result['cash_div_year'] > 0,
(result['cash_div_year'] / result['close']) * 100,
0
)
# 精度处理:保留配置指定的小数位数
result['div_yield'] = result['div_yield'].round(PRECISION_CONFIG)
# 10. 整理列顺序:选择并排列最终输出的列
result = result[['ts_code', 'trade_date', 'close', 'cash_div_tax', 'cash_div_year', 'div_yield']]
return result
if __name__ == "__main__":
# 测试代码
test_code = "600900.SH"
test_start = "20230101"
test_end = "20251231"
try:
result = get_dividend_yield(test_code, test_start, test_end)
print(f"股票 {test_code} 的分红股息率数据:")
print(result.head(100))
print(f"\n数据形状: {result.shape}")
print(f"\n数据列名: {result.columns.tolist()}")
# 检查是否有分红数据
if not result.empty:
print(f"\n股息率统计:")
print(result['div_yield'].describe())
else:
print("未找到分红数据")
except Exception as e:
print(f"测试过程中出现错误: {e}")