skills/financial-report-writing/scripts/analysis_engine.py

470 lines
16 KiB
Python
Raw Permalink Normal View History

"""
投资分析引擎
提供基本面分析技术面分析和估值分析功能
"""
import pandas as pd
import numpy as np
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum
class InvestmentRating(Enum):
"""投资评级"""
BUY = "买入"
ACCUMULATE = "增持"
HOLD = "持有"
REDUCE = "减持"
SELL = "卖出"
class RiskLevel(Enum):
"""风险等级"""
LOW = "低风险"
LOW_MEDIUM = "中低风险"
MEDIUM = "中等风险"
MEDIUM_HIGH = "中高风险"
HIGH = "高风险"
@dataclass
class TechnicalAnalysis:
"""技术分析结果"""
trend: str # 趋势判断
support_level: float # 支撑位
resistance_level: float # 阻力位
volatility: float # 波动率
ma_status: str # 均线状态
technical_summary: str # 技术总结
@dataclass
class FundamentalAnalysis:
"""基本面分析结果"""
roe: float # 净资产收益率
roe_comment: str # ROE评价
pe_ttm: float # 市盈率TTM
pb_lf: float # 市净率LF
valuation_comment: str # 估值评价
fundamental_summary: str # 基本面总结
@dataclass
class InvestmentRecommendation:
"""投资建议"""
rating: InvestmentRating
target_price: float
risk_level: RiskLevel
recommendation_text: str
key_factors: List[str]
class AnalysisEngine:
"""
投资分析引擎
"""
# ROE评价标准
ROE_THRESHOLDS = {
'excellent': 20,
'good': 15,
'average': 10,
'poor': 5
}
# PE评价标准参考值实际应根据行业调整
PE_THRESHOLDS = {
'undervalued': 15,
'fair': 25,
'overvalued': 35
}
# PB评价标准
PB_THRESHOLDS = {
'undervalued': 1.5,
'fair': 3.0,
'overvalued': 5.0
}
def __init__(self, price_df: pd.DataFrame, financial_data: Dict):
"""
初始化分析引擎
Args:
price_df: 价格数据DataFrame
financial_data: 财务数据字典
"""
self.price_df = price_df.copy()
self.financial_data = financial_data
# 计算涨跌幅和收益率
if 'pct_chg' not in self.price_df.columns and 'close' in self.price_df.columns:
self.price_df['pct_chg'] = self.price_df['close'].pct_change() * 100
def analyze_technical(self) -> TechnicalAnalysis:
"""
技术分析
Returns:
TechnicalAnalysis对象
"""
df = self.price_df
# 计算均线
if 'ma5' not in df.columns:
df['ma5'] = df['close'].rolling(window=5).mean()
if 'ma10' not in df.columns:
df['ma10'] = df['close'].rolling(window=10).mean()
if 'ma20' not in df.columns:
df['ma20'] = df['close'].rolling(window=20).mean()
latest = df.iloc[-1]
# 趋势判断
if latest['close'] > latest['ma5'] > latest['ma10'] > latest['ma20']:
trend = "强势上涨"
elif latest['close'] > latest['ma5'] > latest['ma10']:
trend = "震荡上行"
elif latest['close'] < latest['ma5'] < latest['ma10'] < latest['ma20']:
trend = "强势下跌"
elif latest['close'] < latest['ma5'] < latest['ma10']:
trend = "震荡下行"
else:
trend = "震荡整理"
# 计算支撑位和阻力位(基于近期高低点)
recent_data = df.tail(20)
support_level = recent_data['low'].min()
resistance_level = recent_data['high'].max()
# 计算波动率
returns = df['close'].pct_change().dropna()
volatility = returns.std() * np.sqrt(52) * 100 # 年化波动率
# 均线状态
if latest['ma5'] > latest['ma20']:
ma_status = "多头排列"
elif latest['ma5'] < latest['ma20']:
ma_status = "空头排列"
else:
ma_status = "均线粘合"
# 技术总结
if trend in ["强势上涨", "震荡上行"] and ma_status == "多头排列":
technical_summary = "技术面呈上升趋势,建议关注回调买入机会。"
elif trend in ["强势下跌", "震荡下行"]:
technical_summary = "技术面呈下降趋势,建议观望或减仓。"
else:
technical_summary = "技术面呈震荡格局,建议高抛低吸。"
return TechnicalAnalysis(
trend=trend,
support_level=round(support_level, 2),
resistance_level=round(resistance_level, 2),
volatility=round(volatility, 2),
ma_status=ma_status,
technical_summary=technical_summary
)
def analyze_fundamental(self) -> FundamentalAnalysis:
"""
基本面分析
Returns:
FundamentalAnalysis对象
"""
# 获取ROE
roe = self.financial_data.get('roe_wgt', 0) or self.financial_data.get('roe', 0)
# ROE评价
if roe >= self.ROE_THRESHOLDS['excellent']:
roe_comment = "ROE表现优异公司盈利能力强劲"
elif roe >= self.ROE_THRESHOLDS['good']:
roe_comment = "ROE表现良好盈利能力较强"
elif roe >= self.ROE_THRESHOLDS['average']:
roe_comment = "ROE处于行业平均水平"
else:
roe_comment = "ROE偏低需关注盈利能力改善"
# 获取PE和PB
pe_ttm = self.financial_data.get('pe_ttm', 0) or self.price_df['pe_ttm'].iloc[-1] if 'pe_ttm' in self.price_df.columns else 0
pb_lf = self.financial_data.get('pb_lf', 0) or self.price_df['pb_lf'].iloc[-1] if 'pb_lf' in self.price_df.columns else 0
# 估值评价
if pe_ttm <= self.PE_THRESHOLDS['undervalued'] and pb_lf <= self.PB_THRESHOLDS['undervalued']:
valuation_comment = "估值处于历史低位,具备较高安全边际"
elif pe_ttm <= self.PE_THRESHOLDS['fair']:
valuation_comment = "估值处于合理区间"
elif pe_ttm <= self.PE_THRESHOLDS['overvalued']:
valuation_comment = "估值略高,需关注业绩成长性"
else:
valuation_comment = "估值偏高,注意追高风险"
# 基本面总结
if roe >= self.ROE_THRESHOLDS['good'] and pe_ttm <= self.PE_THRESHOLDS['fair']:
fundamental_summary = "公司基本面健康,盈利能力强,估值合理,具备投资价值。"
elif roe >= self.ROE_THRESHOLDS['good']:
fundamental_summary = "公司盈利能力较强,但估值偏高,建议等待回调机会。"
else:
fundamental_summary = "公司基本面一般,建议谨慎观望。"
return FundamentalAnalysis(
roe=round(roe, 2) if roe else 0,
roe_comment=roe_comment,
pe_ttm=round(pe_ttm, 2) if pe_ttm else 0,
pb_lf=round(pb_lf, 2) if pb_lf else 0,
valuation_comment=valuation_comment,
fundamental_summary=fundamental_summary
)
def generate_recommendation(self,
current_price: float,
fundamental: FundamentalAnalysis = None,
technical: TechnicalAnalysis = None) -> InvestmentRecommendation:
"""
生成投资建议
Args:
current_price: 当前价格
fundamental: 基本面分析结果
technical: 技术面分析结果
Returns:
InvestmentRecommendation对象
"""
if fundamental is None:
fundamental = self.analyze_fundamental()
if technical is None:
technical = self.analyze_technical()
# 综合评分
score = 0
# 基本面评分
if fundamental.roe >= self.ROE_THRESHOLDS['excellent']:
score += 3
elif fundamental.roe >= self.ROE_THRESHOLDS['good']:
score += 2
elif fundamental.roe >= self.ROE_THRESHOLDS['average']:
score += 1
# 估值评分
if fundamental.pe_ttm <= self.PE_THRESHOLDS['undervalued']:
score += 3
elif fundamental.pe_ttm <= self.PE_THRESHOLDS['fair']:
score += 2
elif fundamental.pe_ttm <= self.PE_THRESHOLDS['overvalued']:
score += 1
# 技术面评分
if technical.trend in ["强势上涨", "震荡上行"]:
score += 2
elif technical.trend == "震荡整理":
score += 1
# 风险等级
if technical.volatility > 50:
risk_level = RiskLevel.HIGH
elif technical.volatility > 35:
risk_level = RiskLevel.MEDIUM_HIGH
elif technical.volatility > 25:
risk_level = RiskLevel.MEDIUM
elif technical.volatility > 15:
risk_level = RiskLevel.LOW_MEDIUM
else:
risk_level = RiskLevel.LOW
# 目标价格计算基于PE和支撑阻力
target_price_pe = current_price * (self.PE_THRESHOLDS['fair'] / max(fundamental.pe_ttm, 1))
target_price_resistance = technical.resistance_level * 1.1
target_price = round((target_price_pe + target_price_resistance) / 2, 2)
# 投资建议
key_factors = []
if score >= 6:
rating = InvestmentRating.BUY
recommendation_text = f"综合基本面和技术面分析,该股投资价值较高。目标价{target_price}元,建议逢低买入。"
key_factors.append("盈利能力强劲ROE表现优异")
elif score >= 4:
rating = InvestmentRating.ACCUMULATE
recommendation_text = f"公司基本面良好,建议关注回调机会逐步建仓。目标价{target_price}元。"
key_factors.append("基本面稳健,估值合理")
elif score >= 2:
rating = InvestmentRating.HOLD
recommendation_text = "公司基本面一般,当前估值合理,建议持有观望。"
key_factors.append("基本面中性,估值合理")
else:
rating = InvestmentRating.REDUCE
recommendation_text = "公司基本面较弱或估值偏高,建议谨慎或减仓。"
key_factors.append("基本面一般或估值偏高")
if technical.trend in ["强势上涨", "震荡上行"]:
key_factors.append("技术面呈上升趋势")
elif technical.trend in ["强势下跌", "震荡下行"]:
key_factors.append("技术面呈下降趋势")
if fundamental.pe_ttm <= self.PE_THRESHOLDS['undervalued']:
key_factors.append("估值处于历史低位")
return InvestmentRecommendation(
rating=rating,
target_price=target_price,
risk_level=risk_level,
recommendation_text=recommendation_text,
key_factors=key_factors
)
def generate_core_summary(self,
fundamental: FundamentalAnalysis = None,
technical: TechnicalAnalysis = None,
recommendation: InvestmentRecommendation = None) -> List[str]:
"""
生成核心提要要点
Returns:
要点列表
"""
if fundamental is None:
fundamental = self.analyze_fundamental()
if technical is None:
technical = self.analyze_technical()
if recommendation is None:
# 获取最新价格
latest_price = self.price_df['close'].iloc[-1] if 'close' in self.price_df.columns else 0
recommendation = self.generate_recommendation(latest_price, fundamental, technical)
summary_points = []
# ROE要点
if fundamental.roe > 0:
summary_points.append(
f"该公司最新净资产收益率ROE{fundamental.roe}%{fundamental.roe_comment}"
)
# 估值要点
if fundamental.pe_ttm > 0:
summary_points.append(
f"当前市盈率TTM{fundamental.pe_ttm}倍,{fundamental.valuation_comment}"
)
# 技术面要点
summary_points.append(
f"技术面显示股价呈现{technical.trend}趋势,{technical.technical_summary}"
)
# 投资建议要点
summary_points.append(
f"给予\"{recommendation.rating.value}\"评级,目标价格{recommendation.target_price}元,风险等级为{recommendation.risk_level.value}"
)
return summary_points
def get_price_statistics(self) -> Dict:
"""
获取价格统计数据
Returns:
统计数据字典
"""
df = self.price_df
if len(df) == 0:
return {}
return {
'latest_price': round(df['close'].iloc[-1], 2),
'highest_price': round(df['high'].max(), 2),
'lowest_price': round(df['low'].min(), 2),
'avg_price': round(df['close'].mean(), 2),
'total_change': round(((df['close'].iloc[-1] / df['close'].iloc[0]) - 1) * 100, 2),
'max_daily_gain': round(df['pct_chg'].max(), 2) if 'pct_chg' in df.columns else 0,
'max_daily_loss': round(df['pct_chg'].min(), 2) if 'pct_chg' in df.columns else 0,
'volatility': round(df['close'].pct_change().std() * np.sqrt(52) * 100, 2)
}
def perform_comprehensive_analysis(price_df: pd.DataFrame,
financial_data: Dict,
current_price: float = None) -> Dict:
"""
执行综合分析并返回所有结果
Args:
price_df: 价格数据
financial_data: 财务数据
current_price: 当前价格可选
Returns:
包含所有分析结果的字典
"""
engine = AnalysisEngine(price_df, financial_data)
if current_price is None:
current_price = price_df['close'].iloc[-1]
technical = engine.analyze_technical()
fundamental = engine.analyze_fundamental()
recommendation = engine.generate_recommendation(current_price, fundamental, technical)
summary_points = engine.generate_core_summary(fundamental, technical, recommendation)
price_stats = engine.get_price_statistics()
return {
'technical': technical,
'fundamental': fundamental,
'recommendation': recommendation,
'summary_points': summary_points,
'price_stats': price_stats
}
if __name__ == '__main__':
# 测试代码
import numpy as np
# 创建模拟数据
dates = pd.date_range(start='2025-01-01', periods=60, freq='W')
np.random.seed(42)
prices = 30 + np.cumsum(np.random.randn(60) * 0.5)
price_df = pd.DataFrame({
'date': dates,
'open': prices * 0.98,
'high': prices * 1.03,
'low': prices * 0.97,
'close': prices,
'volume': np.random.randint(1000000, 5000000, 60),
'pe_ttm': 25 + np.random.randn(60) * 2,
'pb_lf': 3 + np.random.randn(60) * 0.2
})
financial_data = {
'roe_wgt': 16.5,
'pe_ttm': 25.3,
'pb_lf': 3.1,
'sec_name': '测试公司'
}
# 执行分析
results = perform_comprehensive_analysis(price_df, financial_data)
print("=== 技术分析 ===")
print(f"趋势: {results['technical'].trend}")
print(f"支撑位: {results['technical'].support_level}")
print(f"阻力位: {results['technical'].resistance_level}")
print("\n=== 基本面分析 ===")
print(f"ROE: {results['fundamental'].roe}%")
print(f"PE TTM: {results['fundamental'].pe_ttm}")
print(f"PB LF: {results['fundamental'].pb_lf}")
print("\n=== 投资建议 ===")
print(f"评级: {results['recommendation'].rating.value}")
print(f"目标价: {results['recommendation'].target_price}")
print(f"风险等级: {results['recommendation'].risk_level.value}")
print("\n=== 核心提要 ===")
for i, point in enumerate(results['summary_points'], 1):
print(f"{i}. {point}")