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

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()