init repo

This commit is contained in:
2026-04-25 19:21:28 +08:00
commit 35414c74a2
26 changed files with 3529 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# OS
.DS_Store
# Env
.env
.env.*
# Python
__pycache__/
.pytest_cache/
.mypy_cache/
.venv/
venv/
# Node
node_modules/
.next/
dist/
build/
.nuxt/
.output/
# Misc
*.log

3
.memory/worklog.json Normal file
View File

@@ -0,0 +1,3 @@
{
"entries": []
}

15
.project.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "美股低价值公司分析系统",
"description": "一个专门用于分析美股低价值投资机会的智能分析系统,提供全面的财务分析、估值计算和投资建议。",
"status": "archived",
"kind": "research",
"created": "2025-09-20",
"stack": [
"Python"
],
"urls": [],
"worklog": {
"path": ".memory/worklog.json",
"auto": true
}
}

21
AGENTS.md Normal file
View File

@@ -0,0 +1,21 @@
# 美股低价值公司分析系统 Agent Rules
## Must Read First
- `.project.json` 是机器真源:公网链接、快捷登录、凭证引用都以它为准
- `RULES.md` 是人工规则和部署事实:启动命令、平台、域名、注意事项都写这里
- 不允许编造不存在的域名、账号、密码;未知就保持空白并明确标记待补充
## Deployment Metadata Contract
- 任何任务只要新增、删除或修改公网地址,必须在同一次任务里更新 `.project.json`
- `urls[]` 推荐显式写 `type``app``backend``docs``admin``repo`
- 项目专属的网页登录信息,如果允许放进仓库,就写 `.project.json.quick_login`
- 不能直接入库的敏感登录,不要伪造 `quick_login`,改为写 `.project.json.credentials` 引用
- 数据库密码、API Key、服务器 root 密码,不属于 `quick_login`
## Completion Gate
- 部署完成后,不允许在 `.project.json` 缺少最新公网链接的状态下结束任务
- 部署完成后,必须同步更新 `RULES.md` 的部署事实
- 如果只更新了代码但没回写部署元数据,这个任务不算完成

21
CLAUDE.md Normal file
View File

@@ -0,0 +1,21 @@
# 美股低价值公司分析系统 Agent Rules
## Must Read First
- `.project.json` 是机器真源:公网链接、快捷登录、凭证引用都以它为准
- `RULES.md` 是人工规则和部署事实:启动命令、平台、域名、注意事项都写这里
- 不允许编造不存在的域名、账号、密码;未知就保持空白并明确标记待补充
## Deployment Metadata Contract
- 任何任务只要新增、删除或修改公网地址,必须在同一次任务里更新 `.project.json`
- `urls[]` 推荐显式写 `type``app``backend``docs``admin``repo`
- 项目专属的网页登录信息,如果允许放进仓库,就写 `.project.json.quick_login`
- 不能直接入库的敏感登录,不要伪造 `quick_login`,改为写 `.project.json.credentials` 引用
- 数据库密码、API Key、服务器 root 密码,不属于 `quick_login`
## Completion Gate
- 部署完成后,不允许在 `.project.json` 缺少最新公网链接的状态下结束任务
- 部署完成后,必须同步更新 `RULES.md` 的部署事实
- 如果只更新了代码但没回写部署元数据,这个任务不算完成

177
README.md Normal file
View File

@@ -0,0 +1,177 @@
# 美股低价值公司分析系统
一个专门用于分析美股低价值投资机会的智能分析系统,提供全面的财务分析、估值计算和投资建议。
## 功能特点
### 🎯 核心功能
- **实时数据收集**: 从Yahoo Finance获取最新股价和财务数据
- **智能估值分析**: DCF模型、相对估值法、财务指标分析
- **风险评估**: 波动率、Beta系数、财务健康度评估
- **投资建议**: 基于多维度分析的买入/持有/卖出建议
- **报告生成**: 自动生成PDF详细报告和文本摘要
### 📊 分析维度
1. **估值吸引力** (40%权重)
- PE、PB、PS、PEG比率分析
- DCF内在价值计算
- 相对估值对比
2. **财务健康度** (30%权重)
- 流动性指标 (流动比率、速动比率)
- 偿债能力 (债务比率、利息覆盖倍数)
- 盈利能力 (ROE、ROA、利润率)
3. **成长性** (20%权重)
- 收入增长率分析
- 利润增长率分析
- 历史增长趋势
4. **风险控制** (10%权重)
- Beta系数分析
- 波动率计算
- 最大回撤分析
## 安装和使用
### 环境要求
- Python 3.8+
- 网络连接(用于获取实时数据)
### 安装依赖
```bash
pip install -r requirements.txt
```
### 使用方法
#### 1. 单只股票完整分析
```bash
python main.py AAPL
```
#### 2. 强制刷新数据重新分析
```bash
python main.py AAPL --refresh
```
#### 3. 快速筛选(检查是否符合低价值标准)
```bash
python main.py --screen AAPL
```
#### 4. 批量分析多只股票
```bash
python main.py --batch AAPL,MSFT,GOOGL,TSLA
```
## 输出文件
### 报告文件
- `reports/{股票代码}_analysis_report.pdf` - 详细PDF分析报告
- `reports/{股票代码}_summary.txt` - 简要文本摘要
- `reports/charts/` - 图表文件HTML格式
### 数据库
- `stock_analysis.db` - SQLite数据库存储历史数据和分析结果
## 分析流程
### 每次分析的标准流程:
1. **数据获取阶段** (30秒内)
- 验证股票代码和基本信息
- 获取最新股价和交易数据
- 下载最近4个季度的财务数据
2. **快速筛选阶段** (1分钟内)
- 计算关键估值指标PE、PB、PS等
- 检查是否符合"低价值"标准
- 识别异常财务指标
3. **深度分析阶段** (2-3分钟)
- 进行DCF估值计算
- 分析财务健康度
- 评估行业地位和竞争优势
- 计算风险指标
4. **报告生成阶段** (1分钟内)
- 生成综合分析报告
- 提供投资建议和风险提示
- 保存分析结果到数据库
## 低价值投资标准
系统默认的低价值投资筛选标准:
- **PE比率**: ≤ 15
- **PB比率**: ≤ 1.5
- **PS比率**: ≤ 2.0
- **最小市值**: ≥ 1亿美元
- **债务比率**: ≤ 60%
- **流动比率**: ≥ 1.2
- **ROE**: ≥ 5%
## 配置说明
可以在 `config.py` 中修改分析参数:
```python
LOW_VALUE_CRITERIA = {
'max_pe_ratio': 15, # 最大市盈率
'max_pb_ratio': 1.5, # 最大市净率
'max_ps_ratio': 2.0, # 最大市销率
'min_market_cap': 100_000_000, # 最小市值
'max_debt_ratio': 0.6, # 最大债务比率
'min_current_ratio': 1.2, # 最小流动比率
'min_roe': 0.05, # 最小ROE
}
```
## 注意事项
1. **数据来源**: 系统使用Yahoo Finance作为主要数据源数据可能存在延迟
2. **投资风险**: 本系统仅供投资参考,不构成投资建议
3. **数据准确性**: 建议结合其他数据源进行交叉验证
4. **网络要求**: 需要稳定的网络连接获取实时数据
## 系统架构
```
├── main.py # 主程序入口
├── config.py # 配置文件
├── database.py # 数据库管理
├── data_collector.py # 数据收集模块
├── analysis_engine.py # 分析引擎
├── report_generator.py # 报告生成器
├── requirements.txt # 依赖包列表
└── README.md # 使用说明
```
## 示例输出
### 控制台输出示例
```
=== AAPL 分析结果 ===
投资建议: 买入
综合评分: 75.2/100
关键优势:
• 估值吸引力高
• 财务健康度良好
• 成长性优秀
关键担忧:
• 风险较高
详细报告已生成: reports/AAPL_analysis_report.pdf
简要报告已生成: reports/AAPL_summary.txt
```
## 技术支持
如有问题或建议,请检查:
1. 网络连接是否正常
2. 股票代码是否正确
3. 依赖包是否完整安装
4. 查看日志文件 `stock_analysis.log`

25
RULES.md Normal file
View File

@@ -0,0 +1,25 @@
# 20250920-股票分析
## 启动
- `python3 main.py`
## 核心文件
- `README.md`
- `analysis_engine.py`
- `analysis_reports/`
- `analysis_storage.py`
- `config.py`
- `create_rvph_report.py`
- `data_collector.py`
- `database.py`
- `main.py`
- `report_generator.py`
- `reports/`
- `run_analysis.py`
## 规则
- 研究/分析类项目,核心产出为文档
- 修改前先通读已有文档,保持结论一致性

430
analysis_engine.py Normal file
View File

