From 97b0edc3241195e183dde4aca238a1a784d44240 Mon Sep 17 00:00:00 2001 From: xucheng Date: Mon, 15 Dec 2025 22:05:19 +0900 Subject: [PATCH] Initial commit --- .env | 2 + DOC/formulas.md | 64 ++ DOC/implementation_plan.md | 57 ++ DOC/requirements.md | 45 ++ DOC/task.md | 24 + DOC/walkthrough.md | 74 ++ README.md | 216 +++++ data/CN/300750.SZ/balance_sheet.csv | 13 + data/CN/300750.SZ/cash_flow.csv | 13 + data/CN/300750.SZ/income_statement.csv | 13 + data/CN/300750.SZ/report.html | 753 ++++++++++++++++++ data/CN/300750.SZ/report.md | 87 ++ data/CN/600519.SH/balance_sheet.csv | 13 + data/CN/600519.SH/cash_flow.csv | 13 + data/CN/600519.SH/income_statement.csv | 13 + data/CN/600519.SH/report.html | 750 +++++++++++++++++ data/CN/600519.SH/report.md | 87 ++ data/US/AAPL/balance_sheet.csv | 11 + data/US/AAPL/cash_flow.csv | 11 + data/US/AAPL/income_statement.csv | 11 + data/US/AAPL/report.md | 4 + main.py | 98 +++ nohup.out | 0 requirements.txt | 5 + .../__pycache__/calculator.cpython-312.pyc | Bin 0 -> 17701 bytes .../__pycache__/calculator.cpython-313.pyc | Bin 0 -> 18338 bytes src/analysis/calculator.py | 344 ++++++++ .../alpha_vantage_fetcher.cpython-312.pyc | Bin 0 -> 6535 bytes .../alpha_vantage_fetcher.cpython-313.pyc | Bin 0 -> 6656 bytes src/fetchers/__pycache__/base.cpython-312.pyc | Bin 0 -> 1652 bytes src/fetchers/__pycache__/base.cpython-313.pyc | Bin 0 -> 1652 bytes .../__pycache__/factory.cpython-312.pyc | Bin 0 -> 1203 bytes .../__pycache__/factory.cpython-313.pyc | Bin 0 -> 1293 bytes .../tushare_fetcher.cpython-312.pyc | Bin 0 -> 9959 bytes .../tushare_fetcher.cpython-313.pyc | Bin 0 -> 10114 bytes src/fetchers/alpha_vantage_fetcher.py | 130 +++ src/fetchers/base.py | 31 + src/fetchers/factory.py | 18 + src/fetchers/tushare_fetcher.py | 259 ++++++ .../html_generator.cpython-313.pyc | Bin 0 -> 11646 bytes .../markdown_generator.cpython-312.pyc | Bin 0 -> 10407 bytes .../markdown_generator.cpython-313.pyc | Bin 0 -> 7306 bytes src/reporting/html_generator.py | 254 ++++++ src/reporting/markdown_generator.py | 157 ++++ .../__pycache__/file_io.cpython-312.pyc | Bin 0 -> 2304 bytes .../__pycache__/file_io.cpython-313.pyc | Bin 0 -> 2373 bytes src/storage/file_io.py | 30 + 47 files changed, 3600 insertions(+) create mode 100644 .env create mode 100644 DOC/formulas.md create mode 100644 DOC/implementation_plan.md create mode 100644 DOC/requirements.md create mode 100644 DOC/task.md create mode 100644 DOC/walkthrough.md create mode 100644 README.md create mode 100644 data/CN/300750.SZ/balance_sheet.csv create mode 100644 data/CN/300750.SZ/cash_flow.csv create mode 100644 data/CN/300750.SZ/income_statement.csv create mode 100644 data/CN/300750.SZ/report.html create mode 100644 data/CN/300750.SZ/report.md create mode 100644 data/CN/600519.SH/balance_sheet.csv create mode 100644 data/CN/600519.SH/cash_flow.csv create mode 100644 data/CN/600519.SH/income_statement.csv create mode 100644 data/CN/600519.SH/report.html create mode 100644 data/CN/600519.SH/report.md create mode 100644 data/US/AAPL/balance_sheet.csv create mode 100644 data/US/AAPL/cash_flow.csv create mode 100644 data/US/AAPL/income_statement.csv create mode 100644 data/US/AAPL/report.md create mode 100644 main.py create mode 100644 nohup.out create mode 100644 requirements.txt create mode 100644 src/analysis/__pycache__/calculator.cpython-312.pyc create mode 100644 src/analysis/__pycache__/calculator.cpython-313.pyc create mode 100644 src/analysis/calculator.py create mode 100644 src/fetchers/__pycache__/alpha_vantage_fetcher.cpython-312.pyc create mode 100644 src/fetchers/__pycache__/alpha_vantage_fetcher.cpython-313.pyc create mode 100644 src/fetchers/__pycache__/base.cpython-312.pyc create mode 100644 src/fetchers/__pycache__/base.cpython-313.pyc create mode 100644 src/fetchers/__pycache__/factory.cpython-312.pyc create mode 100644 src/fetchers/__pycache__/factory.cpython-313.pyc create mode 100644 src/fetchers/__pycache__/tushare_fetcher.cpython-312.pyc create mode 100644 src/fetchers/__pycache__/tushare_fetcher.cpython-313.pyc create mode 100644 src/fetchers/alpha_vantage_fetcher.py create mode 100644 src/fetchers/base.py create mode 100644 src/fetchers/factory.py create mode 100644 src/fetchers/tushare_fetcher.py create mode 100644 src/reporting/__pycache__/html_generator.cpython-313.pyc create mode 100644 src/reporting/__pycache__/markdown_generator.cpython-312.pyc create mode 100644 src/reporting/__pycache__/markdown_generator.cpython-313.pyc create mode 100644 src/reporting/html_generator.py create mode 100644 src/reporting/markdown_generator.py create mode 100644 src/storage/__pycache__/file_io.cpython-312.pyc create mode 100644 src/storage/__pycache__/file_io.cpython-313.pyc create mode 100644 src/storage/file_io.py diff --git a/.env b/.env new file mode 100644 index 0000000..19c874a --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +TUSHARE_TOKEN=f62b415de0a5a947fcb693b66cd299dd6242868bf04ad687800c7f3f +ALPHA_VANTAGE_KEY=QQ9R0SFHQHAXDTHD diff --git a/DOC/formulas.md b/DOC/formulas.md new file mode 100644 index 0000000..b71325b --- /dev/null +++ b/DOC/formulas.md @@ -0,0 +1,64 @@ +# 财务指标计算公式与字段说明 (Financial Indicators Formulas) + +本通过展示了系统中生成的各项财务指标的计算逻辑及对应的数据来源字段。 + +## 1. 主要指标 (Key Metrics) + +| 指标 (Key) | 显示名称 | 计算公式 (Formula) | Tushare字段 (CN) | Alpha Vantage字段 (US) | +| :--- | :--- | :--- | :--- | :--- | +| **ROE** | ROE | `(归母净利润 / 净资产) * 100%` | `n_income_attr_p` / `total_hldr_eqy_exc_min_int` | `netIncome` / `totalShareholderEquity` | +| **ROA** | ROA | `(净利润 / 总资产) * 100%` | `n_income_attr_p` / `total_assets` | `netIncome` / `totalAssets` | +| **ROIC** | ROCE/ROIC | `EBIT / (净资产 + 有息负债)` | `ebit` / (`equity` + `debt`) | `ebit` / (`equity` + `debt`) | +| **GrossMargin** | 毛利率 | `((收入 - 成本) / 收入) * 100%` | `(revenue - cogs) / revenue` | `grossProfit / totalRevenue` | +| **NetMargin** | 净利润率 | `(归母净利润 / 收入) * 100%` | `n_income_attr_p / revenue` | `netIncome / totalRevenue` | +| **revenue** | 收入 (亿) | `营业收入 / 10^8` | `total_revenue` | `totalRevenue` | +| **net_income** | 净利润 (亿) | `归母净利润 / 10^8` | `n_income_attr_p` | `netIncome` | +| **ocf** | 经营净现金流 (亿) | `经营活动产生的现金流量净额 / 10^8` | `n_cashflow_act` | `operatingCashflow` | +| **Capex** | 资本开支 (亿) | `购建固定资产...支付的现金` | `c_pay_acq_const_fiolta` | `capitalExpenditures` | +| **FCF** | 自由现金流 (亿) | `经营净现金流 - 资本开支` | `ocf - capex` | `ocf - capex` | + +## 2. 费用指标 (Expense Ratios) + +| 指标 (Key) | 显示名称 | 计算公式 (Formula) | 来源字段 (CN) | +| :--- | :--- | :--- | :--- | +| **SellingRatio** | 销售费用率 | `销售费用 / 收入` | `sell_exp` | +| **AdminRatio** | 管理费用率 | `管理费用 / 收入` | `admin_exp` | +| **RDRatio** | 研发费用率 | `研发费用 / 收入` | `rd_exp` | +| **DepreciationRatio** | 折旧费用占比 | `折旧 / 收入` | `depr_fa_coga_dpba` (CashFlow) | +| **OtherExpenseRatio** | 其他费用率 | `毛利率 - 销售费率 - 管理费率 - 研发费率 - 净利率 - 所得税率` (注: 近似倒挤包含财务费用等) | 计算得出 | +| **TaxRate** | 所得税率 | `所得税费用 / 利润总额` | `income_tax / total_profit` | + +## 3. 资产占比 (Asset Structure) + +| 指标 (Key) | 显示名称 | 计算公式 (Formula) | Tushare字段 (CN) | +| :--- | :--- | :--- | :--- | +| **CashRatio** | 现金占比 | `货币资金 / 总资产` | `money_cap` | +| **InventoryRatio** | 库存占比 | `存货 / 总资产` | `inventories` | +| **ReceivablesRatio** | 应收款占比 | `应收账款 / 总资产` | `accounts_receiv` | +| **PrepaymentRatio** | 预付款占比 | `预付款项 / 总资产` | `prepayment` | +| **FixedAssetsRatio** | 固定资产占比 | `固定资产 / 总资产` | `fix_assets` | +| **LongTermInvestmentRatio** | 长期投资占比 | `长期股权投资 / 总资产` | `lt_eqt_invest` | +| **GoodwillRatio** | 商誉占比 | `商誉 / 总资产` | `goodwill` | +| **OtherAssetsRatio** | 其他资产占比 | `100% - 上述7项占比之和` | (计算得出) | +| **PayablesRatio** | 应付款占比 | `应付账款 / 总资产` | `acct_payable` | +| **AdvanceReceiptsRatio** | 预收款占比 | `(预收款项 + 合同负债) / 总资产` | `adv_receipts` + `contract_liab` | +| **ShortTermDebtRatio** | 短期借款占比 | `短期借款 / 总资产` | `st_borr` | +| **LongTermDebtRatio** | 长期借款占比 | `长期借款 / 总资产` | `lt_borr` | +| **OperatingAssetsRatio** | 运营资产占比 | `(总资产 - 现金 - 长期投资 - 交易性金融资产) / 总资产` | (计算得出) | +| **InterestBearingDebtRatio**| 有息负债率 | `(短期借款 + 长期借款) / 总资产` | `st_borr` + `lt_borr` | + +## 4. 周转能力 (Turnover) + +* **存货周转天数**: `365 * 存货 / 营业成本` +* **应收款周转天数**: `365 * 应收账款 / 营业收入` +* **应付款周转天数**: `365 * 应付账款 / 营业成本` +* **固定资产周转率**: `营业收入 / 固定资产` +* **总资产周转率**: `营业收入 / 总资产` + +## 5. 市场表现 & 人均 + +* **市值**: 总市值 (亿) +* **PE**: 市盈率 (TTM) +* **PB**: 市净率 +* **人均创收**: `收入 / 员工人数` +* **人均创利**: `净利润 / 员工人数` diff --git a/DOC/implementation_plan.md b/DOC/implementation_plan.md new file mode 100644 index 0000000..770f66b --- /dev/null +++ b/DOC/implementation_plan.md @@ -0,0 +1,57 @@ +# 实施计划:财务数据获取与分析系统 + +# 目标描述 +开发一个后端工具,用于获取中国、香港、美国及日本股市的10年财务报表,保存到本地,计算财务指标,并输出带有样式的 Markdown 报告。 + +## 用户需确认事项 +- **API Keys**: 需要 Tushare 和 Alpha Vantage 的 API Key。 +- **日本市场数据**: Alpha Vantage 对日股财务数据的支持情况待验证。 +- **财务指标**: 需确认具体的计算指标列表。 + +## 拟定变更 + +### 1. 项目初始化 +- 创建项目目录结构。 +- 初始化依赖管理 `requirements.txt` (pandas, requests, tushare, alpha_vantage)。 +- 创建 `.env` 文件用于存放 API Keys。 + +### 2. 模块:数据获取器 (`src/fetchers`) +- 定义抽象基类 `DataFetcher`。 +- **TushareFetcher (CN & HK)**: + - 实现与 Tushare 的连接。 + - 方法:获取利润表、资产负债表、现金流量表。 + - 适配 A 股和港股代码后缀。 +- **AlphaVantageFetcher (US & JP)**: + - 实现与 Alpha Vantage 的连接。 + - 方法:同上。 + - 针对日本市场进行适配测试。 +- **工厂模式**: `get_fetcher(market, api_keys)` 根据市场返回对应的获取器实例。 + +### 3. 模块:数据存储 (`src/storage`) +- 实现将 DataFrame 保存为 CSV 的功能。 +- 目录结构:`data/{market}/{stock_code}/{statement_type}.csv`。 + +### 4. 模块:分析器 (`src/analysis`) +- 加载本地 CSV 数据。 +- 计算指标 (初步规划): + - **盈利能力**: ROE (净资产收益率), Net Profit Margin (净利率), Gross Margin (毛利率)。 + - **偿债能力**: Debt-to-Equity (产权比率/负债权益比), Current Ratio (流动比率)。 + - **成长能力**: Revenue Growth (营收增长率), Net Income Growth (净利增长率)。 +- 返回汇总的 DataFrame。 + +### 5. 模块:报告生成器 (`src/reporting`) +- 类 `MarkdownReporter`. +- 方法 `generate_table(indicator_data)`. +- **样式实现**: + - 使用 HTML `` 标签嵌入 Markdown,通过 `style="background-color: ..."` 实现背景色。 + +## 验证计划 + +### 自动化测试 +- 编写单元测试,Mock API 响应,测试数据解析逻辑。 +- 测试指标计算公式的准确性。 + +### 人工验证 +1. **中国/香港股票**: 获取茅台 (600519.SH) 和 腾讯 (00700.HK),检查文件是否生成,内容是否正确。 +2. **美国/日本股票**: 获取 Apple (AAPL) 和 Sony (6758.T),检查数据获取情况。 +3. **报告检查**: 打开生成的 `report.md`,确认颜色样式能否正确渲染。 diff --git a/DOC/requirements.md b/DOC/requirements.md new file mode 100644 index 0000000..49f05ea --- /dev/null +++ b/DOC/requirements.md @@ -0,0 +1,45 @@ +# 需求文档:公司财务数据获取与分析系统 + +## 1. 项目概述 +构建一个后端系统,根据证券代码和所属市场,获取目标公司的财务数据(资产负债表、利润表、现金流量表)。系统将保存原始数据,计算关键财务指标,并生成一份带有样式(背景色)的 Markdown 格式报告。 + +## 2. 项目范围 +- **目标市场**: + - **中国 (CN)**: 使用 Tushare API。 + - **香港 (HK)**: 使用 Tushare API (已确认支持)。 + - **美国 (US)**: 使用 Alpha Vantage API。 + - **日本 (JP)**: 尝试使用 Alpha Vantage API (需验证数据完整性)。 +- **数据覆盖**:最近 10 年的年度数据(或可获取的历史数据)。 +- **数据内容**:三大主要财务报表(利润表、资产负债表、现金流量表)。 + +## 3. 功能需求 + +### 3.1 数据获取 +- **输入**:`证券代码` (Stock Code), `市场` (CN, HK, US, JP)。 +- **逻辑**: + - 若 `市场 == CN` 或 `HK`: 调用 Tushare API。 + - 若 `市场 == US`: 调用 Alpha Vantage API。 + - 若 `市场 == JP`: 尝试调用 Alpha Vantage API。 +- **规模**:获取过去 10 年的数据。 + +### 3.2 数据存储 +- 将获取的原始财务报表保存为本地文件。 +- **格式**:CSV (推荐,便于后续处理)。 +- **命名规范**:`data/{市场}/{代码}_{报表类型}.csv`。 + +### 3.3 数据分析 +- 读取本地保存的财务报表文件。 +- 计算关键财务指标 (具体指标待定,预设包含:ROE, 净利率, 负债率等)。 + +### 3.4 报告生成 +- 生成 Markdown 格式的汇总报告。 +- **样式要求**:表格需包含背景色(如根据指标优劣显示红/绿,或隔行变色)。 + - *注*:由于标准 Markdown 不支持背景色,将采用嵌入 HTML 的方式实现。 + +## 4. 技术需求 +- **开发语言**:Python。 +- **配置管理**:API Key (Tushare, Alpha Vantage) 需可配置(建议使用环境变量或配置文件)。 + +## 5. 待确认事项 +1. **日本市场数据源**:Alpha Vantage 对日本市场的财务报表支持程度需在实施阶段验证。 +2. **具体的财务指标**:用户需通过文档或沟通确认具体需要计算哪些指标。 diff --git a/DOC/task.md b/DOC/task.md new file mode 100644 index 0000000..022859b --- /dev/null +++ b/DOC/task.md @@ -0,0 +1,24 @@ +# 财务数据获取与分析系统任务列表 + +## 第一阶段:规划与需求 +- [x] 定义需求和规格说明书 (PRD) +- [x] 制定详细实施计划 +- [x] 获取用户 API Keys (Tushare, Alpha Vantage) +- [x] 确认香港/日本市场数据源 (HK->Tushare, JP->Alpha Vantage) + +## 第二阶段:实施 - 数据获取 +- [x] 搭建项目结构 (Python/Backend) +- [x] 实现 Tushare 客户端 (支持 CN, HK) +- [x] 实现 Alpha Vantage 客户端 (支持 US, JP) +- [x] 实现各市场数据获取逻辑 +- [x] 实现文件存储功能 (保存原始数据) + +## 第三阶段:实施 - 分析与输出 +- [x] 实现财务指标计算逻辑 +- [x] 实现带有样式的 Markdown 表格生成 + +## 第四阶段:验证 +- [x] 验证中国/香港市场数据获取 +- [x] 验证美国/日本市场数据获取 +- [x] 验证指标计算准确性 +- [x] 验证 Markdown 输出格式与样式 diff --git a/DOC/walkthrough.md b/DOC/walkthrough.md new file mode 100644 index 0000000..7b55b77 --- /dev/null +++ b/DOC/walkthrough.md @@ -0,0 +1,74 @@ +# 财务数据获取与分析系统 - 执行演示 + +本演示记录了使用该后端系统获取中国(Tushare)和美国(Alpha Vantage)市场股票数据,并生成财务分析报告的全过程。 + +## 1. 验证目标 +- **中国市场**: 获取 贵州茅台 (600519.SH) 近 10 年财务数据。 +- **美国市场**: 获取 Apple Inc. (AAPL) 近 10 年财务数据。 +- **功能**: 下载数据 -> 本地存储 (CSV) -> 计算指标 (ROE, 净利率等) -> 生成带有颜色标记的 Markdown 报告。 + +## 2. 执行过程 + +通过运行 `main.py` 脚本执行了验证: + +```bash +python main.py +``` + +### 运行日志摘要 + +```text +Processing CN stock: 600519.SH... +Fetching Income Statement... +Fetching Balance Sheet... +Fetching Cash Flow... +Saved data/CN/600519.SH/income_statement.csv +Saved data/CN/600519.SH/balance_sheet.csv +Saved data/CN/600519.SH/cash_flow.csv +Calculating Financial Indicators... +Generating Report... +Analysis completed! Report saved to: data/CN/600519.SH/report.md +-------------------------------------------------- + +Processing US stock: AAPL... +Fetching Income Statement... +Fetching Balance Sheet... +Fetching Cash Flow... +Saved data/US/AAPL/income_statement.csv +Saved data/US/AAPL/balance_sheet.csv +Saved data/US/AAPL/cash_flow.csv +Calculating Financial Indicators... +Generating Report... +Analysis completed! Report saved to: data/US/AAPL/report.md +``` + +## 3. 输出结果演示 + +### 3.1 生成的文件结构 +系统按预期在 `data/` 目录下生成了对应的文件夹和文件: +``` +data/ +├── CN/ +│ └── 600519.SH/ +│ ├── balance_sheet.csv +│ ├── cash_flow.csv +│ ├── income_statement.csv +│ └── report.md +└── US/ + └── AAPL/ + ├── balance_sheet.csv (等) + └── report.md +``` + +### 3.2 报表内容示例 (AAPL) +生成的 `report.md` 包含带有背景色的 Markdown/HTML 表格,直观展示财务健康状况。 + +*(以下为报告片段)* +> **# Financial Analysis Report: US AAPL** +> +>
... + +## 4. 结论 +- **数据获取**: 成功连接 Tushare 和 Alpha Vantage API 并下载了数据。 +- **数据清洗**: 修复了日期格式不一致问题,确保了指标计算时年份正确对齐。 +- **报告生成**: 成功生成了包含 ROE、净利率、偿债能力分析的 Markdown 报告。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e850ebc --- /dev/null +++ b/README.md @@ -0,0 +1,216 @@ +# 财务报告生成器 + +这是一个命令行工具,用于获取A股、港股和美股上市公司的财务数据,并生成结构化的财务分析报告。报告包含HTML和Markdown两种格式。 + +## 功能 + +- 支持中国大陆 (CN)、香港 (HK) 和美国 (US) 三个市场。 +- 从 Tushare Pro 和 Alpha Vantage 两个API获取财务数据。 +- 计算数十种核心财务指标,涵盖盈利能力、资产结构、周转效率等多个维度。 +- 自动生成格式化的 HTML 和 Markdown 报告,便于阅读和分析。 + +## 环境配置 + +1. **安装依赖** + 在项目根目录下运行以下命令来安装所有必需的 Python 库: + ```bash + pip install -r requirements.txt + ``` + +2. **设置API密钥** + 程序需要调用 Tushare Pro 和 Alpha Vantage 的API来获取数据。请先注册这两个服务以获取免费的API密钥(Token)。 + + - **Tushare Pro**: [https://tushare.pro](https://tushare.pro) (主要用于A股和港股数据) + - **Alpha Vantage**: [https://www.alphavantage.co](https://www.alphavantage.co) (用于美股数据) + + 获取密钥后,请在项目根目录下创建一个名为 `.env` 的文件,并按以下格式填入您的密钥: + + ``` + TUSHARE_TOKEN=YOUR_TUSHARE_TOKEN_HERE + ALPHA_VANTAGE_KEY=YOUR_ALPHA_VANTAGE_KEY_HERE + ``` + +## 如何运行 + +使用以下命令来运行程序: + +```bash +python main.py +``` + +### 参数说明 + +- ``: 必填参数,指定目标市场。 + - `CN`: 中国A股市场 + - `HK`: 中国香港市场 + - `US`: 美国市场 + +- ``: 必填参数,指定目标公司的股票代码。 + - **A股 (CN)**: Tushare标准格式,例如 `600519.SH` (贵州茅台), `000001.SZ` (平安银行)。 + - **港股 (HK)**: Tushare标准格式,例如 `00700.HK` (腾讯控股)。 + - **美股 (US)**: 标准代码,例如 `AAPL` (Apple Inc.), `GOOGL` (Alphabet Inc.)。 + +### 运行示例 + +- 分析贵州茅台 (A股): + ```bash + python main.py CN 600519.SH + ``` +- 分析苹果公司 (美股): + ```bash + python main.py US AAPL + ``` +- **无参数运行**: + 如果直接运行 `python main.py`,程序将执行内置的默认测试案例(贵州茅台和苹果公司)。 + +## 数据来源 (API) + +程序通过两个API获取所有原始财务数据: + +### 1. Tushare Pro (A股 / 港股) + +- `income` / `hk_income`: 利润表 +- `balancesheet` / `hk_balancesheet`: 资产负债表 +- `cashflow` / `hk_cashflow`: 现金流量表 +- `daily_basic`: 每日指标 (用于获取股价, PE, PB, 市值, 股息率) +- `stock_basic`: 公司基本信息 (公司名称, 上市日期) +- `stock_company`: 上市公司信息 (员工人数) +- `stk_holdernumber`: 股东户数 + +### 2. Alpha Vantage (美股) + +- `INCOME_STATEMENT`: 利润表 +- `BALANCE_SHEET`: 资产负债表 +- `CASH_FLOW`: 现金流量表 +- `OVERVIEW`: 公司概览 (用于获取 PE, PB, 市值, 员工人数) +- `GLOBAL_QUOTE`: 全球报价 (用于获取最新股价) + +### API 调用次数 + +每次运行分析,程序会向对应的API服务发出一系列请求。不同市场的调用次数如下: + +- **A股 / 港股 (Tushare)**: 一次完整的分析大约需要 **9次** API调用,以获取三张财务报表、公司基本信息、历史和当前的各种市场指标。 +- **美股 (Alpha Vantage)**: 一次完整的分析大约需要 **5次** API调用,以获取三张财务报表、公司概览和最新股价。 + +*注意:上述次数是基于当前代码逻辑的估算,未来代码变更可能会影响实际调用次数。* + +## 指标与计算方法 + +> 下列指标的实现以标准财报口径为基础,并在部分地方做了个人化的口径定义,方便进行结构化对比分析。 + +### 1. 主要指标 + +- **ROE (净资产收益率)**: + \[\text{ROE} = \frac{\text{净利润}}{\text{期末净资产(total\\_equity)}}\] +- **ROA (总资产收益率)**: + \[\text{ROA} = \frac{\text{净利润}}{\text{总资产(total\\_assets)}}\] +- **ROIC (ROCE/ROIC)**: + - 先计算有息负债: + \[\text{有息负债} = \text{短期借款(short\\_term\\_debt)} + \text{长期借款(long\\_term\\_debt)}\] + - 再计算投入资本: + \[\text{投入资本(InvestedCapital)} = \text{净资产(total\\_equity)} + \text{有息负债}\] + - EBIT 优先使用接口字段,若缺失则按“利润总额 + 财务费用(利息支出)”推算: + \[\text{EBIT} = \text{total\\_profit} + \text{fin\\_exp}\] + - 最终: + \[\text{ROIC} = \frac{\text{EBIT}}{\text{投入资本}}\] +- **毛利率 (GrossMargin)**: + \[\text{毛利率} = \frac{\text{毛利}}{\text{收入}}\] + 其中毛利优先使用接口提供的 `gross_profit`,若缺失则按 `收入 - 营业成本(cogs)` 计算。 +- **净利润率 (NetMargin)**: + \[\text{净利润率} = \frac{\text{净利润}}{\text{收入}}\] +- **收入 (revenue)**:利润表中的营业总收入 / 总营收。 +- **收入增速 (RevenueGrowth)**:按“同一报告期上年同期”计算,而不是简单相邻两期: + \[\text{收入增速} = \frac{\text{本期收入} - \text{上年同日收入}}{|\text{上年同日收入}|}\] + 代码中通过把 `date_str` 减去 10000(例如 20240930 → 20230930)来精确匹配上年同期;如果找不到完全相同的日期,则该期增速记为缺失。 +- **净利润 (net_income)**:利润表中的归母净利润 (A股优先使用 `n_income_attr_p` / `net_profit_attr_p`)。 +- **净利润增速 (NetIncomeGrowth)**:同上逻辑,只是把收入换成净利润。 +- **经营净现金流 (ocf)**:经营活动产生的现金流量净额,统一映射为 `ocf` 字段。 +- **资本开支 (Capex)**:投资活动中购建固定资产、无形资产等的现金流出,取绝对值: + \[\text{Capex} = |\text{capex}|\] +- **自由现金流 (FCF)**: + \[\text{FCF} = \text{经营净现金流(ocf)} - \text{资本开支(Capex)}\] +- **分红 (dividends)**:现金流量表中“支付股利、利润或利息的现金流出”,统一到 `dividends` 字段。 +- **总资产 (total_assets)**:直接取自资产负债表。 +- **净资产 (total_equity)**:直接取自资产负债表。 +- **商誉 (goodwill)**:直接取自资产负债表。 + +### 2. 费用指标 + +- **销售费用率 (SellingRatio)**: + \[\text{销售费用率} = \frac{\text{销售费用(selling\\_exp)}}{\text{收入(revenue)}}\] +- **管理费用率 (AdminRatio)**: + \[\text{管理费用率} = \frac{\text{管理费用(admin\\_exp)}}{\text{收入}}\] +- **研发费用率 (RDRatio)**: + \[\text{研发费用率} = \frac{\text{研发费用(rd\\_exp)}}{\text{收入}}\] +- **其他费用率 (OtherExpenseRatio)**:这是一个“残差项”,按以下方式反推: + \[\text{其他费用率} = \text{毛利率} - \text{净利润率} - \text{销售费用率} - \text{管理费用率} - \text{研发费用率}\] + 若其中某些费用率缺失,则在计算中按 0 处理。 +- **折旧费用占比 (DepreciationRatio)**: + \[\text{折旧占比} = \frac{|\text{折旧费用(depreciation)}|}{\text{收入}}\] +- **所得税率 (TaxRate)**: + \[\text{所得税率} = \frac{\text{所得税费用(income\\_tax)}}{\text{利润总额(total\\_profit)}}\] + +### 3. 资产占比 + +> 所有“XX占比”均使用期末总资产 `total_assets` 作为分母。 + +- **现金占比 (CashRatio)**:货币资金 / 总资产。 +- **库存占比 (InventoryRatio)**:存货 / 总资产。 +- **应收款占比 (ReceivablesRatio)**:应收账款 / 总资产。 +- **预付款占比 (PrepaymentRatio)**:预付款项 / 总资产。 +- **固定资产占比 (FixedAssetsRatio)**:固定资产 / 总资产。 +- **长期投资占比 (LongTermInvestmentRatio)**:长期股权投资等 / 总资产。 +- **商誉占比 (GoodwillRatio)**:商誉 / 总资产。 +- **其他资产占比 (OtherAssetsRatio)**:**这是自定义口径**,用来反映“非核心资产”的残差: + \[\text{其他资产占比} = 1 - (\text{现金占比} + \text{库存占比} + \text{应收款占比} + \text{预付款占比} + \text{固定资产占比} + \text{长期投资占比} + \text{商誉占比})\] + 换句话说,上述“主要资产类别”的占比之和从 1 中扣除,剩余部分统一归入“其他资产”。 +- **应付款占比 (PayablesRatio)**:应付账款 / 总资产。 +- **预收款占比 (AdvanceReceiptsRatio)**: + \[\text{预收款占比} = \frac{\text{预收账款(adv\\_receipts)} + \text{合同负债(contract\\_liab)}}{\text{总资产}}\] +- **短期借款占比 (ShortTermDebtRatio)**:短期借款 / 总资产(含“一年内到期的长期负债”等合并后的口径)。 +- **长期借款占比 (LongTermDebtRatio)**:长期借款 / 总资产(含公司债等并入的口径)。 +- **运营资产占比 (OperatingAssetsRatio)**:**也是自定义口径**,用来衡量企业被运营环节占用的资产规模: + \[\text{运营资产占比} = \text{库存占比} + \text{应收款占比} + \text{预付款占比} - \text{应付款占比} - \text{预收款占比}\] + 直观理解:运营资产 = 应收 + 存货 + 预付 − 应付 − 预收。 +- **有息负债率 (InterestBearingDebtRatio)**: + \[\text{有息负债率} = \frac{\text{短期借款} + \text{长期借款}}{\text{总资产}}\] + +### 4. 周转能力 + +- **存货周转天数 (InventoryDays)**: + \[\text{存货周转天数} = \frac{\text{存货} \times 365}{\text{销售成本(cogs)}}\] +- **应收款周转天数 (ReceivablesDays)**: + \[\text{应收款周转天数} = \frac{\text{应收账款} \times 365}{\text{收入}}\] +- **应付款周转天数 (PayablesDays)**: + \[\text{应付款周转天数} = \frac{\text{应付账款} \times 365}{\text{销售成本(cogs)}}\] +- **固定资产周转率 (FixedAssetsTurnover)**: + \[\text{固定资产周转率} = \frac{\text{收入}}{\text{固定资产}}\] +- **总资产周转率 (TotalAssetTurnover)**: + \[\text{总资产周转率} = \frac{\text{收入}}{\text{总资产}}\] + +### 5. 人均效率 + +> 员工相关数据主要来自市场数据接口(如 Tushare 的 `stock_company`),并被挂到最近一个年度报告期(年报,通常是 12 月 31 日)上。 + +- **员工人数 (Employees)**:公司披露的员工总数。 +- **人均创收 (RevenuePerEmp)**: + \[\text{人均创收} = \frac{\text{年度收入}}{\text{员工人数}}\] +- **人均创利 (ProfitPerEmp)**: + \[\text{人均创利} = \frac{\text{年度净利润}}{\text{员工人数}}\] +- **人均薪酬 (AvgWage)**: + \[\text{人均薪酬} = \frac{\text{支付给职工及为职工支付的现金}}{\text{员工人数}}\] + +### 6. 市场表现 + +- **股价 (Price)**:来自 Tushare `daily_basic` 或 Alpha Vantage `GLOBAL_QUOTE` 的收盘价。 +- **市值 (MarketCap)**: + - A股 / 港股:基于 Tushare 的总市值字段 `total_mv`,内部统一换算为货币金额(再在展示层以“亿元”为单位显示)。 + - 美股:直接使用 Alpha Vantage `MarketCapitalization` 字段。 +- **PE / PB**:直接使用数据源的市盈率、市净率字段,用于横向比较。 +- **股东户数 (Shareholders)**:来自 Tushare 的 `stk_holdernumber`,在历史模式下会按报告期向前寻找最近一次披露的数据。 + +## 输出文件 + +程序运行成功后,会创建相应的 `data///` 目录,并在其中生成两份报告文件: +- `report.html`: 优化阅读体验的网页版报告。 +- `report.md`: Markdown格式的纯文本报告。 diff --git a/data/CN/300750.SZ/balance_sheet.csv b/data/CN/300750.SZ/balance_sheet.csv new file mode 100644 index 0000000..7861519 --- /dev/null +++ b/data/CN/300750.SZ/balance_sheet.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,report_type,comp_type,end_type,total_share,cap_rese,undistr_porfit,surplus_rese,special_rese,money_cap,trad_asset,notes_receiv,accounts_receiv,oth_receiv,prepayment,div_receiv,int_receiv,inventories,amor_exp,nca_within_1y,sett_rsrv,loanto_oth_bank_fi,premium_receiv,reinsur_receiv,reinsur_res_receiv,pur_resale_fa,oth_cur_assets,current_assets,fa_avail_for_sale,htm_invest,lt_eqt_invest,invest_real_estate,time_deposits,oth_assets,lt_rec,fix_assets,cip,const_materials,fixed_assets_disp,produc_bio_assets,oil_and_gas_assets,intan_assets,r_and_d,goodwill,lt_amor_exp,defer_tax_assets,decr_in_disbur,oth_nca,total_nca,cash_reser_cb,depos_in_oth_bfi,prec_metals,deriv_assets,rr_reins_une_prem,rr_reins_outstd_cla,rr_reins_lins_liab,rr_reins_lthins_liab,refund_depos,ph_pledge_loans,refund_cap_depos,indep_acct_assets,client_depos,client_prov,transac_seat_fee,invest_as_receiv,total_assets,lt_borr,st_borr,cb_borr,depos_ib_deposits,loan_oth_bank,trading_fl,notes_payable,acct_payable,adv_receipts,sold_for_repur_fa,comm_payable,payroll_payable,taxes_payable,int_payable,div_payable,oth_payable,acc_exp,deferred_inc,st_bonds_payable,payable_to_reinsurer,rsrv_insur_cont,acting_trading_sec,acting_uw_sec,non_cur_liab_due_1y,oth_cur_liab,current_liabilities,bond_payable,lt_payable,specific_payables,estimated_liab,defer_tax_liab,defer_inc_non_cur_liab,oth_ncl,total_ncl,depos_oth_bfi,deriv_liab,depos,agency_bus_liab,oth_liab,prem_receiv_adva,depos_received,ph_invest,reser_une_prem,reser_outstd_claims,reser_lins_liab,reser_lthins_liab,indept_acc_liab,pledge_borr,indem_payable,policy_div_payable,total_liabilities,treasury_share,ordin_risk_reser,forex_differ,invest_loss_unconf,minority_int,total_equity,total_hldr_eqy_inc_min_int,total_liab_hldr_eqy,lt_payroll_payable,oth_comp_income,oth_eqt_tools,oth_eqt_tools_p_shr,lending_funds,acc_receivable,st_fin_payable,payables,hfs_assets,hfs_sales,cost_fin_assets,fair_value_fin_assets,contract_assets,contract_liab,accounts_receiv_bill,accounts_pay,oth_rcv_total,fix_assets_total,cip_total,oth_pay_total,long_pay_total,debt_invest,oth_debt_invest,update_flag +300750.SZ,20251021,20251021,20250930,1,1,3,4562854001.0,156365576000.0,151715049000.0,2272701000.0,38434000.0,324241586000.0,43260528000.0,418837000.0,66481235000.0,,13557167000.0,75434000.0,,80211558000.0,,98616000.0,,,,,,,10578550000.0,574228137000.0,,,59831799000.0,,,,193099000.0,128622702000.0,37365635000.0,,,,,15025388000.0,,890939000.0,4908611000.0,28542552000.0,,23937431000.0,321853995000.0,,,,36492000.0,,,,,,,,,,,,,896082131000.0,78441925000.0,15314463000.0,,,,,82967668000.0,131958477000.0,,,,21821691000.0,8851546000.0,,98000000.0,,,,,,,,,25005358000.0,4985363000.0,340940328000.0,8422115000.0,,,85843047000.0,2261098000.0,25224182000.0,4739536000.0,208129732000.0,,,,,,,,,,,,,,,,,549070060000.0,7097993000.0,,,,32764095000.0,314247976000.0,347012071000.0,896082131000.0,,6391356000.0,,,,,,,,,,,398446000.0,40678197000.0,66900072000.0,214926145000.0,2862290000.0,128622702000.0,37365635000.0,9357565000.0,1519965000.0,,,1 +300750.SZ,20250315,20250315,20241231,1,1,4,4403466458.0,116756136000.0,126601541000.0,2194779000.0,35551000.0,303511993000.0,14282253000.0,130403000.0,64135510000.0,2141729000.0,5969685000.0,65217000.0,1000.0,59835533000.0,,72972000.0,,,,,,,6286465000.0,510142089000.0,,,54791525000.0,,,,151342000.0,112589053000.0,29754703000.0,,,,,14419804000.0,,894757000.0,4593980000.0,24118834000.0,,19275483000.0,276516035000.0,,,,,,,,,,,,,,,,,786658123000.0,81238456000.0,19696282000.0,,,,,67356323000.0,130977408000.0,,,,18653079000.0,9436442000.0,0.0,5400161000.0,10761762000.0,,,,,,,,22881417000.0,2058196000.0,317171534000.0,11922623000.0,1606480000.0,0.0,71926943000.0,1231236000.0,22041069000.0,5400795000.0,196030416000.0,,2116017000.0,,,,,,,,,,,,,,,513201949000.0,2712804000.0,,,,26526141000.0,246930033000.0,273456174000.0,786658123000.0,,-348637000.0,,,,,,,,,,,400626000.0,27834446000.0,64265913000.0,198333731000.0,2206947000.0,112589053000.0,29754703000.0,16161923000.0,1606480000.0,,,0 +300750.SZ,20241019,20241019,20240930,1,1,3,4402375669.0,115137443600.0,117181223700.0,2192566200.0,33103400.0,264675795100.0,22002410300.0,292441400.0,66702709300.0,,6887337600.0,504261300.0,,55215275300.0,,200635500.0,,,,,,,6577524400.0,466764516700.0,,,54175331200.0,,,,128324700.0,110653484500.0,25200973600.0,,,,,14448153400.0,,883700900.0,4528856500.0,22121062800.0,,22428362600.0,271470487700.0,,,,,,,,,,,,,,,,,738235004400.0,85444436200.0,15239389800.0,,,,,63651540000.0,123140013700.0,,,,17368871900.0,8144202500.0,,22722000.0,,,,,,,,,16081511700.0,2926818100.0,281070698000.0,12104785500.0,,,65430208700.0,1180577400.0,21899574000.0,5492450500.0,193863663000.0,,2499377200.0,,,,,,,,,,,,,,,474934361000.0,2776131700.0,,,,26344412200.0,236956231200.0,263300643400.0,738235004400.0,,785650400.0,,,,,,,,,,,363142300.0,22651661600.0,66995150700.0,186791553700.0,3444015600.0,110653484500.0,25200973600.0,9367311600.0,1594834100.0,,,0 +300750.SZ,20240316,20240316,20231231,1,1,4,4399041236.0,87907212600.0,103244625900.0,2192566200.0,9355000.0,264306514700.0,7767200.0,1751724600.0,64020533400.0,3012579200.0,6962872600.0,425984800.0,0.0,45433890100.0,,56827500.0,,,,,,,8286024600.0,449788001700.0,,,50027694100.0,,,,9839600.0,115387960100.0,25011907100.0,,,,,15675876200.0,,707881700.0,4695780000.0,17395585300.0,,21145073300.0,267380039400.0,,,,,,,,,,,,,,,,,717168041100.0,83448981700.0,15181012100.0,,,,,77514940700.0,117038773600.0,,,,14846251200.0,11741826300.0,0.0,29915700.0,13624086000.0,,,,,,,,7008874200.0,2091627800.0,287001069100.0,19237014400.0,1520256200.0,0.0,51638913500.0,1364905800.0,21448987400.0,31341466200.0,210283821400.0,,3941409600.0,,,,,,,,,,,,,,,497284890500.0,1572971600.0,,,,22175098200.0,197708052400.0,219883150600.0,717168041100.0,,1528223100.0,,,,,,,,,,,233964100.0,23982351900.0,65772258000.0,194553714300.0,3438564000.0,115387960100.0,25011907100.0,13654001700.0,1520256200.0,,,1 +300750.SZ,20230310,20230310,20221231,1,1,4,2442514524.0,88904372100.0,63242753100.0,1214302900.0,,191043409500.0,1981328100.0,3526083700.0,57966516900.0,8678379900.0,15843284400.0,,,76668898800.0,,403711500.0,,,,,,,11907028600.0,387734857000.0,,,17595207400.0,,,,44316100.0,89070834700.0,35397650600.0,,,,,9539963200.0,,704065200.0,2294776000.0,9483660400.0,,25101316500.0,213217494900.0,,,,575638000.0,,,,,,,,,,,,,600952351900.0,59099358400.0,14415402500.0,,,,,126229468200.0,94534976000.0,,,,9476018400.0,4792441200.0,,8319600.0,15005751100.0,,,,,,,,7232224400.0,1622032600.0,295761419300.0,19177888600.0,1050000000.0,0.0,19697374600.0,1807813000.0,19966701700.0,6910284100.0,128281770600.0,,,,,,,,,,,,,,,,,424043189900.0,253991000.0,,,,12427910400.0,164481251600.0,176909162000.0,600952351900.0,,8931300000.0,,,,,,,,,,,174863000.0,22444785300.0,61492600600.0,220764444200.0,8678379900.0,89070834700.0,35397650600.0,15014070600.0,1050000000.0,,,0 +300750.SZ,20220422,20220422,20211231,1,1,4,2330851200.0,43163696500.0,34095467500.0,1158471200.0,,89071889700.0,1363972900.0,1463828000.0,23753548200.0,3114909600.0,6466439300.0,,,40199691900.0,,201655900.0,,,,,,,5292231900.0,177734938700.0,,,10949033600.0,,,,619282400.0,41275333300.0,30998159500.0,,,,,4479606400.0,,527850700.0,1264339100.0,5542554400.0,,20575419100.0,129931922300.0,,,,243105100.0,,,,,,,,,,,,,307666860900.0,22119078800.0,12123056900.0,,,,,58405751300.0,48784286300.0,,,,5122787800.0,2403797500.0,0.0,6590800.0,6169623600.0,,,,,,,,3548532800.0,1242490300.0,149344832600.0,15855052000.0,1010000000.0,0.0,9953762700.0,1038576900.0,12099359100.0,3228720400.0,65699853800.0,,,,,,,,,,,,,,,,,215044686400.0,443534900.0,,,,8108903200.0,84513271300.0,92622174500.0,307666860900.0,,4208319800.0,,,,,,,,,,,77285500.0,11537915300.0,25217376200.0,107190037600.0,3114909600.0,41275333300.0,30998159500.0,6176214400.0,1010000000.0,,,0 +300750.SZ,20210428,20210428,20201231,1,1,4,2329474028.0,41662151603.08,18640918703.75,1157782633.55,,68424116053.67,3288071512.61,9877156349.23,11293523722.88,3303956813.15,997118630.25,,,13224640950.39,,81548616.67,,,,,,,969240539.21,112864989321.68,,,4813072905.14,,,,372156591.66,19621648443.02,5750351820.37,,,,,2517935725.46,,147951887.23,363551716.95,3167109948.33,,5002631587.8,43753437618.91,,,,1330347108.86,,,,,,,,,,,,,156618426940.59,6068163254.2,6335080182.17,,,,,15636589526.9,15634844308.52,,,,2657564914.42,1321059090.43,0.0,6172824.12,4401603465.43,,,,,,,,1349038696.49,760008999.58,54977189803.22,14382255950.87,1193938630.3,0.0,6797704877.32,85518810.08,3918939197.71,,32446520720.48,,,,,,,,,,,,,,,,,87423710523.7,710020552.82,,,,4987417050.31,64207299366.58,69194716416.89,156618426940.59,,1126992951.02,,,,,,,,,,,75269024.76,6875227795.16,21170680072.11,31271433835.42,3303956813.15,19621648443.02,5750351820.37,4407776289.55,1193938630.3,,,1 +300750.SZ,20200425,20200425,20191231,1,1,4,2208399700.0,21630448577.59,13652965292.41,1097245469.55,,32269635327.07,1389585592.37,9649949692.85,8338535645.35,4568565748.8,538163094.42,,,11480549879.88,,,,,,,,,1647816662.94,71694937173.28,,,1540452827.51,,,,,17417348593.44,1996524778.01,,,,,2302317207.14,,147951887.23,394096018.07,2079210533.02,,2248533970.82,29657039538.04,,,,1812135529.6,,,,,,,,,,,,,101351976711.32,4980563181.26,2125646681.77,,,,286915936.0,17420197790.4,10692137500.67,6161443242.83,,,1582275521.53,962984568.04,,2418687.95,5295890304.78,,,,,,,,1077468495.09,,45607378729.06,1508339195.7,873618580.61,,5289773262.4,91191949.71,813236654.86,,13556722824.54,,,,,,,,,,,,,,,,,59164101553.6,1074894790.0,,,,4052891263.24,38134983894.48,42187875157.72,101351976711.32,,620819644.93,,,,,,,,,,,,,17988485338.2,28112335291.07,4568565748.8,17417348593.44,1996524778.01,5298308992.73,873618580.61,,,0 +300750.SZ,20190425,20190425,20181231,1,1,4,2195017400.0,21372918712.25,9515006632.3,985878418.69,,27731189739.92,,9742890628.44,6224857396.53,589281314.94,864640798.47,,92808117.05,7076101849.47,,,,,,,,,1076991664.73,53911422755.37,1516521098.2,,965198180.81,,,,,11574665757.11,1623838222.94,,,,,1346171137.42,,100419270.78,305828515.4,1240737742.63,,1298901335.85,19972281261.14,,,,512661245.82,,,,,,,,,,,,,73883704016.51,3490767815.96,1180092100.11,,,,314247518.1,11841128076.55,7057075077.4,4994400867.91,,,1122253456.83,722536564.72,19842845.23,,2904341329.33,,,,,,,,929024032.37,,31084941868.55,,943414523.31,,2512382681.52,40984489.33,611042047.22,,7598591557.34,,,,,,,,,,,,,,,,,38683533425.89,793701060.0,,,,2261889695.31,32938280895.31,35200170590.62,73883704016.51,,-336839207.93,,,,,,,,,,,,,15967748024.97,18898203153.95,682089431.99,11574665757.11,1623838222.94,2924184174.56,943414523.31,,,0 +300750.SZ,20180522,20180522,20171231,1,1,4,1955193267.0,15354587816.94,6504904798.9,638253676.69,,14080936626.3,,5458335290.35,6918521550.9,90630850.1,305835456.79,,52310504.72,3417757092.32,,,,,,,,,2695030983.77,33033102914.27,1961291000.0,,791027220.9,,,,,8219496581.74,2974364031.46,,,,,1408760249.33,,100419270.78,139310483.53,510045198.34,,525068808.1,16629782844.18,,,,13744559.02,,,,,,,,,,,,,49662885758.45,2129095275.13,2245096000.7,,,,,8812715586.2,4978257325.43,203165478.74,,,517308034.99,436196766.94,15205262.28,,317156883.9,,,,,,,,364944599.97,,17890045939.15,,895045177.66,,1789007284.61,68992112.81,419460871.47,,5301600721.68,,,,,,,,,,,,,,,,,23191646660.83,,,,,1769799526.51,24701439571.11,26471239097.62,49662885758.45,,248500011.58,,,,,,,,,,,,,12376856841.25,13790972911.63,142941354.82,8219496581.74,2974364031.46,332362146.18,895045177.66,,,0 +300750.SZ,20180522,20180522,20161231,1,1,4,613321800.0,11608553987.09,2965362316.99,299841288.9,,2456530834.16,,570522837.1,7315840024.05,183806946.21,101183102.22,,7564477.11,1359772316.12,,,,,,,,,9760704235.56,21761324772.53,143400000.0,,169658176.39,,,,,3727473865.79,1226129855.44,,,,,621645950.26,,100419270.78,14206421.31,264912007.47,,559144024.11,6826989571.55,,,,,,,,,,,,,,,,,28588314344.08,302000000.0,1226587998.8,,,,,4394039468.71,3173395264.55,87727135.56,,,446249267.28,248297683.21,5776292.16,,360240077.32,,,,,,,,240511234.77,,10182824422.36,,950079201.39,,930580458.65,28130130.54,403581087.31,,2614370877.89,,,,,,,,,,,,,,,,,12797195300.25,,,,,302472760.51,15488646283.32,15791119043.83,28588314344.08,,1566890.34,,,,,,,5400000.0,,,,,,7886362861.15,7567434733.26,191371423.32,3727473865.79,1226129855.44,366016369.48,950079201.39,,,0 +300750.SZ,20180522,20180522,20151231,1,1,4,470590000.0,369014163.84,376303017.19,37079169.44,,1292935327.52,,422182294.7,2393930005.73,55484188.21,70469398.58,,,1041611188.85,,,,,,,,,227855161.28,5504467564.87,5000000.0,,,,,,,1313098299.52,474893794.61,,6710.03,,,502758023.88,,100419270.78,6133369.98,135241432.42,,630939035.37,3168489936.59,,,,,,,,,,,,,,,,,8672957501.46,,576558942.87,,,,,1041204473.76,1472399950.48,170045760.44,,,198490932.79,252617557.72,3639309.42,,311755056.01,,,,,,,,1322574459.48,,5349286442.97,,917666570.26,,581118102.95,30220779.54,296337217.09,,1825342669.84,,,,,,,,,,,,,,,,,7174629112.81,,,,,244663210.07,1253665178.58,1498328388.65,8672957501.46,,678828.11,,,,,,,,,,,,,2816112300.43,2513604424.24,55484188.21,1313105009.55,474893794.61,315394365.43,917666570.26,,,0 diff --git a/data/CN/300750.SZ/cash_flow.csv b/data/CN/300750.SZ/cash_flow.csv new file mode 100644 index 0000000..33a7611 --- /dev/null +++ b/data/CN/300750.SZ/cash_flow.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,comp_type,report_type,end_type,net_profit,finan_exp,c_fr_sale_sg,recp_tax_rends,n_depos_incr_fi,n_incr_loans_cb,n_inc_borr_oth_fi,prem_fr_orig_contr,n_incr_insured_dep,n_reinsur_prem,n_incr_disp_tfa,ifc_cash_incr,n_incr_disp_faas,n_incr_loans_oth_bank,n_cap_incr_repur,c_fr_oth_operate_a,c_inf_fr_operate_a,c_paid_goods_s,c_paid_to_for_empl,c_paid_for_taxes,n_incr_clt_loan_adv,n_incr_dep_cbob,c_pay_claims_orig_inco,pay_handling_chrg,pay_comm_insur_plcy,oth_cash_pay_oper_act,st_cash_out_act,net_cash_flow,oth_recp_ral_inv_act,c_disp_withdrwl_invest,c_recp_return_invest,n_recp_disp_fiolta,n_recp_disp_sobu,stot_inflows_inv_act,c_pay_acq_const_fiolta,c_paid_invest,n_disp_subs_oth_biz,oth_pay_ral_inv_act,n_incr_pledge_loan,stot_out_inv_act,n_cashflow_inv_act,c_recp_borrow,proc_issue_bonds,oth_cash_recp_ral_fnc_act,stot_cash_in_fnc_act,free_cashflow,c_prepay_amt_borr,c_pay_dist_dpcp_int_exp,incl_dvd_profit_paid_sc_ms,oth_cashpay_ral_fnc_act,stot_cashout_fnc_act,n_cash_flows_fnc_act,eff_fx_flu_cash,n_incr_cash_cash_equ,c_cash_equ_beg_period,c_cash_equ_end_period,c_recp_cap_contrib,incl_cash_rec_saims,uncon_invest_loss,prov_depr_assets,depreciation,amort_intang_assets,lt_amort_deferred_exp,decr_deferred_exp,incr_acc_exp,loss_disp_fiolta,loss_scr_fa,loss_fv_chg,invest_loss,decr_def_inc_tax_assets,incr_def_inc_tax_liab,decr_inventories,decr_oper_payable,incr_oper_payable,others,im_net_cashflow_oper_act,conv_debt_into_cap,conv_copbonds_due_within_1y,fa_fnc_leases,im_n_incr_cash_equ,net_dism_capital_add,net_cash_rece_sec,credit_impa_loss,use_right_asset_dep,oth_loss_asset,end_bal_cash,beg_bal_cash,end_bal_cash_equ,beg_bal_cash_equ,update_flag +300750.SZ,20251021,20251021,20250930,1,1,3,,,338126567000.0,6320906000.0,,,,,,,,,,,,18609171000.0,363056644000.0,224120825000.0,23413386000.0,26742749000.0,,,,,,8119254000.0,282396214000.0,80660430000.0,19691000.0,4443202000.0,1942895000.0,68109000.0,27411000.0,6501309000.0,30087780000.0,31540210000.0,2045273000.0,119537000.0,,63792799000.0,-57291490000.0,30597681000.0,,1125480000.0,74995566000.0,,32237114000.0,33558798000.0,,4923115000.0,70719026000.0,4276539000.0,-621254000.0,27024225000.0,270159734000.0,297183959000.0,43272406000.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 +300750.SZ,20250315,20250315,20241231,1,1,4,54006794000.0,5165670000.0,417525378000.0,10506188000.0,,,,,,,,,,,,16847851000.0,444879417000.0,285455632000.0,25499653000.0,28529188000.0,,,,,,8404599000.0,347889072000.0,96990345000.0,963920000.0,2028899000.0,1838083000.0,75110000.0,,4906012000.0,31179943000.0,22169451000.0,244022000.0,187907000.0,,53781323000.0,-48875311000.0,30540129000.0,,292179000.0,33392735000.0,,19972240000.0,25807432000.0,496051000.0,2137299000.0,47916971000.0,-14524236000.0,-1596552000.0,31994247000.0,238165487000.0,270159734000.0,2560428000.0,1959694000.0,,8423325000.0,22437872000.0,470401000.0,1790382000.0,,,-19319000.0,257488000.0,-664223000.0,-4384806000.0,-6700359000.0,320345000.0,-16606853000.0,634969000.0,31206935000.0,,96990345000.0,,,,31994247000.0,,,872526000.0,239273000.0,,270159734000.0,238165487000.0,,,0 +300750.SZ,20241019,20241019,20240930,1,1,3,,,317539939100.0,8174888000.0,,,,,,,,,,,,10828118000.0,336542945100.0,220637018600.0,19328624300.0,23235105400.0,,,,,,5898595800.0,269099344000.0,67443601100.0,525584000.0,512532700.0,1472067000.0,55631200.0,,2565814800.0,21268347400.0,28950734900.0,241214500.0,,,50460296800.0,-47894481900.0,17823560900.0,,,19734321900.0,-13691482401.3572,13951582300.0,24858845700.0,496051400.0,1419576600.0,40230004500.0,-20495682600.0,-2265019600.0,-3211583000.0,238165486900.0,234953903900.0,1910761000.0,1488073700.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +300750.SZ,20240316,20240316,20231231,1,1,4,46761034500.0,3013733800.0,417943222600.0,12739610500.0,,,,,,,,,,,,15724664000.0,446407497100.0,310521178200.0,21140596600.0,17117191500.0,,,,,,4802406300.0,353581372700.0,92826124400.0,1239798700.0,7651158400.0,1711392900.0,12853500.0,3306900.0,10618510400.0,33624896500.0,5649689400.0,321445400.0,210243200.0,,39806274600.0,-29187764200.0,46595746400.0,,366758000.0,50286500800.0,32528281889.0684,23795322000.0,9481092900.0,469828200.0,2293723300.0,35570138100.0,14716362700.0,2181446800.0,80536169700.0,157629317200.0,238165486900.0,3323996400.0,2926448200.0,,5853926900.0,21098130600.0,330992000.0,1099265800.0,,,-16983500.0,55557800.0,-46270400.0,-3825925800.0,-7809038300.0,156446100.0,31029323200.0,-34168243000.0,29013934100.0,,92826124400.0,,,,80536169700.0,,,254041400.0,117593400.0,,238165486900.0,157629317200.0,,,1 +300750.SZ,20230310,20230310,20221231,1,1,4,33457143500.0,969747300.0,305775248400.0,9478689900.0,,,,,,,,,,,,14557214600.0,329811152800.0,235327104000.0,18157352000.0,10529733400.0,,,,,,4588120100.0,268602309600.0,61208843300.0,1531307500.0,1307996200.0,740372300.0,593900.0,,3580269900.0,48215268100.0,12764660700.0,,6740182400.0,,67720111200.0,-64139841300.0,50957726600.0,,5208177600.0,103621111500.0,26286437536.8799,17605770500.0,3551469400.0,,197440400.0,21354680400.0,82266431200.0,2788148900.0,82123582000.0,75505735200.0,157629317200.0,47455207300.0,2092259300.0,,2826926600.0,11960578600.0,236449600.0,894129200.0,,,5322700.0,37927500.0,-400241300.0,-3044935500.0,-4451227000.0,9145100.0,-39877670300.0,-58697293400.0,115418278300.0,,61208843300.0,,,,82123582000.0,,,1146247900.0,161383600.0,,157629317200.0,75505735200.0,,,0 +300750.SZ,20220422,20220422,20211231,1,1,4,17860730100.0,1580329100.0,130616575600.0,414542800.0,,,,,,,,,,,,14298549300.0,145329667700.0,86393876300.0,9423183000.0,4127789000.0,,,,,,2476810700.0,102421659000.0,42908008700.0,4094314000.0,571745100.0,228638800.0,3223100.0,58414400.0,4956335300.0,43767770800.0,11725718900.0,295800300.0,2948104700.0,,58737394700.0,-53781059400.0,26276582500.0,,3235028400.0,31063023600.0,21236726212.8648,5457907900.0,1568025100.0,,378512600.0,7404445600.0,23658578000.0,-711778200.0,12073749200.0,63431986000.0,75505735200.0,1551412800.0,795200000.0,,2047739400.0,5847138200.0,159202600.0,340253100.0,,,23190300.0,67375700.0,,-1561030800.0,-1232106000.0,215449900.0,-28856721000.0,-27816000500.0,73855678100.0,,42908008700.0,,,,12073749200.0,,,,90030200.0,,75505735200.0,63431986000.0,,,1 +300750.SZ,20210428,20210428,20201231,1,1,4,6103918052.46,772394855.22,54002988575.54,151928522.94,,,,,,,,,,,,6397335867.06,60552252965.54,33461951736.87,4027848128.71,2265587429.63,,,,,,2366963038.37,42122350333.58,18429902631.96,2735874410.58,44779761.91,24087086.33,24649.08,,2804765907.9,13302355759.04,4088418479.98,,466447835.43,,17857222074.45,-15052456166.55,9450920678.78,,13199266407.5,43186548078.44,-3068497270.4043,4743701233.75,898809819.12,7825842.99,112602486.19,5755113539.06,37431434539.38,-576950661.91,40231930342.88,23200055644.02,63431985986.9,20536360992.16,923523424.41,,1169471948.67,4576797794.5,135243814.08,155847906.24,,,9890379.23,47137042.0,-286915936.0,26192466.45,-772834310.15,-5673139.63,-2401630461.19,-3415569969.88,11747641389.69,,18429902631.96,,,,40231930342.88,,,,,,63431985986.9,23200055644.02,,,1 +300750.SZ,20200425,20200425,20191231,1,1,4,5012673897.87,270468281.25,52149078427.56,395686584.53,,,,,,,,,,,,3763244302.06,56308009314.15,33260080970.87,3704062487.97,2688800053.78,,,,,,3183111244.73,42836054757.35,13471954556.8,15477402748.49,0.23,2563085.15,15088255.27,10026813.18,15505080902.32,9626986411.07,907758218.55,,3114021020.85,,13648765650.47,1856315251.85,4616632167.94,,1498800000.0,7333217057.62,-817733.8461,2418452429.42,573302388.77,,173132704.46,3164887522.65,4168329534.97,14801651.37,19511400994.99,3688654649.03,23200055644.02,1217784889.68,721974398.68,,1670005549.8,4137828483.56,113911953.13,157299905.57,,,-1382204.06,18928639.15,-27331582.1,-20752318.89,-838669146.42,50207460.38,-5775387378.74,-4768945377.05,12637959848.72,,13471954556.8,,,,19511400994.99,,,,,,23200055644.02,3688654649.03,,,0 +300750.SZ,20190425,20190425,20181231,1,1,4,3735896487.72,262258592.79,33853639616.99,37127687.41,,,,,,,,,,,,2274383357.82,36165150662.22,19041614626.94,2299961469.07,1868081717.0,,,,,,1639227148.68,24848884961.69,11316265700.53,70629509.16,5258600.0,3655688.2,294506.1,,79838303.46,6629274672.98,192025942.3,,12746210861.04,,19567511476.32,-19487673172.86,4123196326.07,,510282473.44,10908434734.88,5771848024.1698,3493721841.0,216119536.13,,155877429.51,3865718806.64,7042715928.24,27440781.07,-1101250763.02,4789905412.05,3688654649.03,6274955935.37,108785163.6,,974912150.01,2125110234.63,95733856.19,83377065.66,,,91538964.57,15782022.0,314247518.1,-184397531.48,-669755258.27,15459358.23,-4371312786.43,-3143244270.65,11851141407.59,119517889.87,11316265700.53,,,,-1101250763.02,,,,,,3688654649.03,4789905412.05,,,0 +300750.SZ,20180522,20180522,20171231,1,1,4,4194056577.66,112588566.26,18872908615.92,21013193.89,,,,,,,,,,,,529176792.58,19423098602.39,12357592298.17,2122637417.2,1493296692.69,,,,,,1108823984.08,17082350392.14,2340748210.25,758831340.52,448002008.14,2565089.09,572921.94,,1209971359.69,7180281117.24,1665503314.78,,656.0,,8845785088.02,-7635813728.33,4476769278.83,,9028894.69,10664444151.52,4099608510.9298,1421628377.86,81600198.58,,228481941.8,1731710518.24,8932733633.28,-13763656.75,3623904458.45,1166000953.6,4789905412.05,6178645978.0,1168645100.0,,244744030.88,1279538608.41,75101189.69,26541967.12,,,78311541.52,,,-1344305303.77,-245133190.87,-2604999.44,-2290181545.88,-5075995986.77,5163299958.56,124786796.88,2340748210.25,,,,3623904458.45,,,,,,4789905412.05,1166000953.6,,,0 +300750.SZ,20180522,20180522,20161231,1,1,4,2918436854.29,65609349.18,11524664337.65,1476437.43,,,,,,,,,,,,421522517.2,11947663292.28,5954917349.27,1249671965.87,1559891278.12,,,,,,1074055972.23,9838536565.49,2109126726.79,61944950.69,10027000.0,,5046154.0,,77018104.69,2800818908.69,262828666.62,,9441784763.04,,12505432338.35,-12428414233.66,1930654288.91,,747351550.61,13809851365.52,-10067555462.0948,937312259.12,53696810.21,,1847820297.11,2838829366.44,10971021999.08,-2077449.43,649657042.78,516343910.82,1166000953.6,11131845526.0,7840000.0,,233858870.04,731417250.67,47998770.85,5071074.74,,,158205248.94,,,-76080348.84,-129670575.05,-2090649.0,-320169108.33,-14762136096.64,13031595453.28,207080632.66,2109126726.79,,,,649657042.78,,,,,,1166000953.6,516343910.82,,,0 +300750.SZ,20180522,20180522,20151231,1,1,4,950581074.45,113184042.2,4153743551.55,670144.46,,,,,,,,,,,,221173494.7,4375587190.71,2302132506.69,437554338.26,474554245.93,,,,,,496812115.82,3711053206.7,664533984.01,1088566666.05,,,,,1088566666.05,1553786649.12,,173225298.0,2900000.0,,1729911947.12,-641345281.07,830867703.13,,893371268.45,1874742721.58,1290805842.4247,628641795.8,69496886.29,,736699312.71,1434837994.8,439904726.78,-800509.91,462292919.81,54050991.01,516343910.82,150503750.0,500000.0,,58641678.66,164100734.24,24986934.33,3237903.35,,,590103.88,,,-1455431.76,-110143619.54,-2194284.46,-697915929.01,-2315594532.87,2476515310.54,,664533984.01,,,,462292919.81,,,,,,516343910.82,54050991.01,,,0 diff --git a/data/CN/300750.SZ/income_statement.csv b/data/CN/300750.SZ/income_statement.csv new file mode 100644 index 0000000..d228873 --- /dev/null +++ b/data/CN/300750.SZ/income_statement.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,report_type,comp_type,end_type,basic_eps,diluted_eps,total_revenue,revenue,int_income,prem_earned,comm_income,n_commis_income,n_oth_income,n_oth_b_income,prem_income,out_prem,une_prem_reser,reins_income,n_sec_tb_income,n_sec_uw_income,n_asset_mg_income,oth_b_income,fv_value_chg_gain,invest_income,ass_invest_income,forex_gain,total_cogs,oper_cost,int_exp,comm_exp,biz_tax_surchg,sell_exp,admin_exp,fin_exp,assets_impair_loss,prem_refund,compens_payout,reser_insur_liab,div_payt,reins_exp,oper_exp,compens_payout_refu,insur_reser_refu,reins_cost_refund,other_bus_cost,operate_profit,non_oper_income,non_oper_exp,nca_disploss,total_profit,income_tax,n_income,net_income,minority_gain,oth_compr_income,t_compr_income,compr_inc_attr_p,compr_inc_attr_m_s,ebit,ebitda,insurance_exp,undist_profit,distable_profit,rd_exp,fin_exp_int_exp,fin_exp_int_inc,transfer_surplus_rese,transfer_housing_imprest,transfer_oth,adj_lossgain,withdra_legal_surplus,withdra_legal_pubfund,withdra_biz_devfund,withdra_rese_fund,withdra_oth_ersu,workers_welfare,distr_profit_shrhder,prfshare_payable_dvd,comshare_payable_dvd,capit_comstock_div,continued_net_profit,update_flag +300750.SZ,20251021,20251021,20250930,1,1,3,11.02,11.02,283071987000.0,283071987000.0,,,,,,,,,,,,,,,878537000.0,5236818000.0,4943750000.0,,236417478000.0,211427147000.0,,,1843066000.0,2408522000.0,8231715000.0,-7015786000.0,-4077847000.0,,,,,,,,,,,60552312000.0,372284000.0,212195000.0,,60712402000.0,8415535000.0,52296866000.0,49034109000.0,3262757000.0,7455238000.0,59752104000.0,56472049000.0,3280055000.0,51409807000.0,,,,,15067826000.0,2183477000.0,8060682000.0,,,,,,,,,,,,,,,52296866000.0,1 +300750.SZ,20250315,20250315,20241231,1,1,4,11.58,11.58,362012554000.0,362012554000.0,,,,,,,,,,,,,,,664223000.0,3987823000.0,3743040000.0,,312599750000.0,273518959000.0,,,2057466000.0,3562797000.0,9689839000.0,-4131918000.0,-8423325000.0,,,,,,,,,,,64051799000.0,135422000.0,1005182000.0,,63182039000.0,9175245000.0,54006794000.0,50744682000.0,3262113000.0,-1687613000.0,52319181000.0,48940398000.0,3378782000.0,61464661000.0,86402589000.0,,,,18606756000.0,3879076000.0,9502997000.0,,,,,,,,,,,,,,,54006794000.0,1 +300750.SZ,20241019,20241019,20240930,1,1,3,8.1894,8.1829,259044748600.0,259044748600.0,,,,,,,,,,,,,,,190410200.0,3127166700.0,2712745400.0,,223014892600.0,186032900900.0,,,1557473800.0,10927707300.0,6774455800.0,-2894208700.0,-6651709400.0,,,,,,,,,,,46125749700.0,142603700.0,547867400.0,,45720486000.0,6987230700.0,38733255300.0,36001073800.0,2732181500.0,-568007300.0,38165248000.0,35252110200.0,2913137800.0,46518082900.0,,,,,13073136100.0,2966060400.0,7126535700.0,,,,,,,,,,,,,,,38733255300.0,1 +300750.SZ,20240316,20240316,20231231,1,1,4,11.79,11.779,400917044900.0,400917044900.0,,,,,,,,,,,,,,,46270400.0,3189201200.0,3745762000.0,,356718585800.0,309070434000.0,,,1695507700.0,17954440500.0,8461824300.0,-4927697400.0,-5853926900.0,,,,,,,,,,,53718302100.0,503675200.0,307924000.0,,53914053300.0,7153018800.0,46761034500.0,44121248300.0,2639786100.0,-4711859000.0,42049175500.0,39568104200.0,2481071300.0,51182922200.0,73828904000.0,,,,18356108400.0,3446515800.0,8321802100.0,,,,,,,,,,,,,,,46761034500.0,1 +300750.SZ,20230310,20230310,20221231,1,1,4,12.9178,12.8795,328593987500.0,328593987500.0,,,,,,,,,,,,,,,400241300.0,2514538600.0,2614517000.0,,297718806500.0,262049609200.0,,,907484500.0,11099401200.0,6978669400.0,-2799985800.0,-2826926600.0,,,,,,,,,,,36821983000.0,159426800.0,308553600.0,,36672856200.0,3215712700.0,33457143500.0,30729163500.0,2727980100.0,5046510500.0,38503654000.0,35452143600.0,3051510400.0,31406613600.0,44659154600.0,,,,15510453500.0,2132375400.0,3987365200.0,,,,,,,,,,,,,,,33457143500.0,1 +300750.SZ,20220422,20220422,20211231,1,1,4,6.876,6.8392,130355796400.0,130355796400.0,,,,,,,,,,,,,,,,1232699000.0,575836900.0,,113415030000.0,96093722300.0,,,486534200.0,4367869400.0,3368937100.0,-641200000.0,-2034437800.0,,,,,,,,,,,19823729200.0,183039700.0,119639800.0,,19887129100.0,2026399000.0,17860730100.0,15931317900.0,1929412300.0,3013987200.0,20874717400.0,19012644700.0,1862072600.0,18123376500.0,24560000600.0,,,,7691427600.0,1161100400.0,2323262000.0,,,,,,,,,,,,,,,17860730100.0,1 +300750.SZ,20210428,20210428,20201231,1,1,4,2.4942,2.4848,50319487697.2,50319487697.2,,,,,,,,,,,,,,,286915936.0,-117648607.8,-4027332.09,,44655315480.69,36349153592.22,,,295129893.6,2216709532.73,1768115240.89,-712642421.45,-827489419.04,,,,,,,,,,,6959489551.43,94318062.61,71254204.8,,6982553409.24,878635356.78,6103918052.46,5583338710.38,520579342.08,505828698.82,6609746751.28,6089512016.47,520234734.81,6289688334.33,11157577849.15,,,,3569377694.03,640434316.54,1494600958.67,,,,,,,,,,,,,,,6103918052.46,1 +300750.SZ,20200425,20200425,20191231,1,1,4,2.0937,2.0887,45788020642.41,45788020642.41,,,,,,,,,,,,,,,27331582.1,-79604902.02,-11899568.84,,40624707856.41,32482760512.62,,,272228105.62,2156553541.51,1832673929.87,-781621299.53,-1434329163.69,,,,,,,,,,,5758793258.1,62428112.63,60456806.48,,5760764564.25,748090666.38,5012673897.87,4560307432.71,452366465.16,957658852.86,5970332750.73,5517966285.57,452366465.16,5722463917.76,10131504260.02,,,,2992107516.52,289254465.49,1078256966.28,,,,,,,,,,,,,,,5012673897.87,0 +300750.SZ,20190425,20190425,20181231,1,1,4,1.6412,1.6407,29611265434.22,29611265434.22,,,,,,,,,,,,,,,-314247518.1,184397531.48,-4264014.31,,25729175371.38,19902284153.15,,,171183911.7,1378868425.55,1590659572.27,-279733226.14,974912150.01,,,,,,,,,,,4168476326.68,62303262.42,25966337.17,,4204813251.93,468916764.21,3735896487.72,3387035207.64,348861280.08,-585339219.51,3150557268.21,2801695988.13,348861280.08,4110132051.73,6414353208.21,,,,1991000384.84,204435332.83,565817388.3,,,,,,,,,,,,,,,3735896487.72,0 +300750.SZ,20180522,20180522,20171231,1,1,4,2.0084,,19996860806.33,19996860806.33,,,,,,,,,,,,,,,,1344305303.77,-49976783.37,,16875255721.27,12740187148.7,,,95900521.0,795766091.83,2956488278.51,42169650.35,244744030.88,,,,,,,,,,,4832020495.66,18655542.93,2575814.31,,4848100224.28,654043646.62,4194056577.66,3877954869.7,316101707.96,246933121.24,4440989698.9,4124887990.94,316101707.96,3608196383.76,4989378148.98,,,,,,,,,,,,,,,,,,,,,4194056577.66,0 +300750.SZ,20180522,20180522,20161231,1,1,4,1.8736,,14878985098.12,14878985098.12,,,,,,,,,,,,,,,,76080348.84,21669756.19,,11584739495.37,8376801892.4,,,109215284.54,632190722.09,2152228887.34,80443838.96,233858870.04,,,,,,,,,,,3212120702.65,188974466.62,881463.97,,3400213705.3,481776851.01,2918436854.29,2851821419.26,66615435.03,888062.23,2919324916.52,2852709481.49,66615435.03,3374689441.71,4159176537.97,,,,,,,,,,,,,,,,,,,,,2918436854.29,0 +300750.SZ,20180522,20180522,20151231,1,1,4,0.7755,,5702884874.34,5702884874.34,,,,,,,,,,,,,,,,1455431.76,-9658518.48,,4658155646.48,3499202013.73,,,39629825.6,330920265.24,620575323.94,109186539.31,58641678.66,,,,,,,,,,,1045594555.74,71190367.14,16752779.33,,1100032143.55,149451069.1,950581074.45,930646375.1,19934699.35,494869.71,951075944.16,931141244.81,19934699.35,1153915767.17,1346241339.09,,,,,,,,,,,,,,,,,,,,,950581074.45,0 diff --git a/data/CN/300750.SZ/report.html b/data/CN/300750.SZ/report.html new file mode 100644 index 0000000..f0ee38d --- /dev/null +++ b/data/CN/300750.SZ/report.html @@ -0,0 +1,753 @@ +Financial Report +

