skills/financial-report-writing/scripts/data_fetcher.py
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

266 lines
8.1 KiB
Python
Raw 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.

"""
Wind 数据获取模块
提供统一的接口获取股票历史数据、财务数据和技术指标
"""
from WindPy import w
import pandas as pd
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Tuple
class WindDataFetcher:
"""Wind数据获取器"""
def __init__(self):
"""初始化Wind连接"""
self._start()
def _start(self):
"""启动Wind连接"""
result = w.start()
if result.ErrorCode != 0:
raise ConnectionError(f"Wind连接失败: {result.Data}")
print("Wind连接成功")
def _stop(self):
"""关闭Wind连接"""
w.stop()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._stop()
@staticmethod
def _handle_wsd_result(result, field_names):
"""处理wsd返回结果"""
if result.ErrorCode != 0:
raise ValueError(f"数据获取失败: {result.Data}")
# 转换为DataFrame
data_dict = dict(zip(field_names, result.Data))
df = pd.DataFrame(data_dict, columns=field_names)
df['date'] = result.Times
return df
def get_daily_data(self,
stock_code: str,
start_date: str,
end_date: str,
fields: List[str]) -> pd.DataFrame:
"""
获取日线数据
Args:
stock_code: 股票代码,如 '300866.SZ'
start_date: 开始日期,格式 'YYYY-MM-DD'
end_date: 结束日期,格式 'YYYY-MM-DD'
fields: 数据字段列表
Returns:
DataFrame包含日期和指定字段的数据
"""
result = w.wsd(stock_code, fields, start_date, end_date)
return self._handle_wsd_result(result, fields)
def get_stock_basic(self, stock_code: str) -> Dict:
"""
获取股票基本信息
Args:
stock_code: 股票代码
Returns:
包含股票名称、行业、上市日期等信息的字典
"""
fields = ['sec_name', 'industry', 'ipo_date', 'list_date', 'trade_status']
result = w.wss(stock_code, fields)
if result.ErrorCode != 0:
raise ValueError(f"基本信息获取失败: {result.Data}")
return dict(zip(fields, result.Data[0]))
def get_technical_indicators(self,
stock_code: str,
start_date: str,
end_date: str) -> pd.DataFrame:
"""
获取技术指标数据
Args:
stock_code: 股票代码
start_date: 开始日期
end_date: 结束日期
Returns:
DataFrame包含MA、成交量等技术指标
"""
fields = [
'close', 'open', 'high', 'low', 'volume', 'amt', 'pct_chg',
'ma5', 'ma10', 'ma20', 'ma60',
'pe_ttm', 'pb_lf', 'ps_ttm', 'pcf_ncf_ttm'
]
return self.get_daily_data(stock_code, start_date, end_date, fields)
def get_financial_data(self,
stock_code: str,
report_date: str = '') -> Dict:
"""
获取财务数据
Args:
stock_code: 股票代码
report_date: 报告期,格式 'YYYYMMDD',默认最新
Returns:
包含ROE、营收、净利润等财务指标的字典
"""
fields = [
'roe_wgt', 'roa2', 'net_profit_to_profit', 'total_revenue_ps',
'profit_to_gr', 'ebit_ps', 'assets_to_eqt', 'debt_to_assets',
'current_ratio', 'quick_ratio', 'op_income_to_revenue'
]
result = w.wss(stock_code, fields, 'rptDate={}'.format(report_date))
if result.ErrorCode != 0:
raise ValueError(f"财务数据获取失败: {result.Data}")
return dict(zip(fields, result.Data[0]))
def get_weekly_data(self,
stock_code: str,
start_date: str,
end_date: str,
fields: List[str] = None) -> pd.DataFrame:
"""
获取并汇总周线数据
Args:
stock_code: 股票代码
start_date: 开始日期
end_date: 结束日期
fields: 数据字段默认为基本OHLCV
Returns:
DataFrame包含周线数据
"""
if fields is None:
fields = ['close', 'open', 'high', 'low', 'volume', 'amt', 'pct_chg']
# 获取日线数据
df_daily = self.get_daily_data(stock_code, start_date, end_date, fields)
# 转换为周线数据
df_daily.set_index('date', inplace=True)
df_weekly = df_daily.resample('W').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'sum',
'amt': 'sum',
'pct_chg': 'sum' # 周涨跌幅近似为日涨跌幅之和
}).dropna()
df_weekly.reset_index(inplace=True)
return df_weekly
def get_stock_list_by_sector(self, sector_name: str, limit: int = 10) -> List[str]:
"""
获取某行业的龙头股票列表
Args:
sector_name: 行业名称
limit: 返回数量
Returns:
股票代码列表
"""
# 使用Wind的行业板块数据
fields = ['sec_name', 'ipo_date']
# 这里简化处理实际需要根据Wind板块接口调整
print(f"注意: 行业股票列表获取需要根据实际Wind板块接口实现")
return []
def calculate_volatility(self, df: pd.DataFrame, annualized: bool = True) -> float:
"""
计算波动率
Args:
df: 包含收盘价的DataFrame
annualized: 是否年化
Returns:
波动率(百分比)
"""
returns = df['close'].pct_change().dropna()
volatility = returns.std()
if annualized:
# 日数据年化系数sqrt(252)周数据年化系数sqrt(52)
n = len(df)
if n > 250: # 日线数据
volatility *= 252 ** 0.5
else: # 周线数据
volatility *= 52 ** 0.5
return volatility * 100 # 转换为百分比
# 便捷函数
def fetch_stock_report_data(stock_code: str,
days_back: int = 90,
weekly: bool = True) -> Tuple[pd.DataFrame, Dict]:
"""
获取股票报告所需的数据
Args:
stock_code: 股票代码
days_back: 获取多少天的数据
weekly: 是否为周线数据
Returns:
(price_df, financial_dict) 价格数据和财务数据
"""
end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=days_back)).strftime('%Y-%m-%d')
with WindDataFetcher() as fetcher:
# 获取价格数据
if weekly:
price_df = fetcher.get_weekly_data(stock_code, start_date, end_date)
else:
price_df = fetcher.get_technical_indicators(stock_code, start_date, end_date)
# 获取财务数据
financial_data = fetcher.get_financial_data(stock_code)
# 获取基本信息
basic_info = fetcher.get_stock_basic(stock_code)
# 合并财务信息
financial_data.update(basic_info)
return price_df, financial_data
if __name__ == '__main__':
# 测试代码
with WindDataFetcher() as fetcher:
# 示例:获取某股票的数据
code = '300866.SZ'
start = '2025-01-01'
end = '2026-02-06'
print(f"获取 {code} 的日线数据...")
df = fetcher.get_technical_indicators(code, start, end)
print(df.tail())
print(f"\n获取 {code} 的基本信息...")
info = fetcher.get_stock_basic(code)
print(info)