@@ -0,0 +1,430 @@
"""
分析引擎 - 核心估值和财务分析模块
"""
import pandas as pd
import numpy as np
from typing import Dict, List, Tuple, Optional
import logging
from datetime import datetime, timedelta
from config import LOW_VALUE_CRITERIA
logger = logging.getLogger(__name__)
class StockAnalyzer:
def __init__(self):
self.criteria = LOW_VALUE_CRITERIA
def calculate_valuation_metrics(self, data: Dict) -> Dict:
"""计算估值指标"""
try:
key_metrics = data.get('key_metrics', {})
company_info = data.get('company_info', {})
# 基础估值指标
pe_ratio = key_metrics.get('pe_ratio', 0)
pb_ratio = key_metrics.get('pb_ratio', 0)
ps_ratio = key_metrics.get('ps_ratio', 0)
peg_ratio = key_metrics.get('peg_ratio', 0)
# 市值
market_cap = company_info.get('market_cap', 0)
# 计算估值分数 (0-100分分数越高表示越被低估)
valuation_score = 0
# PE比率评分
if pe_ratio > 0 and pe_ratio <= self.criteria['max_pe_ratio']:
pe_score = max(0, 100 - (pe_ratio / self.criteria['max_pe_ratio']) * 50)
valuation_score += pe_score * 0.3
# PB比率评分
if pb_ratio > 0 and pb_ratio <= self.criteria['max_pb_ratio']:
pb_score = max(0, 100 - (pb_ratio / self.criteria['max_pb_ratio']) * 50)
valuation_score += pb_score * 0.3
# PS比率评分
if ps_ratio > 0 and ps_ratio <= self.criteria['max_ps_ratio']:
ps_score = max(0, 100 - (ps_ratio / self.criteria['max_ps_ratio']) * 50)
valuation_score += ps_score * 0.2
# PEG比率评分
if peg_ratio > 0 and peg_ratio <= 1.0:
peg_score = max(0, 100 - peg_ratio * 50)
valuation_score += peg_score * 0.2
return {
'pe_ratio': pe_ratio,
'pb_ratio': pb_ratio,
'ps_ratio': ps_ratio,
'peg_ratio': peg_ratio,
'market_cap': market_cap,
'valuation_score': min(100, max(0, valuation_score)),
'is_undervalued': pe_ratio <= self.criteria['max_pe_ratio'] and
pb_ratio <= self.criteria['max_pb_ratio'] and
ps_ratio <= self.criteria['max_ps_ratio']
}
except Exception as e:
logger.error(f"计算估值指标失败: {e}")
return {'valuation_score': 0, 'is_undervalued': False}
def calculate_financial_health(self, data: Dict) -> Dict:
"""计算财务健康度"""
try:
key_metrics = data.get('key_metrics', {})
financial_statements = data.get('financial_statements', {})
# 获取最新财务数据
latest_financial = self._get_latest_financial_data(financial_statements)
# 流动性指标
current_ratio = key_metrics.get('current_ratio', 0)
quick_ratio = key_metrics.get('quick_ratio', 0)
# 偿债能力指标
debt_to_equity = key_metrics.get('debt_to_equity', 0)
debt_ratio = self._calculate_debt_ratio(latest_financial)
# 盈利能力指标
roe = key_metrics.get('return_on_equity', 0)
roa = key_metrics.get('return_on_assets', 0)
profit_margin = key_metrics.get('profit_margin', 0)
operating_margin = key_metrics.get('operating_margin', 0)
# 计算财务健康分数
health_score = 0
# 流动性评分 (30%)
if current_ratio >= self.criteria['min_current_ratio']:
liquidity_score = min(100, (current_ratio / 2.0) * 100)
health_score += liquidity_score * 0.3
# 偿债能力评分 (30%)
if debt_ratio <= self.criteria['max_debt_ratio']:
debt_score = max(0, 100 - (debt_ratio / self.criteria['max_debt_ratio']) * 100)
health_score += debt_score * 0.3
# 盈利能力评分 (40%)
if roe >= self.criteria['min_roe']:
profitability_score = min(100, (roe / 0.2) * 100) # 20% ROE为满分
health_score += profitability_score * 0.4
return {
'current_ratio': current_ratio,
'quick_ratio': quick_ratio,
'debt_to_equity': debt_to_equity,
'debt_ratio': debt_ratio,
'roe': roe,
'roa': roa,
'profit_margin': profit_margin,
'operating_margin': operating_margin,
'health_score': min(100, max(0, health_score)),
'is_healthy': (current_ratio >= self.criteria['min_current_ratio'] and
debt_ratio <= self.criteria['max_debt_ratio'] and
roe >= self.criteria['min_roe'])
}
except Exception as e:
logger.error(f"计算财务健康度失败: {e}")
return {'health_score': 0, 'is_healthy': False}
def calculate_growth_metrics(self, data: Dict) -> Dict:
"""计算成长性指标"""
try:
key_metrics = data.get('key_metrics', {})
financial_statements = data.get('financial_statements', {})
# 收入增长率
revenue_growth = key_metrics.get('revenue_growth', 0)
earnings_growth = key_metrics.get('earnings_growth', 0)
# 计算历史增长率
historical_growth = self._calculate_historical_growth(financial_statements)
# 成长性评分
growth_score = 0
# 收入增长率评分
if revenue_growth > 0:
revenue_score = min(100, revenue_growth * 100)
growth_score += revenue_score * 0.4
# 利润增长率评分
if earnings_growth > 0:
earnings_score = min(100, earnings_growth * 100)
growth_score += earnings_score * 0.4
# 历史增长率评分
if historical_growth > 0:
historical_score = min(100, historical_growth * 100)
growth_score += historical_score * 0.2
return {
'revenue_growth': revenue_growth,
'earnings_growth': earnings_growth,
'historical_growth': historical_growth,
'growth_score': min(100, max(0, growth_score)),
'is_growing': revenue_growth > 0.05 and earnings_growth > 0.05 # 5%以上增长
}
except Exception as e:
logger.error(f"计算成长性指标失败: {e}")
return {'growth_score': 0, 'is_growing': False}
def calculate_risk_metrics(self, data: Dict) -> Dict:
"""计算风险指标"""
try:
key_metrics = data.get('key_metrics', {})
stock_prices = data.get('stock_prices', pd.DataFrame())
# Beta系数
beta = key_metrics.get('beta', 1.0)
# 计算波动率
volatility = self._calculate_volatility(stock_prices)
# 计算最大回撤
max_drawdown = self._calculate_max_drawdown(stock_prices)
# 风险评分 (分数越高表示风险越低)
risk_score = 100
# Beta风险评分
if beta > 1.5:
risk_score -= (beta - 1.5) * 20
elif beta < 0.8:
risk_score -= (0.8 - beta) * 10
# 波动率风险评分
if volatility > 0.3: # 30%以上波动率
risk_score -= (volatility - 0.3) * 100
# 回撤风险评分
if max_drawdown > 0.2: # 20%以上回撤
risk_score -= (max_drawdown - 0.2) * 100
return {
'beta': beta,
'volatility': volatility,
'max_drawdown': max_drawdown,
'risk_score': max(0, min(100, risk_score)),
'risk_level': self._get_risk_level(risk_score)
}
except Exception as e:
logger.error(f"计算风险指标失败: {e}")
return {'risk_score': 50, 'risk_level': '中等'}
def perform_dcf_valuation(self, data: Dict) -> Dict:
"""执行DCF估值分析"""
try:
financial_statements = data.get('financial_statements', {})
key_metrics = data.get('key_metrics', {})
# 获取最新财务数据
latest_financial = self._get_latest_financial_data(financial_statements)
if not latest_financial:
return {'dcf_value': 0, 'dcf_score': 0}
# 基础参数
revenue = latest_financial.get('revenue', 0)
net_income = latest_financial.get('net_income', 0)
total_assets = latest_financial.get('total_assets', 0)
if revenue <= 0 or net_income <= 0:
return {'dcf_value': 0, 'dcf_score': 0}
# 假设参数
growth_rate = min(0.1, max(0.02, key_metrics.get('revenue_growth', 0.05))) # 2%-10%
discount_rate = 0.1 # 10%折现率
terminal_growth_rate = 0.03 # 3%永续增长率
# 计算自由现金流 (简化版)
fcf = net_income * 0.8 # 假设80%的净利润为自由现金流
# 5年预测
years = 5
dcf_value = 0
for year in range(1, years + 1):
future_fcf = fcf * ((1 + growth_rate) ** year)
discounted_fcf = future_fcf / ((1 + discount_rate) ** year)
dcf_value += discounted_fcf
# 终值计算
terminal_fcf = fcf * ((1 + growth_rate) ** years) * (1 + terminal_growth_rate)
terminal_value = terminal_fcf / (discount_rate - terminal_growth_rate)
discounted_terminal_value = terminal_value / ((1 + discount_rate) ** years)
total_dcf_value = dcf_value + discounted_terminal_value
# 获取当前股价进行对比
current_price = self._get_current_price(data)
if current_price > 0:
dcf_score = min(100, (total_dcf_value / current_price) * 50) # 50%以上溢价为满分
else:
dcf_score = 0
return {
'dcf_value': total_dcf_value,
'current_price': current_price,
'dcf_score': dcf_score,
'is_undervalued_dcf': total_dcf_value > current_price if current_price > 0 else False
}
except Exception as e:
logger.error(f"DCF估值分析失败: {e}")
return {'dcf_value': 0, 'dcf_score': 0}
def generate_investment_recommendation(self, analysis_results: Dict) -> Dict:
"""生成投资建议"""
try:
valuation_score = analysis_results.get('valuation_score', 0)
health_score = analysis_results.get('financial_health_score', 0)
growth_score = analysis_results.get('growth_score', 0)
risk_score = analysis_results.get('risk_score', 0)
# 计算综合评分
overall_score = (
valuation_score * 0.4 +
health_score * 0.3 +
growth_score * 0.2 +
risk_score * 0.1
)
# 生成建议
if overall_score >= 80:
recommendation = "强烈买入"
confidence = ""
elif overall_score >= 65:
recommendation = "买入"
confidence = "中高"
elif overall_score >= 50:
recommendation = "持有"
confidence = "中等"
elif overall_score >= 35:
recommendation = "观望"
confidence = "中低"
else:
recommendation = "卖出"
confidence = ""
# 风险提示
risk_warnings = []
if risk_score < 40:
risk_warnings.append("高风险投资")
if health_score < 50:
risk_warnings.append("财务健康度较低")
if growth_score < 30:
risk_warnings.append("成长性不足")
return {
'recommendation': recommendation,
'confidence': confidence,
'overall_score': overall_score,
'risk_warnings': risk_warnings,
'key_strengths': self._get_key_strengths(analysis_results),
'key_concerns': self._get_key_concerns(analysis_results)
}
except Exception as e:
logger.error(f"生成投资建议失败: {e}")
return {'recommendation': '无法分析', 'overall_score': 0}
def _get_latest_financial_data(self, financial_statements: Dict) -> Dict:
"""获取最新财务数据"""
if not financial_statements:
return {}
# 按年份排序,获取最新的数据
sorted_periods = sorted(financial_statements.keys(), reverse=True)
return financial_statements.get(sorted_periods[0], {})
def _calculate_debt_ratio(self, financial_data: Dict) -> float:
"""计算债务比率"""
total_liabilities = financial_data.get('total_liabilities', 0)
total_assets = financial_data.get('total_assets', 0)
if total_assets > 0:
return total_liabilities / total_assets
return 0
def _calculate_historical_growth(self, financial_statements: Dict) -> float:
"""计算历史增长率"""
if len(financial_statements) < 2:
return 0
# 获取最近两年的收入数据
sorted_periods = sorted(financial_statements.keys(), reverse=True)
if len(sorted_periods) < 2:
return 0
current_revenue = financial_statements[sorted_periods[0]].get('revenue', 0)
previous_revenue = financial_statements[sorted_periods[1]].get('revenue', 0)
if previous_revenue > 0:
return (current_revenue - previous_revenue) / previous_revenue
return 0
def _calculate_volatility(self, stock_prices: pd.DataFrame) -> float:
"""计算波动率"""
if stock_prices.empty or 'close' not in stock_prices.columns:
return 0
returns = stock_prices['close'].pct_change().dropna()
return returns.std() * np.sqrt(252) # 年化波动率
def _calculate_max_drawdown(self, stock_prices: pd.DataFrame) -> float:
"""计算最大回撤"""
if stock_prices.empty or 'close' not in stock_prices.columns:
return 0
prices = stock_prices['close']
peak = prices.expanding().max()
drawdown = (prices - peak) / peak
return abs(drawdown.min())
def _get_risk_level(self, risk_score: float) -> str:
"""获取风险等级"""
if risk_score >= 80:
return ""
elif risk_score >= 60:
return "中低"
elif risk_score >= 40:
return "中等"
elif risk_score >= 20:
return "中高"
else:
return ""
def _get_current_price(self, data: Dict) -> float:
"""获取当前股价"""
stock_prices = data.get('stock_prices', pd.DataFrame())
if not stock_prices.empty and 'close' in stock_prices.columns:
return stock_prices['close'].iloc[-1]
return 0
def _get_key_strengths(self, analysis_results: Dict) -> List[str]:
"""获取关键优势"""
strengths = []
if analysis_results.get('valuation_score', 0) > 70:
strengths.append("估值吸引力高")
if analysis_results.get('financial_health_score', 0) > 70:
strengths.append("财务健康度良好")
if analysis_results.get('growth_score', 0) > 70:
strengths.append("成长性优秀")
if analysis_results.get('risk_score', 0) > 70:
strengths.append("风险控制良好")
return strengths
def _get_key_concerns(self, analysis_results: Dict) -> List[str]:
"""获取关键担忧"""
concerns = []
if analysis_results.get('valuation_score', 0) < 40:
concerns.append("估值偏高")
if analysis_results.get('financial_health_score', 0) < 40:
concerns.append("财务健康度不佳")
if analysis_results.get('growth_score', 0) < 40:
concerns.append("成长性不足")
if analysis_results.get('risk_score', 0) < 40:
concerns.append("风险较高")
return concerns

View File

@@ -0,0 +1,17 @@
{
"analyses": [
{
"symbol": "RVPH",
"timestamp": "20250920_015345",
"analysis_date": "2025-09-20T01:53:45.876451",
"company_name": "Reviva Pharmaceuticals Holdings, Inc.",
"recommendation": "观望/高风险投机",
"overall_score": 35,
"files": {
"detailed": "RVPH_detailed_20250920_015345.json",
"summary": "RVPH_summary_20250920_015345.md",
"raw_data": "RVPH_raw_data_20250920_015345.json"
}
}
]
}

View File

@@ -0,0 +1,112 @@
{
"analysis_metadata": {
"symbol": "RVPH",
"analysis_date": "2025-09-20T01:53:45.875995",
"timestamp": "20250920_015345",
"version": "1.0"
},
"company_info": {
"name": "Reviva Pharmaceuticals Holdings, Inc.",
"symbol": "RVPH",
"industry": "Biotechnology",
"sector": "Healthcare",
"market_cap": 17710000,
"employees": 14,
"website": "https://www.revivapharma.com",
"country": "United States",
"currency": "USD"
},
"financial_data": {
"revenue": 0,
"net_income": -29100000,
"total_assets": 15500000,
"total_liabilities": 14690000,
"cash": 1020000,
"debt": 0
},
"valuation_analysis": {
"pe_ratio": null,
"pb_ratio": null,
"ps_ratio": null,
"peg_ratio": null,
"market_cap": 17710000,
"enterprise_value": 7460000,
"valuation_score": 15,
"is_undervalued": false
},
"financial_health": {
"current_ratio": 0.9,
"quick_ratio": 0.9,
"debt_to_equity": null,
"debt_ratio": 0.95,
"roe": -9.16,
"roa": -2.75,
"profit_margin": null,
"operating_margin": null,
"health_score": 10,
"is_healthy": false
},
"growth_analysis": {
"revenue_growth": 0,
"earnings_growth": 0,
"historical_growth": 0,
"growth_score": 5,
"is_growing": false
},
"risk_analysis": {
"beta": -0.06,
"volatility": 0.894,
"max_drawdown": 0.93,
"risk_score": 20,
"risk_level": "极高"
},
"investment_recommendation": {
"recommendation": "观望/高风险投机",
"confidence": "高",
"overall_score": 35,
"risk_warnings": [
"极高风险投资",
"财务健康度极差",
"无收入来源",
"持续亏损",
"依赖融资维持运营",
"监管审批不确定性",
"可能完全损失本金"
],
"key_strengths": [
"专注精神分裂症治疗",
"主要产品临床试验积极",
"分析师全部给予买入评级",
"获得FDA对齐"
],
"key_concerns": [
"无商业化产品",
"持续亏损",
"高债务比率",
"股价大幅下跌",
"流动性风险"
]
},
"analyst_opinions": {
"total_analysts": 13,
"buy_ratings": 13,
"hold_ratings": 0,
"sell_ratings": 0,
"average_target_price": 14.46,
"price_target_range": "2.00 - 20.00"
},
"market_data": {
"current_price": 0.26,
"previous_close": 0.42,
"day_change": -0.16,
"day_change_percent": -37.89,
"volume": 29973974,
"avg_volume": 2720000,
"market_cap": 17710000,
"shares_outstanding": 68000000,
"float": 60810000,
"insider_ownership": 10.59,
"institutional_ownership": 21.25
},
"raw_data": {}
}

View File