Financial Report

+
yearrevenuenet_incomeROENetMarginDebtToEquityCurrentRatio
2024391,035,000,00093,736,000,000147.28%23.97%4.550.82
2023383,285,000,00096,995,000,000156.08%25.31%4.670.99
+ + + + + + +
代码简称日期上市日期PEPB股息率(%)
300750.SZ宁德时代2025-12-142018-06-1135.245.691.72%
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
指标2025 Q32024202320222021202020192018201720162015
主要指标
ROE15.60%20.55%22.32%18.68%18.85%8.70%11.96%10.28%15.70%18.41%74.23%
ROA5.47%6.45%6.15%5.11%5.18%3.56%4.50%4.58%7.81%9.98%10.73%
ROCE/ROIC16.36%24.89%25.89%19.09%21.44%9.80%15.01%12.48%14.61%21.79%92.04%
毛利率25.31%24.44%22.91%20.25%26.28%27.76%29.06%32.79%36.29%43.70%38.64%
净利润率17.32%14.02%11.01%9.35%12.22%11.10%9.96%11.44%19.39%19.17%16.32%
收入2,830.723,620.134,009.173,285.941,303.56503.19457.88296.11199.97148.7957.03
收入增速9.28%-9.70%22.01%152.07%159.06%9.90%54.63%48.08%34.40%160.90%-
净利润490.34507.45441.21307.29159.3155.8345.6033.8738.7828.529.31
净利润增速36.20%15.01%43.58%92.89%185.34%22.43%34.64%-12.66%35.98%206.43%-
经营净现金流806.60969.90928.26612.09429.08184.30134.72113.1623.4121.096.65
资本开支300.88311.80336.25482.15437.68133.0296.2766.2971.8028.0115.54
自由现金流505.73658.10592.01129.94-8.6051.2838.4546.87-48.40-6.92-8.89
分红-----------
总资产8,960.827,866.587,171.686,009.523,076.671,566.181,013.52738.84496.63285.8886.73
净资产3,142.482,469.301,977.081,644.81845.13642.07381.35329.38247.01154.8912.54
商誉8.918.957.087.045.281.481.481.001.001.001.00
费用指标
销售费用率0.85%0.98%4.48%3.38%3.35%4.41%4.71%4.66%3.98%4.25%5.80%
管理费用率2.91%2.68%2.11%2.12%2.58%3.51%4.00%5.37%14.78%14.46%10.88%
研发费用率5.32%5.14%4.58%4.72%5.90%7.09%6.53%6.72%---
其他费用率-1.09%1.63%0.74%0.68%2.23%1.66%3.85%4.60%-1.87%5.82%5.64%
折旧费用占比-6.20%5.26%3.64%4.49%9.10%9.04%7.18%6.40%4.92%2.88%
所得税率13.86%14.52%13.27%8.77%10.19%12.58%12.99%11.15%13.49%14.17%13.59%
资产占比
现金占比36.18%38.58%36.85%31.79%28.95%43.69%31.84%37.53%28.35%8.59%14.91%
库存占比8.95%7.61%6.34%12.76%13.07%8.44%11.33%9.58%6.88%4.76%12.01%
应收款占比7.42%8.15%8.93%9.65%7.72%7.21%8.23%8.43%13.93%25.59%27.60%
预付款占比1.51%0.76%0.97%2.64%2.10%0.64%0.53%1.17%0.62%0.35%0.81%
固定资产占比14.35%14.31%16.09%14.82%13.42%12.53%17.19%15.67%16.55%13.04%15.14%
长期投资占比6.68%6.97%6.98%2.93%3.56%3.07%1.52%1.31%1.59%0.59%-
商誉占比0.10%0.11%0.10%0.12%0.17%0.09%0.15%0.14%0.20%0.35%1.16%
其他资产占比24.80%23.51%23.75%25.30%31.02%24.32%29.22%26.19%31.87%46.72%28.37%
应付款占比14.73%16.65%16.32%15.73%15.86%9.98%10.55%9.55%10.02%11.10%16.98%
预收款占比4.54%3.54%3.34%3.73%3.75%4.39%6.08%6.76%0.41%0.31%1.96%
短期借款占比4.50%5.41%3.09%3.60%5.09%4.91%3.16%2.85%5.26%5.13%21.90%
长期借款占比8.75%10.33%11.64%9.83%7.19%3.87%4.91%4.72%4.29%1.06%0.00%
运营资产占比-1.38%-3.67%-3.43%5.57%3.28%1.92%3.46%2.86%11.00%19.29%21.49%
有息负债率13.25%15.74%14.73%13.44%12.28%8.78%8.07%7.58%9.54%6.19%21.90%
周转能力
存货周转天数13879531061521321291299759108
应收款周转天数8564586466816676126179153
应付款周转天数227174138131185156120129142138153
固定资产周转率2.203.223.473.693.162.562.632.562.433.994.34
总资产周转率0.320.460.560.550.420.320.450.400.400.520.66
人均效率
员工人数-131,988---------
人均创收(万)-274.28---------
人均创利(万)-38.45---------
人均薪酬(万)-19.32---------
市场表现
股价402.00266.00163.26393.42588.00351.11106.4073.80---
市值(亿)18,33811,7137,1829,60913,7058,1792,3501,620---
PE36.1426.5523.3760.32245.47179.3569.3741.77---
PB6.324.943.986.4018.7413.336.365.01---
股东户数226,368212,061260,992183,317141,963133,06076,71082,514---
\ No newline at end of file diff --git a/data/CN/300750.SZ/report.md b/data/CN/300750.SZ/report.md new file mode 100644 index 0000000..d56d5e0 --- /dev/null +++ b/data/CN/300750.SZ/report.md @@ -0,0 +1,87 @@ +# 宁德时代 (300750.SZ) - Financial Report +*Report generated on: 2025-12-14* + +| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) | +|:---|:---|:---|:---|:---|:---| +| 300750.SZ | 宁德时代 | 2018-06-11 | 35.24 | 5.69 | 1.72% | + + +## 主要指标 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| ROE | 15.60% | 20.55% | 22.32% | 18.68% | 18.85% | 8.70% | 11.96% | 10.28% | 15.70% | 18.41% | 74.23% | +| ROA | 5.47% | 6.45% | 6.15% | 5.11% | 5.18% | 3.56% | 4.50% | 4.58% | 7.81% | 9.98% | 10.73% | +| ROCE/ROIC | 16.36% | 24.89% | 25.89% | 19.09% | 21.44% | 9.80% | 15.01% | 12.48% | 14.61% | 21.79% | 92.04% | +| 毛利率 | 25.31% | 24.44% | 22.91% | 20.25% | 26.28% | 27.76% | 29.06% | 32.79% | 36.29% | 43.70% | 38.64% | +| 净利润率 | 17.32% | 14.02% | 11.01% | 9.35% | 12.22% | 11.10% | 9.96% | 11.44% | 19.39% | 19.17% | 16.32% | +| 收入(亿) | 2,830.72 | 3,620.13 | 4,009.17 | 3,285.94 | 1,303.56 | 503.19 | 457.88 | 296.11 | 199.97 | 148.79 | 57.03 | +| 收入增速 | 9.28% | -9.70% | 22.01% | 152.07% | 159.06% | 9.90% | 54.63% | 48.08% | 34.40% | 160.90% | - | +| 净利润(亿) | 490.34 | 507.45 | 441.21 | 307.29 | 159.31 | 55.83 | 45.60 | 33.87 | 38.78 | 28.52 | 9.31 | +| 净利润增速 | 36.20% | 15.01% | 43.58% | 92.89% | 185.34% | 22.43% | 34.64% | -12.66% | 35.98% | 206.43% | - | +| 经营净现金流(亿) | 806.60 | 969.90 | 928.26 | 612.09 | 429.08 | 184.30 | 134.72 | 113.16 | 23.41 | 21.09 | 6.65 | +| 资本开支(亿) | 300.88 | 311.80 | 336.25 | 482.15 | 437.68 | 133.02 | 96.27 | 66.29 | 71.80 | 28.01 | 15.54 | +| 自由现金流(亿) | 505.73 | 658.10 | 592.01 | 129.94 | -8.60 | 51.28 | 38.45 | 46.87 | -48.40 | -6.92 | -8.89 | +| 分红(亿) | - | - | - | - | - | - | - | - | - | - | - | +| 总资产(亿) | 8,960.82 | 7,866.58 | 7,171.68 | 6,009.52 | 3,076.67 | 1,566.18 | 1,013.52 | 738.84 | 496.63 | 285.88 | 86.73 | +| 净资产(亿) | 3,142.48 | 2,469.30 | 1,977.08 | 1,644.81 | 845.13 | 642.07 | 381.35 | 329.38 | 247.01 | 154.89 | 12.54 | +| 商誉(亿) | 8.91 | 8.95 | 7.08 | 7.04 | 5.28 | 1.48 | 1.48 | 1.00 | 1.00 | 1.00 | 1.00 | + + +## 费用指标 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 销售费用率 | 0.85% | 0.98% | 4.48% | 3.38% | 3.35% | 4.41% | 4.71% | 4.66% | 3.98% | 4.25% | 5.80% | +| 管理费用率 | 2.91% | 2.68% | 2.11% | 2.12% | 2.58% | 3.51% | 4.00% | 5.37% | 14.78% | 14.46% | 10.88% | +| 研发费用率 | 5.32% | 5.14% | 4.58% | 4.72% | 5.90% | 7.09% | 6.53% | 6.72% | - | - | - | +| 其他费用率 | -1.09% | 1.63% | 0.74% | 0.68% | 2.23% | 1.66% | 3.85% | 4.60% | -1.87% | 5.82% | 5.64% | +| 折旧费用占比 | - | 6.20% | 5.26% | 3.64% | 4.49% | 9.10% | 9.04% | 7.18% | 6.40% | 4.92% | 2.88% | +| 所得税率 | 13.86% | 14.52% | 13.27% | 8.77% | 10.19% | 12.58% | 12.99% | 11.15% | 13.49% | 14.17% | 13.59% | + + +## 资产占比 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 现金占比 | 36.18% | 38.58% | 36.85% | 31.79% | 28.95% | 43.69% | 31.84% | 37.53% | 28.35% | 8.59% | 14.91% | +| 库存占比 | 8.95% | 7.61% | 6.34% | 12.76% | 13.07% | 8.44% | 11.33% | 9.58% | 6.88% | 4.76% | 12.01% | +| 应收款占比 | 7.42% | 8.15% | 8.93% | 9.65% | 7.72% | 7.21% | 8.23% | 8.43% | 13.93% | 25.59% | 27.60% | +| 预付款占比 | 1.51% | 0.76% | 0.97% | 2.64% | 2.10% | 0.64% | 0.53% | 1.17% | 0.62% | 0.35% | 0.81% | +| 固定资产占比 | 14.35% | 14.31% | 16.09% | 14.82% | 13.42% | 12.53% | 17.19% | 15.67% | 16.55% | 13.04% | 15.14% | +| 长期投资占比 | 6.68% | 6.97% | 6.98% | 2.93% | 3.56% | 3.07% | 1.52% | 1.31% | 1.59% | 0.59% | - | +| 商誉占比 | 0.10% | 0.11% | 0.10% | 0.12% | 0.17% | 0.09% | 0.15% | 0.14% | 0.20% | 0.35% | 1.16% | +| 其他资产占比 | 24.80% | 23.51% | 23.75% | 25.30% | 31.02% | 24.32% | 29.22% | 26.19% | 31.87% | 46.72% | 28.37% | +| 应付款占比 | 14.73% | 16.65% | 16.32% | 15.73% | 15.86% | 9.98% | 10.55% | 9.55% | 10.02% | 11.10% | 16.98% | +| 预收款占比 | 4.54% | 3.54% | 3.34% | 3.73% | 3.75% | 4.39% | 6.08% | 6.76% | 0.41% | 0.31% | 1.96% | +| 短期借款占比 | 4.50% | 5.41% | 3.09% | 3.60% | 5.09% | 4.91% | 3.16% | 2.85% | 5.26% | 5.13% | 21.90% | +| 长期借款占比 | 8.75% | 10.33% | 11.64% | 9.83% | 7.19% | 3.87% | 4.91% | 4.72% | 4.29% | 1.06% | 0.00% | +| 运营资产占比 | -1.38% | -3.67% | -3.43% | 5.57% | 3.28% | 1.92% | 3.46% | 2.86% | 11.00% | 19.29% | 21.49% | +| 有息负债率 | 13.25% | 15.74% | 14.73% | 13.44% | 12.28% | 8.78% | 8.07% | 7.58% | 9.54% | 6.19% | 21.90% | + + +## 周转能力 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 存货周转天数 | 138 | 79 | 53 | 106 | 152 | 132 | 129 | 129 | 97 | 59 | 108 | +| 应收款周转天数 | 85 | 64 | 58 | 64 | 66 | 81 | 66 | 76 | 126 | 179 | 153 | +| 应付款周转天数 | 227 | 174 | 138 | 131 | 185 | 156 | 120 | 129 | 142 | 138 | 153 | +| 固定资产周转率 | 2.20 | 3.22 | 3.47 | 3.69 | 3.16 | 2.56 | 2.63 | 2.56 | 2.43 | 3.99 | 4.34 | +| 总资产周转率 | 0.32 | 0.46 | 0.56 | 0.55 | 0.42 | 0.32 | 0.45 | 0.40 | 0.40 | 0.52 | 0.66 | + + +## 人均效率 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 员工人数 | - | 131,988 | - | - | - | - | - | - | - | - | - | +| 人均创收(万) | - | 274.28 | - | - | - | - | - | - | - | - | - | +| 人均创利(万) | - | 38.45 | - | - | - | - | - | - | - | - | - | +| 人均薪酬(万) | - | 19.32 | - | - | - | - | - | - | - | - | - | + + +## 市场表现 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 股价 | 402.00 | 266.00 | 163.26 | 393.42 | 588.00 | 351.11 | 106.40 | 73.80 | - | - | - | +| 市值(亿) | 18,338 | 11,713 | 7,182 | 9,609 | 13,705 | 8,179 | 2,350 | 1,620 | - | - | - | +| PE | 36.14 | 26.55 | 23.37 | 60.32 | 245.47 | 179.35 | 69.37 | 41.77 | - | - | - | +| PB | 6.32 | 4.94 | 3.98 | 6.40 | 18.74 | 13.33 | 6.36 | 5.01 | - | - | - | +| 股东户数 | 226,368 | 212,061 | 260,992 | 183,317 | 141,963 | 133,060 | 76,710 | 82,514 | - | - | - | + diff --git a/data/CN/600519.SH/balance_sheet.csv b/data/CN/600519.SH/balance_sheet.csv new file mode 100644 index 0000000..38434f8 --- /dev/null +++ b/data/CN/600519.SH/balance_sheet.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,report_type,comp_type,end_type,total_share,cap_rese,undistr_porfit,surplus_rese,special_rese,money_cap,trad_asset,notes_receiv,accounts_receiv,oth_receiv,prepayment,div_receiv,int_receiv,inventories,amor_exp,nca_within_1y,sett_rsrv,loanto_oth_bank_fi,premium_receiv,reinsur_receiv,reinsur_res_receiv,pur_resale_fa,oth_cur_assets,current_assets,fa_avail_for_sale,htm_invest,lt_eqt_invest,invest_real_estate,time_deposits,oth_assets,lt_rec,fix_assets,cip,const_materials,fixed_assets_disp,produc_bio_assets,oil_and_gas_assets,intan_assets,r_and_d,goodwill,lt_amor_exp,defer_tax_assets,decr_in_disbur,oth_nca,total_nca,cash_reser_cb,depos_in_oth_bfi,prec_metals,deriv_assets,rr_reins_une_prem,rr_reins_outstd_cla,rr_reins_lins_liab,rr_reins_lthins_liab,refund_depos,ph_pledge_loans,refund_cap_depos,indep_acct_assets,client_depos,client_prov,transac_seat_fee,invest_as_receiv,total_assets,lt_borr,st_borr,cb_borr,depos_ib_deposits,loan_oth_bank,trading_fl,notes_payable,acct_payable,adv_receipts,sold_for_repur_fa,comm_payable,payroll_payable,taxes_payable,int_payable,div_payable,oth_payable,acc_exp,deferred_inc,st_bonds_payable,payable_to_reinsurer,rsrv_insur_cont,acting_trading_sec,acting_uw_sec,non_cur_liab_due_1y,oth_cur_liab,current_liabilities,bond_payable,lt_payable,specific_payables,estimated_liab,defer_tax_liab,defer_inc_non_cur_liab,oth_ncl,total_ncl,depos_oth_bfi,deriv_liab,depos,agency_bus_liab,oth_liab,prem_receiv_adva,depos_received,ph_invest,reser_une_prem,reser_outstd_claims,reser_lins_liab,reser_lthins_liab,indept_acc_liab,pledge_borr,indem_payable,policy_div_payable,total_liabilities,treasury_share,ordin_risk_reser,forex_differ,invest_loss_unconf,minority_int,total_equity,total_hldr_eqy_inc_min_int,total_liab_hldr_eqy,lt_payroll_payable,oth_comp_income,oth_eqt_tools,oth_eqt_tools_p_shr,lending_funds,acc_receivable,st_fin_payable,payables,hfs_assets,hfs_sales,cost_fin_assets,fair_value_fin_assets,contract_assets,contract_liab,accounts_receiv_bill,accounts_pay,oth_rcv_total,fix_assets_total,cip_total,oth_pay_total,long_pay_total,debt_invest,oth_debt_invest,update_flag +600519.SH,20251030,20251030,20250930,1,1,3,1252270215.0,1374964415.72,210875009053.38,48503784606.05,,51753057846.45,,5209529939.88,25531737.62,,21229757.91,,,55858862716.48,,4686422347.31,,135402538025.64,,,,3505663836.03,53251632.86,256587161700.86,,,,3818474.9,,,,21170758112.81,3534669471.18,,,,,8649225375.62,165998778.47,,137287075.5,5811949314.43,2724893752.7,181022050.45,48151023229.0,,,,,,,,,,,,,,,,,304738184929.86,,,,14473441763.35,,,,2822271882.72,,,,471949375.75,6840000739.89,,,,,,,,,,,57447774.54,974593908.67,38763379268.53,,,,,64664514.62,,,269780788.48,,,,,,,,,,,,,,,,,39033160057.01,6000465970.56,1061529724.0,,,8635060486.62,257069964386.23,265705024872.85,304738184929.86,,-1055242.36,,,,,,,,,,,,7749027043.43,5235061677.5,2822271882.72,71073860.68,21170758112.81,3534669471.18,5374646780.18,,1010008247.97,496798634.84,1 +600519.SH,20250403,20250403,20241231,1,1,4,1256197800.0,1374964415.72,182787415205.05,46635793479.39,,59295822956.89,248513280.0,1984407967.5,18974192.75,29955673.47,26975033.2,,,54343285157.47,,1210959803.42,,127187293298.17,,,,7220310691.1,160176582.69,251726674636.66,,,,7625167.83,,,,21871446747.14,2149619937.05,,,,,8850205831.0,98522878.42,,152105949.85,5520006868.83,2381228475.45,232395817.46,47217905282.04,,,,,,,,,,,,,,,,,298944579918.7,,,,23102858820.97,,,,3514969230.31,,,,5824371399.07,7717686541.38,,,5429006700.86,,,,,,,,111951112.2,1222693799.51,56515990618.96,,,,,103367763.38,,,417274179.14,,,,,,,,,,,,,,,,,56933264798.1,,1061529724.0,,,8905330721.13,233105984399.47,242011315120.6,298944579918.7,,-9916224.69,,,,,,,,,,,,9592453014.66,2003382160.25,3514969230.31,29955673.47,21871446747.14,2149619937.05,5429006700.86,,1515174439.92,,0 +600519.SH,20241026,20241026,20240930,1,1,3,1256197800.0,1374964415.72,192903581645.13,41119548595.8,,60084745614.27,200794360.04,1149046136.5,10131702.74,,41754330.78,,,48224880009.62,,154135489.78,,122685688179.71,,,,5785433457.13,19751378.42,238419577361.93,,,,4384742.38,,,,20237604798.2,2878910487.96,,,,,8668801938.29,160771184.78,,154464372.98,5907973419.43,2579164247.78,213888818.21,48351145595.76,,,,,,,,,,,,,,,,,286770722957.69,,,,12434986061.3,,,,2652185045.16,,,,446945580.18,6342664524.46,,57000000.0,,,,,,,,,47926667.37,1235273121.23,38723890549.12,,,,,74818111.11,,,358668170.4,,,,,,,,,,,,,,,,,39082558719.52,,1061529724.0,,,9978709237.89,237709455000.28,247688164238.17,286770722957.69,,-6367180.37,,,,,,,,,,,,9930767399.77,1159177839.24,2652185045.16,63216702.94,20237604798.2,2878910487.96,5633142149.65,,3227025895.78,,0 +600519.SH,20240403,20240403,20231231,1,1,4,1256197800.0,1374964415.72,172983178300.09,38998763095.13,,69070136376.12,400712059.93,13933440.0,60373410.41,27502107.3,34585111.79,,,46435185061.53,,,,105553836462.58,,,,3504849885.05,71403906.57,225172517821.28,,,,4138545.33,,,,19909280655.97,2137464700.45,,,,,8572267313.84,218015555.49,,160058930.14,4645887425.1,2130818189.27,109563497.23,47527142270.97,,,,,,,,,,,,,,,,,272699660092.25,,,,12034492909.95,,,,3093091103.67,,,,5401921213.77,6949663893.87,,,5213133685.87,,,,,,,,57054879.48,1822498012.3,48697611501.2,,,,,78943062.19,,,345579296.23,,,,,,,,,,,,,,,,,49043190797.43,,1061529724.0,,,7987897687.39,215668571607.43,223656469294.82,272699660092.25,,-6061727.51,,,,,,,,,,,,14125755802.29,74306850.41,3093091103.67,27502107.3,19909280655.97,2137464700.45,5213133685.87,,5323002071.02,,1 +600519.SH,20230331,20230331,20221231,1,1,4,1256197800.0,1374964415.72,161301978184.73,32522779178.88,,58274318733.23,,105453212.0,20937144.0,31818622.84,897377162.27,,,38824374236.24,,2123601333.33,,116172711554.59,,,,,160843674.42,216611435672.92,,,,5335046.99,,,,19742622547.86,2208329892.95,,,,,7083177226.45,190536632.6,,146455346.9,3458931368.11,4134744407.92,,37753369322.33,,,,,,,,,,,,,,,,,254364804995.25,,,,12874043355.42,,,,2408371053.69,,,,4782311242.41,6896555423.83,,,4543842833.87,,,,,,,,109351155.28,1979272808.9,49065668798.38,,,,,,,,334447942.79,,,,,,,,,,,,,,,,,49400116741.17,,1061529724.0,,,7458015858.08,197506672396.0,204964688254.08,254364804995.25,,-10776907.33,,,,,,,,,,,,15471920924.98,126390356.0,2408371053.69,31818622.84,19742622547.86,2208329892.95,4543842833.87,,380685319.09,,0 +600519.SH,20220331,20220331,20211231,1,1,4,1256197800.0,1374964415.72,160716861920.19,25142832818.16,,51810243607.11,,,,33158974.32,389109841.28,,,33394365084.83,,,,135067287778.03,,,,,71527560.74,220765692846.31,,,,5242431.75,,,,17472173182.85,2321988541.82,,,,,6208358330.24,,,139342455.82,2237206443.84,3425175000.0,2059761333.33,34402502313.59,,,,,,,,,,,,,,,,,255168195159.9,,,,21763575647.32,,,,2009832495.56,,,,3677845718.53,11979802144.01,,,4124404781.29,,,,,,,,104319886.87,1535976293.22,57914222254.82,,,,,,,,296466199.74,,,,,,,,,,,,,,,,,58210688454.56,,1061529724.0,,,7418137908.05,189539368797.29,196957506705.34,255168195159.9,,-13017880.78,,,,,,,,,,,,12718465288.02,,2009832495.56,33158974.32,17472173182.85,2321988541.82,4124404781.29,,170468623.71,,0 +600519.SH,20210331,20210331,20201231,1,1,4,1256197800.0,1374964415.72,137594403807.99,20174922608.93,,36091090060.9,,1532728979.67,,34488582.19,898436259.15,,,28869087678.06,,,,118199586541.06,,,,,26736855.91,185652154956.94,,,,,,,,16225082847.29,2447444843.03,,,,,4817170981.91,,,147721526.43,1123225086.37,2953036834.8,,27743655570.52,,,,,,,,,,,,,,,,,213395810527.46,,,,14241859949.77,,,,1342267668.12,,,,2981125503.86,8919821015.58,,,3257245259.42,,,,,,,,,1609801368.51,45673669912.95,,,,,1457513.23,,,1457513.23,,,,,,,,,,,,,,,,,45675127426.18,,927577822.67,,,6397948013.72,161322735087.56,167720683101.28,213395810527.46,,-5331367.75,,,,,,,,,,,,13321549147.69,1532728979.67,1342267668.12,34488582.19,16225082847.29,2447444843.03,3257245259.42,,20143397.78,,1 +600519.SH,20200422,20200422,20191231,1,1,4,1256197800.0,1374964415.72,115892337407.39,16595699037.02,,13251817237.85,,1463000645.08,,76540490.99,1549477339.41,,,25284920806.33,,,,117377810563.27,,,,,20904926.15,159024472009.08,,,,,,,,15144182726.19,2518938271.72,,,,,4728027345.7,,,158284338.19,1099946947.57,48750000.0,,24017900033.42,,,,,,,,,,,,,,,,,183042372042.5,,,,11048756010.02,,,,1513676611.44,13740329698.82,,,2445071026.57,8755949266.98,11081.87,446880000.0,3142625517.14,,,,,,,,,,41093299212.84,,,,,72692601.01,,,72692601.01,,,,,,,,,,,,,,,,,41165991813.85,,898349936.77,,,5866030353.54,136010349875.11,141876380228.65,183042372042.5,,-7198721.79,,,,,,,,,,,,,1463000645.08,1513676611.44,76540490.99,15144182726.19,2518938271.72,3589516599.01,,,,0 +600519.SH,20190329,20190329,20181231,1,1,4,1256197800.0,1374964415.72,95981943953.56,13444221244.84,,112074791420.06,,563739710.0,,50000548.65,1182378508.06,,343889944.47,23506950842.22,,,,,,,,,140084334.11,137861835307.57,29000000.0,,,,,,,15248556585.02,1954322968.68,,,,,3499175374.52,,,168414678.77,1049294821.45,36075000.0,,21984839428.44,,,,,,,,,,,,,,,,,159846674736.01,,,,11473011885.36,,,,1178296416.59,13576516813.44,,,2034514658.91,10771075966.85,42770451.84,,3362000620.49,,,,,,,,,,42438186813.48,,,,,,,,,,,,,,,,,,,,,,,,,42438186813.48,,788302643.63,,,4569923590.48,112838564332.05,117408487922.53,159846674736.01,,-7065725.7,,,,,,,,,,,,,563739710.0,1178296416.59,393890493.12,15248556585.02,1954322968.68,3404771072.33,,,,0 +600519.SH,20180328,20180328,20171231,1,1,4,1256197800.0,1374964415.72,80011307450.33,8215595509.69,,87868869913.34,,1221706039.0,,31323463.35,790807322.07,,241458615.89,22057481376.46,,,,,,,,,37539231.49,112249185961.6,29000000.0,,,,,,,15244096632.02,2016405005.77,,,,,3458622239.38,,,177859674.54,1401797361.77,33150000.0,,22360930913.48,,,,,,,,,,,,,,,,,134610116875.08,,,,10462613754.14,,,,992055910.47,14429106902.38,,,1901644193.64,7726135741.9,23414593.67,,3039948303.8,,,,,,,,,,38574919400.0,,,15570000.0,,,,,15570000.0,,,,,,,,,,,,,,,,,38590489400.0,,600859229.62,,,4568104646.12,91451522828.96,96019627475.08,134610116875.08,,-7401576.4,,,,,,,,,,,,,1221706039.0,992055910.47,272782079.24,15244096632.02,2016405005.77,3063362897.47,15570000.0,,,0 +600519.SH,20170415,20170415,20161231,1,1,4,1256197800.0,1374964415.72,62717808036.61,7135649963.12,,66854962118.22,,817627172.0,,77227565.37,1046100696.92,,140904856.88,20622251825.55,,,,390000000.0,,,,,231474570.63,90180548805.57,29000000.0,,,,,,,14453177439.34,2745579995.68,,,,,3531740625.6,,,188118776.51,1745539120.68,60833517.03,,22753989474.84,,,,,,,,,,,,,,,,,112934538280.41,,,,10778818332.13,,,,1040608203.18,17541082237.01,,,1628507252.03,4272289194.57,34481635.33,,1724638571.44,,,,,,,,,,37020425425.69,,,15570000.0,,,,,15570000.0,,,,,,,,,,,,,,,,,37035995425.69,,420758409.36,,,3004405071.47,72894137783.25,75898542854.72,112934538280.41,,-11240841.56,,,,,,,,,,,,,817627172.0,1040608203.18,218132422.25,14453177439.34,2745579995.68,1759120206.77,15570000.0,,,0 +600519.SH,20160324,20160324,20151231,1,1,4,1256197800.0,1374964415.72,54878964497.77,6210524497.54,,36800749895.06,,8578935406.82,230768.89,48219018.75,1477734859.9,,85347051.47,18013297022.7,,,,,,,,,,65004514023.59,29000000.0,,,,,,,11415953189.72,4895150716.51,260855.92,682594.04,,,3582462431.04,,,198603537.81,1155336074.14,19500000.0,,21296949399.18,,,,,,,,,,,,,,,,,86301463422.77,,,,5967622299.13,,,,880976072.09,8261582073.04,,,975477747.06,2515516156.83,27409447.4,,1423139205.93,,,,,,,,,,20051723001.48,,,15570000.0,,,,,15570000.0,,,,,,,,,,,,,,,,,20067293001.48,,218361303.43,,,2308191982.3,63925978438.99,66234170421.29,86301463422.77,,-13034075.47,,,,,,,,,,,,,8579166175.71,880976072.09,133566070.22,11416635783.76,4895411572.43,1450548653.33,15570000.0,,,1 diff --git a/data/CN/600519.SH/cash_flow.csv b/data/CN/600519.SH/cash_flow.csv new file mode 100644 index 0000000..ef85022 --- /dev/null +++ b/data/CN/600519.SH/cash_flow.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,comp_type,report_type,end_type,net_profit,finan_exp,c_fr_sale_sg,recp_tax_rends,n_depos_incr_fi,n_incr_loans_cb,n_inc_borr_oth_fi,prem_fr_orig_contr,n_incr_insured_dep,n_reinsur_prem,n_incr_disp_tfa,ifc_cash_incr,n_incr_disp_faas,n_incr_loans_oth_bank,n_cap_incr_repur,c_fr_oth_operate_a,c_inf_fr_operate_a,c_paid_goods_s,c_paid_to_for_empl,c_paid_for_taxes,n_incr_clt_loan_adv,n_incr_dep_cbob,c_pay_claims_orig_inco,pay_handling_chrg,pay_comm_insur_plcy,oth_cash_pay_oper_act,st_cash_out_act,net_cash_flow,oth_recp_ral_inv_act,c_disp_withdrwl_invest,c_recp_return_invest,n_recp_disp_fiolta,n_recp_disp_sobu,stot_inflows_inv_act,c_pay_acq_const_fiolta,c_paid_invest,n_disp_subs_oth_biz,oth_pay_ral_inv_act,n_incr_pledge_loan,stot_out_inv_act,n_cashflow_inv_act,c_recp_borrow,proc_issue_bonds,oth_cash_recp_ral_fnc_act,stot_cash_in_fnc_act,free_cashflow,c_prepay_amt_borr,c_pay_dist_dpcp_int_exp,incl_dvd_profit_paid_sc_ms,oth_cashpay_ral_fnc_act,stot_cashout_fnc_act,n_cash_flows_fnc_act,eff_fx_flu_cash,n_incr_cash_cash_equ,c_cash_equ_beg_period,c_cash_equ_end_period,c_recp_cap_contrib,incl_cash_rec_saims,uncon_invest_loss,prov_depr_assets,depreciation,amort_intang_assets,lt_amort_deferred_exp,decr_deferred_exp,incr_acc_exp,loss_disp_fiolta,loss_scr_fa,loss_fv_chg,invest_loss,decr_def_inc_tax_assets,incr_def_inc_tax_liab,decr_inventories,decr_oper_payable,incr_oper_payable,others,im_net_cashflow_oper_act,conv_debt_into_cap,conv_copbonds_due_within_1y,fa_fnc_leases,im_n_incr_cash_equ,net_dism_capital_add,net_cash_rece_sec,credit_impa_loss,use_right_asset_dep,oth_loss_asset,end_bal_cash,beg_bal_cash,end_bal_cash_equ,beg_bal_cash_equ,update_flag +600519.SH,20251030,20251030,20250930,1,1,3,,,138791719991.59,,-8659745768.25,,,,,,,2171830654.51,,,,4429717886.39,136733522764.24,8595277757.58,12393481128.92,59108713942.04,358885355.55,12507272127.36,,105733217.59,,7567357079.93,98536720608.97,38196802155.27,24708323.39,13105136000.0,80202855.44,87042.0,,13210134220.83,2282862269.89,16345831315.16,,4808731.52,,18633502316.57,-5423368095.74,,,,,33738068687.0576,,37212595804.69,2541434067.5,6031207701.59,43243803506.28,-43243803506.28,2742531.2,-10467626915.55,169970089257.83,159502462342.28,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +600519.SH,20250403,20250403,20241231,1,1,4,89334728025.9,14474584.09,182645203339.89,,11060205782.1,,,,,,,3122827833.43,,,,3258097705.14,200086334660.56,10824874163.03,15352541000.17,77055621533.11,262376929.65,-4585245646.29,,97061751.28,,9015412761.18,107622642492.13,92463692168.43,5532536.42,8550000000.0,92382151.66,715708.44,,8648630396.52,4678712053.56,5745136000.0,,9984973.67,,10433833027.23,-1785202630.71,,,,,56965618673.6519,,70951027702.94,2164148570.54,116478781.87,71067506484.81,-71067506484.81,-1082747.55,19609900305.36,150360188952.47,169970089257.83,,,,,1721165327.14,249170059.35,20191550.34,,,-388852.05,7287333.87,-60980724.35,-9130340.37,-874119443.73,24424701.19,-7908100095.94,1761268977.59,8065959951.08,,92463692168.43,,,,19609900305.36,,,23248436.03,94492678.29,,164297949257.83,147360188952.47,5672140000.0,3000000000.0,1 +600519.SH,20241026,20241026,20240930,1,1,3,,,129685318970.82,2818171.29,394283234.48,,,,,,,2181721943.36,,,,2374170205.15,134638312525.1,8148825214.67,12669415741.48,56721478607.21,464241534.03,5154820988.02,,66013822.7,,6992130399.54,90216926307.65,44421386217.45,5532536.42,6200000000.0,68399488.96,75010.5,,6274007035.88,2874366279.39,4000000000.0,,5049812.44,,6879416091.83,-605409055.95,,,,,26947775900.0195,,38942461843.34,156098570.54,46854621.47,38989316464.81,-38989316464.81,-65439.09,4826595257.6,150360188952.47,155186784210.07,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 +600519.SH,20240403,20240403,20231231,1,1,4,77521476277.8,12624628.35,163699909417.62,1500047.04,-810223002.76,,,,,,,3018786023.42,,,,2346196470.63,168256168955.95,11029476036.21,13845293907.26,66683472509.22,-2051930316.19,1570003429.01,,142896151.21,,7943709518.14,101662921234.86,66593247721.09,4605886.63,7549947301.15,140715000.0,24948352.95,,7720216540.73,2619755888.79,14817852800.0,,7021867.1,,17444630555.89,-9724414015.16,,,,,77775322924.6562,,58754786730.01,2204530367.41,134315261.93,58889101991.94,-58889101991.94,1718255.65,-2018550030.36,152378738982.83,150360188952.47,,,,,1651428992.2,196656866.73,16886608.86,,,479736.97,-1632253.14,-3151962.5,-34025967.82,-1050934956.22,-83685028.8,-7610810825.29,-3465130974.53,-591505967.47,,66593247721.09,,,,-2018550030.36,,,-37871293.26,72443839.21,,147360188952.47,152378738982.83,3000000000.0,,1 +600519.SH,20230331,20230331,20221231,1,1,4,65375136766.75,12023204.77,140691678592.0,33191912.56,-8916033228.67,,,,,,,3247615476.04,,,,2759422171.88,137815874923.81,8357859151.03,11752241598.62,62043324506.36,723778672.0,13037761321.9,,79226410.98,,5123087432.89,101117279093.78,36698595830.03,4971762.18,,5880000.0,355149.0,,11206911.18,5306546416.54,210000000.0,,31486829.54,,5548033246.08,-5536826334.9,,,,,62574291590.3602,,57370196191.46,2618815078.45,54332788.37,57424528979.83,-57424528979.83,911088.01,-26261848396.69,178640587379.52,152378738982.83,,,,,1443574818.5,156016278.9,11487619.04,,,-213235.69,20780992.88,,-63840000.0,-1221724924.27,,-5430009151.41,-15051874095.8,-8644820580.52,,36698595830.03,,,,-26261848396.69,,,14686546.25,77371590.63,,152378738982.83,178640587379.52,,,1 +600519.SH,20220331,20220331,20211231,1,1,4,55720529956.46,13529867.76,119320536796.65,,7511166145.93,,,,,,,3145747032.91,,,,1643536862.48,131620986837.97,7745959630.9,10061366201.66,44609684025.28,484244272.0,559089326.28,,163462728.48,,4368504506.0,67592310690.6,64028676147.37,9983452.63,6079930.68,860000.0,2463474.29,,19386857.6,3408784532.01,2150000000.0,,23048029.93,,5581832561.94,-5562445704.34,,,,,46165171072.2219,,26476019839.37,2240195683.97,88121549.59,26564141388.96,-26564141388.96,-2026542.6,31900062511.47,146740524868.05,178640587379.52,,,,,1344833911.39,124084418.97,10687874.77,,,,11920829.77,2244726.29,-58255937.39,-1113981357.47,-1457513.23,-4525277406.77,504305749.89,11880880539.56,,64028676147.37,,,,31900062511.47,,,13022441.19,101608046.18,,178640587379.52,146740524868.05,,,1 +600519.SH,20210331,20210331,20201231,1,1,4,49523329882.4,,107024384560.17,,3189100199.87,,,,,,,3075945383.34,,,,221421226.63,113510851370.01,7230646129.19,8161813197.26,41622706350.37,2978755728.0,-2506406682.56,,107241768.26,,4047026186.46,61841782676.98,51669068693.03,6675319.03,314906521.48,,495904.85,,322077745.36,2089769498.78,20000000.0,,17535402.3,,2127304901.08,-1805227155.72,,,,,49690759472.5839,,24091029750.51,2704262179.11,36507157.75,24127536908.26,-24127536908.26,380639.36,25736685268.41,121003839599.64,146740524868.05,,,,,1195956468.6,110349099.0,10562811.76,,,,100113.92,-4897994.43,-305631.46,-23278138.8,-71235087.78,-3584166871.73,-504008375.44,4945290607.14,,51669068693.03,,,,25736685268.41,,,71371809.85,,,146740524868.05,121003839599.64,,,1 +600519.SH,20200422,20200422,20191231,1,1,4,43970000792.51,,94980138631.64,,-437417306.8,,,,,,,3667633965.97,,,,1234081863.41,99444437154.22,5521948744.75,7669863126.24,39841352755.42,13000000.0,-4503181198.43,,175423942.78,,5315417150.9,54233824521.66,45210612632.56,7321070.4,,,38080.0,,7359150.4,3148864661.38,,,24180232.27,,3173044893.65,-3165685743.25,,,,833000000.0,-79056107120.0982,,20117402829.22,1853543015.02,,20117402829.22,-19284402829.22,27240.01,22760551300.1,98243288299.54,121003839599.64,833000000.0,833000000.0,,5313489.8,1149884850.35,83262106.36,10331490.16,,,32123.57,478391.99,14018472.46,,-50890686.63,-3504618.12,-1777969964.11,3424860902.28,-1615204718.06,,45210612632.56,,,,22760551300.1,,,,,,121003839599.64,98243288299.54,,,1 +600519.SH,20190329,20190329,20181231,1,1,4,37829617756.81,,84268695732.62,,1010398131.22,,,,,,,3444983166.57,,,,621558368.29,89345635398.7,5298518032.55,6653137733.66,32032178125.92,3000000.0,920713957.76,,117086309.02,,2935766833.07,47960400991.98,41385234406.72,11244181.3,,,,,11244181.3,1606750226.28,,,33456659.58,,1640206885.86,-1628962704.56,,,,,40235854299.3852,,16441093160.06,2624173549.23,,16441093160.06,-16441093160.06,29006.86,23315207548.96,74928080750.58,98243288299.54,,,,1289685.01,1084662728.58,80431667.22,10331100.62,,,,1808930.93,,,352502540.32,,-1449469465.76,525665014.45,2948394448.54,,41385234406.72,,,,23315207548.96,,,,,,98243288299.54,74928080750.58,,,0 +600519.SH,20180328,20180328,20171231,1,1,4,29006423236.0,,64421479343.02,,-316204577.99,,,,,,,2722025536.31,,,,542162210.47,67369462511.81,4875768504.16,5489606122.48,23065648503.05,-28393350.8,8727170068.53,,146330216.72,,2940296363.54,45216426427.68,22153036084.13,21430672.95,,,16450.0,,21447122.95,1125017192.45,,,17075145.1,,1142092337.55,-1120645214.6,,,,6000000.0,29606965018.1472,,8905177880.8,379363405.61,,8905177880.8,-8899177880.8,72948.86,12133285937.59,62794794812.99,74928080750.58,6000000.0,6000000.0,,-8053703.95,1035052733.45,80522705.77,10259101.97,,,,3291895.5,,,343741758.91,,-1435229550.91,-458728523.99,-6424243568.62,,22153036084.13,,,,12133285937.59,,,,,,74928080750.58,62794794812.99,,,0 +600519.SH,20170415,20170415,20161231,1,1,4,17930643109.88,,61012964102.54,,4811196033.0,,,,,,,1265842778.44,,,,189142723.95,67279145637.93,2773020403.27,4674154236.66,17510516331.2,42393350.8,2340362436.74,,115962455.33,,2371486776.88,29827895990.88,37451249647.05,5562351.19,,,92084.5,,5654435.69,1019178136.92,,,88977102.97,,1108155239.89,-1102500804.2,,,,16000000.0,39906310883.5675,,8350512252.23,532067286.55,,8350512252.23,-8334512252.23,72317.8,28014308908.42,34780485904.57,62794794812.99,16000000.0,16000000.0,,12327496.22,842728072.04,80457895.99,11008704.17,,,,1869869.13,,,-590203046.54,,-2608954802.85,7669650565.84,14101721783.17,,37451249647.05,,,,28014308908.42,,,,,,62794794812.99,34780485904.57,,,0 +600519.SH,20160324,20160324,20151231,1,1,4,16454996625.22,,37083071835.58,,2011171589.94,,,,,,,766016183.29,,,,153647241.24,40013906850.05,2967732630.37,4536877341.1,14003048933.21,-11600000.0,-848231824.96,,62297196.96,,1867442431.65,22577566708.33,17436340141.72,33357886.05,60050000.0,3869172.05,8772937.39,,106049995.49,2061470481.32,25050000.0,,68319778.76,,2154840260.08,-2048790264.59,,,22000000.0,22000000.0,16532758779.4344,55917672.0,5554101966.61,513009332.72,,5610019638.61,-5588019638.61,-16273531.71,9783256706.81,24997229197.76,34780485904.57,,,,-540313.39,761458678.29,79883270.42,6804749.02,,,,-17419.86,,-3869276.9,-333734968.43,,-3030932654.85,-6705177839.34,10207469291.54,,17436340141.72,,,,9783256706.81,,,,,,34780485904.57,24997229197.76,,,0 diff --git a/data/CN/600519.SH/income_statement.csv b/data/CN/600519.SH/income_statement.csv new file mode 100644 index 0000000..911ae54 --- /dev/null +++ b/data/CN/600519.SH/income_statement.csv @@ -0,0 +1,13 @@ +ts_code,ann_date,f_ann_date,date,report_type,comp_type,end_type,basic_eps,diluted_eps,total_revenue,revenue,int_income,prem_earned,comm_income,n_commis_income,n_oth_income,n_oth_b_income,prem_income,out_prem,une_prem_reser,reins_income,n_sec_tb_income,n_sec_uw_income,n_asset_mg_income,oth_b_income,fv_value_chg_gain,invest_income,ass_invest_income,forex_gain,total_cogs,oper_cost,int_exp,comm_exp,biz_tax_surchg,sell_exp,admin_exp,fin_exp,assets_impair_loss,prem_refund,compens_payout,reser_insur_liab,div_payt,reins_exp,oper_exp,compens_payout_refu,insur_reser_refu,reins_cost_refund,other_bus_cost,operate_profit,non_oper_income,non_oper_exp,nca_disploss,total_profit,income_tax,n_income,net_income,minority_gain,oth_compr_income,t_compr_income,compr_inc_attr_p,compr_inc_attr_m_s,ebit,ebitda,insurance_exp,undist_profit,distable_profit,rd_exp,fin_exp_int_exp,fin_exp_int_inc,transfer_surplus_rese,transfer_housing_imprest,transfer_oth,adj_lossgain,withdra_legal_surplus,withdra_legal_pubfund,withdra_biz_devfund,withdra_rese_fund,withdra_oth_ersu,workers_welfare,distr_profit_shrhder,prfshare_payable_dvd,comshare_payable_dvd,capit_comstock_div,continued_net_profit,update_flag +600519.SH,20251030,20251030,20250930,1,1,3,51.53,51.53,130903889634.88,128453707655.86,2450181979.02,,,,,,,,,,,,,,6956011.89,59165.27,,,41446175513.84,11183972073.77,136006174.86,55753.36,20645707926.55,4478672252.48,5503383668.62,-634730437.77,,,,,,,,,,,,89489694566.48,39970046.24,126220158.04,,89403444454.68,22504639708.51,66898804746.17,64626746712.18,2272058033.99,7966781.33,66906771527.5,64635607694.51,2271163832.99,88847437026.1,,,,,113086640.98,6505588.51,644763065.59,,,,,,,,,,,,,,,66898804746.17,1 +600519.SH,20250403,20250403,20241231,1,1,4,68.64,68.64,174144069958.25,170899152276.34,3244917681.91,,,,,,,,,,,,,,60980724.35,9130340.37,,,54547219888.6,13789482367.98,105127802.03,94078.17,26926161474.99,5639300059.49,9315650060.38,-1470219863.34,,,,,,,,,,,,119688579453.23,70936575.97,120937834.74,,119638578194.46,30303850168.56,89334728025.9,86228146421.62,3106581604.28,-3854497.18,89330873528.72,86224291924.44,3106581604.28,118147859673.12,120232879288.24,,,,218375472.87,14474584.09,1476991223.18,,,,,,,,,,,,,,,89334728025.9,1 +600519.SH,20241026,20241026,20240930,1,1,3,48.42,48.42,123122542625.45,120776131874.69,2346410750.76,,,,,,,,,,,,,,40768101.0,8019358.73,,,39192736850.97,10228077394.34,72011709.35,212030.22,19911791735.67,4235182341.09,5767525028.76,-1172804656.48,,,,,,,,,,,,83996733560.98,54052930.61,1902888.56,,84048883603.03,21017421363.48,63031462239.55,60827552118.51,2203910121.04,-305452.86,63031156786.69,60827246665.65,2203910121.04,82775149003.59,,,,,140401034.71,7226289.08,1170528993.99,,,,,,,,,,,,,,,63031462239.55,1 +600519.SH,20240403,20240403,20231231,1,1,4,59.49,59.49,150560330316.45,147693604994.14,2866725322.31,,,,,,,,,,,,,,3151962.5,34025967.82,,,46923018175.28,11867273851.78,113500129.93,68578.57,22234175898.6,4648613585.82,9729389252.31,-1789503701.48,,,,,,,,,,,,103708655208.38,86779655.95,132881174.52,,103662553689.81,26141077412.01,77521476277.8,74734071550.75,2787404727.05,4715179.82,77526191457.62,74738786730.57,2787404727.05,101882453313.55,103819869620.55,,,,157371873.01,12624628.35,1942301920.98,,,,,,,,,,,,,,,77521476277.8,1 +600519.SH,20230331,20230331,20221231,1,1,4,49.93,49.93,127553959355.97,124099843771.99,3454115583.98,,,,,,,,,,,,,,,63840000.0,,,39762996163.1,10093468616.63,105584206.24,143141.51,18495818534.22,3297724190.94,9012191073.63,-1391805826.72,,,,,,,,,,,,87879521782.39,70852285.4,248884319.61,,87701489748.18,22326352981.43,65375136766.75,62716443738.27,2658693028.48,2240973.45,65377377740.2,62718684711.72,2658693028.48,86423662719.98,88112113027.05,,,,135185680.4,12023204.77,1475422303.64,,,,,,,,,,,,,,,65375136766.75,1 +600519.SH,20220331,20220331,20211231,1,1,4,41.76,41.76,109464278563.89,106190154843.76,3274123720.13,,,,,,,,,,,,,,-2244726.29,58255937.39,,,34789924908.66,8983377809.96,173897197.98,115082.12,15304469070.03,2737369434.78,8450274065.03,-934523406.02,,,,,,,,,,,,74750880777.52,68989219.74,291838102.5,,74528031894.76,18807501938.3,55720529956.46,52460144378.16,3260385578.3,-7686513.03,55712843443.43,52452457865.13,3260385578.3,73760346160.4,75341560411.71,,,,61923213.59,13529867.76,944578412.02,,,,,,,,,,,,,,,55720529956.46,0 +600519.SH,20210331,20210331,20201231,1,1,4,37.17,37.17,97993240501.21,94915380916.72,3077859584.49,,,,,,,,,,,,,,4897994.43,305631.46,,,31376502397.41,8154001476.28,111128537.31,105888.96,13886517290.78,2547745650.95,6789844289.39,-234610582.44,,,,,,,,,,,,66635079882.38,11051136.15,449189027.42,,66196941991.11,16673612108.71,49523329882.4,46697285429.81,2826044452.59,1867354.04,49525197236.44,46699152783.85,2826044452.59,66395265674.05,67712134053.41,,,,50398036.33,,278697733.32,,,,,,,,,,,,,,,49523329882.4,1 +600519.SH,20200422,20200422,20191231,1,1,4,32.8,32.8,88854337488.76,85429573467.25,3424471568.68,,292452.83,,,,,,,,,,,,-14018472.46,,,,29817566523.17,7430013945.12,145752825.87,73178.4,12733292400.79,3278990982.26,6167982844.22,7458015.66,,,,,,,,,,,,59041489276.14,9454451.03,268391929.45,,58782551797.72,14812551005.21,43970000792.51,41206471014.43,2763529778.08,-132996.09,43969867796.42,41206338018.34,2763529778.08,59062997887.83,60306476334.7,,,,48688841.05,,20667205.74,,,,,,,,,,,,,,,43970000792.51,0 +600519.SH,20190329,20190329,20181231,1,1,4,28.02,28.02,77199384110.22,73638872388.03,3559634363.7,,877358.49,,,,,,,,,,,,,,,,25866030564.04,6522921833.77,136317779.84,124387.35,11288926846.97,2572076872.16,5325940762.24,-3521209.23,1289685.01,,,,,,,,,,,51342987681.18,11619526.11,527003759.82,,50827603447.47,12997985690.66,37829617756.81,35203625263.22,2625992493.59,335850.7,37829953607.51,35203961113.92,2625992493.59,51339541471.95,52514966968.37,,,,21953605.93,,14410823.72,,,,,,,,,,,,,,,37829617756.81,0 +600519.SH,20180328,20180328,20171231,1,1,4,21.56,21.56,61062756866.16,58217861314.17,2844310646.33,,584905.66,,,,,,,,,,,,,,,,22122749332.71,5940436371.97,135187797.06,75378.0,8404214470.69,2986068544.99,4720542820.14,-55722346.19,-8053703.95,,,,,,,,,,,38940007533.45,12201990.51,212137381.36,,38740072142.6,9733648906.6,29006423236.0,27079360255.74,1927062980.26,3839265.16,29010262501.16,27083199520.9,1927062980.26,38873575353.49,39999409894.68,,,,,,,,,,,,,,,,,,,,,29006423236.0,0 +600519.SH,20170415,20170415,20161231,1,1,4,13.31,13.31,40155084412.93,38862189993.84,1292722909.66,,171509.43,,,,,,,,,,,,,,,,15889459243.51,3410104085.97,122961049.54,73593.72,6508926343.26,1681052022.9,4187189840.42,-33175188.52,12327496.22,,,,,,,,,,,24265625169.42,8553926.06,316298138.37,1960971.07,23957880957.11,6027237847.23,17930643109.88,16718362734.16,1212280375.72,1793233.91,17932436343.79,16720155968.07,1212280375.72,24243509814.67,25177704486.87,,,,,,,,,,,,,,,,,,,,,,0 +600519.SH,20160324,20160324,20151231,1,1,4,12.34,12.34,33446859045.58,32659583725.28,786545320.3,,730000.0,,,,,,,,,,,,,3869276.9,,,11291736359.61,2538337449.06,74159619.28,62172.83,3449170637.4,1484961519.21,3812852076.19,-67266800.97,-540313.39,,,,,,,,,,,22158991962.87,4823183.32,162100184.85,188439.42,22001714961.34,5546718336.12,16454996625.22,15503090276.38,951906348.84,-12415031.48,16442581593.74,15490675244.9,951906348.84,22087565885.0,22935712582.73,,,,,,,,,,,,,,,,,,,,,,0 diff --git a/data/CN/600519.SH/report.html b/data/CN/600519.SH/report.html new file mode 100644 index 0000000..8c5611f --- /dev/null +++ b/data/CN/600519.SH/report.html @@ -0,0 +1,750 @@ +Financial Report +

