tushare
This commit is contained in:
+330
-110
@@ -8,10 +8,46 @@ import requests
|
||||
import json
|
||||
import pywencai
|
||||
from data_source_manager import data_source_manager
|
||||
from http_timeout import call_with_timeout
|
||||
|
||||
class StockDataFetcher:
|
||||
"""股票数据获取类"""
|
||||
|
||||
# tushare财务字段 -> 中文映射(供AI分析展示)
|
||||
INCOME_TS_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',
|
||||
}
|
||||
BALANCE_TS_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',
|
||||
}
|
||||
CASHFLOW_TS_MAP = {
|
||||
'报告期': 'end_date',
|
||||
'经营活动产生的现金流量净额': 'n_cashflow_act',
|
||||
'投资活动产生的现金流量净额': 'n_cashflow_inv_act',
|
||||
'筹资活动产生的现金流量净额': 'n_cash_flows_fnc_act',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.data = None
|
||||
self.info = None
|
||||
@@ -89,57 +125,70 @@ class StockDataFetcher:
|
||||
if basic_info:
|
||||
info.update(basic_info)
|
||||
|
||||
# 方法1: 尝试获取个股详细信息(akshare)
|
||||
try:
|
||||
stock_info = ak.stock_individual_info_em(symbol=symbol)
|
||||
if stock_info is not None and not stock_info.empty:
|
||||
for _, row in stock_info.iterrows():
|
||||
key = row['item']
|
||||
value = row['value']
|
||||
|
||||
if key == '股票简称':
|
||||
info['name'] = value
|
||||
elif key == '总市值':
|
||||
try:
|
||||
if value and value != '-':
|
||||
info['market_cap'] = float(value)
|
||||
except:
|
||||
pass
|
||||
elif key == '市盈率-动态':
|
||||
try:
|
||||
if value and value != '-':
|
||||
pe_value = float(value)
|
||||
if 0 < pe_value <= 1000:
|
||||
info['pe_ratio'] = pe_value
|
||||
except:
|
||||
pass
|
||||
elif key == '市净率':
|
||||
try:
|
||||
if value and value != '-':
|
||||
pb_value = float(value)
|
||||
if 0 < pb_value <= 100:
|
||||
info['pb_ratio'] = pb_value
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"[Akshare] 获取个股详细信息失败: {e}")
|
||||
# 如果akshare失败,尝试从tushare获取
|
||||
if self.data_source_manager.tushare_available and info['name'] == '未知':
|
||||
print(f"[Tushare] 尝试获取基本信息(tushare)...")
|
||||
# 方法1: 获取详细估值信息(优先tushare,失败时回退akshare)
|
||||
if (info.get('name') == '未知' or info.get('pe_ratio') == 'N/A' or
|
||||
info.get('pb_ratio') == 'N/A' or info.get('market_cap') == 'N/A'):
|
||||
# 优先使用tushare daily_basic(一次获取PE/PB/市值)
|
||||
if self.data_source_manager.tushare_available:
|
||||
try:
|
||||
print(f"[Tushare] 正在获取 {symbol} 的估值信息(主要数据源)...")
|
||||
ts_code = self.data_source_manager._convert_to_ts_code(symbol)
|
||||
df = self.data_source_manager.tushare_api.daily_basic(
|
||||
ts_code=ts_code,
|
||||
trade_date=datetime.now().strftime('%Y%m%d')
|
||||
start_date=(datetime.now() - timedelta(days=10)).strftime('%Y%m%d'),
|
||||
end_date=datetime.now().strftime('%Y%m%d')
|
||||
)
|
||||
if df is not None and not df.empty:
|
||||
row = df.iloc[0]
|
||||
info['pe_ratio'] = row.get('pe', 'N/A')
|
||||
info['pb_ratio'] = row.get('pb', 'N/A')
|
||||
info['market_cap'] = row.get('total_mv', 'N/A')
|
||||
print(f"[Tushare] ✅ 成功获取部分信息")
|
||||
except Exception as te:
|
||||
print(f"[Tushare] ❌ 获取失败: {te}")
|
||||
if info.get('pe_ratio') == 'N/A' and 'pe' in df.columns:
|
||||
info['pe_ratio'] = row.get('pe', 'N/A')
|
||||
if info.get('pb_ratio') == 'N/A' and 'pb' in df.columns:
|
||||
info['pb_ratio'] = row.get('pb', 'N/A')
|
||||
if info.get('market_cap') == 'N/A' and 'total_mv' in df.columns:
|
||||
info['market_cap'] = row.get('total_mv', 'N/A')
|
||||
print(f"[Tushare] ✅ 成功获取估值信息")
|
||||
else:
|
||||
print(f"[Tushare] ❌ 未获取到估值信息,尝试备用数据源")
|
||||
except Exception as e:
|
||||
print(f"[Tushare] ❌ 获取估值信息失败: {e}")
|
||||
|
||||
# tushare未获取到时,回退akshare
|
||||
if (info.get('name') == '未知' or info.get('pe_ratio') == 'N/A' or
|
||||
info.get('pb_ratio') == 'N/A' or info.get('market_cap') == 'N/A'):
|
||||
try:
|
||||
print(f"[Akshare] 正在获取 {symbol} 的详细信息(备用数据源)...")
|
||||
stock_info = ak.stock_individual_info_em(symbol=symbol)
|
||||
if stock_info is not None and not stock_info.empty:
|
||||
for _, row in stock_info.iterrows():
|
||||
key = row['item']
|
||||
value = row['value']
|
||||
|
||||
if key == '股票简称':
|
||||
info['name'] = value
|
||||
elif key == '总市值':
|
||||
try:
|
||||
if value and value != '-':
|
||||
info['market_cap'] = float(value)
|
||||
except:
|
||||
pass
|
||||
elif key == '市盈率-动态':
|
||||
try:
|
||||
if value and value != '-':
|
||||
pe_value = float(value)
|
||||
if 0 < pe_value <= 1000:
|
||||
info['pe_ratio'] = pe_value
|
||||
except:
|
||||
pass
|
||||
elif key == '市净率':
|
||||
try:
|
||||
if value and value != '-':
|
||||
pb_value = float(value)
|
||||
if 0 < pb_value <= 100:
|
||||
info['pb_ratio'] = pb_value
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"[Akshare] 获取个股详细信息失败: {e}")
|
||||
|
||||
# 方法2: 尝试获取历史价格和涨跌幅(如果网络允许)
|
||||
# try:
|
||||
@@ -204,7 +253,10 @@ class StockDataFetcher:
|
||||
# 方法3: 使用百度估值数据获取市盈率和市净率
|
||||
if info['pe_ratio'] == 'N/A':
|
||||
try:
|
||||
pe_data = ak.stock_zh_valuation_baidu(symbol=symbol, indicator="市盈率(TTM)")
|
||||
pe_data = call_with_timeout(
|
||||
ak.stock_zh_valuation_baidu, timeout=15,
|
||||
symbol=symbol, indicator="市盈率(TTM)"
|
||||
)
|
||||
if pe_data is not None and not pe_data.empty:
|
||||
latest_pe = pe_data.iloc[-1]['value']
|
||||
if latest_pe and latest_pe != '-':
|
||||
@@ -216,7 +268,10 @@ class StockDataFetcher:
|
||||
|
||||
if info['pb_ratio'] == 'N/A':
|
||||
try:
|
||||
pb_data = ak.stock_zh_valuation_baidu(symbol=symbol, indicator="市净率")
|
||||
pb_data = call_with_timeout(
|
||||
ak.stock_zh_valuation_baidu, timeout=15,
|
||||
symbol=symbol, indicator="市净率"
|
||||
)
|
||||
if pb_data is not None and not pb_data.empty:
|
||||
latest_pb = pb_data.iloc[-1]['value']
|
||||
if latest_pb and latest_pb != '-':
|
||||
@@ -262,6 +317,32 @@ class StockDataFetcher:
|
||||
"exchange": "香港交易所"
|
||||
}
|
||||
|
||||
# 优先使用tushare(hk_basic + hk_daily)
|
||||
if self.data_source_manager.tushare_available:
|
||||
try:
|
||||
print(f"[Tushare] 正在获取港股信息(主要数据源)...")
|
||||
ts_code = f"{hk_code}.HK"
|
||||
bdf = self.data_source_manager.tushare_api.hk_basic(ts_code=ts_code)
|
||||
if bdf is not None and not bdf.empty and 'name' in bdf.columns:
|
||||
info['name'] = bdf.iloc[0].get('name', '未知')
|
||||
|
||||
end = datetime.now().strftime('%Y%m%d')
|
||||
start = (datetime.now() - timedelta(days=10)).strftime('%Y%m%d')
|
||||
hdf = self.data_source_manager.tushare_api.hk_daily(
|
||||
ts_code=ts_code, start_date=start, end_date=end
|
||||
)
|
||||
if hdf is not None and not hdf.empty:
|
||||
latest = hdf.iloc[0]
|
||||
info['current_price'] = latest.get('close', 'N/A')
|
||||
info['change_percent'] = latest.get('pct_chg', 'N/A')
|
||||
|
||||
if info['current_price'] != 'N/A':
|
||||
print(f"[Tushare] ✅ 成功获取港股信息")
|
||||
return info
|
||||
print(f"[Tushare] ❌ 未获取到港股行情,尝试备用数据源")
|
||||
except Exception as e:
|
||||
print(f"[Tushare] 获取港股信息失败: {e}")
|
||||
|
||||
# 方法1: 获取港股实时行情
|
||||
try:
|
||||
# 使用akshare获取港股实时数据
|
||||
@@ -331,12 +412,7 @@ class StockDataFetcher:
|
||||
|
||||
def _get_us_stock_info(self, symbol):
|
||||
"""获取美股基本信息"""
|
||||
import time
|
||||
|
||||
try:
|
||||
# 添加延迟避免频率限制
|
||||
time.sleep(1)
|
||||
|
||||
ticker = yf.Ticker(symbol)
|
||||
|
||||
# 先尝试获取历史数据(通常更稳定)
|
||||
@@ -487,7 +563,36 @@ class StockDataFetcher:
|
||||
else:
|
||||
start_date = (datetime.now() - timedelta(days=365)).strftime('%Y%m%d')
|
||||
|
||||
# 获取港股历史数据
|
||||
# 优先使用tushare(hk_daily)
|
||||
if self.data_source_manager.tushare_available:
|
||||
try:
|
||||
print(f"[Tushare] 正在获取港股历史数据(主要数据源)...")
|
||||
ts_code = f"{hk_code}.HK"
|
||||
df = self.data_source_manager.tushare_api.hk_daily(
|
||||
ts_code=ts_code,
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
if df is not None and not df.empty:
|
||||
df = df.rename(columns={
|
||||
'trade_date': 'Date',
|
||||
'open': 'Open',
|
||||
'high': 'High',
|
||||
'low': 'Low',
|
||||
'close': 'Close',
|
||||
'vol': 'Volume'
|
||||
})
|
||||
df['Date'] = pd.to_datetime(df['Date'])
|
||||
df = df.sort_values('Date')
|
||||
df.set_index('Date', inplace=True)
|
||||
print(f"[Tushare] ✅ 成功获取港股历史数据")
|
||||
return df
|
||||
else:
|
||||
print(f"[Tushare] ❌ 未获取到港股历史数据,尝试备用数据源")
|
||||
except Exception as e:
|
||||
print(f"[Tushare] 获取港股历史数据失败: {e}")
|
||||
|
||||
# 回退akshare
|
||||
df = ak.stock_hk_hist(symbol=hk_code, period="daily",
|
||||
start_date=start_date, end_date=end_date, adjust="qfq")
|
||||
|
||||
@@ -600,6 +705,112 @@ class StockDataFetcher:
|
||||
except Exception as e:
|
||||
return {"error": f"获取财务数据失败: {str(e)}"}
|
||||
|
||||
def _convert_ts_financial_records(self, df, field_map, limit=8):
|
||||
"""将tushare财务表转换为统一的记录列表"""
|
||||
try:
|
||||
data_list = []
|
||||
for _, row in df.head(limit).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_list
|
||||
except Exception as e:
|
||||
print(f"转换tushare财务数据异常: {e}")
|
||||
return None
|
||||
|
||||
def _convert_ts_ratios(self, df):
|
||||
"""将tushare财务指标转换为AI使用的比率字典"""
|
||||
try:
|
||||
df = df.sort_values('end_date', ascending=False)
|
||||
if df.empty:
|
||||
return {}
|
||||
row = df.iloc[0]
|
||||
mapping = {
|
||||
'净资产收益率(ROE)': 'roe',
|
||||
'总资产报酬率(ROA)': 'roa',
|
||||
'销售毛利率': 'grossprofit_margin',
|
||||
'销售净利率': 'netprofit_margin',
|
||||
'资产负债率': 'debt_to_assets',
|
||||
'流动比率': 'current_ratio',
|
||||
'速动比率': 'quick_ratio',
|
||||
'存货周转率': 'inventory_turnover',
|
||||
'应收账款周转率': 'ar_turnover',
|
||||
'总资产周转率': 'assets_turnover',
|
||||
'营业收入同比增长': 'or_yoy',
|
||||
'净利润同比增长': 'netprofit_yoy',
|
||||
'EPS': 'eps',
|
||||
}
|
||||
ratios = {'报告期': str(row.get('end_date', 'N/A'))}
|
||||
for ch_name, ts_name in mapping.items():
|
||||
if ts_name in df.columns:
|
||||
value = row.get(ts_name)
|
||||
if value is None or (isinstance(value, float) and pd.isna(value)):
|
||||
ratios[ch_name] = "N/A"
|
||||
else:
|
||||
try:
|
||||
ratios[ch_name] = str(value)
|
||||
except:
|
||||
ratios[ch_name] = "N/A"
|
||||
return ratios
|
||||
except Exception as e:
|
||||
print(f"转换tushare财务指标异常: {e}")
|
||||
return {}
|
||||
|
||||
def _get_financial_data_from_tushare(self, symbol):
|
||||
"""优先从tushare获取财务三表与财务指标"""
|
||||
try:
|
||||
if not self.data_source_manager.tushare_available:
|
||||
return None
|
||||
ts_code = self.data_source_manager._convert_to_ts_code(symbol)
|
||||
result = {}
|
||||
|
||||
# 利润表
|
||||
df = self.data_source_manager.tushare_api.income(ts_code=ts_code)
|
||||
if df is not None and not df.empty:
|
||||
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
|
||||
records = self._convert_ts_financial_records(df, self.INCOME_TS_MAP)
|
||||
if records:
|
||||
result["income_statement"] = records
|
||||
|
||||
# 资产负债表
|
||||
df = self.data_source_manager.tushare_api.balancesheet(ts_code=ts_code)
|
||||
if df is not None and not df.empty:
|
||||
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
|
||||
records = self._convert_ts_financial_records(df, self.BALANCE_TS_MAP)
|
||||
if records:
|
||||
result["balance_sheet"] = records
|
||||
|
||||
# 现金流量表
|
||||
df = self.data_source_manager.tushare_api.cashflow(ts_code=ts_code)
|
||||
if df is not None and not df.empty:
|
||||
df = df.drop_duplicates(subset=['end_date']).sort_values('end_date', ascending=False)
|
||||
records = self._convert_ts_financial_records(df, self.CASHFLOW_TS_MAP)
|
||||
if records:
|
||||
result["cash_flow"] = records
|
||||
|
||||
# 财务指标
|
||||
df = self.data_source_manager.tushare_api.fina_indicator(ts_code=ts_code)
|
||||
if df is not None and not df.empty:
|
||||
ratios = self._convert_ts_ratios(df)
|
||||
if ratios:
|
||||
result["financial_ratios"] = ratios
|
||||
|
||||
if result:
|
||||
print(f"[Tushare] ✅ 成功获取财务数据(主要数据源)")
|
||||
return result or None
|
||||
except Exception as e:
|
||||
print(f"[Tushare] ❌ 获取财务数据失败: {e}")
|
||||
return None
|
||||
|
||||
def _get_chinese_financial_data(self, symbol):
|
||||
"""获取中国股票财务数据"""
|
||||
financial_data = {
|
||||
@@ -612,68 +823,77 @@ class StockDataFetcher:
|
||||
}
|
||||
|
||||
try:
|
||||
# 1. 获取资产负债表
|
||||
try:
|
||||
balance_sheet = ak.stock_financial_abstract_ths(symbol=symbol, indicator="资产负债表")
|
||||
if balance_sheet is not None and not balance_sheet.empty:
|
||||
financial_data["balance_sheet"] = balance_sheet.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取资产负债表失败: {e}")
|
||||
# 0. 优先使用tushare获取财务三表与财务指标
|
||||
ts_financial = self._get_financial_data_from_tushare(symbol)
|
||||
if ts_financial:
|
||||
financial_data.update(ts_financial)
|
||||
|
||||
# 2. 获取利润表
|
||||
try:
|
||||
income_statement = ak.stock_financial_abstract_ths(symbol=symbol, indicator="利润表")
|
||||
if income_statement is not None and not income_statement.empty:
|
||||
financial_data["income_statement"] = income_statement.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取利润表失败: {e}")
|
||||
# 1. 获取资产负债表(tushare未获取到时回退akshare)
|
||||
if financial_data["balance_sheet"] is None:
|
||||
try:
|
||||
balance_sheet = ak.stock_financial_abstract_ths(symbol=symbol, indicator="资产负债表")
|
||||
if balance_sheet is not None and not balance_sheet.empty:
|
||||
financial_data["balance_sheet"] = balance_sheet.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取资产负债表失败: {e}")
|
||||
|
||||
# 3. 获取现金流量表
|
||||
try:
|
||||
cash_flow = ak.stock_financial_abstract_ths(symbol=symbol, indicator="现金流量表")
|
||||
if cash_flow is not None and not cash_flow.empty:
|
||||
financial_data["cash_flow"] = cash_flow.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取现金流量表失败: {e}")
|
||||
# 2. 获取利润表(tushare未获取到时回退akshare)
|
||||
if financial_data["income_statement"] is None:
|
||||
try:
|
||||
income_statement = ak.stock_financial_abstract_ths(symbol=symbol, indicator="利润表")
|
||||
if income_statement is not None and not income_statement.empty:
|
||||
financial_data["income_statement"] = income_statement.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取利润表失败: {e}")
|
||||
|
||||
# 4. 获取主要财务指标
|
||||
try:
|
||||
financial_abstract = ak.stock_financial_abstract(symbol=symbol)
|
||||
if financial_abstract is not None and not financial_abstract.empty:
|
||||
# 提取关键财务指标
|
||||
key_indicators = [
|
||||
'净资产收益率(ROE)', '总资产报酬率(ROA)', '销售毛利率', '销售净利率',
|
||||
'资产负债率', '流动比率', '速动比率', '存货周转率', '应收账款周转率',
|
||||
'总资产周转率', '营业收入同比增长', '净利润同比增长'
|
||||
]
|
||||
|
||||
# 筛选出包含关键指标的行
|
||||
indicator_rows = financial_abstract[financial_abstract['指标'].isin(key_indicators)]
|
||||
|
||||
if not indicator_rows.empty:
|
||||
# 获取最新的报告期数据(第一列日期)
|
||||
date_columns = [col for col in financial_abstract.columns if col not in ['选项', '指标']]
|
||||
if date_columns:
|
||||
latest_date = date_columns[0] # 最新日期列
|
||||
|
||||
# 构建财务比率字典
|
||||
financial_ratios = {"报告期": latest_date}
|
||||
|
||||
# 提取每个指标的最新值
|
||||
for _, row in indicator_rows.iterrows():
|
||||
indicator_name = row['指标']
|
||||
value = row.get(latest_date, 'N/A')
|
||||
if value is not None and not (isinstance(value, float) and pd.isna(value)):
|
||||
try:
|
||||
financial_ratios[indicator_name] = str(value)
|
||||
except:
|
||||
# 3. 获取现金流量表(tushare未获取到时回退akshare)
|
||||
if financial_data["cash_flow"] is None:
|
||||
try:
|
||||
cash_flow = ak.stock_financial_abstract_ths(symbol=symbol, indicator="现金流量表")
|
||||
if cash_flow is not None and not cash_flow.empty:
|
||||
financial_data["cash_flow"] = cash_flow.head(8).to_dict('records')
|
||||
except Exception as e:
|
||||
print(f"获取现金流量表失败: {e}")
|
||||
|
||||
# 4. 获取主要财务指标(tushare未获取到时回退akshare)
|
||||
if not financial_data["financial_ratios"]:
|
||||
try:
|
||||
financial_abstract = ak.stock_financial_abstract(symbol=symbol)
|
||||
if financial_abstract is not None and not financial_abstract.empty:
|
||||
# 提取关键财务指标
|
||||
key_indicators = [
|
||||
'净资产收益率(ROE)', '总资产报酬率(ROA)', '销售毛利率', '销售净利率',
|
||||
'资产负债率', '流动比率', '速动比率', '存货周转率', '应收账款周转率',
|
||||
'总资产周转率', '营业收入同比增长', '净利润同比增长'
|
||||
]
|
||||
|
||||
# 筛选出包含关键指标的行
|
||||
indicator_rows = financial_abstract[financial_abstract['指标'].isin(key_indicators)]
|
||||
|
||||
if not indicator_rows.empty:
|
||||
# 获取最新的报告期数据(第一列日期)
|
||||
date_columns = [col for col in financial_abstract.columns if col not in ['选项', '指标']]
|
||||
if date_columns:
|
||||
latest_date = date_columns[0] # 最新日期列
|
||||
|
||||
# 构建财务比率字典
|
||||
financial_ratios = {"报告期": latest_date}
|
||||
|
||||
# 提取每个指标的最新值
|
||||
for _, row in indicator_rows.iterrows():
|
||||
indicator_name = row['指标']
|
||||
value = row.get(latest_date, 'N/A')
|
||||
if value is not None and not (isinstance(value, float) and pd.isna(value)):
|
||||
try:
|
||||
financial_ratios[indicator_name] = str(value)
|
||||
except:
|
||||
financial_ratios[indicator_name] = "N/A"
|
||||
else:
|
||||
financial_ratios[indicator_name] = "N/A"
|
||||
else:
|
||||
financial_ratios[indicator_name] = "N/A"
|
||||
|
||||
financial_data["financial_ratios"] = financial_ratios
|
||||
except Exception as e:
|
||||
print(f"获取财务指标失败: {e}")
|
||||
|
||||
financial_data["financial_ratios"] = financial_ratios
|
||||
except Exception as e:
|
||||
print(f"获取财务指标失败: {e}")
|
||||
|
||||
# 注意:季报数据现在由 quarterly_report_data.py 模块使用 akshare 获取(8期完整季报)
|
||||
# 不再使用问财获取季报,避免重复
|
||||
|
||||
Reference in New Issue
Block a user