@@ -0,0 +1,105 @@
{
"company_info": {
"name": "Reviva Pharmaceuticals Holdings, Inc.",
"symbol": "RVPH",
"industry": "Biotechnology",
"sector": "Healthcare",
"market_cap": 17710000,
"employees": 14,
"website": "https://www.revivapharma.com",
"country": "United States",
"currency": "USD"
},
"financial_data": {
"revenue": 0,
"net_income": -29100000,
"total_assets": 15500000,
"total_liabilities": 14690000,
"cash": 1020000,
"debt": 0
},
"valuation_analysis": {
"pe_ratio": null,
"pb_ratio": null,
"ps_ratio": null,
"peg_ratio": null,
"market_cap": 17710000,
"enterprise_value": 7460000,
"valuation_score": 15,
"is_undervalued": false
},
"financial_health": {
"current_ratio": 0.9,
"quick_ratio": 0.9,
"debt_to_equity": null,
"debt_ratio": 0.95,
"roe": -9.16,
"roa": -2.75,
"profit_margin": null,
"operating_margin": null,
"health_score": 10,
"is_healthy": false
},
"growth_analysis": {
"revenue_growth": 0,
"earnings_growth": 0,
"historical_growth": 0,
"growth_score": 5,
"is_growing": false
},
"risk_analysis": {
"beta": -0.06,
"volatility": 0.894,
"max_drawdown": 0.93,
"risk_score": 20,
"risk_level": "极高"
},
"investment_recommendation": {
"recommendation": "观望/高风险投机",
"confidence": "高",
"overall_score": 35,
"risk_warnings": [
"极高风险投资",
"财务健康度极差",
"无收入来源",
"持续亏损",
"依赖融资维持运营",
"监管审批不确定性",
"可能完全损失本金"
],
"key_strengths": [
"专注精神分裂症治疗",
"主要产品临床试验积极",
"分析师全部给予买入评级",
"获得FDA对齐"
],
"key_concerns": [
"无商业化产品",
"持续亏损",
"高债务比率",
"股价大幅下跌",
"流动性风险"
]
},
"analyst_opinions": {
"total_analysts": 13,
"buy_ratings": 13,
"hold_ratings": 0,
"sell_ratings": 0,
"average_target_price": 14.46,
"price_target_range": "2.00 - 20.00"
},
"market_data": {
"current_price": 0.26,
"previous_close": 0.42,
"day_change": -0.16,
"day_change_percent": -37.89,
"volume": 29973974,
"avg_volume": 2720000,
"market_cap": 17710000,
"shares_outstanding": 68000000,
"float": 60810000,
"insider_ownership": 10.59,
"institutional_ownership": 21.25
}
}

View File

@@ -0,0 +1,70 @@
# RVPH 股票分析报告
## 📊 基本信息
- **公司名称**: Reviva Pharmaceuticals Holdings, Inc.
- **行业**: Biotechnology
- **市值**: $17,710,000
- **分析时间**: 2025-09-20 01:53:45
## 🎯 投资建议
- **建议**: 观望/高风险投机
- **信心度**: 高
- **综合评分**: 35.0/100
## 📈 各维度评分
| 维度 | 评分 | 评级 |
|------|------|------|
| 估值吸引力 | 15.0/100 | 较差 |
| 财务健康度 | 10.0/100 | 较差 |
| 成长性 | 5.0/100 | 较差 |
| 风险控制 | 20.0/100 | 较差 |
## ✅ 关键优势
- 专注精神分裂症治疗
- 主要产品临床试验积极
- 分析师全部给予买入评级
- 获得FDA对齐
## ⚠️ 关键担忧
- 无商业化产品
- 持续亏损
- 高债务比率
- 股价大幅下跌
- 流动性风险
## 🚨 风险提示
- 极高风险投资
- 财务健康度极差
- 无收入来源
- 持续亏损
- 依赖融资维持运营
- 监管审批不确定性
- 可能完全损失本金
## 📋 详细指标
### 估值指标
- PE比率: None
- PB比率: None
- PS比率: None
- PEG比率: None
### 财务健康度
- 流动比率: 0.9
- 速动比率: 0.9
- 债务比率: 0.95
- ROE: -9.16
### 成长性指标
- 收入增长率: 0
- 利润增长率: 0
- 历史增长率: 0
### 风险指标
- Beta系数: -0.06
- 波动率: 0.894
- 最大回撤: 0.93
- 风险等级: 极高
---
*分析时间: 2025-09-20 01:53:45*
*报告ID: 20250920_015345*

282
analysis_storage.py Normal file
View File

@@ -0,0 +1,282 @@
"""
分析结果存储模块 - 标准化保存分析报告
"""
import os
import json
from datetime import datetime
from typing import Dict, Any
import logging
logger = logging.getLogger(__name__)
class AnalysisStorage:
def __init__(self, base_dir: str = "analysis_reports"):
self.base_dir = base_dir
self.ensure_directories()
def ensure_directories(self):
"""确保必要的目录存在"""
directories = [
self.base_dir,
os.path.join(self.base_dir, "detailed"),
os.path.join(self.base_dir, "summaries"),
os.path.join(self.base_dir, "charts"),
os.path.join(self.base_dir, "raw_data")
]
for directory in directories:
os.makedirs(directory, exist_ok=True)
def save_analysis_report(self, symbol: str, analysis_data: Dict[str, Any]) -> Dict[str, str]:
"""
保存分析报告到文件
Args:
symbol: 股票代码
analysis_data: 分析数据
Returns:
保存的文件路径字典
"""
try:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
date_str = datetime.now().strftime("%Y-%m-%d")
# 1. 保存详细分析报告 (JSON格式)
detailed_file = self._save_detailed_report(symbol, analysis_data, timestamp)
# 2. 保存简要报告 (Markdown格式)
summary_file = self._save_summary_report(symbol, analysis_data, timestamp)
# 3. 保存原始数据 (JSON格式)
raw_data_file = self._save_raw_data(symbol, analysis_data, timestamp)
# 4. 更新索引文件
self._update_index(symbol, analysis_data, timestamp)
return {
'detailed_report': detailed_file,
'summary_report': summary_file,
'raw_data': raw_data_file,
'timestamp': timestamp,
'date': date_str
}
except Exception as e:
logger.error(f"保存分析报告失败 {symbol}: {e}")
return {}
def _save_detailed_report(self, symbol: str, analysis_data: Dict, timestamp: str) -> str:
"""保存详细分析报告"""
filename = f"{symbol}_detailed_{timestamp}.json"
filepath = os.path.join(self.base_dir, "detailed", filename)
detailed_report = {
"analysis_metadata": {
"symbol": symbol,
"analysis_date": datetime.now().isoformat(),
"timestamp": timestamp,
"version": "1.0"
},
"company_info": analysis_data.get('company_info', {}),
"financial_data": analysis_data.get('financial_data', {}),
"valuation_analysis": analysis_data.get('valuation_analysis', {}),
"financial_health": analysis_data.get('financial_health', {}),
"growth_analysis": analysis_data.get('growth_analysis', {}),
"risk_analysis": analysis_data.get('risk_analysis', {}),
"investment_recommendation": analysis_data.get('investment_recommendation', {}),
"analyst_opinions": analysis_data.get('analyst_opinions', {}),
"market_data": analysis_data.get('market_data', {}),
"raw_data": analysis_data.get('raw_data', {})
}
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(detailed_report, f, ensure_ascii=False, indent=2)
logger.info(f"详细报告已保存: {filepath}")
return filepath
def _save_summary_report(self, symbol: str, analysis_data: Dict, timestamp: str) -> str:
"""保存简要分析报告 (Markdown格式)"""
filename = f"{symbol}_summary_{timestamp}.md"
filepath = os.path.join(self.base_dir, "summaries", filename)
# 生成Markdown格式的简要报告
markdown_content = self._generate_markdown_summary(symbol, analysis_data, timestamp)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(markdown_content)
logger.info(f"简要报告已保存: {filepath}")
return filepath
def _save_raw_data(self, symbol: str, analysis_data: Dict, timestamp: str) -> str:
"""保存原始数据"""
filename = f"{symbol}_raw_data_{timestamp}.json"
filepath = os.path.join(self.base_dir, "raw_data", filename)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(analysis_data, f, ensure_ascii=False, indent=2)
logger.info(f"原始数据已保存: {filepath}")
return filepath
def _update_index(self, symbol: str, analysis_data: Dict, timestamp: str):
"""更新索引文件"""
index_file = os.path.join(self.base_dir, "analysis_index.json")
# 读取现有索引
if os.path.exists(index_file):
with open(index_file, 'r', encoding='utf-8') as f:
index_data = json.load(f)
else:
index_data = {"analyses": []}
# 添加新分析记录
new_entry = {
"symbol": symbol,
"timestamp": timestamp,
"analysis_date": datetime.now().isoformat(),
"company_name": analysis_data.get('company_info', {}).get('name', ''),
"recommendation": analysis_data.get('investment_recommendation', {}).get('recommendation', 'N/A'),
"overall_score": analysis_data.get('investment_recommendation', {}).get('overall_score', 0),
"files": {
"detailed": f"{symbol}_detailed_{timestamp}.json",
"summary": f"{symbol}_summary_{timestamp}.md",
"raw_data": f"{symbol}_raw_data_{timestamp}.json"
}
}
index_data["analyses"].append(new_entry)
# 按时间倒序排列
index_data["analyses"].sort(key=lambda x: x["timestamp"], reverse=True)
# 保存索引
with open(index_file, 'w', encoding='utf-8') as f:
json.dump(index_data, f, ensure_ascii=False, indent=2)
def _generate_markdown_summary(self, symbol: str, analysis_data: Dict, timestamp: str) -> str:
"""生成Markdown格式的简要报告"""
company_info = analysis_data.get('company_info', {})
investment_rec = analysis_data.get('investment_recommendation', {})
valuation = analysis_data.get('valuation_analysis', {})
financial_health = analysis_data.get('financial_health', {})
growth = analysis_data.get('growth_analysis', {})
risk = analysis_data.get('risk_analysis', {})
markdown = f"""# {symbol} 股票分析报告
## 📊 基本信息
- **公司名称**: {company_info.get('name', 'N/A')}
- **行业**: {company_info.get('industry', 'N/A')}
- **市值**: ${company_info.get('market_cap', 0):,.0f}
- **分析时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
## 🎯 投资建议
- **建议**: {investment_rec.get('recommendation', 'N/A')}
- **信心度**: {investment_rec.get('confidence', 'N/A')}
- **综合评分**: {investment_rec.get('overall_score', 0):.1f}/100
## 📈 各维度评分
| 维度 | 评分 | 评级 |
|------|------|------|
| 估值吸引力 | {valuation.get('valuation_score', 0):.1f}/100 | {self._get_rating(valuation.get('valuation_score', 0))} |
| 财务健康度 | {financial_health.get('health_score', 0):.1f}/100 | {self._get_rating(financial_health.get('health_score', 0))} |
| 成长性 | {growth.get('growth_score', 0):.1f}/100 | {self._get_rating(growth.get('growth_score', 0))} |
| 风险控制 | {risk.get('risk_score', 0):.1f}/100 | {self._get_rating(risk.get('risk_score', 0))} |
## ✅ 关键优势
{self._format_list(investment_rec.get('key_strengths', []))}
## ⚠️ 关键担忧
{self._format_list(investment_rec.get('key_concerns', []))}
## 🚨 风险提示
{self._format_list(investment_rec.get('risk_warnings', []))}
## 📋 详细指标
### 估值指标
- PE比率: {valuation.get('pe_ratio', 'N/A')}
- PB比率: {valuation.get('pb_ratio', 'N/A')}
- PS比率: {valuation.get('ps_ratio', 'N/A')}
- PEG比率: {valuation.get('peg_ratio', 'N/A')}
### 财务健康度
- 流动比率: {financial_health.get('current_ratio', 'N/A')}
- 速动比率: {financial_health.get('quick_ratio', 'N/A')}
- 债务比率: {financial_health.get('debt_ratio', 'N/A')}
- ROE: {financial_health.get('roe', 'N/A')}
### 成长性指标
- 收入增长率: {growth.get('revenue_growth', 'N/A')}
- 利润增长率: {growth.get('earnings_growth', 'N/A')}
- 历史增长率: {growth.get('historical_growth', 'N/A')}
### 风险指标
- Beta系数: {risk.get('beta', 'N/A')}
- 波动率: {risk.get('volatility', 'N/A')}
- 最大回撤: {risk.get('max_drawdown', 'N/A')}
- 风险等级: {risk.get('risk_level', 'N/A')}
---
*分析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*
*报告ID: {timestamp}*
"""
return markdown
def _get_rating(self, score: float) -> str:
"""根据分数获取评级"""
if score >= 90:
return "优秀"
elif score >= 80:
return "良好"
elif score >= 70:
return "中等"
elif score >= 60:
return "一般"
else:
return "较差"
def _format_list(self, items: list) -> str:
"""格式化列表"""
if not items:
return ""
return "\n".join([f"- {item}" for item in items])
def get_analysis_history(self, symbol: str = None) -> list:
"""获取分析历史"""
index_file = os.path.join(self.base_dir, "analysis_index.json")
if not os.path.exists(index_file):
return []
with open(index_file, 'r', encoding='utf-8') as f:
index_data = json.load(f)
analyses = index_data.get('analyses', [])
if symbol:
analyses = [a for a in analyses if a['symbol'].upper() == symbol.upper()]
return analyses
def get_latest_analysis(self, symbol: str) -> Dict:
"""获取最新分析结果"""
history = self.get_analysis_history(symbol)
if not history:
return {}
latest = history[0] # 已按时间倒序排列
# 读取详细报告
detailed_file = os.path.join(self.base_dir, "detailed", latest['files']['detailed'])
if os.path.exists(detailed_file):
with open(detailed_file, 'r', encoding='utf-8') as f:
return json.load(f)
return {}
def list_all_analyses(self) -> list:
"""列出所有分析记录"""
return self.get_analysis_history()