Financial Report

+ + + + + + + +
代码简称日期上市日期PEPB股息率(%)
600519.SH贵州茅台2025-12-142001-08-2720.636.923.64%
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
指标2025 Q32024202320222021202020192018201720162015
主要指标
ROE25.14%36.99%34.65%31.75%27.68%28.95%30.30%31.20%29.61%22.94%24.25%
ROA21.21%28.84%27.41%24.66%20.56%21.88%22.51%22.02%20.12%14.80%17.96%
ROCE/ROIC34.56%50.68%47.24%43.76%38.92%41.16%43.43%45.50%42.51%33.26%34.55%
毛利率91.46%92.08%92.12%92.09%91.79%91.68%91.64%91.55%90.27%91.51%92.41%
净利润率49.37%49.52%49.64%49.17%47.92%47.65%46.38%45.60%44.35%41.63%46.35%
收入1,309.041,741.441,505.601,275.541,094.64979.93888.54771.99610.63401.55334.47
收入增速6.32%15.66%18.04%16.53%11.71%10.29%15.10%26.43%52.07%20.06%-
净利润646.27862.28747.34627.16524.60466.97412.06352.04270.79167.18155.03
净利润增速6.25%15.38%19.16%19.55%12.34%13.33%17.05%30.00%61.97%7.84%-
经营净现金流381.97924.64665.93366.99640.29516.69452.11413.85221.53374.51174.36
资本开支22.8346.7926.2053.0734.0920.9031.4916.0711.2510.1920.61
自由现金流359.14877.85639.73313.92606.20495.79420.62397.78210.28364.32153.75
分红-----------
总资产3,047.382,989.452,727.002,543.652,551.682,133.961,830.421,598.471,346.101,129.35863.01
净资产2,570.702,331.062,156.691,975.071,895.391,613.231,360.101,128.39914.52728.94639.26
商誉-----------
费用指标
销售费用率3.42%3.24%3.09%2.59%2.50%2.60%3.69%3.33%4.89%4.19%4.44%
管理费用率4.20%5.35%6.46%7.07%7.72%6.93%6.94%6.90%7.73%10.43%11.40%
研发费用率0.09%0.13%0.10%0.11%0.06%0.05%0.05%0.03%---
其他费用率34.37%33.85%32.83%33.16%33.59%34.45%34.58%35.69%33.30%35.26%30.22%
折旧费用占比-0.99%1.10%1.13%1.23%1.22%1.29%1.41%1.70%2.10%2.28%
所得税率25.17%25.33%25.22%25.46%25.24%25.19%25.20%25.57%25.13%25.16%25.21%
资产占比
现金占比16.98%19.84%25.33%22.91%20.30%16.91%7.24%70.11%65.28%59.20%42.64%
库存占比18.33%18.18%17.03%15.26%13.09%13.53%13.81%14.71%16.39%18.26%20.87%
应收款占比0.01%0.01%0.02%0.01%------0.00%
预付款占比0.01%0.01%0.01%0.35%0.15%0.42%0.85%0.74%0.59%0.93%1.71%
固定资产占比6.95%7.32%7.30%7.76%6.85%7.60%8.27%9.54%11.32%12.80%13.23%
长期投资占比-----------
商誉占比-----------
其他资产占比57.72%54.65%50.31%53.70%59.61%61.53%69.83%4.90%6.43%8.82%21.54%
应付款占比0.93%1.18%1.13%0.95%0.79%0.63%0.83%0.74%0.74%0.92%1.02%
预收款占比2.54%3.21%5.18%6.08%4.98%6.24%7.51%8.49%10.72%15.53%9.57%
短期借款占比0.02%0.04%0.02%0.04%0.04%0.00%0.00%0.00%0.00%0.00%0.00%
长期借款占比0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%0.00%
运营资产占比14.88%13.81%10.75%8.59%7.47%7.08%6.33%6.22%5.52%2.73%11.99%
有息负债率0.02%0.04%0.02%0.04%0.04%0.00%0.00%0.00%0.00%0.00%0.00%
周转能力
存货周转天数1,8231,4381,4281,4031,3561,2921,2421,3151,3552,2072,590
应收款周转天数0000------0
应付款周转天数929395878160746560111126
固定资产周转率6.187.967.566.466.276.045.875.064.012.782.93
总资产周转率0.430.580.550.500.430.460.490.480.450.360.39
人均效率
员工人数-34,750---------
人均创收(万)-501.13---------
人均创利(万)-248.14---------
人均薪酬(万)-44.18---------
市场表现
股价1443.991524.001726.001727.002050.001998.001183.00590.01697.49334.15218.19
市值(亿)18,08319,14421,68221,69525,75225,09914,8617,4128,7624,1982,741
PE20.9725.6234.5741.3555.1560.9142.2127.3752.4127.0817.86
PB7.779.2211.1912.1114.7716.9111.867.2410.396.124.58
股东户数220,658201,582150,025145,225175,267114,26789,06288,93263,98053,43746,869
\ No newline at end of file diff --git a/data/CN/600519.SH/report.md b/data/CN/600519.SH/report.md new file mode 100644 index 0000000..eba3bb5 --- /dev/null +++ b/data/CN/600519.SH/report.md @@ -0,0 +1,87 @@ +# 贵州茅台 (600519.SH) - Financial Report +*Report generated on: 2025-12-14* + +| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) | +|:---|:---|:---|:---|:---|:---| +| 600519.SH | 贵州茅台 | 2001-08-27 | 20.63 | 6.92 | 3.64% | + + +## 主要指标 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| ROE | 25.14% | 36.99% | 34.65% | 31.75% | 27.68% | 28.95% | 30.30% | 31.20% | 29.61% | 22.94% | 24.25% | +| ROA | 21.21% | 28.84% | 27.41% | 24.66% | 20.56% | 21.88% | 22.51% | 22.02% | 20.12% | 14.80% | 17.96% | +| ROCE/ROIC | 34.56% | 50.68% | 47.24% | 43.76% | 38.92% | 41.16% | 43.43% | 45.50% | 42.51% | 33.26% | 34.55% | +| 毛利率 | 91.46% | 92.08% | 92.12% | 92.09% | 91.79% | 91.68% | 91.64% | 91.55% | 90.27% | 91.51% | 92.41% | +| 净利润率 | 49.37% | 49.52% | 49.64% | 49.17% | 47.92% | 47.65% | 46.38% | 45.60% | 44.35% | 41.63% | 46.35% | +| 收入(亿) | 1,309.04 | 1,741.44 | 1,505.60 | 1,275.54 | 1,094.64 | 979.93 | 888.54 | 771.99 | 610.63 | 401.55 | 334.47 | +| 收入增速 | 6.32% | 15.66% | 18.04% | 16.53% | 11.71% | 10.29% | 15.10% | 26.43% | 52.07% | 20.06% | - | +| 净利润(亿) | 646.27 | 862.28 | 747.34 | 627.16 | 524.60 | 466.97 | 412.06 | 352.04 | 270.79 | 167.18 | 155.03 | +| 净利润增速 | 6.25% | 15.38% | 19.16% | 19.55% | 12.34% | 13.33% | 17.05% | 30.00% | 61.97% | 7.84% | - | +| 经营净现金流(亿) | 381.97 | 924.64 | 665.93 | 366.99 | 640.29 | 516.69 | 452.11 | 413.85 | 221.53 | 374.51 | 174.36 | +| 资本开支(亿) | 22.83 | 46.79 | 26.20 | 53.07 | 34.09 | 20.90 | 31.49 | 16.07 | 11.25 | 10.19 | 20.61 | +| 自由现金流(亿) | 359.14 | 877.85 | 639.73 | 313.92 | 606.20 | 495.79 | 420.62 | 397.78 | 210.28 | 364.32 | 153.75 | +| 分红(亿) | - | - | - | - | - | - | - | - | - | - | - | +| 总资产(亿) | 3,047.38 | 2,989.45 | 2,727.00 | 2,543.65 | 2,551.68 | 2,133.96 | 1,830.42 | 1,598.47 | 1,346.10 | 1,129.35 | 863.01 | +| 净资产(亿) | 2,570.70 | 2,331.06 | 2,156.69 | 1,975.07 | 1,895.39 | 1,613.23 | 1,360.10 | 1,128.39 | 914.52 | 728.94 | 639.26 | +| 商誉(亿) | - | - | - | - | - | - | - | - | - | - | - | + + +## 费用指标 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 销售费用率 | 3.42% | 3.24% | 3.09% | 2.59% | 2.50% | 2.60% | 3.69% | 3.33% | 4.89% | 4.19% | 4.44% | +| 管理费用率 | 4.20% | 5.35% | 6.46% | 7.07% | 7.72% | 6.93% | 6.94% | 6.90% | 7.73% | 10.43% | 11.40% | +| 研发费用率 | 0.09% | 0.13% | 0.10% | 0.11% | 0.06% | 0.05% | 0.05% | 0.03% | - | - | - | +| 其他费用率 | 34.37% | 33.85% | 32.83% | 33.16% | 33.59% | 34.45% | 34.58% | 35.69% | 33.30% | 35.26% | 30.22% | +| 折旧费用占比 | - | 0.99% | 1.10% | 1.13% | 1.23% | 1.22% | 1.29% | 1.41% | 1.70% | 2.10% | 2.28% | +| 所得税率 | 25.17% | 25.33% | 25.22% | 25.46% | 25.24% | 25.19% | 25.20% | 25.57% | 25.13% | 25.16% | 25.21% | + + +## 资产占比 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 现金占比 | 16.98% | 19.84% | 25.33% | 22.91% | 20.30% | 16.91% | 7.24% | 70.11% | 65.28% | 59.20% | 42.64% | +| 库存占比 | 18.33% | 18.18% | 17.03% | 15.26% | 13.09% | 13.53% | 13.81% | 14.71% | 16.39% | 18.26% | 20.87% | +| 应收款占比 | 0.01% | 0.01% | 0.02% | 0.01% | - | - | - | - | - | - | 0.00% | +| 预付款占比 | 0.01% | 0.01% | 0.01% | 0.35% | 0.15% | 0.42% | 0.85% | 0.74% | 0.59% | 0.93% | 1.71% | +| 固定资产占比 | 6.95% | 7.32% | 7.30% | 7.76% | 6.85% | 7.60% | 8.27% | 9.54% | 11.32% | 12.80% | 13.23% | +| 长期投资占比 | - | - | - | - | - | - | - | - | - | - | - | +| 商誉占比 | - | - | - | - | - | - | - | - | - | - | - | +| 其他资产占比 | 57.72% | 54.65% | 50.31% | 53.70% | 59.61% | 61.53% | 69.83% | 4.90% | 6.43% | 8.82% | 21.54% | +| 应付款占比 | 0.93% | 1.18% | 1.13% | 0.95% | 0.79% | 0.63% | 0.83% | 0.74% | 0.74% | 0.92% | 1.02% | +| 预收款占比 | 2.54% | 3.21% | 5.18% | 6.08% | 4.98% | 6.24% | 7.51% | 8.49% | 10.72% | 15.53% | 9.57% | +| 短期借款占比 | 0.02% | 0.04% | 0.02% | 0.04% | 0.04% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | +| 长期借款占比 | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | +| 运营资产占比 | 14.88% | 13.81% | 10.75% | 8.59% | 7.47% | 7.08% | 6.33% | 6.22% | 5.52% | 2.73% | 11.99% | +| 有息负债率 | 0.02% | 0.04% | 0.02% | 0.04% | 0.04% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% | + + +## 周转能力 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 存货周转天数 | 1,823 | 1,438 | 1,428 | 1,403 | 1,356 | 1,292 | 1,242 | 1,315 | 1,355 | 2,207 | 2,590 | +| 应收款周转天数 | 0 | 0 | 0 | 0 | - | - | - | - | - | - | 0 | +| 应付款周转天数 | 92 | 93 | 95 | 87 | 81 | 60 | 74 | 65 | 60 | 111 | 126 | +| 固定资产周转率 | 6.18 | 7.96 | 7.56 | 6.46 | 6.27 | 6.04 | 5.87 | 5.06 | 4.01 | 2.78 | 2.93 | +| 总资产周转率 | 0.43 | 0.58 | 0.55 | 0.50 | 0.43 | 0.46 | 0.49 | 0.48 | 0.45 | 0.36 | 0.39 | + + +## 人均效率 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 员工人数 | - | 34,750 | - | - | - | - | - | - | - | - | - | +| 人均创收(万) | - | 501.13 | - | - | - | - | - | - | - | - | - | +| 人均创利(万) | - | 248.14 | - | - | - | - | - | - | - | - | - | +| 人均薪酬(万) | - | 44.18 | - | - | - | - | - | - | - | - | - | + + +## 市场表现 +| 指标 | 2025 Q3 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | +|:---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| 股价 | 1443.99 | 1524.00 | 1726.00 | 1727.00 | 2050.00 | 1998.00 | 1183.00 | 590.01 | 697.49 | 334.15 | 218.19 | +| 市值(亿) | 18,083 | 19,144 | 21,682 | 21,695 | 25,752 | 25,099 | 14,861 | 7,412 | 8,762 | 4,198 | 2,741 | +| PE | 20.97 | 25.62 | 34.57 | 41.35 | 55.15 | 60.91 | 42.21 | 27.37 | 52.41 | 27.08 | 17.86 | +| PB | 7.77 | 9.22 | 11.19 | 12.11 | 14.77 | 16.91 | 11.86 | 7.24 | 10.39 | 6.12 | 4.58 | +| 股东户数 | 220,658 | 201,582 | 150,025 | 145,225 | 175,267 | 114,267 | 89,062 | 88,932 | 63,980 | 53,437 | 46,869 | + diff --git a/data/US/AAPL/balance_sheet.csv b/data/US/AAPL/balance_sheet.csv new file mode 100644 index 0000000..140175d --- /dev/null +++ b/data/US/AAPL/balance_sheet.csv @@ -0,0 +1,11 @@ +date,reportedCurrency,totalAssets,current_assets,cashAndCashEquivalentsAtCarryingValue,cashAndShortTermInvestments,inventory,currentNetReceivables,totalNonCurrentAssets,propertyPlantEquipment,accumulatedDepreciationAmortizationPPE,intangibleAssets,intangibleAssetsExcludingGoodwill,goodwill,investments,longTermInvestments,shortTermInvestments,otherCurrentAssets,otherNonCurrentAssets,total_liabilities,current_liabilities,currentAccountsPayable,deferredRevenue,currentDebt,shortTermDebt,totalNonCurrentLiabilities,capitalLeaseObligations,longTermDebt,currentLongTermDebt,longTermDebtNoncurrent,shortLongTermDebtTotal,otherCurrentLiabilities,otherNonCurrentLiabilities,total_equity,treasuryStock,retainedEarnings,commonStock,commonStockSharesOutstanding +2025-09-30,USD,359241000000,147957000000,35934000000,35934000000,5718000000,72957000000,211284000000,61039000000,None,None,None,None,None,77723000000,18763000000,14585000000,None,285508000000,165631000000,69860000000,None,None,20329000000,119877000000,None,78328000000,20329000000,None,98657000000,66387000000,41549000000,73733000000,None,-14264000000,93568000000,15004697000 +2024-09-30,USD,364980000000,152987000000,29943000000,29943000000,7286000000,66243000000,211993000000,55914000000,None,None,None,None,None,91479000000,35228000000,14287000000,None,308030000000,176392000000,68960000000,None,None,22511000000,131638000000,None,85750000000,20879000000,None,119059000000,50071000000,36634000000,56950000000,None,-19154000000,83276000000,15408095000 +2023-09-30,USD,352583000000,143566000000,29965000000,29965000000,6331000000,29508000000,209017000000,43715000000,None,None,None,None,None,100544000000,31590000000,14695000000,None,290437000000,145308000000,62611000000,None,None,15807000000,145129000000,12842000000,95281000000,15807000000,None,111947000000,58829000000,49848000000,62146000000,None,-214000000,73812000000,15812547000 +2022-09-30,USD,352755000000,135405000000,23646000000,23646000000,4946000000,28184000000,217350000000,42117000000,72340000000,None,None,None,None,120805000000,24658000000,21223000000,None,302083000000,153982000000,64115000000,None,None,21110000000,148101000000,12411000000,98959000000,21110000000,None,120881000000,60845000000,49142000000,50672000000,None,-3068000000,64849000000,16325819000 +2021-09-30,USD,351002000000,134836000000,34940000000,34940000000,6580000000,26278000000,216166000000,39440000000,70283000000,None,None,None,None,127877000000,27699000000,14111000000,None,287912000000,125481000000,54763000000,None,None,15613000000,162431000000,11803000000,109106000000,15613000000,None,124719000000,47493000000,28636000000,63090000000,None,5562000000,57365000000,16864919000 +2020-09-30,USD,323888000000,143713000000,38016000000,38016000000,4061000000,16120000000,180175000000,36766000000,66760000000,None,None,None,None,100887000000,52927000000,11264000000,None,258549000000,105392000000,42296000000,None,None,13769000000,153157000000,9842000000,98667000000,13769000000,None,112436000000,42684000000,26320000000,65339000000,None,14966000000,50779000000,17528214000 +2019-09-30,USD,338516000000,162819000000,48844000000,48844000000,4106000000,22926000000,175697000000,37378000000,58579000000,None,None,None,None,105341000000,51713000000,12352000000,None,248028000000,105718000000,46236000000,None,None,16240000000,142310000000,None,91807000000,16240000000,None,108047000000,37720000000,20958000000,90488000000,None,45898000000,45174000000,18595652000 +2018-09-30,USD,365725000000,131339000000,25913000000,25913000000,3956000000,23186000000,234386000000,41304000000,49099000000,None,None,None,None,170799000000,40388000000,12087000000,None,258578000000,116866000000,55888000000,None,None,20748000000,141712000000,None,93735000000,20748000000,None,114483000000,32687000000,11165000000,107147000000,None,70400000000,40201000000,20000436000 +2017-09-30,USD,375319000000,128645000000,20289000000,20289000000,4855000000,2093000000,246674000000,33783000000,41293000000,2298000000,2298000000,5717000000,None,194714000000,53892000000,13936000000,None,241272000000,100814000000,49049000000,None,None,18473000000,140458000000,None,97207000000,18473000000,None,115680000000,25744000000,40415000000,134047000000,0,98330000000,35867000000,21006768000 +2016-09-30,USD,321686000000,106869000000,20484000000,20484000000,2132000000,527000000,214817000000,27010000000,34235000000,3206000000,3206000000,5414000000,None,170430000000,46671000000,8283000000,None,193437000000,79006000000,37294000000,None,None,11605000000,114431000000,None,75427000000,11605000000,None,87032000000,22027000000,36074000000,128249000000,None,96364000000,31251000000,22001124000 diff --git a/data/US/AAPL/cash_flow.csv b/data/US/AAPL/cash_flow.csv new file mode 100644 index 0000000..5526925 --- /dev/null +++ b/data/US/AAPL/cash_flow.csv @@ -0,0 +1,11 @@ +date,reportedCurrency,net_cash_flow,paymentsForOperatingActivities,proceedsFromOperatingActivities,changeInOperatingLiabilities,changeInOperatingAssets,depreciation,capitalExpenditures,changeInReceivables,changeInInventory,profitLoss,cashflowFromInvestment,cashflowFromFinancing,proceedsFromRepaymentsOfShortTermDebt,paymentsForRepurchaseOfCommonStock,paymentsForRepurchaseOfEquity,paymentsForRepurchaseOfPreferredStock,dividendPayout,dividendPayoutCommonStock,dividendPayoutPreferredStock,proceedsFromIssuanceOfCommonStock,proceedsFromIssuanceOfLongTermDebtAndCapitalSecuritiesNet,proceedsFromIssuanceOfPreferredStock,proceedsFromRepurchaseOfEquity,proceedsFromSaleOfTreasuryStock,changeInCashAndCashEquivalents,changeInExchangeRate,netIncome +2025-09-30,USD,111482000000,None,None,None,None,11698000000,12715000000,None,1400000000,None,15195000000,-120686000000,None,None,None,None,15421000000,15421000000,None,None,None,None,-90711000000,None,None,None,112010000000 +2024-09-30,USD,118254000000,None,None,None,None,11445000000,9447000000,None,-1046000000,None,2935000000,-121983000000,None,None,None,None,15234000000,15234000000,None,None,None,None,-94949000000,None,None,None,93736000000 +2023-09-30,USD,110543000000,None,None,None,None,11519000000,10959000000,-417000000,-1618000000,None,3705000000,-108488000000,None,None,None,None,15025000000,15025000000,None,None,None,None,-77550000000,None,5760000000,None,96995000000 +2022-09-30,USD,122151000000,None,None,None,None,11104000000,10708000000,-9343000000,1484000000,None,-22354000000,-110749000000,None,None,None,None,14841000000,14841000000,None,None,None,None,-89402000000,None,-10952000000,None,99803000000 +2021-09-30,USD,104038000000,None,None,None,None,11284000000,11085000000,-14028000000,-2642000000,None,-14545000000,-93353000000,None,None,None,None,14467000000,14467000000,None,None,None,None,-85971000000,None,-3860000000,None,94680000000 +2020-09-30,USD,80674000000,None,None,None,None,11056000000,7309000000,8470000000,-127000000,None,-4289000000,-86820000000,None,None,None,None,14081000000,14081000000,None,None,None,None,-72358000000,None,-10435000000,None,57411000000 +2019-09-30,USD,69391000000,None,None,None,None,12547000000,10495000000,3176000000,-289000000,None,45896000000,-90976000000,None,None,None,None,14119000000,14119000000,None,None,None,None,-66897000000,None,24311000000,None,55256000000 +2018-09-30,USD,77434000000,None,None,None,None,10903000000,13313000000,-13332000000,828000000,None,16066000000,-87876000000,None,None,None,None,13712000000,13712000000,None,None,None,None,-72738000000,None,5624000000,None,59531000000 +2017-09-30,USD,63598000000,None,None,None,None,10157000000,12451000000,-6347000000,-2723000000,None,-46446000000,-17347000000,None,None,None,None,12769000000,12769000000,None,None,None,None,-32900000000,None,-195000000,None,48351000000 +2016-09-30,USD,65824000000,None,None,None,None,10505000000,12734000000,1044000000,217000000,None,-45977000000,-20483000000,None,None,None,None,12150000000,12150000000,None,None,None,None,-29722000000,None,-636000000,None,45687000000 diff --git a/data/US/AAPL/income_statement.csv b/data/US/AAPL/income_statement.csv new file mode 100644 index 0000000..fd7c2de --- /dev/null +++ b/data/US/AAPL/income_statement.csv @@ -0,0 +1,11 @@ +date,reportedCurrency,grossProfit,revenue,costOfRevenue,costofGoodsAndServicesSold,operatingIncome,sellingGeneralAndAdministrative,researchAndDevelopment,operatingExpenses,investmentIncomeNet,netInterestIncome,interestIncome,interestExpense,nonInterestIncome,otherNonOperatingIncome,depreciation,depreciationAndAmortization,incomeBeforeTax,incomeTaxExpense,interestAndDebtExpense,netIncomeFromContinuingOperations,comprehensiveIncomeNetOfTax,ebit,ebitda,net_income +2025-09-30,USD,195201000000,416161000000,220960000000,220960000000,133050000000,27601000000,34550000000,62151000000,None,None,None,None,None,None,None,11698000000,132729000000,20719000000,None,112010000000,None,132729000000,144427000000,112010000000 +2024-09-30,USD,180683000000,391035000000,210352000000,210352000000,123216000000,26097000000,31370000000,57467000000,None,0,0,0,None,None,None,11445000000,123485000000,29749000000,None,93736000000,None,123216000000,134661000000,93736000000 +2023-09-30,USD,169148000000,383285000000,214137000000,214137000000,114301000000,24932000000,29915000000,54847000000,None,-183000000,3750000000,3933000000,None,-382000000,None,11519000000,113736000000,16741000000,None,96995000000,None,114301000000,125820000000,96995000000 +2022-09-30,USD,170782000000,394328000000,223546000000,223546000000,119437000000,25094000000,26251000000,51573000000,None,-106000000,2825000000,2931000000,None,334000000,None,11104000000,119103000000,19300000000,None,99803000000,None,119437000000,130541000000,99803000000 +2021-09-30,USD,152836000000,365817000000,212981000000,212981000000,108949000000,21973000000,21914000000,43887000000,None,198000000,2843000000,2645000000,None,258000000,None,11284000000,109207000000,14527000000,None,94680000000,None,111852000000,123136000000,94680000000 +2020-09-30,USD,104956000000,274515000000,169559000000,169559000000,66288000000,19916000000,18752000000,38668000000,None,890000000,3763000000,2873000000,None,803000000,None,11056000000,67091000000,9680000000,None,57411000000,None,69964000000,81020000000,57411000000 +2019-09-30,USD,98392000000,260174000000,161782000000,161782000000,63930000000,18245000000,16217000000,34462000000,None,1385000000,4961000000,3576000000,None,1807000000,None,12547000000,65737000000,10481000000,None,55256000000,None,69313000000,81860000000,55256000000 +2018-09-30,USD,101839000000,265595000000,163756000000,163756000000,70898000000,16705000000,14236000000,30941000000,None,2446000000,5686000000,3240000000,None,2005000000,None,10903000000,72903000000,13372000000,None,59531000000,None,76143000000,87046000000,59531000000 +2017-09-30,USD,88186000000,229234000000,141048000000,141048000000,61344000000,15261000000,11581000000,26842000000,None,2878000000,5201000000,2323000000,None,2745000000,None,10157000000,64089000000,15738000000,None,48351000000,None,66412000000,76569000000,48351000000 +2016-09-30,USD,84263000000,215639000000,131376000000,131376000000,60024000000,14194000000,10045000000,24239000000,None,2543000000,3999000000,1456000000,None,1348000000,None,10505000000,61372000000,15685000000,None,45687000000,None,62828000000,73333000000,45687000000 diff --git a/data/US/AAPL/report.md b/data/US/AAPL/report.md new file mode 100644 index 0000000..37c9b85 --- /dev/null +++ b/data/US/AAPL/report.md @@ -0,0 +1,4 @@ +# Financial Analysis Report: US AAPL + + +
yearrevenuenet_incomeROENetMarginDebtToEquityCurrentRatio
2025416,161,000,000112,010,000,000151.91%26.92%3.870.89
2024391,035,000,00093,736,000,000164.59%23.97%5.410.87
2023383,285,000,00096,995,000,000156.08%25.31%4.670.99
2022394,328,000,00099,803,000,000196.96%25.31%5.960.88
2021365,817,000,00094,680,000,000150.07%25.88%4.561.07
2020274,515,000,00057,411,000,00087.87%20.91%3.961.36
2019260,174,000,00055,256,000,00061.06%21.24%2.741.54
2018265,595,000,00059,531,000,00055.56%22.41%2.411.12
2017229,234,000,00048,351,000,00036.07%21.09%1.801.28
2016215,639,000,00045,687,000,00035.62%21.19%1.511.35
\ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..7ea7962 --- /dev/null +++ b/main.py @@ -0,0 +1,98 @@ +import os +import sys +from dotenv import load_dotenv + +sys.path.append(os.path.join(os.path.dirname(__file__), 'src')) + +from fetchers.factory import FetcherFactory +from storage.file_io import DataStorage +from analysis.calculator import FinancialCalculator +from reporting.markdown_generator import MarkdownReporter +from reporting.html_generator import HtmlReporter + +def run_analysis(market, symbol): + load_dotenv() + tushare_token = os.getenv('TUSHARE_TOKEN') + av_key = os.getenv('ALPHA_VANTAGE_KEY') + + print(f"\nProcessing {market} stock: {symbol}...") + # 1. Fetch Data + try: + fetcher = FetcherFactory.get_fetcher(market, tushare_token, av_key) + + print("Fetching Income Statement...") + df_inc = fetcher.get_income_statement(symbol) + print("Fetching Balance Sheet...") + df_bal = fetcher.get_balance_sheet(symbol) + print("Fetching Cash Flow...") + df_cf = fetcher.get_cash_flow(symbol) + print("Fetching Market Metrics...") + metrics = fetcher.get_market_metrics(symbol) + print(f"Metrics: {metrics}") + + # 1.1 Fetch Historical Metrics + historical_metrics_df = None + if not df_inc.empty: + # Tushare dates are often YYYYMMDD. Align format. + dates = df_inc['date'].tolist() if 'date' in df_inc.columns else [] + # Also combine from bal/cf if needed? usually inc covers reporting periods. + if dates and hasattr(fetcher, 'get_historical_metrics'): + print(f"Fetching Historical Market Metrics for {len(dates)} periods...") + historical_metrics_df = fetcher.get_historical_metrics(symbol, dates) + + # 2. Save Data + storage = DataStorage() + storage.save_data(df_inc, market, symbol, "income_statement") + storage.save_data(df_bal, market, symbol, "balance_sheet") + storage.save_data(df_cf, market, symbol, "cash_flow") + + # 3. Calculate Indicators + print("Calculating Financial Indicators...") + calculator = FinancialCalculator() + df_analysis = calculator.process_data(df_inc, df_bal, df_cf, metrics, market, historical_metrics_df) + + if df_analysis.empty: + print("Not enough data to calculate indicators.") + return + + # 4. Generate Reports + print("Generating Reports...") + md_reporter = MarkdownReporter() + html_reporter = HtmlReporter() + + md_content = md_reporter.generate_report(df_analysis, market, symbol, metrics) + html_content = html_reporter.generate_report(df_analysis, market, symbol, metrics) + + output_dir = os.path.join("data", market, symbol) + md_path = os.path.join(output_dir, "report.md") + html_path = os.path.join(output_dir, "report.html") + + with open(md_path, "w", encoding='utf-8') as f: + f.write(md_content) + + with open(html_path, "w", encoding='utf-8') as f: + f.write(html_content) + + print(f"\nAnalysis completed! Reports saved to:") + print(f" - Markdown: {md_path}") + print(f" - HTML: {html_path}") + print("-" * 50) + + except Exception as e: + print(f"Error occurred: {str(e)}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + if len(sys.argv) > 2: + market = sys.argv[1] + symbol = sys.argv[2] + run_analysis(market, symbol) + else: + print("Usage: python main.py ") + # Default test cases if run without args (for manual testing) + print("Running default test cases:") + # Test CN + run_analysis('CN', '600519.SH') + # Test US + run_analysis('US', 'AAPL') diff --git a/nohup.out b/nohup.out new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..afd6950 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +pandas +requests +tushare +alpha_vantage +python-dotenv diff --git a/src/analysis/__pycache__/calculator.cpython-312.pyc b/src/analysis/__pycache__/calculator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c22f10ee795efaa4934ab0c35194511f0b8e8bd GIT binary patch literal 17701 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 pd.DataFrame: + """ + Input DFs are expected to be raw API output (with some renaming from Fetcher, but we can re-map here for safety). + Output: A DataFrame where index is Years (descending), columns are all calculated indicators. + """ + # 1. Standardize and Merge + # We need a robust date/year index. + # Tushare: end_date (YYYYMMDD). AV: fiscalDateEnding (YYYY-MM-DD). + # Fetchers already normalize some cols, but let's map extensively. + + df_inc = self._map_columns(df_inc, 'income', market) + df_bal = self._map_columns(df_bal, 'balance', market) + df_cf = self._map_columns(df_cf, 'cashflow', market) + + # Merge on 'date_str' (normalized YYYY-MM-DD or YYYYMMDD string) + # We need to create a unified key. Fetchers return 'date' column. + # Let's clean 'date' to string. + for df in [df_inc, df_bal, df_cf]: + if not df.empty and 'date' in df.columns: + df['date_str'] = df['date'].astype(str).str.replace('-', '') # Standardize to YYYYMMDD + + # Start with Income Statement as base (usually most critical for periods) + if df_inc.empty: + return pd.DataFrame() + + df_merged = pd.merge(df_inc, df_bal, on='date_str', how='outer', suffixes=('', '_bal')) + df_merged = pd.merge(df_merged, df_cf, on='date_str', how='outer', suffixes=('', '_cf')) + + if df_merged.empty: + return pd.DataFrame() + + # Sort desc + df_merged = df_merged.sort_values('date_str', ascending=False) + + # 2. Calculate Indicators + # Helper for safer division + def safe_div(a, b): + return a / b.replace(0, np.nan) + + # --- Major Indicators --- + # Revenue, NetIncome (mapped) + # Growth Rates (YoY) - Note: Data is desc, so shift(-1) is previous year + # Helper for YoY Growth + def calc_yoy_growth(df, col): + growths = [] + for idx, row in df.iterrows(): + try: + curr_date = str(row['date_str']) + # target prev year date: 20250930 -> 20240930 + target_date = str(int(curr_date) - 10000) + + # Find matching row + match = df[df['date_str'] == target_date] + if not match.empty and col in row and pd.notna(row[col]): + prev_val = match.iloc[0].get(col) + curr_val = row[col] + if prev_val and prev_val != 0: + growths.append((curr_val - prev_val) / abs(prev_val)) + else: + growths.append(np.nan) + else: + # Fallback: if no match found (e.g. historical annuals might be contiguous), + # check if next row is exact 1 year prior? + # Actually strict matching is safer to avoid Q3 vs FY comparison issues user reported. + growths.append(np.nan) + except: + growths.append(np.nan) + return growths + + if 'revenue' in df_merged.columns: + df_merged['RevenueGrowth'] = calc_yoy_growth(df_merged, 'revenue') + if 'net_income' in df_merged.columns: + df_merged['NetIncomeGrowth'] = calc_yoy_growth(df_merged, 'net_income') + + # Margins + if 'gross_profit' in df_merged.columns and 'revenue' in df_merged.columns: + df_merged['GrossMargin'] = safe_div(df_merged['gross_profit'], df_merged['revenue']) + if 'net_income' in df_merged.columns and 'revenue' in df_merged.columns: + df_merged['NetMargin'] = safe_div(df_merged['net_income'], df_merged['revenue']) + + # Returns + # ROE + if 'net_income' in df_merged.columns and 'total_equity' in df_merged.columns: + df_merged['ROE'] = safe_div(df_merged['net_income'], df_merged['total_equity']) + + # ROA + if 'net_income' in df_merged.columns and 'total_assets' in df_merged.columns: + df_merged['ROA'] = safe_div(df_merged['net_income'], df_merged['total_assets']) + + # ROIC + if 'short_term_debt' in df_merged.columns and 'long_term_debt' in df_merged.columns: + df_merged['InterestBearingDebt'] = df_merged['short_term_debt'].fillna(0) + df_merged['long_term_debt'].fillna(0) + if 'total_equity' in df_merged.columns: + df_merged['InvestedCapital'] = df_merged['total_equity'] + df_merged['InterestBearingDebt'] + if 'ebit' in df_merged.columns: + df_merged['ROIC'] = safe_div(df_merged['ebit'], df_merged['InvestedCapital']) + + # Cash Flow + if 'capex' in df_merged.columns: + df_merged['Capex'] = df_merged['capex'].abs() + if 'ocf' in df_merged.columns: + df_merged['FCF'] = df_merged['ocf'] - df_merged['Capex'] + + # --- Expenses --- + + # --- Expenses --- + if 'selling_exp' in df_merged.columns and 'revenue' in df_merged.columns: + df_merged['SellingRatio'] = safe_div(df_merged['selling_exp'], df_merged['revenue']) + if 'admin_exp' in df_merged.columns and 'revenue' in df_merged.columns: + df_merged['AdminRatio'] = safe_div(df_merged['admin_exp'], df_merged['revenue']) + if 'rd_exp' in df_merged.columns and 'revenue' in df_merged.columns: + df_merged['RDRatio'] = safe_div(df_merged['rd_exp'], df_merged['revenue']) + # Tax Rate + if 'income_tax' in df_merged.columns and 'total_profit' in df_merged.columns: + df_merged['TaxRate'] = safe_div(df_merged['income_tax'], df_merged['total_profit']) + + # Other Expense Ratio (GrossMargin - Selling - Admin - RD - NetMargin) + if 'GrossMargin' in df_merged.columns and 'NetMargin' in df_merged.columns: + other_ratio = df_merged['GrossMargin'] - df_merged['NetMargin'] + if 'SellingRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['SellingRatio'].fillna(0) + if 'AdminRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['AdminRatio'].fillna(0) + if 'RDRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['RDRatio'].fillna(0) + df_merged['OtherExpenseRatio'] = other_ratio + + # Depreciation Expense Ratio + if 'depreciation' in df_merged.columns and 'revenue' in df_merged.columns: + # depreciation might be negative in some sources? Usually positive expense. + # safe_div logic check + df_merged['DepreciationRatio'] = safe_div(df_merged['depreciation'].abs(), df_merged['revenue']) + + if 'total_assets' in df_merged.columns: + assets = df_merged['total_assets'] + # Restore missing ratios + if 'cash' in df_merged.columns: df_merged['CashRatio'] = safe_div(df_merged['cash'], assets) + if 'inventory' in df_merged.columns: df_merged['InventoryRatio'] = safe_div(df_merged['inventory'], assets) + if 'receivables' in df_merged.columns: df_merged['ReceivablesRatio'] = safe_div(df_merged['receivables'], assets) + if 'accounts_payable' in df_merged.columns: df_merged['PayablesRatio'] = safe_div(df_merged['accounts_payable'], assets) + + # Prepayment + if 'prepayment' in df_merged.columns: df_merged['PrepaymentRatio'] = safe_div(df_merged['prepayment'], assets) + + # Fixed Assets + if 'fixed_assets' in df_merged.columns: df_merged['FixedAssetsRatio'] = safe_div(df_merged['fixed_assets'], assets) + + # Long Term Investment + if 'lt_invest' in df_merged.columns: df_merged['LongTermInvestmentRatio'] = safe_div(df_merged['lt_invest'], assets) + + # Goodwill + if 'goodwill' in df_merged.columns: df_merged['GoodwillRatio'] = safe_div(df_merged['goodwill'], assets) + + # Other Assets: User requested specific residual formula + # 1 - Cash - Inventory - Receivables - Prepayment - FixedAssets - LTInvest - Goodwill + # Use fillna(0) to ensure subtraction works + known_assets_ratio = ( + df_merged.get('CashRatio', 0).fillna(0) + + df_merged.get('InventoryRatio', 0).fillna(0) + + df_merged.get('ReceivablesRatio', 0).fillna(0) + + df_merged.get('PrepaymentRatio', 0).fillna(0) + + df_merged.get('FixedAssetsRatio', 0).fillna(0) + + df_merged.get('LongTermInvestmentRatio', 0).fillna(0) + + df_merged.get('GoodwillRatio', 0).fillna(0) + ) + df_merged['OtherAssetsRatio'] = 1 - known_assets_ratio + + # Advance Receipts (adv_receipts + contract_liab) + adv = df_merged.get('adv_receipts', 0).fillna(0) + df_merged.get('contract_liab', 0).fillna(0) + df_merged['AdvanceReceiptsRatio'] = safe_div(adv, assets) + + # Debts + # Handle potential column name collision from merge (prioritize Balance Sheet source) + st_col = 'short_term_debt_bal' if 'short_term_debt_bal' in df_merged.columns else 'short_term_debt' + lt_col = 'long_term_debt_bal' if 'long_term_debt_bal' in df_merged.columns else 'long_term_debt' + + st_debt = df_merged.get(st_col, 0).fillna(0) + lt_debt = df_merged.get(lt_col, 0).fillna(0) + + df_merged['ShortTermDebtRatio'] = safe_div(st_debt, assets) + df_merged['LongTermDebtRatio'] = safe_div(lt_debt, assets) + + # Interest Bearing Debt Ratio + df_merged['InterestBearingDebtRatio'] = safe_div(st_debt + lt_debt, assets) + + # Operating Assets Ratio (New user-defined formula) + # InventoryRatio + ReceivablesRatio + PrepaymentRatio - PayablesRatio - AdvanceReceiptsRatio + inv_ratio = df_merged.get('InventoryRatio', 0).fillna(0) + rec_ratio = df_merged.get('ReceivablesRatio', 0).fillna(0) + prep_ratio = df_merged.get('PrepaymentRatio', 0).fillna(0) + pay_ratio = df_merged.get('PayablesRatio', 0).fillna(0) + adv_ratio = df_merged.get('AdvanceReceiptsRatio', 0).fillna(0) + df_merged['OperatingAssetsRatio'] = inv_ratio + rec_ratio + prep_ratio - pay_ratio - adv_ratio + + # --- Turnover (Days) --- + if 'cogs' in df_merged.columns: + if 'inventory' in df_merged.columns: + df_merged['InventoryDays'] = safe_div(df_merged['inventory'] * 365, df_merged['cogs']) + if 'accounts_payable' in df_merged.columns: + df_merged['PayablesDays'] = safe_div(df_merged['accounts_payable'] * 365, df_merged['cogs']) + if 'revenue' in df_merged.columns: + if 'receivables' in df_merged.columns: + df_merged['ReceivablesDays'] = safe_div(df_merged['receivables'] * 365, df_merged['revenue']) + if 'fixed_assets' in df_merged.columns: + df_merged['FixedAssetsTurnover'] = safe_div(df_merged['revenue'], df_merged['fixed_assets']) + if 'total_assets' in df_merged.columns: + df_merged['TotalAssetTurnover'] = safe_div(df_merged['revenue'], df_merged['total_assets']) + + # --- Market Metrics --- + if historical_metrics is not None and not historical_metrics.empty and not df_merged.empty: + # Merge historical metrics + # historical_metrics likely has ['date_str', 'Price', 'PE', 'PB', 'MarketCap', 'Shareholders'] + # We merge on date_str. + # Use distinct suffixes to avoid collision if columns exist? + df_merged = pd.merge(df_merged, historical_metrics, on='date_str', how='left', suffixes=('', '_hist')) + + # Fill missing with latest metrics for the first row if historical missing (e.g. today's report? usually historical covers it) + # Or prioritize historical. + # Columns in historical: Price, PE, PB, MarketCap, Shareholders. + # If they exist in df_merged after merge, good. + # Ensure 'Employees' is still filled from market_metrics (stock_company is usually single latest) + # df_merged.loc[df_merged.index[0], 'Employees'] = market_metrics.get('employee_count') # MOVED to Annual Logic below + + # If historical is missing for latest date (e.g. slight mismatch), fallback to snapshot? + if pd.isna(df_merged.iloc[0].get('Price')): + df_merged.loc[df_merged.index[0], 'Price'] = market_metrics.get('price') + if pd.isna(df_merged.iloc[0].get('PE')): + df_merged.loc[df_merged.index[0], 'PE'] = market_metrics.get('pe') + # ... checks for others if needed. + + # Shareholders fallback + if pd.isna(df_merged.iloc[0].get('Shareholders')): + df_merged.loc[df_merged.index[0], 'Shareholders'] = market_metrics.get('total_share_holders') + + elif not df_merged.empty: # Fallback to existing logic (snapshot only) + df_merged.loc[df_merged.index[0], 'Price'] = market_metrics.get('price') + df_merged.loc[df_merged.index[0], 'MarketCap'] = market_metrics.get('market_cap') + df_merged.loc[df_merged.index[0], 'PE'] = market_metrics.get('pe') + df_merged.loc[df_merged.index[0], 'PB'] = market_metrics.get('pb') + # df_merged.loc[df_merged.index[0], 'Employees'] <-- Removed + df_merged.loc[df_merged.index[0], 'Shareholders'] = market_metrics.get('total_share_holders') + + # --- Employees & Per-Employee Metrics --- + # Logic: Employee count usually comes from the latest Annual Report. + # Assign to the most recent '1231' row. + if not df_merged.empty: + # Find rows ending with 1231 + annual_idxs = df_merged.index[df_merged['date_str'].astype(str).str.endswith('1231')] + if len(annual_idxs) > 0: + target_idx = annual_idxs[0] # The latest annual report in the sorted list + + emps = market_metrics.get('employee_count', 0) + if emps > 0: + df_merged.loc[target_idx, 'Employees'] = emps + if 'revenue' in df_merged.columns: + df_merged.loc[target_idx, 'RevenuePerEmp'] = df_merged.loc[target_idx, 'revenue'] / emps + if 'net_income' in df_merged.columns: + df_merged.loc[target_idx, 'ProfitPerEmp'] = df_merged.loc[target_idx, 'net_income'] / emps + if 'cash_paid_for_employees' in df_merged.columns: + df_merged.loc[target_idx, 'AvgWage'] = df_merged.loc[target_idx, 'cash_paid_for_employees'] / emps + + # Filter out auxiliary comparison periods (e.g. Q3 last year used only for growth calc) + # Keep: Latest Period AND Annual Reports + if not df_merged.empty: + latest_date = df_merged['date_str'].iloc[0] + # condition: is latest OR is annual (ends with 1231) + condition = (df_merged['date_str'] == latest_date) | (df_merged['date_str'].astype(str).str.endswith('1231')) + df_merged = df_merged[condition] + + return df_merged + + def _map_columns(self, df: pd.DataFrame, type: str, market: str) -> pd.DataFrame: + df = df.copy() + if df.empty: return df + cols = df.columns + + mapping = {} + if market == 'CN': + # Tushare mapping + mapping = { + # Income + 'total_revenue': 'revenue', 'oper_cost': 'cogs', 'total_cogs': 'total_costs', + 'sell_exp': 'selling_exp', 'admin_exp': 'admin_exp', 'fin_exp': 'fin_exp', 'rd_exp': 'rd_exp', + 'total_profit': 'total_profit', 'income_tax': 'income_tax', 'n_income_attr_p': 'net_income', 'net_profit_attr_p': 'net_income', + # Balance + 'money_cap': 'cash', 'accounts_receiv': 'receivables', 'inventories': 'inventory', + 'fix_assets': 'fixed_assets', 'total_assets': 'total_assets', 'goodwill': 'goodwill', + 'prepayment': 'prepayment', 'lt_eqt_invest': 'lt_invest', 'oth_assets': 'other_assets', 'trad_asset': 'trading_assets', + # Updated per user request and CSV validation: st_borr, lt_borr + 'st_borr': 'short_term_debt', 'non_cur_liab_due_1y': 'short_term_debt_part', + 'lt_borr': 'long_term_debt', 'bonds_payable': 'long_term_debt_bonds', + 'total_liab': 'total_liabilities', 'total_hldr_eqy_exc_min_int': 'total_equity', 'total_share_holder_equity': 'total_equity', + 'adv_receipts': 'adv_receipts', 'contract_liab': 'contract_liab', + 'acct_payable': 'accounts_payable', + # Cash + 'net_cash_flow': 'ocf', 'n_cashflow_act': 'ocf', 'c_pay_acq_const_fiolta': 'capex', 'c_paid_div_prof_int': 'dividends', + 'c_paid_to_for_empl': 'cash_paid_for_employees' + } + else: + # Alpha Vantage mapping + mapping = { + # Income + 'totalRevenue': 'revenue', 'costOfRevenue': 'cogs', 'grossProfit': 'gross_profit', + 'sellingGeneralAndAdministrative': 'operating_expenses', + 'researchAndDevelopment': 'rd_exp', 'interestExpense': 'fin_exp', + 'incomeBeforeTax': 'total_profit', 'incomeTaxExpense': 'income_tax', 'netIncome': 'net_income', + 'ebit': 'ebit', + # Balance + 'cashAndCashEquivalentsAtCarryingValue': 'cash', 'currentNetReceivables': 'receivables', 'inventory': 'inventory', + 'propertyPlantEquipment': 'fixed_assets', 'totalAssets': 'total_assets', 'goodwill': 'goodwill', + 'otherCurrentAssets': 'prepayment', 'longTermInvestments': 'lt_invest', 'otherNonCurrentAssets': 'other_assets', + 'shortTermDebt': 'short_term_debt', 'currentLongTermDebt': 'short_term_debt_part', + 'longTermDebt': 'long_term_debt', 'totalLiabilities': 'total_liabilities', 'totalShareholderEquity': 'total_equity', + 'currentAccountsPayable': 'accounts_payable', 'deferredRevenue': 'adv_receipts', + # Cash + 'net_cash_flow': 'ocf', 'operatingCashflow': 'ocf', 'capitalExpenditures': 'capex', 'dividendPayout': 'dividends' + } + + # Filter mapping keys that exist in df + actual_map = {k: v for k, v in mapping.items() if k in cols} + df = df.rename(columns=actual_map) + + # Remove duplicate columns (e.g. if 'revenue' existed and we renamed 'total_revenue' to 'revenue') + df = df.loc[:, ~df.columns.duplicated()] + + # Post-processing + if 'short_term_debt' not in df.columns: df['short_term_debt'] = 0 + if 'short_term_debt_part' in df.columns: + df['short_term_debt'] = df['short_term_debt'].fillna(0) + df['short_term_debt_part'].fillna(0) + + if 'long_term_debt' not in df.columns: df['long_term_debt'] = 0 + if 'long_term_debt_bonds' in df.columns: + df['long_term_debt'] = df['long_term_debt'].fillna(0) + df['long_term_debt_bonds'].fillna(0) + + # Gross Profit + if 'gross_profit' not in df.columns and 'revenue' in df.columns and 'cogs' in df.columns: + df['gross_profit'] = df['revenue'] - df['cogs'] + + # EBIT + if 'ebit' not in df.columns and 'total_profit' in df.columns and 'fin_exp' in df.columns: + df['ebit'] = df['total_profit'] + df['fin_exp'] + + return df diff --git a/src/fetchers/__pycache__/alpha_vantage_fetcher.cpython-312.pyc b/src/fetchers/__pycache__/alpha_vantage_fetcher.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09e2dc7c3a2297d60f59e4b2b6dd4ea9c432b548 GIT binary patch literal 6535 zcmc&&Z)_7s7T>kKw%32+#0jwp4sk+4@j(&-Z8*4;U=oKwL->;bmr%O5@oo~EjcsPv zghWn{4@hV3dMdO!C8#}-eriyW&izzZI;lPNAJtW=jj3X7a}rWN^_zR4x~jbo_h#1{ zOiFL zgtRhvS(hOG2p=s$zJ<*Vg(RlH>is^QOo{Ork7P5$gd!n{VSIYoAo9W}G69=RGfYrm zMUi1dEZgh7_xDRctH@aESV(F)7asvCNL*`=e`l+Rf~}+KrxaV26)?pm zFvaec*c8IT!bL=o0ysC)CUGfL>Cctv^9Y9<8le2bCwNaGlnBzt+BQ zgiKJc5q~yndXdDVzn?a5pp?&hU#`|lm=fkOohEgL25Zu5So0|u*`&?8UvtJWk}JFE z&{i@-1K(r40doxyn+Qq44J@sZK&>;z66@&|>lqS8Shqpb@JZJl)C~c`%otxZ4ixK2 za+0{9A0j46UwQuxJ;K`x;Tn2mak?UaP!xF)4-r(9LXk1=_qsh^ z+0I5HaaK6a$D&9Q@KO?gM*jWRVFLZSqb)+okDL|&2&XL&y6bE*N>T+~RE^Fei@p z$%e29k&O;q2=Xx{$WbvAib%39#>un-mar~fHU*S|HA$a6Pp9NX5C0ERes`0Wx zQ!y5eh*$_ZSzPe^sxGH>OGB_uQb-OaZ zJd`*9b0s%a@}ml)s9#9W^DDa_NwaH z-B)+dg%&)S+V))KuA~_&>t3vH%fipvmUq=Hx>~cYR;ZN492N zTQl4C-fGIYw&q+%lf8ExuB79;dk0CPeEmNO9hl!%k*R8$ADc(q>%#pKt_Xo2XC$Ay8x}T zM2MKt(U58agg+d>br`^ZCVcBxg5BfbTgMq^jG%|<|E!0J10Vyac5`$B+89FNsp39} zXuTZ$0K6I##u6S~SWD(%zE(IZXVdI)ffRKl0nf$E@v(F5|Ha(0N6f9zv`ai(XUE!e zIU860I>5#%aM}Q=*nMa>DBuLC)PvxpfDZK{_0qoqe4xG$I**iQd5p9VR!j3$^vmWW zCj-Y%9t@m9m9SWZA$Qud3RW~01zZr$z{WJg$hHH0NA~;sm}4i73C?wSSA#SOpuKMgz@knk;?#|6h0;xjRUk* zN>O+lBaX9(XU3xf2geK_t>7f`!n;vDCJmV2tOenN4r&4d7%M~p$@Bn_KC_BmN*-X3 zp6Ai|5I?CviHc(+LOb!qPCU_a4jV@q74dOHLn;-hoB+v8QSk|0f`y3Fn;(W(Kjzy5 z!o z_Il5?p4WPlhYJ>>qAE!hbSAnR=59~-+~PhLJ{7X-POmX{ea-diYt^s0lZWz-+WDHS zV{6Kqp1#$R?HtN(A6jzP&V?5?W;gyay&=0XkaPE3q2L|W8_4Cb(4Lh?9J2OuoxK)vBcP_!YpGyZb&i#wFuB@#~ z;q4~B7u(MMg@M}hX$M+iEuFl!sAN8aHNIG8Rh1KgBu78L=dL%>{SI$iYpmE@SD1cGc5#x z&y2wTkn`qwLL^^uO879YlrZ+vRRvD81{~r!^G#aQ1^0D8-%HyKL_!C*4VHen{9u3A zk;4IIV9-ApI2`C7R5CfpArXI`kHle%{2HK2!EK-z-#Gxw`1STcjR5q-R z8^y_^Ai}nb$$H$vpF!D{>Yl8^drj4mfuvUAC-{goy=ARkxtjCWAWe(_xk%*g6^r)T zti3j~aeHcW&b~XTyX)Nyxn6#K6J(cFmZTXrM_KvHLzjovXQ82PIl!T z&MPCc;j7`>4)0Qp=X&$C<^|ot&Kst*`A&`hitftJS4;&8SPMp?+?k{izKnXnyYMZ7 z;$0s?0!2h*$(LA@9zQ^>kR}3(P3Cx(SsN` zc!l6FQlZ9pA;gY^gpd@1!%A5&j=(`A2JV@7ID}Pd&m~MgN_EF-W%>X{2o)d#s@QU+ zzpq59Q|akaMf+9BuF}2|W^^1B^%>A_ph7GrBbWp+8Ng%^larVX0a@*m71$*KY4HgQ z(2M7#>87>(@c}lB+#&)};*4|Ah4VRkyXuRZpU!)C<{Nh8pX&I=YE(S3Y_&&zRmncN zKaP-VIwTH0ibJNL@zWggiNhTZiugUN3goS|j|*{}UI}p}*G~z!X2u~-32$`2On3PQ zdYPWSBd1iyv*TzMWW{j?vV%!Dq4F`r2SZ9OqlLGMC|rI*Ph_kM7!|(8Qoe3JCh$1< z@kcoS1e~z7TnKy2D&GJHS_x!m7>}O@BI5Pp_@#y~JNA_orKaoG@{<*oNh|~VWT}sw zU1-nQcc?y6zdir-{x#zjKI4vD4_^ysty`8F8sBVwqdBEZb!NAB-Riy5a3Z-s`P;0m zF7K!)#wecyUj^f6%`mcsVJ4znT)=G`gU|PZI-(?DvR;&sa*eCRbo3M`slOS4&6In-t3W`Ns#L19^F7bIo@Lxqc!Bg#mIdgCDpj?Nt3rpJaxUyF5KyIdywmY^ z$1-jfHkLuRzCb{g(!FDQ+qR6G1+SHIV);-l?Eg#OjlO^3rV_*z?R-WhA;q^#5snpA zOf!tIa50WQx59ISQ2CWB4|9iV$i3(SjN=81_`(np8TfBb8$spNwlvn z>A9pG3)x*4SS*;QI5q++>4UZVQn@c>pUM`+b_*nk3lu0>CLnR60!H(cH>TDiL9!2f zW=JYDBmZO(Uf6U7qk9ngoP1OYTW&lWg2sD@LmcxO z!ljzQ8cxG$U(>#-!#X!|p)SPfS`f!x)E&{5=3#c3@#w!GAG2RDKs}6VnGoW_q!I+4^R}&(>p&Eo(guf!* z2x+QzP3%toA9Vvc&b)^fb_VCba+g4OkU2tr2`Fx~3_r-MN29VzA|_(h$nz0766bl3 zR@Et@w1kborkZ#@BngVb^9qsecHdMdy+K99iucU~{EYIlcR3R8yP8-8D#gRzN#9|w zf9x2x{yHHmd-OXjY&<>~JxjBs-8)RPw zvBDHW&zcovIhMFt#Svr!rwgz68lbv` zeU<(oyU$M3Mms{RqkWDjRC4 z4Rt|1>{(aQ?fLhtXgUC(WW7({U9OjyB{Zt-Kue6rF?(N&>tS6?yj|5p5QP|epHcNO z0Sl|jTD>3RD0X8JqO1~+$jk0;G{@YkO_1e;AO*x&6vq|Su@q54f&@?|`sFZiO#-r+ z1SKTWuP?~T>eYH&kQ^`WR765^^o zjH59=oQO#g_!Je78C!`Zcg%^&Nm4;mB^H$xA_SmbWkl5!UgApzRISk5v5RU9MOPOi zvKUr~#oRjulL)C=N>IU5F&(?21hpb%ATeI~5TNZQdgyW&npz7@EqA4Nr7sOSr#Y!F znpsoVR&&SQw{E|+G4YF;f1gS3&o=v#Q-y|ocSqkHy>lWtvF$*6n(t2Ep1w1iG;KBS zCA@e3Ica@pX;}~FEL~f!mV4F@tQ&Ki-fYjYoa^P}se-+8V?N(AlLruRhlz$>|Z zzT`9!b}`>Gob4IT*@p`q2lE|AvmHk>KYcZGZXpAWj*IyYA=@Ej7DKs?@GVfR@tqUz zoLGM=-!TAxTh{tpr#GfHj%@~0L#YGlu0Ow;Ilu79B_Th(n4Mls&;4QkxAVVW$gD`2 zk=2Y-{=^#HYS?pk`Al-^p}je2|9blrgB*JwAr08y zTAyj`*;v_leG{iTQwY(I*Wy62KYA{8yc-FIYv9x0rabMyhW)SwvPJ7jj>Wdb^iVvvSRZ?m znQZ`+g4TEFR3j0xhsPwEpDuA$t(E7gpH+>g&-nxAPWjJc0?U|WYHO`E2vlLAIE42C z%QCeR5Y>8e`t+D@nm-#nJ?CGuo|K}Cg5*A%h{nY=Q~!}ZH=s>OTyximte#bZx5gO( zUlHTufVgo%imV9)tT4%iRKpp6fPAWsGep>2bSxUZQtGLnOi0pPWL5O9#-!*C5l~03 z5`+VcGOtpJd`O4^+KKSLsMZ0LQ9eEtO#liu#G|kpudE1IM@ppj;0K@=rs{vi-^}L>Xaal!I)%Rc#KRmUnRz& zqlytaDxg-vG$)`pUsilojKfA0b^mVA_I>4BVcv}z>tlaaKLbG?#x`Txt8b53rxcW(6NyAEf&4rc~WKB&)iy_WAf^WbW( z{cPcd?k{Zyhq;IhCiBpO6X99{$ zpI0~?#}d4Ph(%`C6u5EfhZ*2BG@z+on4`New%d)B#C;N(XBr_Q@7!Di#64>Y+z7f! zu;+}NX;^cio1BFiFG3cG>Ol=WTbO56$En%z(=&daoAb^2XZ*8sG}D6|6N}eGIRPhA z1}BfmklcwLErvJ-afBYkK$*b=45#S$eFpyo#yuKLuo`oO7ZOf*8xl!YE|P3qxzFNZ zI)FtPl36+f6U0%?@hC4RpeqsrCYp}YQ>qCsk>pZR;Hhfcm2~7G z>5Yr4BE*XWPdbXVoEJcvvI68LD%k4twzjOTEz{ng+MlxxCp8b<`ym%B?CpW?%=kQtBRM~5IxQs>Mf>K`!=2Aphj7X7q1THXjp#%n}i1YA1CBnt5q4HhD;4g`F(EjNvsZQE6UJ~sqNp7z0t769IKv9nli%{VuLI`+b;u`ooA;eSg z>x5haQtO8G#0>#b$*l`uN`$*z&y#%cF*dS_A_1v#!1d_nwVZ9JNxhdJSMgf$ZObRf8<&@>jp%CkaQ5Zi#;x?Q1{sIfCZv%4k~mcgQ#Rrs z-znAkB;VL!xlUvVK&t)X%H~kccBteRo&ANuu_wkQa@SqFclrIxIm>~quI^v-{zq?0 zlRA;@AAc~F?FuHxlK+ymb`oQWZyRtaI~l5kZwY^qY}ik8G1> zvAqtuwM7I~O7k1*udUmpS#(=iCy@`;=Gd>M?@xb4n*Sh#MtG{CM^BSe@+c*T1*HUa zx(iHkwVjL+M{y6>_43Ld0^*{}<1qzM}vD literal 0 HcmV?d00001 diff --git a/src/fetchers/__pycache__/base.cpython-312.pyc b/src/fetchers/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9bf39660054601a3330e327570a67a2fe162b7d GIT binary patch literal 1652 zcmbtUy>HV%6u+|_H%VgzG~!bfOE8duMry@^s!9a}3+e!3$zomXOJndCcNf4BLx&D5 zNNimZ6BPsP#KM2TKLBO`iHR*VurTqSlSWPj0*RCS?!9}z=l8q!p8d|Ussz@T+bjMD z4R$p4S81#a7eUz~9`R^ELfWFC*3u~2TA;TKjocufzDYcT(^vW^rd3L{M(HaW4K34G zuUyRzv9?r#xhmwU8+&W+2sx6IRByrFBAm3SM_QUkx!%z{ZPREOyyWRzha~3RL&eaM z-Z8w=rtxa*XqEqpHVf#=U(quKbajHBg{WCg?qd4GKViSe{H z1cPLzOv82@WTxP%C!(o)ARxbo0m>FhiNF_ZlWk4lR0-_Un6;P%J*%&C1uWb+}45I^jnJeYLiI{fbmX+!J3t=i*tD{qmsneF+m^5ck!)OO=YG3b1> zet81CXeYvVdBF~uxR=vQ>fd_MGU+^PUjOJky z#3|>F8}}l0z`gc|E*-^UjY+`|1K7&`{K1M%`>{n5znTp)I7TW~-mbNVg_9pKm^de^ zI!+jSy#V!^Q1-z4%*<1A-d;$@XQoq$L&haEYF;skmPEU!3)c0OY`7x{n- z6Ezh07Wo3iCK+h7boRMEB(M!kV%G7%dbu=!18ZmT&GPQ@9;!oq-&a8CBb4^0wp;rn z>iN|i_43N=o7*>kkE%k(xdDM~r@n_Sa)^-Jc(;}~fnXt%XQk=z; r{BqM5&dST9g06`30HN^6;~)k)rSuay_mRwfH%?RQ*|{GCwmj7@7H4lO literal 0 HcmV?d00001 diff --git a/src/fetchers/__pycache__/base.cpython-313.pyc b/src/fetchers/__pycache__/base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f1d294d672fadc727621c4c277184a259ab3a86 GIT binary patch literal 1652 zcmbtUJC74d5bk++*6Z~wFY`D~aHAu*b--EzHaV@3c!&Vd28>R+z*4I{?zM*<&zR~q zoKFNqWG9hqj)-ssMEnb4t}i$wA~H+Ef0_ccIeukj4!w z?WKE1_a9LDSKQDe+{P>f%ZK8Z7Vx!0@zn+V${b%u?&XD;AJ$;Cx6-Vem779~TT(}T z37=flW|qV-<~wp6Va?}*e5zkgneDRQk0$d_k4>zti3L^M!GgL!Cq#i-Kkb{MQA_q!ym-QG>e@)oLhB`WM49!e4_~@HP zPioWpo)Ei4rm|_PNG@RaEur_A`xkXBKlHl zbMBykg>aF|89M9xh0*}!YjLUx zB?rSK8%xQfY?SKR_SRRbe4LVNi8*_AneFyqBIUDvGtrl;7lUz8hGaCfVDgqk-M z*f!kMI3Hx)Q9^#5^9Li5%pylP-;P1)Bu-_TVc5Z+5Uf%;i-%|ryJoo%0eBRgqQF@) z=g=6JiU$F?CwbAHam0m2?VXX9it{qn;&!N;!P_%*xO#!+DVy3><)`P)XNJ?%V_t(c z{Fj%fxZ%IP^mg;jW=?MI?8Ww>oDyZft-rz7_Z8Ih8wKj+^;dT%cfX9PA>)ZD!|8P+ zr>S7eZS=~blJcf!9MOq@9&lM6Zz$5PQ1A=w6v-7YXr8dCZkC0=3KtPVuV0-;Gj%M> X`j=h!hn@KB{%HBnF8s%EDyaSkc20KI literal 0 HcmV?d00001 diff --git a/src/fetchers/__pycache__/factory.cpython-312.pyc b/src/fetchers/__pycache__/factory.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6348861d1b615d75c34cf955e1bcf959d8990f0 GIT binary patch literal 1203 zcmZWo-%Auh9G}@=Uf!Llt7fIS5(JBMYX~8sf>XLRYq5iRTOsF(LcXJ?O2E(r0V zN23RW2z(78{1bZbW#wLU&Euqs%D?^I7oTN(L!Yhc-I0-^C zNpS@gsA84O6;!4QM#RO1j2OD%3r`H+*FG}X1{Ls(KkuJWzxM^R+pq+1cLh38((q#o zyCG`>61h&Uz`8cYd7{q~&he!B3C3$`@EYDy;LIIz1-t|A8@3IoD@k3T!bFk^Q#0!W zVL3Ga&05ycFj{l77}?rl5o;4kV`@eV^5byYT#5?Q57!O9W?EP@jiu>7>Rig0epsij zcgz(WcXhRh3G*EIyQAq6m6;M@+B|RNTyziPBE~#Fm?u~lafHhkIPj`F+@xvR?_p!k zLD(^#yR3%_OU6=`B-Y#ohB;yzljB2%gEPir&|;3U7*sm3TBgw@M9k2ze&u{8B+l3r zex1$WCHUHQX}jlj&zoznl!Iv7)|35c|DmcKb!lFIvf4H4way-?{W~|S>Yx`JJcz4X zmHqhjL-n$M;!(9L<+Y}c)Ehgcs+#a(iSO+_Uf&9(y}+0@$)A`pc(a=cS&Wa&1inO1pn7@WvhS=q}d3NSH#uShUmP zKiDhqgb6m|!NlS+y!Q*e<$)-bcimL&RQI zY+Ar+J6`T3R;q{PnUE&2p~uDkZ&8_xSY%ath1)*!NE&g zxzwby!(f{WSrlomRP?raARV?(xA}o|fQtx%rkU~SMORAi2fr$Kj_T-bSgT3nUiI*1);l+;huaBV;CoRAc+1`FfxA}o?^WHjcA>sjC(@`q zJoJ+WyIF@_Rl@&VtrNfUaCr6bQauQx-Q6At#hmhe;(C^})gcEt{+M+m{y9JoKvxTN z@jVm9w63QER2@;MO|I+8+kQRd5c*$5YT$yl-|mR=UbLaGD_Z{g-6cPSroWlaW9V&A<< z{_KPJ;)9dY;)@UZrCagDt>a?p+1LHz)w9w&iN~Y4nPv40r?hI5M^fU5|TFo1ic}fGe!zq>HlF1C6tX@&wo&lk)nkTaB~X3B(s&PF77NGV*=4wp4t(x4|P0evz2 zP{Ft8*q(ZA6ztWX)?OjC9GS`TOS+56mw)Qhfr*^yfp$yTG*QjV$qU18Evt9ZSvkY> zzk%+jKZjv2l7>rbgQ>nIi6_~-ouSOHq~%zJt15*4l0xnA%ETC-qMN7a<5RTomsP~8 KZ;*l{n|}aq20(}a literal 0 HcmV?d00001 diff --git a/src/fetchers/__pycache__/tushare_fetcher.cpython-312.pyc b/src/fetchers/__pycache__/tushare_fetcher.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c661fe092670f7333ba600d806d79d8135a26f0 GIT binary patch literal 9959 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 literal 0 HcmV?d00001 diff --git a/src/fetchers/__pycache__/tushare_fetcher.cpython-313.pyc b/src/fetchers/__pycache__/tushare_fetcher.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e05201813e8e1712d649be0ffd7c607d519157f GIT binary patch literal 10114 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 L)PEBA78(36?Dd4# literal 0 HcmV?d00001 diff --git a/src/fetchers/alpha_vantage_fetcher.py b/src/fetchers/alpha_vantage_fetcher.py new file mode 100644 index 0000000..49b3ecc --- /dev/null +++ b/src/fetchers/alpha_vantage_fetcher.py @@ -0,0 +1,130 @@ +import requests +import pandas as pd +from .base import DataFetcher + +class AlphaVantageFetcher(DataFetcher): + BASE_URL = "https://www.alphavantage.co/query" + + def __init__(self, api_key: str): + super().__init__(api_key) + + def _fetch_data(self, function: str, symbol: str) -> pd.DataFrame: + params = { + "function": function, + "symbol": symbol, + "apikey": self.api_key + } + try: + response = requests.get(self.BASE_URL, params=params) + data = response.json() + except Exception as e: + print(f"Error requesting {function}: {e}") + return pd.DataFrame() + + # Alpha Vantage returns keys like "annualReports" and "quarterlyReports" + # We need Last 10 Annual + Latest Quarterly + + df_annual = pd.DataFrame() + df_quarterly = pd.DataFrame() + + if "annualReports" in data: + df_annual = pd.DataFrame(data["annualReports"]) + if "fiscalDateEnding" in df_annual.columns: + df_annual = df_annual.sort_values("fiscalDateEnding", ascending=False).head(10) + + if "quarterlyReports" in data: + df_quarterly = pd.DataFrame(data["quarterlyReports"]) + if "fiscalDateEnding" in df_quarterly.columns: + df_quarterly = df_quarterly.sort_values("fiscalDateEnding", ascending=False).head(1) + + if df_annual.empty and df_quarterly.empty: + print(f"Error fetching {function} for {symbol}: {data.keys() if isinstance(data, dict) else data}") + return pd.DataFrame() + + # Combine: Prioritize Annual Reports if dates match + # Logic: 10 Annuals + Latest Quarter IF it's newer. + # If we concat [df_annual, df_quarterly] and drop_duplicates(keep='first'), + # checking fiscalDateEnding: + # If Q-Date > A-Date: Q is kept (unique). + # If Q-Date == A-Date: A is kept (first). + combined = pd.concat([df_annual, df_quarterly]) + + if "fiscalDateEnding" in combined.columns: + combined = combined.drop_duplicates(subset=["fiscalDateEnding"], keep='first') + combined = combined.sort_values("fiscalDateEnding", ascending=False) + + return combined + + def get_market_metrics(self, symbol: str) -> dict: + # 1. Get Overview for PE, PB, MarketCap, Employees + overview_data = {} + try: + params = {"function": "OVERVIEW", "symbol": symbol, "apikey": self.api_key} + r = requests.get(self.BASE_URL, params=params) + overview_data = r.json() + except Exception as e: + print(f"Error fetching OVERVIEW for {symbol}: {e}") + + # 2. Get Global Quote for latest Price + price = 0.0 + try: + params = {"function": "GLOBAL_QUOTE", "symbol": symbol, "apikey": self.api_key} + r = requests.get(self.BASE_URL, params=params) + quote_data = r.json() + if "Global Quote" in quote_data and "05. price" in quote_data["Global Quote"]: + price = float(quote_data["Global Quote"]["05. price"]) + except Exception as e: + print(f"Error fetching GLOBAL_QUOTE for {symbol}: {e}") + + return { + "price": price, + "market_cap": float(overview_data.get("MarketCapitalization", 0) or 0), + "pe": float(overview_data.get("PERatio", 0) or 0), + "pb": float(overview_data.get("PriceToBookRatio", 0) or 0), + "employee_count": int(overview_data.get("FullTimeEmployees", 0) or 0), + "total_share_holders": 0 # Not typically provided in basic AV Overview + } + + def get_income_statement(self, symbol: str) -> pd.DataFrame: + df = self._fetch_data("INCOME_STATEMENT", symbol) + cols_map = { + "fiscalDateEnding": "date", + "totalRevenue": "revenue", + "netIncome": "net_income" + } + df = df.rename(columns=cols_map) + + # Convert numeric columns for analysis, keep others as is + for col in ["revenue", "net_income"]: + if col in df.columns: + df[col] = pd.to_numeric(df[col], errors='coerce') + return df + + def get_balance_sheet(self, symbol: str) -> pd.DataFrame: + df = self._fetch_data("BALANCE_SHEET", symbol) + cols_map = { + "fiscalDateEnding": "date", + "totalShareholderEquity": "total_equity", + "totalLiabilities": "total_liabilities", + "totalCurrentAssets": "current_assets", + "totalCurrentLiabilities": "current_liabilities" + } + df = df.rename(columns=cols_map) + + for col in ["total_equity", "total_liabilities", "current_assets", "current_liabilities"]: + if col in df.columns: + df[col] = pd.to_numeric(df[col], errors='coerce') + return df + + def get_cash_flow(self, symbol: str) -> pd.DataFrame: + df = self._fetch_data("CASH_FLOW", symbol) + cols_map = { + "fiscalDateEnding": "date", + "operatingCashflow": "net_cash_flow", + "depreciationDepletionAndAmortization": "depreciation" + } + df = df.rename(columns=cols_map) + + if "net_cash_flow" in df.columns: + df["net_cash_flow"] = pd.to_numeric(df["net_cash_flow"], errors='coerce') + return df diff --git a/src/fetchers/base.py b/src/fetchers/base.py new file mode 100644 index 0000000..fe0d3c6 --- /dev/null +++ b/src/fetchers/base.py @@ -0,0 +1,31 @@ +from abc import ABC, abstractmethod +import pandas as pd + +class DataFetcher(ABC): + def __init__(self, api_key: str): + self.api_key = api_key + + @abstractmethod + def get_income_statement(self, symbol: str) -> pd.DataFrame: + pass + + @abstractmethod + def get_balance_sheet(self, symbol: str) -> pd.DataFrame: + pass + + @abstractmethod + def get_cash_flow(self, symbol: str) -> pd.DataFrame: + pass + + @abstractmethod + def get_market_metrics(self, symbol: str) -> dict: + """ + Returns a dictionary containing: + - price + - market_cap (value) + - pe + - pb + - total_share_holders (int) + - employee_count (int) + """ + pass diff --git a/src/fetchers/factory.py b/src/fetchers/factory.py new file mode 100644 index 0000000..a940007 --- /dev/null +++ b/src/fetchers/factory.py @@ -0,0 +1,18 @@ +from .tushare_fetcher import TushareFetcher +from .alpha_vantage_fetcher import AlphaVantageFetcher +from .base import DataFetcher + +class FetcherFactory: + @staticmethod + def get_fetcher(market: str, tushare_token: str = None, av_key: str = None) -> DataFetcher: + market = market.upper() + if market in ['CN', 'HK']: + if not tushare_token: + raise ValueError("Tushare token is required for CN/HK markets") + return TushareFetcher(tushare_token) + elif market in ['US', 'JP']: + if not av_key: + raise ValueError("Alpha Vantage key is required for US/JP markets") + return AlphaVantageFetcher(av_key) + else: + raise ValueError(f"Unsupported market: {market}") diff --git a/src/fetchers/tushare_fetcher.py b/src/fetchers/tushare_fetcher.py new file mode 100644 index 0000000..2ea1508 --- /dev/null +++ b/src/fetchers/tushare_fetcher.py @@ -0,0 +1,259 @@ +import tushare as ts +import pandas as pd +from .base import DataFetcher +import time + +class TushareFetcher(DataFetcher): + def __init__(self, api_key: str): + super().__init__(api_key) + ts.set_token(self.api_key) + self.pro = ts.pro_api() + + def _get_ts_code(self, symbol: str) -> str: + # Tushare requires suffix: .SH, .SZ for CN; .HK for HK + # Input might be bare code '600519' or '00700' + # Simple heuristic: if 5 digits, likelihood HK (but could be others). + # Better: User passes full code or we default. + # Implementation assumes user provides Tushare compatible code or we try to guess. + # For now, return as is if it has suffix, otherwise guess based on length? + # A safer bet is to require the user to pass '600519.SH' or '00700.HK' + return symbol + + def _filter_data(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Filter logic: + 1. Sort by end_date (desc). + 2. Remove duplicates (keep first/latest announcement). + 3. Select Latest Quarter. + 4. Select last 10 Annual Reports (end_date ends with '1231'). + 5. Combine and return. + """ + if df.empty or 'end_date' not in df.columns: + return df + + # Ensure sorted by date descending + # Tushare often has 'ann_date' (announcement date) and 'end_date' (report period). + # We sort by end_date mostly. + # Note: If multiple records exist for same end_date (revisions), the API generally puts latest revision first if we sort properly? + # Default behavior: sort by end_date desc. + df = df.sort_values(by='end_date', ascending=False) + + # Deduplicate: one record per end_date + df = df.drop_duplicates(subset=['end_date'], keep='first') + + if df.empty: + return df + + # 1. Latest Quarter (The most recent record) + latest_record = df.iloc[[0]] + + # 2. Comparable Period Last Year (for Growth Rate) + # If latest is 20240930, we need 20230930. + try: + latest_date_str = str(latest_record['end_date'].values[0]) + last_year_date_str = str(int(latest_date_str) - 10000) + comparable_record = df[df['end_date'].astype(str) == last_year_date_str] + except: + comparable_record = pd.DataFrame() + + # 3. Annual Reports (end_date ends with '1231') + # Tushare dates are YYYYMMDD string or int. Cast to str to be safe. + is_annual = df['end_date'].astype(str).str.endswith('1231') + annual_records = df[is_annual].head(10) + + # 4. Combine + combined = pd.concat([latest_record, comparable_record, annual_records]) + + # 5. Deduplicate again (in case latest IS an annual report) + combined = combined.drop_duplicates(subset=['end_date']) + + # 5. Final Sort + combined = combined.sort_values(by='end_date', ascending=False) + + return combined + + def get_income_statement(self, symbol: str) -> pd.DataFrame: + ts_code = self._get_ts_code(symbol) + if ts_code.endswith('.HK'): + df = self.pro.hk_income(ts_code=ts_code) + # HK mapping might differ slightly, checking common fields + # HK Tushare usually has 'revenue', 'net_profit_attr_p' + rename_map = { + 'end_date': 'date', + 'revenue': 'revenue', + 'net_profit_attr_p': 'net_income' # HK specific + } + else: + df = self.pro.income(ts_code=ts_code) + rename_map = { + 'end_date': 'date', + 'revenue': 'revenue', + 'n_income_attr_p': 'net_income' + } + + df = self._filter_data(df) + + # Rename columns but keep all data + df = df.rename(columns=rename_map) + return df + + def get_balance_sheet(self, symbol: str) -> pd.DataFrame: + ts_code = self._get_ts_code(symbol) + if ts_code.endswith('.HK'): + df = self.pro.hk_balancesheet(ts_code=ts_code) + # HK mapping + rename_map = { + 'end_date': 'date', + 'total_share_holder_equity': 'total_equity', # verify this field name for HK + 'total_liab': 'total_liabilities', + 'total_cur_asset': 'current_assets', + 'total_cur_liab': 'current_liabilities' + } + else: + df = self.pro.balancesheet(ts_code=ts_code) + rename_map = { + 'end_date': 'date', + 'total_hldr_eqy_exc_min_int': 'total_equity', + 'total_liab': 'total_liabilities', + 'total_cur_assets': 'current_assets', + 'total_cur_liab': 'current_liabilities' + } + + df = self._filter_data(df) + + df = df.rename(columns=rename_map) + return df + + def get_cash_flow(self, symbol: str) -> pd.DataFrame: + ts_code = self._get_ts_code(symbol) + if ts_code.endswith('.HK'): + df = self.pro.hk_cashflow(ts_code=ts_code) + else: + df = self.pro.cashflow(ts_code=ts_code) + + df = self._filter_data(df) + + df = df.rename(columns={ + 'end_date': 'date', + 'n_cashflow_act': 'net_cash_flow', + 'depr_fa_coga_dpba': 'depreciation' + }) + return df + + def get_market_metrics(self, symbol: str) -> dict: + ts_code = self._get_ts_code(symbol) + metrics = { + "price": 0.0, + "market_cap": 0.0, + "pe": 0.0, + "pb": 0.0, + "total_share_holders": 0, + "employee_count": 0 + } + + try: + # 1. Daily Basic (Price, PE, PB, Market Cap) + # Fetch latest available + df_daily = self.pro.daily_basic(ts_code=ts_code, limit=1) + # If empty (limit=1 might return empty if today is holiday?), try range. + # Tushare daily_basic usually returns latest if no date specified? + # Actually limit=1 without date might work? Or need start_date/end_date. + # Let's try fetching without date args, Tushare usually requires date or returns range. + # Easier: Fetch for specific recent date? Or just query last 10 days and take head(1). + # To be safe, let's just try no args (latest). + if df_daily.empty: + # Fallback: get trade cal and find last trade date? + # Simplified: user wants "latest". + pass + else: + row = df_daily.iloc[0] + metrics["price"] = row.get('close', 0.0) + metrics["pe"] = row.get('pe', 0.0) + metrics["pb"] = row.get('pb', 0.0) + # total_mv is usually in ten thousands? Or standard? + # Tushare doc: total_mv is "Total Market Value (10k)". + # User wants units? We'll normalize to raw value or 10k later. + # Let's keep it as is (10k units) and note it, or convert to full value. + # AlphaVantage returns full value. Let's convert Tushare to full value to standardize? + # Wait, user asked for "Market Cap (Yi Yuan)". + # Tushare total_mv is 10k CNY. So 10000 -> 1 Yi. + # Let's store raw 10k value * 10000 = full value. + metrics["market_cap"] = row.get('total_mv', 0.0) * 10000 + metrics["dividend_yield"] = row.get('dv_ttm', 0.0) + + # 1.5 Stock Basic (Name, List Date) + df_basic = self.pro.stock_basic(ts_code=ts_code, fields='name,list_date') + if not df_basic.empty: + metrics['name'] = df_basic.iloc[0]['name'] + metrics['list_date'] = df_basic.iloc[0]['list_date'] + + # 2. Stock Company (Employees) + df_comp = self.pro.stock_company(ts_code=ts_code, fields='employees') + if not df_comp.empty: + metrics["employee_count"] = int(df_comp.iloc[0].get('employees', 0) or 0) + + # 3. Shareholder Number (Latest) + # Tushare interface: stk_holdernumber + df_holder = self.pro.stk_holdernumber(ts_code=ts_code, limit=1) + if not df_holder.empty: + metrics["total_share_holders"] = int(df_holder.iloc[0].get('holder_num', 0) or 0) + + except Exception as e: + print(f"Error fetching market metrics for {symbol}: {e}") + + return metrics + + def get_historical_metrics(self, symbol: str, dates: list) -> pd.DataFrame: + """ + Fetch historical market metrics (Price, PE, PB, MarketCap, Shareholders) for specific dates. + Optimized to fetch data in bulk ranges to reduce API calls. + """ + ts_code = self._get_ts_code(symbol) + results = [] + + if not dates: + return pd.DataFrame() + + # Deduplicate and sort dates + unique_dates = sorted(list(set([str(d).replace('-', '') for d in dates])), reverse=True) + + try: + import datetime + min_date = min(unique_dates) + max_date = max(unique_dates) + + # 1. Bulk fetch daily basic metrics for the entire date range + df_daily = self.pro.daily_basic(ts_code=ts_code, start_date=min_date, end_date=max_date) + if not df_daily.empty: + df_daily = df_daily.sort_values('trade_date', ascending=False) + + # 2. Bulk fetch shareholder data for the entire date range + df_holder = self.pro.stk_holdernumber(ts_code=ts_code, start_date=min_date, end_date=max_date) + if not df_holder.empty: + df_holder = df_holder.sort_values('end_date', ascending=False) + + for date_str in unique_dates: + metrics = {'date_str': date_str} + + # Find closest daily metric data (on or before the target date) + if not df_daily.empty: + closest_daily = df_daily[df_daily['trade_date'] <= date_str] + if not closest_daily.empty: + row = closest_daily.iloc[0] + metrics['Price'] = row.get('close') + metrics['PE'] = row.get('pe') + metrics['PB'] = row.get('pb') + metrics['MarketCap'] = row.get('total_mv', 0) * 10000 + + # Find closest shareholder data + if not df_holder.empty: + closest_holder = df_holder[df_holder['end_date'] <= date_str] + if not closest_holder.empty: + metrics['Shareholders'] = closest_holder.iloc[0].get('holder_num') + + results.append(metrics) + + except Exception as e: + print(f"Error fetching historical metrics for {symbol}: {e}") + + return pd.DataFrame(results) diff --git a/src/reporting/__pycache__/html_generator.cpython-313.pyc b/src/reporting/__pycache__/html_generator.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d75f923955bc669403eb653bb2eb249c2c34482 GIT binary patch literal 11646 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 F{|6{T8n*xd literal 0 HcmV?d00001 diff --git a/src/reporting/html_generator.py b/src/reporting/html_generator.py new file mode 100644 index 0000000..ae89495 --- /dev/null +++ b/src/reporting/html_generator.py @@ -0,0 +1,254 @@ +import pandas as pd +import numpy as np + +class HtmlReporter: + def generate_report(self, df: pd.DataFrame, market: str, symbol: str, metrics: dict = {}) -> str: + if df.empty: + return f"No breakdown data available for {market} {symbol}" + + # 1. Transpose Data for Presentation (Years as Columns) + # Ensure df is sorted desc by date + df = df.sort_values('date_str', ascending=False) + + # Prepare header (Years/Quarters) + # Format date_str: YYYYMMDD -> YYYY-MM-DD or 2025 Q3? + # Simple heuristic: if month is 03/06/09/12, map to Q. + # But for annuals, we just show Year. + # User said: "Latest year... check which quarter". + headers = [] + for d in df['date_str']: + s = str(d) + if len(s) == 8: + year = s[:4] + month = s[4:6] + if month == '12': + headers.append(year) + elif month in ['03', '01', '02']: headers.append(f"{year} Q1") + elif month in ['06', '04', '05']: headers.append(f"{year} Q2") + elif month in ['09', '07', '08']: headers.append(f"{year} Q3") + else: headers.append(f"{year} {month}") + else: + headers.append(s) # Fallback + + # 2. Define Indicator Groups and Labels + # Mapping: Internal Col Name -> Display Name + indicators = { + "主要指标": [ + ('ROE', 'ROE', 'percent'), + ('ROA', 'ROA', 'percent'), + ('ROIC', 'ROCE/ROIC', 'percent'), + ('GrossMargin', '毛利率', 'percent'), + ('NetMargin', '净利润率', 'percent'), + ('revenue', '收入', 'currency_yi'), + ('RevenueGrowth', '收入增速', 'percent_color'), + ('net_income', '净利润', 'currency_yi'), + ('NetIncomeGrowth', '净利润增速', 'percent_color'), + ('ocf', '经营净现金流', 'currency_yi_color'), + ('Capex', '资本开支', 'currency_yi'), + ('FCF', '自由现金流', 'currency_yi_compare'), + # Dividends/Buybacks might be missing + ('dividends', '分红', 'currency_yi'), + ('total_assets', '总资产', 'currency_yi'), + ('total_equity', '净资产', 'currency_yi'), + ('goodwill', '商誉', 'currency_yi') + ], + "费用指标": [ + ('SellingRatio', '销售费用率', 'percent'), + ('AdminRatio', '管理费用率', 'percent'), + ('RDRatio', '研发费用率', 'percent'), + ('OtherExpenseRatio', '其他费用率', 'percent'), + ('DepreciationRatio', '折旧费用占比', 'percent'), + ('TaxRate', '所得税率', 'percent'), + ], + "资产占比": [ + ('CashRatio', '现金占比', 'percent_alert_30'), + ('InventoryRatio', '库存占比', 'percent'), + ('ReceivablesRatio', '应收款占比', 'percent'), + ('PrepaymentRatio', '预付款占比', 'percent'), + ('FixedAssetsRatio', '固定资产占比', 'percent'), + ('LongTermInvestmentRatio', '长期投资占比', 'percent'), + ('GoodwillRatio', '商誉占比', 'percent'), + ('OtherAssetsRatio', '其他资产占比', 'percent'), + ('PayablesRatio', '应付款占比', 'percent'), + ('AdvanceReceiptsRatio', '预收款占比', 'percent'), + ('ShortTermDebtRatio', '短期借款占比', 'percent'), + ('LongTermDebtRatio', '长期借款占比', 'percent'), + ('OperatingAssetsRatio', '运营资产占比', 'percent'), + ('InterestBearingDebtRatio', '有息负债率', 'percent'), + ], + "周转能力": [ + ('InventoryDays', '存货周转天数', 'int'), + ('ReceivablesDays', '应收款周转天数', 'int_alert_90'), # >90 Red + ('PayablesDays', '应付款周转天数', 'int'), + ('FixedAssetsTurnover', '固定资产周转率', 'float'), + ('TotalAssetTurnover', '总资产周转率', 'float'), + ], + "人均效率": [ + ('Employees', '员工人数', 'int'), + ('RevenuePerEmp', '人均创收(万)', 'currency_wan'), + ('ProfitPerEmp', '人均创利(万)', 'currency_wan'), + ('AvgWage', '人均薪酬(万)', 'currency_wan'), + ], + "市场表现": [ + ('Price', '股价', 'float'), + ('MarketCap', '市值(亿)', 'currency_yi_market'), + ('PE', 'PE', 'float'), + ('PB', 'PB', 'float'), + ('Shareholders', '股东户数', 'int'), + ] + } + + # 3. Build HTML + html = [] + html.append("Financial Report") + + # --- Company Info Table --- + import datetime + today_str = datetime.date.today().strftime("%Y-%m-%d") + name = metrics.get('name', '') + raw_list_date = metrics.get('list_date', '') + # Convert YYYYMMDD to YYYY-MM-DD if possible + if isinstance(raw_list_date, str) and len(raw_list_date) == 8: + list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}" + else: + list_date = raw_list_date + pe = metrics.get('pe', 0) + pb = metrics.get('pb', 0) + div = metrics.get('dividend_yield', 0) + + html.append("

