tushare
This commit is contained in:
+174
-14
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user