35
config.py Normal file
View File

@@ -0,0 +1,35 @@
"""
美股低价值公司分析系统配置文件
"""
import os
from dotenv import load_dotenv
load_dotenv()
# API配置
YAHOO_FINANCE_API = "https://query1.finance.yahoo.com/v8/finance/chart/"
ALPHA_VANTAGE_API_KEY = os.getenv('ALPHA_VANTAGE_API_KEY', '')
# 数据库配置
DATABASE_PATH = "stock_analysis.db"
# 分析参数配置
LOW_VALUE_CRITERIA = {
'max_pe_ratio': 15, # 最大市盈率
'max_pb_ratio': 1.5, # 最大市净率
'max_ps_ratio': 2.0, # 最大市销率
'min_market_cap': 100_000_000, # 最小市值1亿美元
'max_debt_ratio': 0.6, # 最大债务比率
'min_current_ratio': 1.2, # 最小流动比率
'min_roe': 0.05, # 最小ROE5%
}
# 报告配置
REPORT_OUTPUT_DIR = "reports"
CHART_OUTPUT_DIR = "charts"
# 数据更新频率(小时)
DATA_UPDATE_INTERVAL = 24
# 支持的股票市场
SUPPORTED_MARKETS = ['NASDAQ', 'NYSE', 'AMEX']

