Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Value investing analysis tool for Chinese A-share stocks with screening, financial analysis, industry comparison, and DCF valuation.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/financial_analyzer.py
1#!/usr/bin/env python32"""3A股财务分析模块4提供财务健康度分析、杜邦分析、异常检测等功能56依赖: pip install akshare pandas numpy7"""89import argparse10import json11import sys12from datetime import datetime13from typing import Optional, List, Dict1415try:16import pandas as pd17import numpy as np18except ImportError:19print("错误: 请先安装依赖库")20print("pip install pandas numpy")21sys.exit(1)222324class FinancialAnalyzer:25"""财务分析器"""2627def __init__(self, stock_data: Dict = None):28self.stock_data = stock_data or {}29self.analysis_result = {}3031def load_data(self, file_path: str):32"""从JSON文件加载股票数据"""33with open(file_path, 'r', encoding='utf-8') as f:34self.stock_data = json.load(f)3536def _assess_roe(self, roe: float) -> str:37"""根据ROE评估盈利能力"""38if roe > 20:39return "优秀 - ROE超过20%,盈利能力很强"40elif roe > 15:41return "良好 - ROE在15-20%之间,盈利能力较强"42elif roe > 10:43return "一般 - ROE在10-15%之间,盈利能力中等"44return "较弱 - ROE低于10%,盈利能力需要改善"4546def analyze_profitability(self) -> Dict:47"""盈利能力分析"""48result = {49"category": "盈利能力",50"metrics": {},51"trend": [],52"assessment": ""53}5455indicators = self.stock_data.get('financial_indicators', [])56if not indicators:57return result5859metrics = {}60for indicator in indicators[:8]:61period = indicator.get('日期', '')62if period:63metrics[period] = {64"ROE": self._safe_float(indicator.get('净资产收益率', indicator.get('加权净资产收益率'))),65"ROA": self._safe_float(indicator.get('总资产报酬率')),66"毛利率": self._safe_float(indicator.get('销售毛利率')),67"净利率": self._safe_float(indicator.get('销售净利率'))68}6970if not metrics:71return result7273latest = list(metrics.values())[0]74result["metrics"] = {75"当前ROE": latest.get("ROE"),76"当前ROA": latest.get("ROA"),77"当前毛利率": latest.get("毛利率"),78"当前净利率": latest.get("净利率")79}8081roe_values = [v.get("ROE") for v in metrics.values() if v.get("ROE") is not None]82if len(roe_values) >= 2:83trend = "上升" if roe_values[0] > roe_values[-1] else "下降"84result["trend"].append(f"ROE呈{trend}趋势")8586roe = latest.get("ROE")87if roe:88result["assessment"] = self._assess_roe(roe)8990return result9192def analyze_solvency(self) -> Dict:93"""偿债能力分析"""94result = {95"category": "偿债能力",96"metrics": {},97"risks": [],98"assessment": ""99}100101indicators = self.stock_data.get('financial_indicators', [])102if not indicators:103return result104105latest = indicators[0]106107debt_ratio = self._safe_float(latest.get('资产负债率'))108current_ratio = self._safe_float(latest.get('流动比率'))109quick_ratio = self._safe_float(latest.get('速动比率'))110111result["metrics"] = {112"资产负债率": debt_ratio,113"流动比率": current_ratio,114"速动比率": quick_ratio115}116117# 风险评估118risk_checks = [119(debt_ratio and debt_ratio > 70, f"资产负债率偏高 ({debt_ratio:.1f}%),需关注偿债压力"),120(current_ratio and current_ratio < 1, f"流动比率偏低 ({current_ratio:.2f}),短期偿债能力较弱"),121(quick_ratio and quick_ratio < 0.8, f"速动比率偏低 ({quick_ratio:.2f}),短期流动性风险"),122]123result["risks"] = [msg for condition, msg in risk_checks if condition]124125# 综合评估126risk_count = len(result["risks"])127if risk_count == 0:128result["assessment"] = "良好 - 偿债能力指标正常,财务结构稳健"129elif risk_count == 1:130result["assessment"] = "一般 - 存在一项风险指标,需持续关注"131else:132result["assessment"] = "较弱 - 存在多项风险指标,偿债压力较大"133134return result135136def analyze_operation(self) -> Dict:137"""运营效率分析"""138result = {139"category": "运营效率",140"metrics": {},141"observations": [],142"assessment": ""143}144145indicators = self.stock_data.get('financial_indicators', [])146if not indicators:147return result148149latest = indicators[0]150151ar_days = self._safe_float(latest.get('应收账款周转天数'))152inventory_days = self._safe_float(latest.get('存货周转天数'))153asset_turnover = self._safe_float(latest.get('总资产周转率'))154155result["metrics"] = {156"应收账款周转率": self._safe_float(latest.get('应收账款周转率')),157"应收账款周转天数": ar_days,158"存货周转率": self._safe_float(latest.get('存货周转率')),159"存货周转天数": inventory_days,160"总资产周转率": asset_turnover161}162163# 观察分析164observation_checks = [165(ar_days and ar_days > 90, f"应收账款周转天数较长 ({ar_days:.0f}天),回款较慢"),166(inventory_days and inventory_days > 180, f"存货周转天数较长 ({inventory_days:.0f}天),库存管理需关注"),167(asset_turnover and asset_turnover < 0.5, f"总资产周转率较低 ({asset_turnover:.2f}),资产利用效率有待提高"),168]169result["observations"] = [msg for condition, msg in observation_checks if condition]170171if not result["observations"]:172result["assessment"] = "良好 - 运营效率指标正常"173else:174result["assessment"] = "需关注 - " + ";".join(result["observations"])175176return result177178def _assess_growth_rate(self, avg_growth: float) -> str:179"""评估增长率"""180if avg_growth > 20:181return "高成长 - 平均增长率超过20%"182elif avg_growth > 10:183return "稳定成长 - 平均增长率10-20%"184elif avg_growth > 0:185return "低速成长 - 平均增长率0-10%"186return "负增长 - 需要深入分析原因"187188def _analyze_growth_trend(self, values: List[float], name: str) -> Optional[str]:189"""分析增长趋势"""190if not values:191return None192if all(g > 0 for g in values[:4]):193return f"{name}持续正增长"194elif values[0] < 0:195return f"{name}负增长,需关注"196return None197198def analyze_growth(self) -> Dict:199"""成长性分析"""200result = {201"category": "成长性",202"metrics": {},203"trend": [],204"assessment": ""205}206207indicators = self.stock_data.get('financial_indicators', [])208if not indicators:209return result210211revenue_growth = []212profit_growth = []213214for indicator in indicators[:8]:215rev = self._safe_float(indicator.get('主营业务收入增长率', indicator.get('营业收入增长率')))216net = self._safe_float(indicator.get('净利润增长率'))217if rev is not None:218revenue_growth.append(rev)219if net is not None:220profit_growth.append(net)221222if revenue_growth:223result["metrics"]["最近营收增长率"] = revenue_growth[0]224result["metrics"]["平均营收增长率"] = sum(revenue_growth) / len(revenue_growth)225226if profit_growth:227result["metrics"]["最近净利润增长率"] = profit_growth[0]228result["metrics"]["平均净利润增长率"] = sum(profit_growth) / len(profit_growth)229230# 趋势判断231for values, name in [(revenue_growth, "营收"), (profit_growth, "净利润")]:232trend = self._analyze_growth_trend(values, name)233if trend:234result["trend"].append(trend)235236avg_growth = result["metrics"].get("平均净利润增长率", 0) or 0237result["assessment"] = self._assess_growth_rate(avg_growth)238239return result240241def analyze_dupont(self) -> Dict:242"""杜邦分析"""243result = {244"category": "杜邦分析",245"decomposition": {},246"driver": "",247"assessment": ""248}249250indicators = self.stock_data.get('financial_indicators', [])251if not indicators:252return result253254latest = indicators[0] if indicators else {}255256# 杜邦分解: ROE = 净利率 × 资产周转率 × 权益乘数257net_margin = self._safe_float(latest.get('销售净利率'))258asset_turnover = self._safe_float(latest.get('总资产周转率'))259equity_multiplier = self._safe_float(latest.get('权益乘数'))260roe = self._safe_float(latest.get('净资产收益率', latest.get('加权净资产收益率')))261262result["decomposition"] = {263"ROE": roe,264"净利率": net_margin,265"资产周转率": asset_turnover,266"权益乘数": equity_multiplier267}268269# 判断ROE驱动因素270if net_margin and asset_turnover and equity_multiplier:271drivers = []272if net_margin > 15:273drivers.append("高净利率")274if asset_turnover > 1:275drivers.append("高周转")276if equity_multiplier > 2.5:277drivers.append("高杠杆")278279if drivers:280result["driver"] = "ROE主要由" + "、".join(drivers) + "驱动"281else:282result["driver"] = "ROE驱动因素较为均衡"283284return result285286def detect_anomalies(self) -> Dict:287"""财务异常检测"""288result = {289"category": "财务异常检测",290"signals": [],291"risk_level": "低",292"details": []293}294295indicators = self.stock_data.get('financial_indicators', [])296if len(indicators) < 2:297return result298299current = indicators[0]300previous = indicators[1] if len(indicators) > 1 else {}301302# 1. 应收账款异常303ar_growth = self._safe_float(current.get('应收账款增长率'))304revenue_growth = self._safe_float(current.get('营业收入增长率', current.get('主营业务收入增长率')))305if ar_growth and revenue_growth and ar_growth > revenue_growth * 1.5:306result["signals"].append({307"type": "应收账款增速异常",308"description": f"应收账款增速({ar_growth:.1f}%)显著高于营收增速({revenue_growth:.1f}%)",309"severity": "中"310})311312# 2. 存货异常313inventory_growth = self._safe_float(current.get('存货增长率'))314if inventory_growth and revenue_growth and inventory_growth > revenue_growth * 2:315result["signals"].append({316"type": "存货增速异常",317"description": f"存货增速({inventory_growth:.1f}%)远高于营收增速({revenue_growth:.1f}%)",318"severity": "中"319})320321# 3. 毛利率异常波动322current_gm = self._safe_float(current.get('销售毛利率'))323previous_gm = self._safe_float(previous.get('销售毛利率'))324if current_gm and previous_gm:325gm_change = abs(current_gm - previous_gm)326if gm_change > 10:327result["signals"].append({328"type": "毛利率大幅波动",329"description": f"毛利率变动{gm_change:.1f}个百分点,需关注原因",330"severity": "中"331})332333# 4. 经营现金流与净利润背离 (需要现金流数据)334cash_flow = self.stock_data.get('financial_data', {}).get('cash_flow', [])335income = self.stock_data.get('financial_data', {}).get('income_statement', [])336if cash_flow and income:337try:338ocf = self._safe_float(cash_flow[0].get('经营活动产生的现金流量净额'))339net_profit = self._safe_float(income[0].get('净利润'))340if ocf and net_profit and net_profit > 0:341ocf_ratio = ocf / net_profit342if ocf_ratio < 0.5:343result["signals"].append({344"type": "现金流与利润背离",345"description": f"经营现金流/净利润 = {ocf_ratio:.1%},盈利质量存疑",346"severity": "高"347})348except (IndexError, KeyError):349pass350351# 确定风险等级352high_severity = sum(1 for s in result["signals"] if s.get("severity") == "高")353medium_severity = sum(1 for s in result["signals"] if s.get("severity") == "中")354355if high_severity > 0:356result["risk_level"] = "高"357elif medium_severity >= 2:358result["risk_level"] = "中"359else:360result["risk_level"] = "低"361362return result363364def generate_summary(self, level: str = "standard") -> Dict:365"""生成分析摘要"""366summary = {367"code": self.stock_data.get('code', ''),368"name": self.stock_data.get('basic_info', {}).get('name', ''),369"analysis_date": datetime.now().isoformat(),370"level": level371}372373profitability = self.analyze_profitability()374solvency = self.analyze_solvency()375operation = self.analyze_operation()376growth = self.analyze_growth()377anomalies = self.detect_anomalies()378379if level == "summary":380summary["profitability"] = profitability["assessment"]381summary["solvency"] = solvency["assessment"]382summary["growth"] = growth["assessment"]383summary["risk_level"] = anomalies["risk_level"]384else:385# standard 和 deep 级别共享基础分析结果386summary["profitability"] = profitability387summary["solvency"] = solvency388summary["operation"] = operation389summary["growth"] = growth390summary["dupont"] = self.analyze_dupont()391summary["anomalies"] = anomalies392393if level == "deep":394summary["historical_indicators"] = self.stock_data.get('financial_indicators', [])395396summary["score"] = self._calculate_score(profitability, solvency, growth, anomalies)397398return summary399400def _calculate_score(self, profitability, solvency, growth, anomalies) -> int:401"""计算综合评分 (0-100)"""402score = 50403404# 盈利能力评分405roe = profitability.get("metrics", {}).get("当前ROE")406if roe:407if roe > 20:408score += 15409elif roe > 15:410score += 10411elif roe > 10:412score += 5413elif roe < 5:414score -= 5415416# 偿债能力评分417risks = solvency.get("risks", [])418score += 10 if not risks else -len(risks) * 3419420# 成长性评分421avg_growth = growth.get("metrics", {}).get("平均净利润增长率", 0) or 0422if avg_growth > 20:423score += 15424elif avg_growth > 10:425score += 10426elif avg_growth > 0:427score += 5428else:429score -= 5430431# 风险扣分432risk_penalties = {"高": 15, "中": 8}433score -= risk_penalties.get(anomalies.get("risk_level", "低"), 0)434435return max(0, min(100, score))436437def compare_stocks(self, stocks_data: List[Dict]) -> Dict:438"""对比多只股票"""439comparison = {440"comparison_date": datetime.now().isoformat(),441"stocks": [],442"ranking": {}443}444445stock_scores = []446for stock in stocks_data:447self.stock_data = stock448summary = self.generate_summary(level="summary")449comparison["stocks"].append({450"code": stock.get('code', ''),451"name": stock.get('basic_info', {}).get('name', ''),452"score": summary.get("score", 50),453"profitability": summary.get("profitability"),454"solvency": summary.get("solvency"),455"growth": summary.get("growth"),456"risk_level": summary.get("risk_level")457})458stock_scores.append((stock.get('code', ''), summary.get("score", 50)))459460# 排名461stock_scores.sort(key=lambda x: x[1], reverse=True)462comparison["ranking"] = {code: rank + 1 for rank, (code, _) in enumerate(stock_scores)}463464return comparison465466@staticmethod467def _safe_float(value) -> Optional[float]:468"""安全转换为浮点数"""469if value is None or value == '' or value == '--':470return None471try:472if isinstance(value, str):473value = value.replace('%', '').replace(',', '')474return float(value)475except (ValueError, TypeError):476return None477478479def main():480parser = argparse.ArgumentParser(description="A股财务分析器")481parser.add_argument("--input", type=str, required=True, help="输入数据文件 (JSON)")482parser.add_argument("--level", type=str, default="standard",483choices=["summary", "standard", "deep"],484help="分析深度级别")485parser.add_argument("--mode", type=str, default="single",486choices=["single", "comparison"],487help="分析模式: single(单只)/comparison(对比)")488parser.add_argument("--output", type=str, help="输出文件路径 (JSON)")489490args = parser.parse_args()491492# 加载数据493with open(args.input, 'r', encoding='utf-8') as f:494data = json.load(f)495496analyzer = FinancialAnalyzer()497498if args.mode == "single":499analyzer.stock_data = data500result = analyzer.generate_summary(level=args.level)501else:502# 对比模式503stocks = data.get('stocks', [data])504result = analyzer.compare_stocks(stocks)505506# 输出507output_json = json.dumps(result, ensure_ascii=False, indent=2, default=str)508509if args.output:510with open(args.output, 'w', encoding='utf-8') as f:511f.write(output_json)512print(f"分析结果已保存到: {args.output}")513else:514print(output_json)515516517if __name__ == "__main__":518main()519