""" 财务数据处理服务 专门处理财务数据的获取、格式化、分析和展示 """ from typing import Dict, Any, List, Optional, Tuple import asyncio import logging from datetime import datetime, timedelta from ..schemas.data import FinancialDataResponse, MarketDataResponse from ..core.exceptions import DataSourceError, ValidationError from .data_fetcher import DataFetcher logger = logging.getLogger(__name__) class FinancialDataProcessor: """财务数据处理器""" def __init__(self, data_fetcher: DataFetcher): self.data_fetcher = data_fetcher self.retry_count = 3 self.retry_delay = 2 # 秒 async def process_financial_data(self, symbol: str, market: str) -> Dict[str, Any]: """处理财务数据的主入口""" try: # 并行获取财务数据和市场数据 financial_data, market_data = await asyncio.gather( self._fetch_financial_data_with_retry(symbol, market), self._fetch_market_data_with_retry(symbol, market), return_exceptions=True ) # 检查是否有异常 if isinstance(financial_data, Exception): logger.error(f"获取财务数据失败: {str(financial_data)}") raise financial_data if isinstance(market_data, Exception): logger.error(f"获取市场数据失败: {str(market_data)}") # 市场数据失败不影响财务数据处理,使用空数据 market_data = self._create_empty_market_data(symbol, market) # 验证数据完整性 self._validate_financial_data(financial_data) # 格式化数据 formatted_data = self._format_financial_data(financial_data, market_data) # 计算财务比率和指标 calculated_metrics = self._calculate_financial_metrics(financial_data, market_data) # 生成数据质量报告 quality_report = self._generate_quality_report(financial_data, market_data) return { "raw_data": { "financial_data": financial_data.dict(), "market_data": market_data.dict() }, "formatted_tables": formatted_data, "calculated_metrics": calculated_metrics, "quality_report": quality_report, "processing_timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"财务数据处理失败: {symbol} ({market}) - {str(e)}") raise DataSourceError(f"财务数据处理失败: {str(e)}") async def _fetch_financial_data_with_retry(self, symbol: str, market: str) -> FinancialDataResponse: """带重试机制的财务数据获取""" last_exception = None for attempt in range(self.retry_count): try: logger.info(f"获取财务数据 (尝试 {attempt + 1}/{self.retry_count}): {symbol} ({market})") return await self.data_fetcher.fetch_financial_data(symbol, market) except Exception as e: last_exception = e logger.warning(f"财务数据获取失败 (尝试 {attempt + 1}): {str(e)}") if attempt < self.retry_count - 1: await asyncio.sleep(self.retry_delay * (attempt + 1)) # 递增延迟 continue else: break raise DataSourceError(f"财务数据获取失败,已重试 {self.retry_count} 次: {str(last_exception)}") async def _fetch_market_data_with_retry(self, symbol: str, market: str) -> MarketDataResponse: """带重试机制的市场数据获取""" last_exception = None for attempt in range(self.retry_count): try: logger.info(f"获取市场数据 (尝试 {attempt + 1}/{self.retry_count}): {symbol} ({market})") return await self.data_fetcher.fetch_market_data(symbol, market) except Exception as e: last_exception = e logger.warning(f"市场数据获取失败 (尝试 {attempt + 1}): {str(e)}") if attempt < self.retry_count - 1: await asyncio.sleep(self.retry_delay * (attempt + 1)) continue else: break raise DataSourceError(f"市场数据获取失败,已重试 {self.retry_count} 次: {str(last_exception)}") def _create_empty_market_data(self, symbol: str, market: str) -> MarketDataResponse: """创建空的市场数据响应""" return MarketDataResponse( symbol=symbol, market=market, data_source="fallback", last_updated=datetime.utcnow(), price_data={}, volume_data={}, technical_indicators={} ) def _validate_financial_data(self, financial_data: FinancialDataResponse): """验证财务数据完整性""" if not financial_data: raise ValidationError("财务数据为空") # 检查必要的数据字段 required_fields = ["balance_sheet", "income_statement", "cash_flow"] missing_fields = [] for field in required_fields: data = getattr(financial_data, field, None) if not data or not isinstance(data, dict) or not data: missing_fields.append(field) if missing_fields: logger.warning(f"缺少财务数据字段: {missing_fields}") # 不抛出异常,只记录警告,允许部分数据处理 def _format_financial_data(self, financial_data: FinancialDataResponse, market_data: MarketDataResponse) -> List[Dict[str, Any]]: """格式化财务数据为前端表格格式""" tables = [] try: # 资产负债表 if financial_data.balance_sheet: balance_sheet_table = self._create_balance_sheet_table(financial_data.balance_sheet) tables.append(balance_sheet_table) # 利润表 if financial_data.income_statement: income_statement_table = self._create_income_statement_table(financial_data.income_statement) tables.append(income_statement_table) # 现金流量表 if financial_data.cash_flow: cash_flow_table = self._create_cash_flow_table(financial_data.cash_flow) tables.append(cash_flow_table) # 关键指标表 if financial_data.key_metrics or market_data.price_data: key_metrics_table = self._create_key_metrics_table( financial_data.key_metrics or {}, market_data.price_data or {} ) tables.append(key_metrics_table) except Exception as e: logger.error(f"财务数据格式化失败: {str(e)}") # 返回空表格而不是抛出异常 return tables def _create_balance_sheet_table(self, balance_sheet_data: Dict[str, Any]) -> Dict[str, Any]: """创建资产负债表""" total_assets = balance_sheet_data.get("total_assets", 0) return { "title": "资产负债表", "headers": ["项目", "金额(万元)", "占总资产比例"], "rows": [ [ {"label": "总资产", "value": "总资产"}, {"label": "", "value": self._format_currency(total_assets)}, {"label": "", "value": "100.00%"} ], [ {"label": "货币资金", "value": "货币资金"}, {"label": "", "value": self._format_currency(balance_sheet_data.get("monetary_cap", 0))}, {"label": "", "value": self._calculate_percentage( balance_sheet_data.get("monetary_cap", 0), total_assets )} ], [ {"label": "应收账款", "value": "应收账款"}, {"label": "", "value": self._format_currency(balance_sheet_data.get("accounts_receiv", 0))}, {"label": "", "value": self._calculate_percentage( balance_sheet_data.get("accounts_receiv", 0), total_assets )} ], [ {"label": "存货", "value": "存货"}, {"label": "", "value": self._format_currency(balance_sheet_data.get("inventories", 0))}, {"label": "", "value": self._calculate_percentage( balance_sheet_data.get("inventories", 0), total_assets )} ], [ {"label": "总负债", "value": "总负债"}, {"label": "", "value": self._format_currency(balance_sheet_data.get("total_liab", 0))}, {"label": "", "value": self._calculate_percentage( balance_sheet_data.get("total_liab", 0), total_assets )} ], [ {"label": "股东权益", "value": "股东权益"}, {"label": "", "value": self._format_currency(balance_sheet_data.get("total_hldr_eqy_exc_min_int", 0))}, {"label": "", "value": self._calculate_percentage( balance_sheet_data.get("total_hldr_eqy_exc_min_int", 0), total_assets )} ] ] } def _create_income_statement_table(self, income_data: Dict[str, Any]) -> Dict[str, Any]: """创建利润表""" revenue = income_data.get("revenue", 0) return { "title": "利润表", "headers": ["项目", "金额(万元)", "占营收比例"], "rows": [ [ {"label": "营业收入", "value": "营业收入"}, {"label": "", "value": self._format_currency(revenue)}, {"label": "", "value": "100.00%"} ], [ {"label": "营业利润", "value": "营业利润"}, {"label": "", "value": self._format_currency(income_data.get("operate_profit", 0))}, {"label": "", "value": self._calculate_percentage( income_data.get("operate_profit", 0), revenue )} ], [ {"label": "利润总额", "value": "利润总额"}, {"label": "", "value": self._format_currency(income_data.get("total_profit", 0))}, {"label": "", "value": self._calculate_percentage( income_data.get("total_profit", 0), revenue )} ], [ {"label": "净利润", "value": "净利润"}, {"label": "", "value": self._format_currency(income_data.get("n_income", 0))}, {"label": "", "value": self._calculate_percentage( income_data.get("n_income", 0), revenue )} ], [ {"label": "归母净利润", "value": "归母净利润"}, {"label": "", "value": self._format_currency(income_data.get("n_income_attr_p", 0))}, {"label": "", "value": self._calculate_percentage( income_data.get("n_income_attr_p", 0), revenue )} ], [ {"label": "基本每股收益", "value": "基本每股收益"}, {"label": "", "value": f"{income_data.get('basic_eps', 0):.3f}元"}, {"label": "", "value": "-"} ] ] } def _create_cash_flow_table(self, cash_flow_data: Dict[str, Any]) -> Dict[str, Any]: """创建现金流量表""" return { "title": "现金流量表", "headers": ["项目", "金额(万元)", "现金流状况"], "rows": [ [ {"label": "经营活动现金流", "value": "经营活动现金流"}, {"label": "", "value": self._format_currency(cash_flow_data.get("n_cashflow_act", 0))}, {"label": "", "value": self._evaluate_cash_flow(cash_flow_data.get("n_cashflow_act", 0))} ], [ {"label": "投资活动现金流", "value": "投资活动现金流"}, {"label": "", "value": self._format_currency(cash_flow_data.get("n_cashflow_inv_act", 0))}, {"label": "", "value": self._evaluate_cash_flow(cash_flow_data.get("n_cashflow_inv_act", 0))} ], [ {"label": "筹资活动现金流", "value": "筹资活动现金流"}, {"label": "", "value": self._format_currency(cash_flow_data.get("n_cashflow_fin_act", 0))}, {"label": "", "value": self._evaluate_cash_flow(cash_flow_data.get("n_cashflow_fin_act", 0))} ], [ {"label": "期末现金余额", "value": "期末现金余额"}, {"label": "", "value": self._format_currency(cash_flow_data.get("c_cash_equ_end_period", 0))}, {"label": "", "value": "-"} ] ] } def _create_key_metrics_table(self, key_metrics: Dict[str, Any], price_data: Dict[str, Any]) -> Dict[str, Any]: """创建关键财务指标表""" return { "title": "关键财务指标", "headers": ["指标", "数值", "评价"], "rows": [ [ {"label": "市盈率(PE)", "value": "市盈率(PE)"}, {"label": "", "value": f"{key_metrics.get('pe', 0):.2f}"}, {"label": "", "value": self._evaluate_pe_ratio(key_metrics.get('pe', 0))} ], [ {"label": "市净率(PB)", "value": "市净率(PB)"}, {"label": "", "value": f"{key_metrics.get('pb', 0):.2f}"}, {"label": "", "value": self._evaluate_pb_ratio(key_metrics.get('pb', 0))} ], [ {"label": "净资产收益率(ROE)", "value": "净资产收益率(ROE)"}, {"label": "", "value": f"{key_metrics.get('roe', 0):.2f}%"}, {"label": "", "value": self._evaluate_roe(key_metrics.get('roe', 0))} ], [ {"label": "总资产收益率(ROA)", "value": "总资产收益率(ROA)"}, {"label": "", "value": f"{key_metrics.get('roa', 0):.2f}%"}, {"label": "", "value": self._evaluate_roa(key_metrics.get('roa', 0))} ], [ {"label": "毛利率", "value": "毛利率"}, {"label": "", "value": f"{key_metrics.get('gross_margin', 0):.2f}%"}, {"label": "", "value": self._evaluate_gross_margin(key_metrics.get('gross_margin', 0))} ], [ {"label": "资产负债率", "value": "资产负债率"}, {"label": "", "value": f"{key_metrics.get('debt_to_assets', 0):.2f}%"}, {"label": "", "value": self._evaluate_debt_ratio(key_metrics.get('debt_to_assets', 0))} ] ] } def _calculate_financial_metrics(self, financial_data: FinancialDataResponse, market_data: MarketDataResponse) -> Dict[str, Any]: """计算额外的财务指标""" try: balance_sheet = financial_data.balance_sheet or {} income_statement = financial_data.income_statement or {} cash_flow = financial_data.cash_flow or {} price_data = market_data.price_data or {} # 计算流动比率 current_assets = balance_sheet.get("monetary_cap", 0) + balance_sheet.get("accounts_receiv", 0) + balance_sheet.get("inventories", 0) current_liabilities = balance_sheet.get("total_liab", 1) * 0.6 # 估算流动负债为总负债的60% current_ratio = current_assets / current_liabilities if current_liabilities > 0 else 0 # 计算净利润率 revenue = income_statement.get("revenue", 1) net_income = income_statement.get("n_income_attr_p", 0) net_margin = (net_income / revenue * 100) if revenue > 0 else 0 # 计算现金流覆盖率 operating_cash_flow = cash_flow.get("n_cashflow_act", 0) cash_coverage_ratio = (operating_cash_flow / net_income) if net_income > 0 else 0 return { "liquidity_ratios": { "current_ratio": round(current_ratio, 2), "quick_ratio": round(current_ratio * 0.8, 2) # 简化计算 }, "profitability_ratios": { "net_margin": round(net_margin, 2), "operating_margin": round((income_statement.get("operate_profit", 0) / revenue * 100) if revenue > 0 else 0, 2) }, "efficiency_ratios": { "asset_turnover": round((revenue / balance_sheet.get("total_assets", 1)) if balance_sheet.get("total_assets", 0) > 0 else 0, 2), "inventory_turnover": round((revenue / balance_sheet.get("inventories", 1)) if balance_sheet.get("inventories", 0) > 0 else 0, 2) }, "cash_flow_ratios": { "cash_coverage_ratio": round(cash_coverage_ratio, 2), "free_cash_flow": operating_cash_flow + cash_flow.get("n_cashflow_inv_act", 0) } } except Exception as e: logger.error(f"财务指标计算失败: {str(e)}") return {} def _generate_quality_report(self, financial_data: FinancialDataResponse, market_data: MarketDataResponse) -> Dict[str, Any]: """生成数据质量报告""" quality_checks = [] overall_score = 0 total_checks = 0 # 检查财务数据完整性 data_completeness = { "balance_sheet": bool(financial_data.balance_sheet), "income_statement": bool(financial_data.income_statement), "cash_flow": bool(financial_data.cash_flow), "key_metrics": bool(financial_data.key_metrics) } for check_name, is_complete in data_completeness.items(): quality_checks.append({ "check_name": check_name, "status": "pass" if is_complete else "fail", "message": "数据完整" if is_complete else "数据缺失" }) if is_complete: overall_score += 1 total_checks += 1 # 检查市场数据 market_data_complete = bool(market_data.price_data) quality_checks.append({ "check_name": "market_data", "status": "pass" if market_data_complete else "fail", "message": "市场数据完整" if market_data_complete else "市场数据缺失" }) if market_data_complete: overall_score += 1 total_checks += 1 # 计算质量等级 quality_ratio = overall_score / total_checks if total_checks > 0 else 0 if quality_ratio >= 0.9: quality_grade = "优秀" elif quality_ratio >= 0.7: quality_grade = "良好" elif quality_ratio >= 0.5: quality_grade = "一般" else: quality_grade = "较差" return { "overall_score": overall_score, "total_checks": total_checks, "quality_ratio": round(quality_ratio, 2), "quality_grade": quality_grade, "checks": quality_checks, "data_source": financial_data.data_source, "last_updated": financial_data.last_updated.isoformat() } def _format_currency(self, value: float) -> str: """格式化货币数值""" if value == 0: return "0.00" # 转换为万元 value_wan = value / 10000 if abs(value_wan) >= 10000: # 大于1亿,显示为亿元 return f"{value_wan / 10000:.2f}亿" elif abs(value_wan) >= 1: return f"{value_wan:.2f}万" else: return f"{value:.2f}" def _calculate_percentage(self, numerator: float, denominator: float) -> str: """计算百分比""" if denominator == 0: return "0.00%" percentage = (numerator / denominator) * 100 return f"{percentage:.2f}%" def _evaluate_cash_flow(self, cash_flow: float) -> str: """评估现金流状况""" if cash_flow > 0: return "流入" elif cash_flow < 0: return "流出" else: return "平衡" def _evaluate_pe_ratio(self, pe: float) -> str: """评估市盈率""" if pe <= 0: return "亏损" elif pe < 15: return "低估" elif pe < 25: return "合理" elif pe < 40: return "偏高" else: return "高估" def _evaluate_pb_ratio(self, pb: float) -> str: """评估市净率""" if pb < 1: return "破净" elif pb < 2: return "低估" elif pb < 3: return "合理" else: return "偏高" def _evaluate_roe(self, roe: float) -> str: """评估净资产收益率""" if roe < 5: return "较低" elif roe < 15: return "一般" elif roe < 25: return "良好" else: return "优秀" def _evaluate_roa(self, roa: float) -> str: """评估总资产收益率""" if roa < 3: return "较低" elif roa < 8: return "一般" elif roa < 15: return "良好" else: return "优秀" def _evaluate_gross_margin(self, margin: float) -> str: """评估毛利率""" if margin < 10: return "较低" elif margin < 30: return "一般" elif margin < 50: return "良好" else: return "优秀" def _evaluate_debt_ratio(self, debt_ratio: float) -> str: """评估资产负债率""" if debt_ratio < 30: return "保守" elif debt_ratio < 50: return "合理" elif debt_ratio < 70: return "偏高" else: return "风险"