131
create_rvph_report.py Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""
创建RVPH分析报告
"""
import json
from datetime import datetime
from analysis_storage import AnalysisStorage
def create_rvph_analysis():
"""创建RVPH的分析报告"""
# 基于网络搜索的数据创建分析结果
analysis_data = {
'company_info': {
'name': 'Reviva Pharmaceuticals Holdings, Inc.',
'symbol': 'RVPH',
'industry': 'Biotechnology',
'sector': 'Healthcare',
'market_cap': 17710000, # $17.71M
'employees': 14,
'website': 'https://www.revivapharma.com',
'country': 'United States',
'currency': 'USD'
},
'financial_data': {
'revenue': 0,
'net_income': -29100000, # -$29.1M
'total_assets': 15500000, # $15.5M
'total_liabilities': 14690000, # $14.69M
'cash': 1020000, # $1.02M
'debt': 0
},
'valuation_analysis': {
'pe_ratio': None, # 负盈利
'pb_ratio': None, # 负账面价值
'ps_ratio': None, # 无收入
'peg_ratio': None,
'market_cap': 17710000,
'enterprise_value': 7460000, # $7.46M
'valuation_score': 15, # 低估值分数
'is_undervalued': False
},
'financial_health': {
'current_ratio': 0.90,
'quick_ratio': 0.90,
'debt_to_equity': None,
'debt_ratio': 0.95, # 95%债务比率
'roe': -9.16, # -916%
'roa': -2.75, # -275%
'profit_margin': None, # 负值
'operating_margin': None, # 负值
'health_score': 10, # 极低健康度分数
'is_healthy': False
},
'growth_analysis': {
'revenue_growth': 0, # 无收入
'earnings_growth': 0, # 持续亏损
'historical_growth': 0,
'growth_score': 5, # 极低成长性分数
'is_growing': False
},
'risk_analysis': {
'beta': -0.06,
'volatility': 0.894, # 89.4%
'max_drawdown': 0.93, # 93%最大回撤
'risk_score': 20, # 高风险分数
'risk_level': '极高'
},
'investment_recommendation': {
'recommendation': '观望/高风险投机',
'confidence': '',
'overall_score': 35, # 35/100分
'risk_warnings': [
'极高风险投资',
'财务健康度极差',
'无收入来源',
'持续亏损',
'依赖融资维持运营',
'监管审批不确定性',
'可能完全损失本金'
],
'key_strengths': [
'专注精神分裂症治疗',
'主要产品临床试验积极',
'分析师全部给予买入评级',
'获得FDA对齐'
],
'key_concerns': [
'无商业化产品',
'持续亏损',
'高债务比率',
'股价大幅下跌',
'流动性风险'
]
},
'analyst_opinions': {
'total_analysts': 13,
'buy_ratings': 13,
'hold_ratings': 0,
'sell_ratings': 0,
'average_target_price': 14.46,
'price_target_range': '2.00 - 20.00'
},
'market_data': {
'current_price': 0.26,
'previous_close': 0.42,
'day_change': -0.16,
'day_change_percent': -37.89,
'volume': 29973974,
'avg_volume': 2720000,
'market_cap': 17710000,
'shares_outstanding': 68000000,
'float': 60810000,
'insider_ownership': 10.59,
'institutional_ownership': 21.25
}
}
# 保存分析报告
storage = AnalysisStorage()
result = storage.save_analysis_report('RVPH', analysis_data)
print("✅ RVPH分析报告已创建并保存")
print(f"📁 详细报告: {result.get('detailed_report', 'N/A')}")
print(f"📄 简要报告: {result.get('summary_report', 'N/A')}")
print(f"💾 原始数据: {result.get('raw_data', 'N/A')}")
return result
if __name__ == "__main__":
create_rvph_analysis()

216
data_collector.py Normal file
View File

@@ -0,0 +1,216 @@
"""
数据收集模块 - 从各种API获取股票和财务数据
"""
import yfinance as yf
import pandas as pd
import requests
from typing import Dict, List, Optional, Tuple
import time
from datetime import datetime, timedelta
import logging
# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class StockDataCollector:
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
def get_company_info(self, symbol: str) -> Dict:
"""获取公司基本信息"""
try:
ticker = yf.Ticker(symbol)
info = ticker.info
return {
'symbol': symbol,
'name': info.get('longName', ''),
'sector': info.get('sector', ''),
'industry': info.get('industry', ''),
'market_cap': info.get('marketCap', 0),
'employees': info.get('fullTimeEmployees', 0),
'website': info.get('website', ''),
'description': info.get('longBusinessSummary', ''),
'country': info.get('country', ''),
'currency': info.get('currency', 'USD')
}
except Exception as e:
logger.error(f"获取公司信息失败 {symbol}: {e}")
return {}
def get_stock_prices(self, symbol: str, period: str = "1y") -> pd.DataFrame:
"""获取股价数据"""
try:
ticker = yf.Ticker(symbol)
data = ticker.history(period=period)
if data.empty:
logger.warning(f"未找到股价数据: {symbol}")
return pd.DataFrame()
# 重命名列以匹配数据库结构
data = data.reset_index()
data.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'dividends', 'stock_splits']
data = data.drop(['dividends', 'stock_splits'], axis=1)
return data
except Exception as e:
logger.error(f"获取股价数据失败 {symbol}: {e}")
return pd.DataFrame()
def get_financial_statements(self, symbol: str) -> Dict:
"""获取财务报表数据"""
try:
ticker = yf.Ticker(symbol)
# 获取季度和年度财务数据
quarterly_data = {}
annual_data = {}
# 季度数据
try:
quarterly_financials = ticker.quarterly_financials
if not quarterly_financials.empty:
for i, (date, row) in enumerate(quarterly_financials.iterrows()):
quarterly_data[f"Q{i+1}_{date.year}"] = {
'year': date.year,
'quarter': (i % 4) + 1,
'revenue': row.get('Total Revenue', 0),
'net_income': row.get('Net Income', 0),
'total_assets': row.get('Total Assets', 0),
'total_liabilities': row.get('Total Liabilities', 0),
'shareholders_equity': row.get('Stockholders Equity', 0),
'cash': row.get('Cash And Cash Equivalents', 0),
'debt': row.get('Total Debt', 0)
}
except Exception as e:
logger.warning(f"获取季度财务数据失败 {symbol}: {e}")
# 年度数据
try:
annual_financials = ticker.financials
if not annual_financials.empty:
for i, (date, row) in enumerate(annual_financials.iterrows()):
annual_data[f"Annual_{date.year}"] = {
'year': date.year,
'quarter': 0,
'revenue': row.get('Total Revenue', 0),
'net_income': row.get('Net Income', 0),
'total_assets': row.get('Total Assets', 0),
'total_liabilities': row.get('Total Liabilities', 0),
'shareholders_equity': row.get('Stockholders Equity', 0),
'cash': row.get('Cash And Cash Equivalents', 0),
'debt': row.get('Total Debt', 0)
}
except Exception as e:
logger.warning(f"获取年度财务数据失败 {symbol}: {e}")
return {**quarterly_data, **annual_data}
except Exception as e:
logger.error(f"获取财务数据失败 {symbol}: {e}")
return {}
def get_key_metrics(self, symbol: str) -> Dict:
"""获取关键财务指标"""
try:
ticker = yf.Ticker(symbol)
info = ticker.info
return {
'pe_ratio': info.get('trailingPE', 0),
'pb_ratio': info.get('priceToBook', 0),
'ps_ratio': info.get('priceToSalesTrailing12Months', 0),
'peg_ratio': info.get('pegRatio', 0),
'debt_to_equity': info.get('debtToEquity', 0),
'current_ratio': info.get('currentRatio', 0),
'quick_ratio': info.get('quickRatio', 0),
'return_on_equity': info.get('returnOnEquity', 0),
'return_on_assets': info.get('returnOnAssets', 0),
'profit_margin': info.get('profitMargins', 0),
'operating_margin': info.get('operatingMargins', 0),
'revenue_growth': info.get('revenueGrowth', 0),
'earnings_growth': info.get('earningsGrowth', 0),
'beta': info.get('beta', 0),
'dividend_yield': info.get('dividendYield', 0),
'payout_ratio': info.get('payoutRatio', 0)
}
except Exception as e:
logger.error(f"获取关键指标失败 {symbol}: {e}")
return {}
def get_analyst_recommendations(self, symbol: str) -> Dict:
"""获取分析师推荐"""
try:
ticker = yf.Ticker(symbol)
recommendations = ticker.recommendations
if recommendations is None or recommendations.empty:
return {}
# 获取最新的推荐
latest_rec = recommendations.iloc[-1] if not recommendations.empty else None
return {
'latest_recommendation': latest_rec.get('To Grade', '') if latest_rec is not None else '',
'latest_firm': latest_rec.get('Firm', '') if latest_rec is not None else '',
'latest_date': latest_rec.get('Date', '') if latest_rec is not None else '',
'total_recommendations': len(recommendations)
}
except Exception as e:
logger.warning(f"获取分析师推荐失败 {symbol}: {e}")
return {}
def get_news_sentiment(self, symbol: str) -> Dict:
"""获取新闻情绪分析(简化版)"""
try:
ticker = yf.Ticker(symbol)
news = ticker.news
if not news:
return {'sentiment_score': 0, 'news_count': 0}
# 简单的情绪分析实际应用中可以使用更复杂的NLP模型
positive_keywords = ['growth', 'profit', 'increase', 'strong', 'positive', 'beat', 'exceed']
negative_keywords = ['loss', 'decline', 'weak', 'negative', 'miss', 'fall', 'drop']
sentiment_score = 0
for article in news[:10]: # 只分析最近10条新闻
title = article.get('title', '').lower()
summary = article.get('summary', '').lower()
text = title + ' ' + summary
positive_count = sum(1 for word in positive_keywords if word in text)
negative_count = sum(1 for word in negative_keywords if word in text)
sentiment_score += (positive_count - negative_count)
return {
'sentiment_score': sentiment_score,
'news_count': len(news),
'recent_news': news[:5] # 最近5条新闻
}
except Exception as e:
logger.warning(f"获取新闻情绪失败 {symbol}: {e}")
return {'sentiment_score': 0, 'news_count': 0}
def collect_all_data(self, symbol: str) -> Dict:
"""收集所有相关数据"""
logger.info(f"开始收集数据: {symbol}")
all_data = {
'symbol': symbol,
'collection_time': datetime.now().isoformat(),
'company_info': self.get_company_info(symbol),
'stock_prices': self.get_stock_prices(symbol),
'financial_statements': self.get_financial_statements(symbol),
'key_metrics': self.get_key_metrics(symbol),
'analyst_recommendations': self.get_analyst_recommendations(symbol),
'news_sentiment': self.get_news_sentiment(symbol)
}
logger.info(f"数据收集完成: {symbol}")
return all_data

226
database.py Normal file
View File

@@ -0,0 +1,226 @@
"""
数据库管理模块
"""
import sqlite3
import pandas as pd
from datetime import datetime
from typing import Dict, List, Optional
import json
class StockDatabase:
def __init__(self, db_path: str = "stock_analysis.db"):
self.db_path = db_path
self.init_database()
def init_database(self):
"""初始化数据库表结构"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# 公司基本信息表
cursor.execute('''
CREATE TABLE IF NOT EXISTS companies (
symbol TEXT PRIMARY KEY,
name TEXT NOT NULL,
sector TEXT,
industry TEXT,
market_cap REAL,
employees INTEGER,
website TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 股价数据表
cursor.execute('''
CREATE TABLE IF NOT EXISTS stock_prices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
date DATE NOT NULL,
open REAL,
high REAL,
low REAL,
close REAL,
volume INTEGER,
adjusted_close REAL,
FOREIGN KEY (symbol) REFERENCES companies (symbol),
UNIQUE(symbol, date)
)
''')
# 财务数据表
cursor.execute('''
CREATE TABLE IF NOT EXISTS financial_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
period TEXT NOT NULL,
year INTEGER NOT NULL,
quarter INTEGER,
revenue REAL,
net_income REAL,
total_assets REAL,
total_liabilities REAL,
shareholders_equity REAL,
cash REAL,
debt REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (symbol) REFERENCES companies (symbol),
UNIQUE(symbol, period, year, quarter)
)
''')
# 分析结果表
cursor.execute('''
CREATE TABLE IF NOT EXISTS analysis_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
analysis_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
valuation_score REAL,
financial_health_score REAL,
growth_score REAL,
risk_score REAL,
overall_score REAL,
recommendation TEXT,
analysis_data TEXT, -- JSON格式存储详细分析数据
FOREIGN KEY (symbol) REFERENCES companies (symbol)
)
''')
conn.commit()
conn.close()
def save_company_info(self, symbol: str, company_data: Dict):
"""保存公司基本信息"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO companies
(symbol, name, sector, industry, market_cap, employees, website, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
symbol,
company_data.get('name', ''),
company_data.get('sector', ''),
company_data.get('industry', ''),
company_data.get('market_cap', 0),
company_data.get('employees', 0),
company_data.get('website', ''),
datetime.now()
))
conn.commit()
conn.close()
def save_stock_prices(self, symbol: str, price_data: pd.DataFrame):
"""保存股价数据"""
conn = sqlite3.connect(self.db_path)
price_data['symbol'] = symbol
price_data.to_sql('stock_prices', conn, if_exists='append', index=False)
conn.close()
def save_financial_data(self, symbol: str, financial_data: Dict):
"""保存财务数据"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
for period, data in financial_data.items():
cursor.execute('''
INSERT OR REPLACE INTO financial_data
(symbol, period, year, quarter, revenue, net_income, total_assets,
total_liabilities, shareholders_equity, cash, debt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
symbol,
period,
data.get('year', 0),
data.get('quarter', 0),
data.get('revenue', 0),
data.get('net_income', 0),
data.get('total_assets', 0),
data.get('total_liabilities', 0),
data.get('shareholders_equity', 0),
data.get('cash', 0),
data.get('debt', 0)
))
conn.commit()
conn.close()
def save_analysis_result(self, symbol: str, analysis_data: Dict):
"""保存分析结果"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO analysis_results
(symbol, valuation_score, financial_health_score, growth_score,
risk_score, overall_score, recommendation, analysis_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
symbol,
analysis_data.get('valuation_score', 0),
analysis_data.get('financial_health_score', 0),
analysis_data.get('growth_score', 0),
analysis_data.get('risk_score', 0),
analysis_data.get('overall_score', 0),
analysis_data.get('recommendation', ''),
json.dumps(analysis_data.get('detailed_analysis', {}))
))
conn.commit()
conn.close()
def get_company_info(self, symbol: str) -> Optional[Dict]:
"""获取公司信息"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('SELECT * FROM companies WHERE symbol = ?', (symbol,))
result = cursor.fetchone()
conn.close()
if result:
return {
'symbol': result[0],
'name': result[1],
'sector': result[2],
'industry': result[3],
'market_cap': result[4],
'employees': result[5],
'website': result[6]
}
return None
def get_latest_analysis(self, symbol: str) -> Optional[Dict]:
"""获取最新分析结果"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM analysis_results
WHERE symbol = ?
ORDER BY analysis_date DESC
LIMIT 1
''', (symbol,))
result = cursor.fetchone()
conn.close()
if result:
return {
'symbol': result[1],
'analysis_date': result[2],
'valuation_score': result[3],
'financial_health_score': result[4],
'growth_score': result[5],
'risk_score': result[6],
'overall_score': result[7],
'recommendation': result[8],
'analysis_data': json.loads(result[9]) if result[9] else {}
}
return None

330
main.py Normal file
View File

@@ -0,0 +1,330 @@
"""
美股低价值公司分析系统 - 主程序
"""
import sys
import logging
from datetime import datetime
from typing import Dict, Optional
# 导入自定义模块
from data_collector import StockDataCollector
from analysis_engine import StockAnalyzer
from report_generator import ReportGenerator
from database import StockDatabase
from analysis_storage import AnalysisStorage
from config import LOW_VALUE_CRITERIA
# 设置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('stock_analysis.log', encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
class StockAnalysisSystem:
def __init__(self):
self.data_collector = StockDataCollector()
self.analyzer = StockAnalyzer()
self.report_generator = ReportGenerator()
self.database = StockDatabase()
self.storage = AnalysisStorage()
def analyze_stock(self, symbol: str, force_refresh: bool = False) -> Dict:
"""
分析单只股票
Args:
symbol: 股票代码
force_refresh: 是否强制刷新数据
Returns:
分析结果字典
"""
try:
logger.info(f"开始分析股票: {symbol}")
# 检查是否需要刷新数据
if not force_refresh:
latest_analysis = self.database.get_latest_analysis(symbol)
if latest_analysis:
# 检查分析时间是否在24小时内
analysis_time = datetime.fromisoformat(latest_analysis['analysis_date'])
if (datetime.now() - analysis_time).total_seconds() < 86400: # 24小时
logger.info(f"使用缓存的分析结果: {symbol}")
return latest_analysis
# 1. 数据收集阶段
logger.info(f"正在收集数据: {symbol}")
raw_data = self.data_collector.collect_all_data(symbol)
if not raw_data.get('company_info'):
logger.error(f"无法获取公司信息: {symbol}")
return {'error': '无法获取公司信息'}
# 保存数据到数据库
self.database.save_company_info(symbol, raw_data['company_info'])
if not raw_data['stock_prices'].empty:
self.database.save_stock_prices(symbol, raw_data['stock_prices'])
if raw_data['financial_statements']:
self.database.save_financial_data(symbol, raw_data['financial_statements'])
# 2. 分析阶段
logger.info(f"正在进行深度分析: {symbol}")
# 估值分析
valuation_results = self.analyzer.calculate_valuation_metrics(raw_data)
# 财务健康度分析
health_results = self.analyzer.calculate_financial_health(raw_data)
# 成长性分析
growth_results = self.analyzer.calculate_growth_metrics(raw_data)
# 风险分析
risk_results = self.analyzer.calculate_risk_metrics(raw_data)
# DCF估值
dcf_results = self.analyzer.perform_dcf_valuation(raw_data)
# 生成投资建议
investment_recommendation = self.analyzer.generate_investment_recommendation({
'valuation_score': valuation_results.get('valuation_score', 0),
'financial_health_score': health_results.get('health_score', 0),
'growth_score': growth_results.get('growth_score', 0),
'risk_score': risk_results.get('risk_score', 0)
})
# 整合分析结果
analysis_results = {
'symbol': symbol,
'analysis_date': datetime.now().isoformat(),
'valuation_metrics': valuation_results,
'financial_health': health_results,
'growth_metrics': growth_results,
'risk_metrics': risk_results,
'dcf_analysis': dcf_results,
'investment_recommendation': investment_recommendation,
'overall_score': investment_recommendation.get('overall_score', 0),
'recommendation': investment_recommendation.get('recommendation', 'N/A')
}
# 保存分析结果到数据库
self.database.save_analysis_result(symbol, analysis_results)
# 3. 报告生成阶段
logger.info(f"正在生成报告: {symbol}")
# 生成详细报告
report_file = self.report_generator.generate_comprehensive_report(
symbol, raw_data, analysis_results
)
# 生成简要报告
summary_file = self.report_generator.generate_summary_report(
symbol, analysis_results
)
# 4. 保存标准化分析报告
logger.info(f"正在保存分析报告: {symbol}")
storage_result = self.storage.save_analysis_report(symbol, {
'company_info': raw_data.get('company_info', {}),
'financial_data': raw_data.get('financial_statements', {}),
'valuation_analysis': analysis_results.get('valuation_metrics', {}),
'financial_health': analysis_results.get('financial_health', {}),
'growth_analysis': analysis_results.get('growth_metrics', {}),
'risk_analysis': analysis_results.get('risk_metrics', {}),
'investment_recommendation': analysis_results.get('investment_recommendation', {}),
'analyst_opinions': raw_data.get('analyst_recommendations', {}),
'market_data': raw_data.get('stock_prices', {}),
'raw_data': raw_data
})
analysis_results['report_file'] = report_file
analysis_results['summary_file'] = summary_file
analysis_results['storage_files'] = storage_result
logger.info(f"分析完成: {symbol}")
return analysis_results
except Exception as e:
logger.error(f"分析股票失败 {symbol}: {e}")
return {'error': str(e)}
def quick_screening(self, symbol: str) -> Dict:
"""
快速筛选 - 检查是否符合低价值投资标准
Args:
symbol: 股票代码
Returns:
筛选结果
"""
try:
logger.info(f"快速筛选: {symbol}")
# 获取基础数据
raw_data = self.data_collector.collect_all_data(symbol)
if not raw_data.get('company_info'):
return {'error': '无法获取公司信息', 'passes_screening': False}
# 基础筛选条件
company_info = raw_data['company_info']
key_metrics = raw_data.get('key_metrics', {})
screening_results = {
'symbol': symbol,
'company_name': company_info.get('name', ''),
'market_cap': company_info.get('market_cap', 0),
'passes_screening': True,
'criteria_met': [],
'criteria_failed': []
}
# 检查市值
if company_info.get('market_cap', 0) >= LOW_VALUE_CRITERIA['min_market_cap']:
screening_results['criteria_met'].append('市值符合要求')
else:
screening_results['criteria_failed'].append('市值过小')
screening_results['passes_screening'] = False
# 检查PE比率
pe_ratio = key_metrics.get('pe_ratio', 0)
if 0 < pe_ratio <= LOW_VALUE_CRITERIA['max_pe_ratio']:
screening_results['criteria_met'].append(f'PE比率合理 ({pe_ratio:.2f})')
elif pe_ratio > LOW_VALUE_CRITERIA['max_pe_ratio']:
screening_results['criteria_failed'].append(f'PE比率过高 ({pe_ratio:.2f})')
screening_results['passes_screening'] = False
# 检查PB比率
pb_ratio = key_metrics.get('pb_ratio', 0)
if 0 < pb_ratio <= LOW_VALUE_CRITERIA['max_pb_ratio']:
screening_results['criteria_met'].append(f'PB比率合理 ({pb_ratio:.2f})')
elif pb_ratio > LOW_VALUE_CRITERIA['max_pb_ratio']:
screening_results['criteria_failed'].append(f'PB比率过高 ({pb_ratio:.2f})')
screening_results['passes_screening'] = False
# 检查PS比率
ps_ratio = key_metrics.get('ps_ratio', 0)
if 0 < ps_ratio <= LOW_VALUE_CRITERIA['max_ps_ratio']:
screening_results['criteria_met'].append(f'PS比率合理 ({ps_ratio:.2f})')
elif ps_ratio > LOW_VALUE_CRITERIA['max_ps_ratio']:
screening_results['criteria_failed'].append(f'PS比率过高 ({ps_ratio:.2f})')
screening_results['passes_screening'] = False
return screening_results
except Exception as e:
logger.error(f"快速筛选失败 {symbol}: {e}")
return {'error': str(e), 'passes_screening': False}
def batch_analysis(self, symbols: list) -> Dict:
"""
批量分析多只股票
Args:
symbols: 股票代码列表
Returns:
批量分析结果
"""
results = {}
for symbol in symbols:
logger.info(f"批量分析: {symbol}")
results[symbol] = self.analyze_stock(symbol)
return results
def main():
"""主函数 - 命令行接口"""
if len(sys.argv) < 2:
print("使用方法:")
print("python main.py <股票代码> [--refresh]")
print("python main.py --batch <股票代码1,股票代码2,...>")
print("python main.py --screen <股票代码>")
return
system = StockAnalysisSystem()
if sys.argv[1] == '--batch':
# 批量分析
if len(sys.argv) < 3:
print("请提供股票代码列表")
return
symbols = [s.strip() for s in sys.argv[2].split(',')]
results = system.batch_analysis(symbols)
print("\n=== 批量分析结果 ===")
for symbol, result in results.items():
if 'error' in result:
print(f"{symbol}: 分析失败 - {result['error']}")
else:
print(f"{symbol}: {result.get('recommendation', 'N/A')} (评分: {result.get('overall_score', 0):.1f})")
elif sys.argv[1] == '--screen':
# 快速筛选
if len(sys.argv) < 3:
print("请提供股票代码")
return
symbol = sys.argv[2].upper()
result = system.quick_screening(symbol)
print(f"\n=== {symbol} 快速筛选结果 ===")
if 'error' in result:
print(f"筛选失败: {result['error']}")
else:
print(f"公司名称: {result.get('company_name', 'N/A')}")
print(f"市值: ${result.get('market_cap', 0):,.0f}")
print(f"通过筛选: {'' if result.get('passes_screening') else ''}")
if result.get('criteria_met'):
print("符合条件:")
for criteria in result['criteria_met']:
print(f"{criteria}")
if result.get('criteria_failed'):
print("不符合条件:")
for criteria in result['criteria_failed']:
print(f"{criteria}")
else:
# 单只股票分析
symbol = sys.argv[1].upper()
force_refresh = '--refresh' in sys.argv
print(f"正在分析股票: {symbol}")
result = system.analyze_stock(symbol, force_refresh)
if 'error' in result:
print(f"分析失败: {result['error']}")
else:
print(f"\n=== {symbol} 分析结果 ===")
print(f"投资建议: {result.get('recommendation', 'N/A')}")
print(f"综合评分: {result.get('overall_score', 0):.1f}/100")
recommendation = result.get('investment_recommendation', {})
if recommendation.get('key_strengths'):
print("关键优势:")
for strength in recommendation['key_strengths']:
print(f"{strength}")
if recommendation.get('key_concerns'):
print("关键担忧:")
for concern in recommendation['key_concerns']:
print(f"{concern}")
if result.get('report_file'):
print(f"\n详细报告已生成: {result['report_file']}")
if result.get('summary_file'):
print(f"简要报告已生成: {result['summary_file']}")
if __name__ == "__main__":
main()

425
report_generator.py Normal file
View File

@@ -0,0 +1,425 @@
"""
报告生成器 - 生成综合分析报告
"""
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
import os
from datetime import datetime
from typing import Dict, List
import logging
logger = logging.getLogger(__name__)
class ReportGenerator:
def __init__(self, output_dir: str = "reports"):
self.output_dir = output_dir
self.chart_dir = os.path.join(output_dir, "charts")
# 创建输出目录
os.makedirs(self.output_dir, exist_ok=True)
os.makedirs(self.chart_dir, exist_ok=True)
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
def generate_comprehensive_report(self, symbol: str, data: Dict, analysis_results: Dict) -> str:
"""生成综合分析报告"""
try:
# 生成图表
chart_files = self._generate_charts(symbol, data, analysis_results)
# 生成PDF报告
report_file = self._generate_pdf_report(symbol, data, analysis_results, chart_files)
logger.info(f"报告生成完成: {report_file}")
return report_file
except Exception as e:
logger.error(f"生成报告失败: {e}")
return ""
def _generate_charts(self, symbol: str, data: Dict, analysis_results: Dict) -> List[str]:
"""生成图表"""
chart_files = []
try:
# 股价走势图
price_chart = self._create_price_chart(symbol, data)
if price_chart:
chart_files.append(price_chart)
# 财务指标雷达图
radar_chart = self._create_radar_chart(symbol, analysis_results)
if radar_chart:
chart_files.append(radar_chart)
# 估值对比图
valuation_chart = self._create_valuation_chart(symbol, analysis_results)
if valuation_chart:
chart_files.append(valuation_chart)
# 财务健康度仪表盘
health_gauge = self._create_health_gauge(symbol, analysis_results)
if health_gauge:
chart_files.append(health_gauge)
except Exception as e:
logger.error(f"生成图表失败: {e}")
return chart_files
def _create_price_chart(self, symbol: str, data: Dict) -> str:
"""创建股价走势图"""
try:
stock_prices = data.get('stock_prices', pd.DataFrame())
if stock_prices.empty:
return ""
fig = go.Figure()
# 添加收盘价线
fig.add_trace(go.Scatter(
x=stock_prices['date'],
y=stock_prices['close'],
mode='lines',
name='收盘价',
line=dict(color='#1f77b4', width=2)
))
# 添加移动平均线
if len(stock_prices) >= 20:
stock_prices['MA20'] = stock_prices['close'].rolling(window=20).mean()
fig.add_trace(go.Scatter(
x=stock_prices['date'],
y=stock_prices['MA20'],
mode='lines',
name='20日均线',
line=dict(color='orange', width=1, dash='dash')
))
fig.update_layout(
title=f'{symbol} 股价走势图',
xaxis_title='日期',
yaxis_title='价格 (USD)',
template='plotly_white',
height=400
)
chart_file = os.path.join(self.chart_dir, f'{symbol}_price_chart.html')
fig.write_html(chart_file)
return chart_file
except Exception as e:
logger.error(f"创建股价图表失败: {e}")
return ""
def _create_radar_chart(self, symbol: str, analysis_results: Dict) -> str:
"""创建雷达图"""
try:
categories = ['估值吸引力', '财务健康度', '成长性', '风险控制']
values = [
analysis_results.get('valuation_score', 0),
analysis_results.get('financial_health_score', 0),
analysis_results.get('growth_score', 0),
analysis_results.get('risk_score', 0)
]
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=values,
theta=categories,
fill='toself',
name=symbol,
line_color='blue'
))
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 100]
)),
showlegend=True,
title=f'{symbol} 综合评分雷达图',
height=400
)
chart_file = os.path.join(self.chart_dir, f'{symbol}_radar_chart.html')
fig.write_html(chart_file)
return chart_file
except Exception as e:
logger.error(f"创建雷达图失败: {e}")
return ""
def _create_valuation_chart(self, symbol: str, analysis_results: Dict) -> str:
"""创建估值对比图"""
try:
valuation_data = analysis_results.get('valuation_metrics', {})
metrics = ['PE比率', 'PB比率', 'PS比率', 'PEG比率']
values = [
valuation_data.get('pe_ratio', 0),
valuation_data.get('pb_ratio', 0),
valuation_data.get('ps_ratio', 0),
valuation_data.get('peg_ratio', 0)
]
# 行业平均值(示例数据)
industry_avg = [15, 2.5, 3.0, 1.2]
fig = go.Figure()
fig.add_trace(go.Bar(
name=symbol,
x=metrics,
y=values,
marker_color='lightblue'
))
fig.add_trace(go.Bar(
name='行业平均',
x=metrics,
y=industry_avg,
marker_color='lightcoral'
))
fig.update_layout(
title=f'{symbol} 估值指标对比',
xaxis_title='指标',
yaxis_title='数值',
barmode='group',
height=400
)
chart_file = os.path.join(self.chart_dir, f'{symbol}_valuation_chart.html')
fig.write_html(chart_file)
return chart_file
except Exception as e:
logger.error(f"创建估值图表失败: {e}")
return ""
def _create_health_gauge(self, symbol: str, analysis_results: Dict) -> str:
"""创建财务健康度仪表盘"""
try:
health_score = analysis_results.get('financial_health_score', 0)
fig = go.Figure(go.Indicator(
mode = "gauge+number+delta",
value = health_score,
domain = {'x': [0, 1], 'y': [0, 1]},
title = {'text': f"{symbol} 财务健康度"},
delta = {'reference': 50},
gauge = {
'axis': {'range': [None, 100]},
'bar': {'color': "darkblue"},
'steps': [
{'range': [0, 40], 'color': "lightgray"},
{'range': [40, 70], 'color': "yellow"},
{'range': [70, 100], 'color': "green"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 90
}
}
))
fig.update_layout(height=300)
chart_file = os.path.join(self.chart_dir, f'{symbol}_health_gauge.html')
fig.write_html(chart_file)
return chart_file
except Exception as e:
logger.error(f"创建健康度仪表盘失败: {e}")
return ""
def _generate_pdf_report(self, symbol: str, data: Dict, analysis_results: Dict, chart_files: List[str]) -> str:
"""生成PDF报告"""
try:
report_file = os.path.join(self.output_dir, f'{symbol}_analysis_report.pdf')
doc = SimpleDocTemplate(report_file, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# 标题样式
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontSize=18,
spaceAfter=30,
alignment=TA_CENTER,
textColor=colors.darkblue
)
# 添加标题
story.append(Paragraph(f"{symbol} 股票分析报告", title_style))
story.append(Spacer(1, 20))
# 基本信息
company_info = data.get('company_info', {})
story.append(Paragraph("基本信息", styles['Heading2']))
basic_info_data = [
['公司名称', company_info.get('name', 'N/A')],
['行业', company_info.get('industry', 'N/A')],
['市值', f"${company_info.get('market_cap', 0):,.0f}"],
['员工数', f"{company_info.get('employees', 0):,}"],
['分析日期', datetime.now().strftime('%Y-%m-%d %H:%M:%S')]
]
basic_info_table = Table(basic_info_data, colWidths=[2*inch, 4*inch])
basic_info_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 12),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(basic_info_table)
story.append(Spacer(1, 20))
# 分析结果
story.append(Paragraph("分析结果", styles['Heading2']))
# 评分表格
scores_data = [
['分析维度', '评分', '评级'],
['估值吸引力', f"{analysis_results.get('valuation_score', 0):.1f}/100", self._get_rating(analysis_results.get('valuation_score', 0))],
['财务健康度', f"{analysis_results.get('financial_health_score', 0):.1f}/100", self._get_rating(analysis_results.get('financial_health_score', 0))],
['成长性', f"{analysis_results.get('growth_score', 0):.1f}/100", self._get_rating(analysis_results.get('growth_score', 0))],
['风险控制', f"{analysis_results.get('risk_score', 0):.1f}/100", self._get_rating(analysis_results.get('risk_score', 0))],
['综合评分', f"{analysis_results.get('overall_score', 0):.1f}/100", self._get_rating(analysis_results.get('overall_score', 0))]
]
scores_table = Table(scores_data, colWidths=[2*inch, 1.5*inch, 1.5*inch])
scores_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 12),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
story.append(scores_table)
story.append(Spacer(1, 20))
# 投资建议
recommendation = analysis_results.get('investment_recommendation', {})
story.append(Paragraph("投资建议", styles['Heading2']))
story.append(Paragraph(f"<b>建议:</b> {recommendation.get('recommendation', 'N/A')}", styles['Normal']))
story.append(Paragraph(f"<b>信心度:</b> {recommendation.get('confidence', 'N/A')}", styles['Normal']))
story.append(Paragraph(f"<b>综合评分:</b> {recommendation.get('overall_score', 0):.1f}/100", styles['Normal']))
# 关键优势
strengths = recommendation.get('key_strengths', [])
if strengths:
story.append(Spacer(1, 10))
story.append(Paragraph("关键优势:", styles['Heading3']))
for strength in strengths:
story.append(Paragraph(f"{strength}", styles['Normal']))
# 关键担忧
concerns = recommendation.get('key_concerns', [])
if concerns:
story.append(Spacer(1, 10))
story.append(Paragraph("关键担忧:", styles['Heading3']))
for concern in concerns:
story.append(Paragraph(f"{concern}", styles['Normal']))
# 风险提示
risk_warnings = recommendation.get('risk_warnings', [])
if risk_warnings:
story.append(Spacer(1, 10))
story.append(Paragraph("风险提示:", styles['Heading3']))
for warning in risk_warnings:
story.append(Paragraph(f"⚠️ {warning}", styles['Normal']))
story.append(Spacer(1, 20))
# 免责声明
story.append(Paragraph("免责声明", styles['Heading2']))
disclaimer = """
本报告仅供投资参考,不构成投资建议。投资有风险,入市需谨慎。
投资者应根据自身情况做出投资决策,并承担相应风险。
本报告基于公开信息分析,可能存在信息滞后或不准确的情况。
"""
story.append(Paragraph(disclaimer, styles['Normal']))
# 构建PDF
doc.build(story)
return report_file
except Exception as e:
logger.error(f"生成PDF报告失败: {e}")
return ""
def _get_rating(self, score: float) -> str:
"""根据分数获取评级"""
if score >= 90:
return "优秀"
elif score >= 80:
return "良好"
elif score >= 70:
return "中等"
elif score >= 60:
return "一般"
else:
return "较差"
def generate_summary_report(self, symbol: str, analysis_results: Dict) -> str:
"""生成简要报告(文本格式)"""
try:
recommendation = analysis_results.get('investment_recommendation', {})
summary = f"""
=== {symbol} 股票分析摘要 ===
📊 综合评分: {analysis_results.get('overall_score', 0):.1f}/100
💡 投资建议: {recommendation.get('recommendation', 'N/A')}
🎯 信心度: {recommendation.get('confidence', 'N/A')}
📈 各维度评分:
• 估值吸引力: {analysis_results.get('valuation_score', 0):.1f}/100
• 财务健康度: {analysis_results.get('financial_health_score', 0):.1f}/100
• 成长性: {analysis_results.get('growth_score', 0):.1f}/100
• 风险控制: {analysis_results.get('risk_score', 0):.1f}/100
✅ 关键优势:
{chr(10).join([f"{s}" for s in recommendation.get('key_strengths', [])])}
⚠️ 关键担忧:
{chr(10).join([f"{c}" for c in recommendation.get('key_concerns', [])])}
🚨 风险提示:
{chr(10).join([f"{w}" for w in recommendation.get('risk_warnings', [])])}
分析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
# 保存简要报告
summary_file = os.path.join(self.output_dir, f'{symbol}_summary.txt')
with open(summary_file, 'w', encoding='utf-8') as f:
f.write(summary)
return summary_file
except Exception as e:
logger.error(f"生成简要报告失败: {e}")
return ""