Financial Report

") + html.append("") + html.append("") + html.append("") + html.append("") + html.append("") + html.append(f"") + html.append("") + html.append("
代码简称日期上市日期PEPB股息率(%)
{symbol}{name}{today_str}{list_date}{pe:.2f}{pb:.2f}{div:.2f}%
") + + html.append("
") + html.append("") + + # Header Row + html.append("") + for h in headers: + html.append(f"") + html.append("") + + html.append("") + + for group_name, items in indicators.items(): + # Group Header + html.append(f"") + + for key, label, fmt_type in items: + html.append("") + html.append(f"") + + for idx, row_series in df.iterrows(): + val = row_series.get(key, np.nan) + + if pd.isna(val) or val is None: + html.append("") + continue + + # Styling Logic + style_class = "" + disp_val = str(val) + + if key == 'OtherAssetsRatio': + style_class = 'bg-khaki' + + # Logic block + if fmt_type == 'percent': + disp_val = f"{val:.2%}" + # Conditional Specifics + if key == 'ROE' and val > 0.15: style_class = "bg-green" + elif key == 'ROA' and val > 0.10: style_class = "bg-green" + elif key == 'ROIC' and val > 0.15: style_class = "bg-green" + elif key == 'GrossMargin' and val > 0.35: style_class = "bg-green" + elif key == 'NetMargin': + if val > 0.15: style_class = "bg-green" + elif val < 0: style_class = "bg-red" + elif key == 'OperatingAssetsRatio' and val < 0: + style_class = 'bg-green' + + elif fmt_type == 'percent_color': # Revenue/Profit Growth + disp_val = f"{val:.2%}" + if val > 0.15: style_class = "bg-green italic" + elif val < 0: style_class = "text-red italic" + else: style_class = "text-blue-i" + + elif fmt_type == 'currency': + # Should not be used if everything moved to currency_yi, but keep for fallback + disp_val = f"{val:,.2f}" + + elif fmt_type == 'currency_yi': + # 1 Yi = 100,000,000 (10^8) + disp_val = f"{val/100000000:,.2f}" + + elif fmt_type == 'currency_yi_color': # OCF + disp_val = f"{val/100000000:,.2f}" + if val < 0: style_class = "bg-red" + + elif fmt_type == 'currency_yi_compare': # FCF + disp_val = f"{val/100000000:,.2f}" + # Compare to Net Income (need to access current row's net_income) + ni = row_series.get('net_income', 0) + if val > ni: style_class = "bg-green" + elif val < 0: style_class = "bg-red" + + elif fmt_type == 'currency_wan': # Per Employee + disp_val = f"{val/10000:,.2f}" + + elif fmt_type == 'currency_yi_market': # Market Cap + val_yi = val / 100000000 + if abs(val_yi) > 1000: + disp_val = f"{val_yi:,.0f}" + else: + disp_val = f"{val_yi:,.2f}" + + elif fmt_type == 'int': + disp_val = f"{int(val):,}" + + elif fmt_type == 'float': + disp_val = f"{val:.2f}" + + # Special Alerts + if fmt_type == 'percent_alert_30': + disp_val = f"{val:.2%}" + if val > 0.30: style_class = "bg-red" + + if fmt_type == 'float_alert_90': + disp_val = f"{val:.2f}" + if val > 90: style_class = "bg-red" + + if fmt_type == 'int_alert_90': + disp_val = f"{int(val):,}" + if val > 90: style_class = "bg-red" + + html.append(f"") + html.append("") + + html.append("
指标{h}
{group_name}
{label}-{disp_val}
") + return "\n".join(html) diff --git a/src/reporting/markdown_generator.py b/src/reporting/markdown_generator.py new file mode 100644 index 0000000..f3ab2b0 --- /dev/null +++ b/src/reporting/markdown_generator.py @@ -0,0 +1,157 @@ +import pandas as pd +import numpy as np + +class MarkdownReporter: + def generate_report(self, df: pd.DataFrame, market: str, symbol: str, metrics: dict = {}) -> str: + if df.empty: + return f"No breakdown data available for {market} {symbol}" + + df = df.sort_values('date_str', ascending=False) + + headers = [] + for d in df['date_str']: + s = str(d) + if len(s) == 8: + year = s[:4] + month = s[4:6] + if month == '12': + headers.append(year) + elif month in ['03', '01', '02']: headers.append(f"{year} Q1") + elif month in ['06', '04', '05']: headers.append(f"{year} Q2") + elif month in ['09', '07', '08']: headers.append(f"{year} Q3") + else: headers.append(f"{year} {month}") + else: + headers.append(s) + + indicators = { + "主要指标": [ + ('ROE', 'ROE', 'percent'), + ('ROA', 'ROA', 'percent'), + ('ROIC', 'ROCE/ROIC', 'percent'), + ('GrossMargin', '毛利率', 'percent'), + ('NetMargin', '净利润率', 'percent'), + ('revenue', '收入(亿)', 'currency_yi'), + ('RevenueGrowth', '收入增速', 'percent_color'), + ('net_income', '净利润(亿)', 'currency_yi'), + ('NetIncomeGrowth', '净利润增速', 'percent_color'), + ('ocf', '经营净现金流(亿)', 'currency_yi_color'), + ('Capex', '资本开支(亿)', 'currency_yi'), + ('FCF', '自由现金流(亿)', 'currency_yi_compare'), + ('dividends', '分红(亿)', 'currency_yi'), + ('total_assets', '总资产(亿)', 'currency_yi'), + ('total_equity', '净资产(亿)', 'currency_yi'), + ('goodwill', '商誉(亿)', 'currency_yi') + ], + "费用指标": [ + ('SellingRatio', '销售费用率', 'percent'), + ('AdminRatio', '管理费用率', 'percent'), + ('RDRatio', '研发费用率', 'percent'), + ('OtherExpenseRatio', '其他费用率', 'percent'), + ('DepreciationRatio', '折旧费用占比', 'percent'), + ('TaxRate', '所得税率', 'percent'), + ], + "资产占比": [ + ('CashRatio', '现金占比', 'percent_alert_30'), + ('InventoryRatio', '库存占比', 'percent'), + ('ReceivablesRatio', '应收款占比', 'percent'), + ('PrepaymentRatio', '预付款占比', 'percent'), + ('FixedAssetsRatio', '固定资产占比', 'percent'), + ('LongTermInvestmentRatio', '长期投资占比', 'percent'), + ('GoodwillRatio', '商誉占比', 'percent'), + ('OtherAssetsRatio', '其他资产占比', 'percent'), + ('PayablesRatio', '应付款占比', 'percent'), + ('AdvanceReceiptsRatio', '预收款占比', 'percent'), + ('ShortTermDebtRatio', '短期借款占比', 'percent'), + ('LongTermDebtRatio', '长期借款占比', 'percent'), + ('OperatingAssetsRatio', '运营资产占比', 'percent'), + ('InterestBearingDebtRatio', '有息负债率', 'percent'), + ], + "周转能力": [ + ('InventoryDays', '存货周转天数', 'int'), + ('ReceivablesDays', '应收款周转天数', 'int_alert_90'), + ('PayablesDays', '应付款周转天数', 'int'), + ('FixedAssetsTurnover', '固定资产周转率', 'float'), + ('TotalAssetTurnover', '总资产周转率', 'float'), + ], + "人均效率": [ + ('Employees', '员工人数', 'int'), + ('RevenuePerEmp', '人均创收(万)', 'currency_wan'), + ('ProfitPerEmp', '人均创利(万)', 'currency_wan'), + ('AvgWage', '人均薪酬(万)', 'currency_wan'), + ], + "市场表现": [ + ('Price', '股价', 'float'), + ('MarketCap', '市值(亿)', 'currency_yi_market'), + ('PE', 'PE', 'float'), + ('PB', 'PB', 'float'), + ('Shareholders', '股东户数', 'int'), + ] + } + + md = [] + + # --- Company Info Table --- + import datetime + today_str = datetime.date.today().strftime("%Y-%m-%d") + name = metrics.get('name', '') + raw_list_date = metrics.get('list_date', '') + if isinstance(raw_list_date, str) and len(raw_list_date) == 8: + list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}" + else: + list_date = raw_list_date + pe = metrics.get('pe', 0) + pb = metrics.get('pb', 0) + div = metrics.get('dividend_yield', 0) + + md.append(f"# {name} ({symbol}) - Financial Report") + md.append(f"*Report generated on: {today_str}*\n") + md.append("| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) |") + md.append("|:---|:---|:---|:---|:---|:---|") + md.append(f"| {symbol} | {name} | {list_date} | {pe:.2f} | {pb:.2f} | {div:.2f}% |") + md.append("\n") + + # --- Financial Data Table --- + for group_name, items in indicators.items(): + md.append(f"## {group_name}") + + # Table Header + header_line = "| 指标 | " + " | ".join(headers) + " |" + md.append(header_line) + # Table Separator + separator_line = "|" + "|".join([":---"] + ["--:"] * len(headers)) + "|" + md.append(separator_line) + + for key, label, fmt_type in items: + row_data = [label] + for idx, row_series in df.iterrows(): + val = row_series.get(key, np.nan) + + if pd.isna(val) or val is None: + row_data.append("-") + continue + + disp_val = "" + if fmt_type == 'percent' or fmt_type.startswith('percent_'): + disp_val = f"{val:.2%}" + elif fmt_type == 'currency_yi_market': + val_yi = val / 100000000 + if abs(val_yi) > 1000: + disp_val = f"{val_yi:,.0f}" + else: + disp_val = f"{val_yi:,.2f}" + elif fmt_type.startswith('currency_yi'): + disp_val = f"{val/100000000:,.2f}" + elif fmt_type == 'currency_wan': + disp_val = f"{val/10000:,.2f}" + elif fmt_type == 'int' or fmt_type.startswith('int_'): + disp_val = f"{int(val):,}" + elif fmt_type == 'float' or fmt_type.startswith('float_'): + disp_val = f"{val:.2f}" + else: + disp_val = f"{val}" + row_data.append(disp_val) + + md.append("| " + " | ".join(row_data) + " |") + md.append("\n") + + return "\n".join(md) diff --git a/src/storage/__pycache__/file_io.cpython-312.pyc b/src/storage/__pycache__/file_io.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a27c444e5a4724b04acf24938274be8259118bea GIT binary patch literal 2304 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 pd.DataFrame: + file_path = os.path.join(self.base_path, market, symbol, f"{statement_type}.csv") + if not os.path.exists(file_path): + print(f"File not found: {file_path}") + return pd.DataFrame() + return pd.read_csv(file_path)