This commit is contained in:
songzhuoyuan
2026-08-11 20:41:31 +08:00
parent 2ce13e5bae
commit befdc32aea
24 changed files with 1311 additions and 530 deletions
+174 -14
View File
@@ -9,6 +9,8 @@ import io
import warnings
from datetime import datetime
import akshare as ak
from http_timeout import call_with_timeout
from data_source_manager import data_source_manager
warnings.filterwarnings('ignore')
@@ -67,26 +69,34 @@ class QuarterlyReportDataFetcher:
try:
print(f"📊 正在获取 {symbol} 的季报数据...")
# 获取利润表
income_data = self._get_income_statement(symbol)
# 获取利润表(优先tushare,失败时回退akshare
income_data = self._get_income_statement_from_tushare(symbol)
if income_data is None:
income_data = self._get_income_statement(symbol)
if income_data:
data["income_statement"] = income_data
print(f" ✓ 成功获取 {len(income_data.get('data', []))} 期利润表数据")
# 获取资产负债表
balance_data = self._get_balance_sheet(symbol)
# 获取资产负债表(优先tushare,失败时回退akshare
balance_data = self._get_balance_sheet_from_tushare(symbol)
if balance_data is None:
balance_data = self._get_balance_sheet(symbol)
if balance_data:
data["balance_sheet"] = balance_data
print(f" ✓ 成功获取 {len(balance_data.get('data', []))} 期资产负债表数据")
# 获取现金流量表
cash_flow_data = self._get_cash_flow(symbol)
# 获取现金流量表(优先tushare,失败时回退akshare
cash_flow_data = self._get_cash_flow_from_tushare(symbol)
if cash_flow_data is None:
cash_flow_data = self._get_cash_flow(symbol)
if cash_flow_data:
data["cash_flow"] = cash_flow_data
print(f" ✓ 成功获取 {len(cash_flow_data.get('data', []))} 期现金流量表数据")
# 获取财务指标
indicators_data = self._get_financial_indicators(symbol)
# 获取财务指标(优先tushare,失败时回退akshare
indicators_data = self._get_financial_indicators_from_tushare(symbol)
if indicators_data is None:
indicators_data = self._get_financial_indicators(symbol)
if indicators_data:
data["financial_indicators"] = indicators_data
print(f" ✓ 成功获取 {len(indicators_data.get('data', []))} 期财务指标数据")
@@ -108,11 +118,160 @@ class QuarterlyReportDataFetcher:
"""判断是否为中国股票"""
return symbol.isdigit() and len(symbol) == 6
def _convert_ts_records(self, df, field_map, periods):
"""将tushare返回的财务表转换为统一的记录结构"""
try:
data_list = []
for _, row in df.head(periods).iterrows():
item = {}
for ch_name, ts_name in field_map.items():
if ts_name in df.columns:
value = row.get(ts_name)
if value is None or (isinstance(value, float) and pd.isna(value)):
continue
try:
item[ch_name] = str(value)
except:
item[ch_name] = "N/A"
if item:
data_list.append(item)
return {
"data": data_list,
"periods": len(data_list),
"columns": list(field_map.keys()),
"query_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
except Exception as e:
print(f" 转换tushare财务数据异常: {e}")
return None
def _get_income_statement_from_tushare(self, symbol):
"""从tushare获取利润表(优先数据源)"""
try:
if not data_source_manager.tushare_available:
return None
ts_code = data_source_manager._convert_to_ts_code(symbol)
df = data_source_manager.tushare_api.income(ts_code=ts_code)
if df is None or df.empty:
return None
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
field_map = {
'报告期': 'end_date',
'营业总收入': 'total_revenue',
'营业收入': 'revenue',
'营业总成本': 'total_operate_cost',
'营业利润': 'operate_profit',
'利润总额': 'total_profit',
'净利润': 'n_income',
'归属于母公司所有者的净利润': 'n_income_attr_p',
'基本每股收益': 'basic_eps',
'稀释每股收益': 'diluted_eps',
'销售费用': 'sell_exp',
'管理费用': 'admin_exp',
'财务费用': 'fin_exp',
'研发费用': 'rd_exp',
}
result = self._convert_ts_records(df, field_map, self.periods)
if result and result.get('periods'):
print(f" ✓ tushare成功获取 {result['periods']} 期利润表数据")
return result
except Exception as e:
print(f" tushare获取利润表异常: {e}")
return None
def _get_balance_sheet_from_tushare(self, symbol):
"""从tushare获取资产负债表(优先数据源)"""
try:
if not data_source_manager.tushare_available:
return None
ts_code = data_source_manager._convert_to_ts_code(symbol)
df = data_source_manager.tushare_api.balancesheet(ts_code=ts_code)
if df is None or df.empty:
return None
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
field_map = {
'报告期': 'end_date',
'资产总计': 'total_assets',
'流动资产合计': 'total_cur_assets',
'非流动资产合计': 'total_ncur_assets',
'负债合计': 'total_liab',
'流动负债合计': 'total_cur_liab',
'非流动负债合计': 'total_ncur_liab',
'所有者权益合计': 'total_hldr_eqy_inc_min_int',
'归属于母公司股东权益合计': 'total_hldr_eqy_exc_min_int',
}
result = self._convert_ts_records(df, field_map, self.periods)
if result and result.get('periods'):
print(f" ✓ tushare成功获取 {result['periods']} 期资产负债表数据")
return result
except Exception as e:
print(f" tushare获取资产负债表异常: {e}")
return None
def _get_cash_flow_from_tushare(self, symbol):
"""从tushare获取现金流量表(优先数据源)"""
try:
if not data_source_manager.tushare_available:
return None
ts_code = data_source_manager._convert_to_ts_code(symbol)
df = data_source_manager.tushare_api.cashflow(ts_code=ts_code)
if df is None or df.empty:
return None
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
field_map = {
'报告期': 'end_date',
'经营活动产生的现金流量净额': 'n_cashflow_act',
'投资活动产生的现金流量净额': 'n_cashflow_inv_act',
'筹资活动产生的现金流量净额': 'n_cash_flows_fnc_act',
}
result = self._convert_ts_records(df, field_map, self.periods)
if result and result.get('periods'):
print(f" ✓ tushare成功获取 {result['periods']} 期现金流量表数据")
return result
except Exception as e:
print(f" tushare获取现金流量表异常: {e}")
return None
def _get_financial_indicators_from_tushare(self, symbol):
"""从tushare获取财务指标(优先数据源)"""
try:
if not data_source_manager.tushare_available:
return None
ts_code = data_source_manager._convert_to_ts_code(symbol)
df = data_source_manager.tushare_api.fina_indicator(ts_code=ts_code)
if df is None or df.empty:
return None
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
field_map = {
'报告期': 'end_date',
'净资产收益率': 'roe',
'总资产净利率': 'roa',
'销售净利率': 'netprofit_margin',
'销售毛利率': 'grossprofit_margin',
'资产负债率': 'debt_to_assets',
'流动比率': 'current_ratio',
'速动比率': 'quick_ratio',
'应收账款周转率': 'ar_turnover',
'存货周转率': 'inventory_turnover',
'总资产周转率': 'assets_turnover',
'每股收益': 'eps',
'每股净资产': 'bps',
'每股经营现金流': 'cfps',
}
result = self._convert_ts_records(df, field_map, self.periods)
if result and result.get('periods'):
print(f" ✓ tushare成功获取 {result['periods']} 期财务指标数据")
return result
except Exception as e:
print(f" tushare获取财务指标异常: {e}")
return None
def _get_income_statement(self, symbol):
"""获取利润表数据"""
try:
# stock_financial_report_sina - 新浪财经季度利润表
df = ak.stock_financial_report_sina(stock=symbol, symbol="利润表")
df = call_with_timeout(ak.stock_financial_report_sina, timeout=25,
stock=symbol, symbol="利润表")
if df is None or df.empty:
print(f" 未找到利润表数据")
@@ -151,7 +310,8 @@ class QuarterlyReportDataFetcher:
"""获取资产负债表数据"""
try:
# stock_financial_report_sina - 新浪财经季度资产负债表
df = ak.stock_financial_report_sina(stock=symbol, symbol="资产负债表")
df = call_with_timeout(ak.stock_financial_report_sina, timeout=25,
stock=symbol, symbol="资产负债表")
if df is None or df.empty:
print(f" 未找到资产负债表数据")
@@ -190,7 +350,8 @@ class QuarterlyReportDataFetcher:
"""获取现金流量表数据"""
try:
# stock_financial_report_sina - 新浪财经季度现金流量表
df = ak.stock_financial_report_sina(stock=symbol, symbol="现金流量表")
df = call_with_timeout(ak.stock_financial_report_sina, timeout=25,
stock=symbol, symbol="现金流量表")
if df is None or df.empty:
print(f" 未找到现金流量表数据")
@@ -229,7 +390,7 @@ class QuarterlyReportDataFetcher:
"""获取财务指标数据"""
try:
# 使用stock_financial_abstract替代已失效的stock_financial_analysis_indicator
df = ak.stock_financial_abstract(symbol=symbol)
df = call_with_timeout(ak.stock_financial_abstract, timeout=25, symbol=symbol)
if df is None or df.empty:
print(f" 未找到财务指标数据")
@@ -292,7 +453,7 @@ class QuarterlyReportDataFetcher:
text_parts = []
text_parts.append(f"""
【季度财务报告数据 - akshare数据源
【季度财务报告数据 - tushare/akshare自动切换
股票代码:{data.get('symbol', 'N/A')}
数据期数:最近{self.periods}期季报
@@ -423,4 +584,3 @@ if __name__ == "__main__":
print(f"\n获取失败: {data.get('error', '未知错误')}")
print("\n")