11
requirements.txt Normal file
View File

@@ -0,0 +1,11 @@
yfinance==0.2.28
pandas==2.1.4
numpy==1.24.3
matplotlib==3.7.2
seaborn==0.12.2
plotly==5.17.0
requests==2.31.0
beautifulsoup4==4.12.2
reportlab==4.0.7
python-dotenv==1.0.0
schedule==1.2.0

182
run_analysis.py Normal file
View File

@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""
美股分析系统启动脚本
提供交互式界面进行股票分析
"""
import sys
import os
from main import StockAnalysisSystem
def print_banner():
"""打印系统横幅"""
banner = """
╔══════════════════════════════════════════════════════════════╗
║ 美股低价值公司分析系统 ║
║ US Stock Value Analysis ║
╚══════════════════════════════════════════════════════════════╝
"""
print(banner)
def print_menu():
"""打印主菜单"""
menu = """
请选择操作:
1. 分析单只股票 (完整分析)
2. 快速筛选股票 (检查是否符合低价值标准)
3. 批量分析多只股票
4. 查看历史分析结果
5. 系统测试
6. 退出
请输入选项 (1-6): """
return input(menu).strip()
def analyze_single_stock():
"""分析单只股票"""
symbol = input("请输入股票代码 (如: AAPL): ").strip().upper()
if not symbol:
print("❌ 股票代码不能为空")
return
print(f"\n🔍 正在分析 {symbol}...")
print("⏳ 这可能需要2-3分钟请耐心等待...")
system = StockAnalysisSystem()
result = system.analyze_stock(symbol)
if 'error' in result:
print(f"❌ 分析失败: {result['error']}")
else:
print(f"\n{symbol} 分析完成!")
print(f"📊 投资建议: {result.get('recommendation', 'N/A')}")
print(f"🎯 综合评分: {result.get('overall_score', 0):.1f}/100")
recommendation = result.get('investment_recommendation', {})
if recommendation.get('key_strengths'):
print("\n💪 关键优势:")
for strength in recommendation['key_strengths']:
print(f"{strength}")
if recommendation.get('key_concerns'):
print("\n⚠️ 关键担忧:")
for concern in recommendation['key_concerns']:
print(f"{concern}")
if result.get('report_file'):
print(f"\n📄 详细报告: {result['report_file']}")
if result.get('summary_file'):
print(f"📝 简要报告: {result['summary_file']}")
def quick_screen_stock():
"""快速筛选股票"""
symbol = input("请输入股票代码 (如: AAPL): ").strip().upper()
if not symbol:
print("❌ 股票代码不能为空")
return
print(f"\n🔍 正在快速筛选 {symbol}...")
system = StockAnalysisSystem()
result = system.quick_screening(symbol)
if 'error' in result:
print(f"❌ 筛选失败: {result['error']}")
else:
print(f"\n📋 {symbol} 筛选结果:")
print(f"🏢 公司名称: {result.get('company_name', 'N/A')}")
print(f"💰 市值: ${result.get('market_cap', 0):,.0f}")
if result.get('passes_screening'):
print("✅ 通过低价值投资筛选")
else:
print("❌ 未通过低价值投资筛选")
if result.get('criteria_met'):
print("\n✅ 符合条件:")
for criteria in result['criteria_met']:
print(f"{criteria}")
if result.get('criteria_failed'):
print("\n❌ 不符合条件:")
for criteria in result['criteria_failed']:
print(f"{criteria}")
def batch_analyze_stocks():
"""批量分析股票"""
symbols_input = input("请输入股票代码,用逗号分隔 (如: AAPL,MSFT,GOOGL): ").strip()
if not symbols_input:
print("❌ 股票代码不能为空")
return
symbols = [s.strip().upper() for s in symbols_input.split(',')]
print(f"\n🔍 正在批量分析 {len(symbols)} 只股票...")
print("⏳ 这可能需要较长时间,请耐心等待...")
system = StockAnalysisSystem()
results = system.batch_analysis(symbols)
print(f"\n📊 批量分析结果:")
print("=" * 60)
for symbol, result in results.items():
if 'error' in result:
print(f"{symbol}: {result['error']}")
else:
recommendation = result.get('recommendation', 'N/A')
score = result.get('overall_score', 0)
print(f"📈 {symbol}: {recommendation} (评分: {score:.1f})")
def view_history():
"""查看历史分析结果"""
print("\n📚 历史分析结果功能开发中...")
print("💡 提示: 分析结果已保存在数据库中,详细报告在 reports/ 目录下")
def run_system_test():
"""运行系统测试"""
print("\n🧪 正在运行系统测试...")
try:
from test_system import main as test_main
test_main()
except ImportError:
print("❌ 测试模块未找到")
except Exception as e:
print(f"❌ 测试失败: {e}")
def main():
"""主函数"""
print_banner()
while True:
try:
choice = print_menu()
if choice == '1':
analyze_single_stock()
elif choice == '2':
quick_screen_stock()
elif choice == '3':
batch_analyze_stocks()
elif choice == '4':
view_history()
elif choice == '5':
run_system_test()
elif choice == '6':
print("\n👋 感谢使用美股分析系统!")
break
else:
print("❌ 无效选项,请重新选择")
input("\n按回车键继续...")
print("\n" + "="*60)
except KeyboardInterrupt:
print("\n\n👋 程序已退出")
break
except Exception as e:
print(f"\n❌ 发生错误: {e}")
input("按回车键继续...")
if __name__ == "__main__":
main()

BIN
stock_analysis.db Normal file

Binary file not shown.

165
test_system.py Normal file
View File

@@ -0,0 +1,165 @@
"""
系统测试脚本
"""
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from main import StockAnalysisSystem
import logging
# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def test_quick_screening():
"""测试快速筛选功能"""
print("=== 测试快速筛选功能 ===")
system = StockAnalysisSystem()
# 测试一些知名股票
test_symbols = ['AAPL', 'MSFT', 'GOOGL', 'TSLA', 'NVDA']
for symbol in test_symbols:
print(f"\n正在测试 {symbol}...")
try:
result = system.quick_screening(symbol)
if 'error' in result:
print(f"{symbol}: {result['error']}")
else:
status = "✅ 通过" if result.get('passes_screening') else "❌ 未通过"
print(f"{status} {symbol}: {result.get('company_name', 'N/A')}")
if result.get('criteria_met'):
print(" 符合条件:")
for criteria in result['criteria_met']:
print(f"{criteria}")
if result.get('criteria_failed'):
print(" 不符合条件:")
for criteria in result['criteria_failed']:
print(f"{criteria}")
except Exception as e:
print(f"{symbol}: 测试失败 - {e}")
def test_data_collection():
"""测试数据收集功能"""
print("\n=== 测试数据收集功能 ===")
system = StockAnalysisSystem()
# 测试AAPL数据收集
symbol = 'AAPL'
print(f"正在收集 {symbol} 数据...")
try:
raw_data = system.data_collector.collect_all_data(symbol)
if raw_data.get('company_info'):
company_info = raw_data['company_info']
print(f"✅ 公司信息: {company_info.get('name', 'N/A')}")
print(f" 行业: {company_info.get('industry', 'N/A')}")
print(f" 市值: ${company_info.get('market_cap', 0):,.0f}")
else:
print("❌ 无法获取公司信息")
if not raw_data['stock_prices'].empty:
print(f"✅ 股价数据: {len(raw_data['stock_prices'])} 条记录")
else:
print("❌ 无法获取股价数据")
if raw_data['financial_statements']:
print(f"✅ 财务数据: {len(raw_data['financial_statements'])} 个期间")
else:
print("❌ 无法获取财务数据")
key_metrics = raw_data.get('key_metrics', {})
if key_metrics:
print(f"✅ 关键指标: PE={key_metrics.get('pe_ratio', 0):.2f}, PB={key_metrics.get('pb_ratio', 0):.2f}")
else:
print("❌ 无法获取关键指标")
except Exception as e:
print(f"❌ 数据收集失败: {e}")
def test_analysis_engine():
"""测试分析引擎"""
print("\n=== 测试分析引擎 ===")
system = StockAnalysisSystem()
# 先收集数据
symbol = 'AAPL'
print(f"正在分析 {symbol}...")
try:
raw_data = system.data_collector.collect_all_data(symbol)
if not raw_data.get('company_info'):
print("❌ 无法获取数据进行分析")
return
# 测试各个分析模块
print(" 估值分析...")
valuation = system.analyzer.calculate_valuation_metrics(raw_data)
print(f" 估值评分: {valuation.get('valuation_score', 0):.1f}")
print(" 财务健康度分析...")
health = system.analyzer.calculate_financial_health(raw_data)
print(f" 健康度评分: {health.get('health_score', 0):.1f}")
print(" 成长性分析...")
growth = system.analyzer.calculate_growth_metrics(raw_data)
print(f" 成长性评分: {growth.get('growth_score', 0):.1f}")
print(" 风险分析...")
risk = system.analyzer.calculate_risk_metrics(raw_data)
print(f" 风险评分: {risk.get('risk_score', 0):.1f}")
print(" DCF估值...")
dcf = system.analyzer.perform_dcf_valuation(raw_data)
print(f" DCF价值: ${dcf.get('dcf_value', 0):,.2f}")
print(" 生成投资建议...")
recommendation = system.analyzer.generate_investment_recommendation({
'valuation_score': valuation.get('valuation_score', 0),
'financial_health_score': health.get('health_score', 0),
'growth_score': growth.get('growth_score', 0),
'risk_score': risk.get('risk_score', 0)
})
print(f" 投资建议: {recommendation.get('recommendation', 'N/A')}")
print(f" 综合评分: {recommendation.get('overall_score', 0):.1f}")
print("✅ 分析引擎测试完成")
except Exception as e:
print(f"❌ 分析引擎测试失败: {e}")
def main():
"""运行所有测试"""
print("🚀 开始系统测试...")
try:
# 测试数据收集
test_data_collection()
# 测试快速筛选
test_quick_screening()
# 测试分析引擎
test_analysis_engine()
print("\n✅ 所有测试完成!")
print("\n💡 提示:")
print("- 如果看到网络错误,请检查网络连接")
print("- 如果看到数据获取失败可能是API限制或股票代码错误")
print("- 系统已准备就绪,可以使用 python main.py <股票代码> 进行分析")
except Exception as e:
print(f"❌ 测试过程中出现错误: {e}")
if __name__ == "__main__":
main()

183
view_analysis.py Normal file
View File

@@ -0,0 +1,183 @@
#!/usr/bin/env python3
"""
查看分析历史工具
"""
import os
import sys
import json
from datetime import datetime
from analysis_storage import AnalysisStorage
def print_banner():
"""打印横幅"""
banner = """
╔══════════════════════════════════════════════════════════════╗
║ 分析历史查看工具 ║
║ Analysis History Viewer ║
╚══════════════════════════════════════════════════════════════╝
"""
print(banner)
def list_all_analyses():
"""列出所有分析记录"""
storage = AnalysisStorage()
analyses = storage.list_all_analyses()
if not analyses:
print("📭 暂无分析记录")
return
print(f"📊 共找到 {len(analyses)} 条分析记录:")
print("=" * 80)
for i, analysis in enumerate(analyses, 1):
print(f"{i:2d}. {analysis['symbol']} - {analysis['company_name']}")
print(f" 时间: {analysis['analysis_date']}")
print(f" 建议: {analysis['recommendation']}")
print(f" 评分: {analysis['overall_score']:.1f}/100")
print(f" 文件: {analysis['files']['summary']}")
print("-" * 80)
def view_analysis_details(symbol: str):
"""查看特定股票的分析详情"""
storage = AnalysisStorage()
analysis_data = storage.get_latest_analysis(symbol)
if not analysis_data:
print(f"❌ 未找到 {symbol} 的分析记录")
return
print(f"\n📈 {symbol} 最新分析结果:")
print("=" * 60)
# 基本信息
metadata = analysis_data.get('analysis_metadata', {})
company_info = analysis_data.get('company_info', {})
investment_rec = analysis_data.get('investment_recommendation', {})
print(f"公司名称: {company_info.get('name', 'N/A')}")
print(f"分析时间: {metadata.get('analysis_date', 'N/A')}")
print(f"投资建议: {investment_rec.get('recommendation', 'N/A')}")
print(f"综合评分: {investment_rec.get('overall_score', 0):.1f}/100")
# 各维度评分
valuation = analysis_data.get('valuation_analysis', {})
financial_health = analysis_data.get('financial_health', {})
growth = analysis_data.get('growth_analysis', {})
risk = analysis_data.get('risk_analysis', {})
print(f"\n📊 各维度评分:")
print(f" 估值吸引力: {valuation.get('valuation_score', 0):.1f}/100")
print(f" 财务健康度: {financial_health.get('health_score', 0):.1f}/100")
print(f" 成长性: {growth.get('growth_score', 0):.1f}/100")
print(f" 风险控制: {risk.get('risk_score', 0):.1f}/100")
# 关键优势
strengths = investment_rec.get('key_strengths', [])
if strengths:
print(f"\n✅ 关键优势:")
for strength in strengths:
print(f"{strength}")
# 关键担忧
concerns = investment_rec.get('key_concerns', [])
if concerns:
print(f"\n⚠️ 关键担忧:")
for concern in concerns:
print(f"{concern}")
# 风险提示
warnings = investment_rec.get('risk_warnings', [])
if warnings:
print(f"\n🚨 风险提示:")
for warning in warnings:
print(f"{warning}")
def view_analysis_history(symbol: str):
"""查看特定股票的分析历史"""
storage = AnalysisStorage()
history = storage.get_analysis_history(symbol)
if not history:
print(f"❌ 未找到 {symbol} 的分析历史")
return
print(f"\n📚 {symbol} 分析历史 (共 {len(history)} 条记录):")
print("=" * 80)
for i, record in enumerate(history, 1):
print(f"{i:2d}. 时间: {record['analysis_date']}")
print(f" 建议: {record['recommendation']}")
print(f" 评分: {record['overall_score']:.1f}/100")
print(f" 文件: {record['files']['summary']}")
print("-" * 80)
def export_analysis_summary():
"""导出分析摘要"""
storage = AnalysisStorage()
analyses = storage.list_all_analyses()
if not analyses:
print("📭 暂无分析记录可导出")
return
# 生成摘要报告
summary_file = f"analysis_summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
with open(summary_file, 'w', encoding='utf-8') as f:
f.write("# 股票分析摘要报告\n\n")
f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"分析记录数: {len(analyses)}\n\n")
f.write("## 分析记录列表\n\n")
for i, analysis in enumerate(analyses, 1):
f.write(f"### {i}. {analysis['symbol']} - {analysis['company_name']}\n")
f.write(f"- **分析时间**: {analysis['analysis_date']}\n")
f.write(f"- **投资建议**: {analysis['recommendation']}\n")
f.write(f"- **综合评分**: {analysis['overall_score']:.1f}/100\n")
f.write(f"- **详细报告**: {analysis['files']['summary']}\n\n")
print(f"✅ 分析摘要已导出到: {summary_file}")
def main():
"""主函数"""
if len(sys.argv) < 2:
print("使用方法:")
print("python3 view_analysis.py list # 列出所有分析记录")
print("python3 view_analysis.py view <股票代码> # 查看最新分析结果")
print("python3 view_analysis.py history <股票代码> # 查看分析历史")
print("python3 view_analysis.py export # 导出分析摘要")
return
command = sys.argv[1].lower()
if command == 'list':
print_banner()
list_all_analyses()
elif command == 'view':
if len(sys.argv) < 3:
print("❌ 请提供股票代码")
return
symbol = sys.argv[2].upper()
print_banner()
view_analysis_details(symbol)
elif command == 'history':
if len(sys.argv) < 3:
print("❌ 请提供股票代码")
return
symbol = sys.argv[2].upper()
print_banner()
view_analysis_history(symbol)
elif command == 'export':
print_banner()
export_analysis_summary()
else:
print("❌ 无效命令")
if __name__ == "__main__":
main()

157
使用指南.md Normal file
View File

@@ -0,0 +1,157 @@
# 美股低价值公司分析系统 - 使用指南
## 🎯 系统概述
这是一个专门用于分析美股低价值投资机会的智能分析系统。当您提供公司名称或股票代码时,系统将自动执行以下标准流程:
### 📋 每次分析的标准流程
#### 1. 数据获取阶段 (30秒内)
- ✅ 验证股票代码和基本信息
- ✅ 获取最新股价和交易数据
- ✅ 下载最近4个季度的财务数据
- ✅ 收集关键财务指标
#### 2. 快速筛选阶段 (1分钟内)
- ✅ 计算关键估值指标PE、PB、PS等
- ✅ 检查是否符合"低价值"标准
- ✅ 识别异常财务指标
- ✅ 评估基本投资可行性
#### 3. 深度分析阶段 (2-3分钟)
- ✅ 进行DCF估值计算
- ✅ 分析财务健康度
- ✅ 评估行业地位和竞争优势
- ✅ 计算风险指标
- ✅ 生成多维度评分
#### 4. 报告生成阶段 (1分钟内)
- ✅ 生成综合分析报告
- ✅ 提供投资建议和风险提示
- ✅ 保存分析结果到数据库
- ✅ 创建可视化图表
## 🚀 快速开始
### 方法一:交互式界面(推荐)
```bash
python run_analysis.py
```
### 方法二:命令行直接使用
```bash
# 分析单只股票
python main.py AAPL
# 快速筛选
python main.py --screen AAPL
# 批量分析
python main.py --batch AAPL,MSFT,GOOGL
```
## 📊 分析维度说明
### 1. 估值吸引力 (40%权重)
- **PE比率**: 市盈率 ≤ 15
- **PB比率**: 市净率 ≤ 1.5
- **PS比率**: 市销率 ≤ 2.0
- **PEG比率**: 市盈率相对盈利增长比率 ≤ 1.0
- **DCF估值**: 内在价值计算
### 2. 财务健康度 (30%权重)
- **流动性**: 流动比率 ≥ 1.2
- **偿债能力**: 债务比率 ≤ 60%
- **盈利能力**: ROE ≥ 5%
- **现金流**: 经营现金流分析
### 3. 成长性 (20%权重)
- **收入增长率**: 年收入增长趋势
- **利润增长率**: 净利润增长趋势
- **历史增长**: 3年复合增长率
### 4. 风险控制 (10%权重)
- **Beta系数**: 相对市场波动性
- **波动率**: 价格波动标准差
- **最大回撤**: 历史最大跌幅
## 📈 输出结果说明
### 控制台输出
```
=== AAPL 分析结果 ===
投资建议: 买入
综合评分: 75.2/100
关键优势:
• 估值吸引力高
• 财务健康度良好
• 成长性优秀
关键担忧:
• 风险较高
详细报告已生成: reports/AAPL_analysis_report.pdf
简要报告已生成: reports/AAPL_summary.txt
```
### 文件输出
- **PDF详细报告**: 包含完整分析、图表、投资建议
- **文本摘要**: 关键信息快速查看
- **HTML图表**: 交互式可视化图表
- **数据库记录**: 历史数据和分析结果
## 🎯 投资建议等级
| 评分范围 | 投资建议 | 说明 |
|---------|---------|------|
| 90-100 | 强烈买入 | 优秀投资机会,风险可控 |
| 80-89 | 买入 | 良好投资机会,值得关注 |
| 70-79 | 持有 | 中等投资机会,谨慎考虑 |
| 60-69 | 观望 | 投资价值一般,等待时机 |
| 0-59 | 卖出 | 投资价值较低,建议回避 |
## ⚠️ 重要提示
### 使用前准备
1. **网络连接**: 需要稳定的网络连接获取实时数据
2. **股票代码**: 使用标准美股代码AAPL, MSFT, GOOGL
3. **数据延迟**: Yahoo Finance数据可能存在15-20分钟延迟
### 投资风险提示
- ⚠️ 本系统仅供投资参考,不构成投资建议
- ⚠️ 投资有风险,入市需谨慎
- ⚠️ 建议结合其他数据源进行交叉验证
- ⚠️ 历史表现不代表未来收益
### 系统限制
- 📊 数据来源主要依赖Yahoo Finance
- ⏰ 分析时间单只股票需要2-3分钟
- 💾 存储空间:数据库和报告文件会占用磁盘空间
- 🔄 数据更新建议24小时内不重复分析同一股票
## 🛠️ 故障排除
### 常见问题
1. **网络错误**: 检查网络连接,稍后重试
2. **股票代码错误**: 确认代码正确如AAPL不是apple
3. **数据获取失败**: 可能是API限制等待后重试
4. **分析失败**: 查看日志文件 `stock_analysis.log`
### 系统要求
- Python 3.8+
- 内存至少2GB可用内存
- 磁盘至少500MB可用空间
- 网络:稳定的互联网连接
## 📞 技术支持
如遇到问题,请检查:
1. 查看日志文件了解详细错误信息
2. 确认网络连接正常
3. 验证股票代码格式正确
4. 检查系统依赖是否完整安装
---
**祝您投资顺利!** 🚀📈

166
分析报告使用说明.md Normal file
View File

@@ -0,0 +1,166 @@
# 分析报告存储系统使用说明
## 📁 文件结构
每次分析完成后,系统会自动创建以下文件结构:
```
analysis_reports/
├── detailed/ # 详细分析报告 (JSON格式)
│ └── {股票代码}_detailed_{时间戳}.json
├── summaries/ # 简要分析报告 (Markdown格式)
│ └── {股票代码}_summary_{时间戳}.md
├── charts/ # 图表文件 (HTML格式)
│ └── {股票代码}_*.html
├── raw_data/ # 原始数据 (JSON格式)
│ └── {股票代码}_raw_data_{时间戳}.json
└── analysis_index.json # 分析记录索引
```
## 🔍 查看分析记录
### 1. 列出所有分析记录
```bash
python3 view_analysis.py list
```
### 2. 查看特定股票的最新分析结果
```bash
python3 view_analysis.py view <股票代码>
# 例如: python3 view_analysis.py view RVPH
```
### 3. 查看特定股票的分析历史
```bash
python3 view_analysis.py history <股票代码>
# 例如: python3 view_analysis.py history RVPH
```
### 4. 导出所有分析摘要
```bash
python3 view_analysis.py export
```
## 📊 分析报告格式
### 简要报告 (Markdown格式)
- **文件名**: `{股票代码}_summary_{时间戳}.md`
- **内容**: 包含基本信息、投资建议、各维度评分、关键指标等
- **用途**: 快速查看分析结果,便于阅读和分享
### 详细报告 (JSON格式)
- **文件名**: `{股票代码}_detailed_{时间戳}.json`
- **内容**: 完整的分析数据,包括所有计算过程和原始数据
- **用途**: 程序化处理、深度分析、数据挖掘
### 原始数据 (JSON格式)
- **文件名**: `{股票代码}_raw_data_{时间戳}.json`
- **内容**: 从API获取的原始数据
- **用途**: 数据备份、重新分析、审计
## 🎯 分析维度说明
### 1. 估值吸引力 (40%权重)
- **PE比率**: 市盈率
- **PB比率**: 市净率
- **PS比率**: 市销率
- **PEG比率**: 市盈率相对盈利增长比率
- **DCF估值**: 内在价值计算
### 2. 财务健康度 (30%权重)
- **流动比率**: 流动资产/流动负债
- **速动比率**: (流动资产-存货)/流动负债
- **债务比率**: 总负债/总资产
- **ROE**: 净资产收益率
- **ROA**: 总资产收益率
### 3. 成长性 (20%权重)
- **收入增长率**: 年收入增长趋势
- **利润增长率**: 净利润增长趋势
- **历史增长率**: 3年复合增长率
### 4. 风险控制 (10%权重)
- **Beta系数**: 相对市场波动性
- **波动率**: 价格波动标准差
- **最大回撤**: 历史最大跌幅
## 📈 投资建议等级
| 评分范围 | 投资建议 | 说明 |
|---------|---------|------|
| 90-100 | 强烈买入 | 优秀投资机会,风险可控 |
| 80-89 | 买入 | 良好投资机会,值得关注 |
| 70-79 | 持有 | 中等投资机会,谨慎考虑 |
| 60-69 | 观望 | 投资价值一般,等待时机 |
| 0-59 | 卖出 | 投资价值较低,建议回避 |
## 🔧 系统功能
### 自动存储
- 每次分析完成后自动保存到标准化格式
- 按时间戳组织文件,便于管理
- 自动更新索引文件
### 历史追踪
- 记录所有分析历史
- 支持按股票代码查看历史
- 支持导出分析摘要
### 多格式输出
- Markdown格式便于阅读
- JSON格式便于程序处理
- 支持图表可视化
## 📝 使用示例
### 分析新股票
```bash
# 分析单只股票
python3 main.py AAPL
# 快速筛选
python3 main.py --screen AAPL
# 批量分析
python3 main.py --batch AAPL,MSFT,GOOGL
```
### 查看分析结果
```bash
# 查看所有分析记录
python3 view_analysis.py list
# 查看AAPL的最新分析
python3 view_analysis.py view AAPL
# 查看AAPL的分析历史
python3 view_analysis.py history AAPL
```
### 导出报告
```bash
# 导出所有分析摘要
python3 view_analysis.py export
```
## ⚠️ 注意事项
1. **文件管理**: 分析报告会占用磁盘空间,建议定期清理旧文件
2. **数据备份**: 重要分析结果建议备份到其他位置
3. **版本控制**: 每次分析都会生成新文件,不会覆盖历史记录
4. **权限管理**: 确保有足够的文件读写权限
## 🚀 高级功能
### 自定义分析报告
可以修改 `analysis_storage.py` 中的 `_generate_markdown_summary` 方法来自定义报告格式。
### 批量导出
可以编写脚本批量导出特定时间段或特定股票的分析报告。
### 数据分析
可以使用详细报告中的JSON数据进行进一步的数据分析和可视化。
---
**系统已准备就绪!** 每次分析完成后,您都可以通过上述命令查看和管理分析报告。