claude强化功能

This commit is contained in:
2026-06-14 11:54:45 +08:00
parent cc8dff4e57
commit e524a3589a
43 changed files with 13421 additions and 73 deletions

View File

@@ -0,0 +1,495 @@
"""财报深度解读 — 关键指标趋势、AI摘要、同行对比、异常预警。
功能:
1. 财报关键指标趋势
2. AI财报摘要
3. 同行对比
4. 财报异常预警
5. 财报发布日历
"""
import datetime as dt
import json
from typing import List, Dict, Any, Optional
from collections import defaultdict
import numpy as np
from sqlalchemy import select, and_, func, desc
from db import get_session
from models import FinancialReport, Security, StockMetric
import llm
def seed_sample_reports():
"""生成示例财报数据(用于演示)"""
with get_session() as s:
stocks = s.execute(
select(Security.code, Security.name).limit(50)
).all()
saved = 0
for code, name in stocks:
# 生成最近4个季度的财报
base_date = dt.date(2023, 12, 31)
for i in range(4):
report_date = base_date - dt.timedelta(days=i * 90)
publish_date = report_date + dt.timedelta(days=30)
# 检查是否已存在
exists = s.execute(
select(FinancialReport).where(
and_(
FinancialReport.code == code,
FinancialReport.report_date == report_date
)
)
).scalar_one_or_none()
if exists:
continue
# 模拟财务数据
base_revenue = np.random.uniform(10, 500)
growth = np.random.uniform(-20, 50)
report = FinancialReport(
code=code,
name=name,
report_date=report_date,
publish_date=publish_date,
report_type='Q' + str((report_date.month // 3) or 4),
revenue=round(base_revenue * (1 + i * 0.1), 2),
net_profit=round(base_revenue * np.random.uniform(0.05, 0.2), 2),
roe=round(np.random.uniform(5, 25), 2),
gross_margin=round(np.random.uniform(20, 60), 2),
revenue_growth=round(growth, 2),
profit_growth=round(growth + np.random.uniform(-10, 10), 2),
inventory=round(base_revenue * np.random.uniform(0.1, 0.3), 2),
receivable=round(base_revenue * np.random.uniform(0.15, 0.4), 2),
debt_ratio=round(np.random.uniform(30, 70), 2)
)
s.add(report)
saved += 1
s.commit()
return {"ok": True, "saved": saved}
def get_report_trend(code: str, periods: int = 8) -> Dict[str, Any]:
"""获取财报关键指标趋势
Args:
code: 股票代码
periods: 统计期数
Returns:
趋势数据
"""
with get_session() as s:
reports = s.execute(
select(FinancialReport)
.where(FinancialReport.code == code)
.order_by(desc(FinancialReport.report_date))
.limit(periods)
).scalars().all()
if not reports:
return {"ok": False, "msg": "暂无财报数据"}
# 反转顺序(从旧到新)
reports = list(reversed(reports))
# 提取趋势数据
dates = [r.report_date.isoformat() for r in reports]
trend_data = {
"revenue": [r.revenue for r in reports],
"net_profit": [r.net_profit for r in reports],
"roe": [r.roe for r in reports],
"gross_margin": [r.gross_margin for r in reports],
"revenue_growth": [r.revenue_growth for r in reports],
"profit_growth": [r.profit_growth for r in reports],
"debt_ratio": [r.debt_ratio for r in reports]
}
# 计算趋势(上升/下降/平稳)
def calc_trend(values):
if len(values) < 2:
return "平稳"
recent = np.mean(values[-2:])
previous = np.mean(values[:2]) if len(values) >= 4 else values[0]
change = (recent - previous) / previous if previous != 0 else 0
if change > 0.1:
return "上升"
elif change < -0.1:
return "下降"
else:
return "平稳"
trends = {
key: calc_trend(values)
for key, values in trend_data.items()
}
return {
"ok": True,
"code": code,
"name": reports[0].name,
"dates": dates,
"data": trend_data,
"trends": trends,
"latest": {
"revenue": reports[-1].revenue,
"net_profit": reports[-1].net_profit,
"roe": reports[-1].roe,
"gross_margin": reports[-1].gross_margin,
"revenue_growth": reports[-1].revenue_growth,
"profit_growth": reports[-1].profit_growth
}
}
def generate_ai_summary(code: str) -> Dict[str, Any]:
"""生成AI财报摘要
Args:
code: 股票代码
Returns:
AI摘要
"""
with get_session() as s:
# 获取最新财报
report = s.execute(
select(FinancialReport)
.where(FinancialReport.code == code)
.order_by(desc(FinancialReport.report_date))
.limit(1)
).scalar_one_or_none()
if not report:
return {"ok": False, "msg": "暂无财报数据"}
# 如果已有摘要,直接返回
if report.ai_summary:
return {
"ok": True,
"summary": report.ai_summary,
"report_date": report.report_date.isoformat()
}
# 构建提示词
prompt = f"""请用一句话总结以下财报数据40字以内
公司:{report.name}{report.code}
报告期:{report.report_date}
营业收入:{report.revenue}亿元,同比增长{report.revenue_growth:+.1f}%
净利润:{report.net_profit}亿元,同比增长{report.profit_growth:+.1f}%
ROE{report.roe}%
毛利率:{report.gross_margin}%
要求:
1. 一句话说明业绩是增长还是下降
2. 提及最亮眼或最担忧的指标
3. 给出简短评价(优秀/良好/一般/较差)
4. 不超过40字
示例业绩稳步增长ROE达20%创新高,盈利能力优秀。
"""
# 调用AI
if llm.enabled():
try:
summary = llm.ask(prompt, max_tokens=100)
# 保存摘要
report.ai_summary = summary
s.commit()
except Exception as e:
summary = f"营收{report.revenue}亿元({report.revenue_growth:+.1f}%),净利润{report.net_profit}亿元({report.profit_growth:+.1f}%)"
else:
summary = f"营收{report.revenue}亿元({report.revenue_growth:+.1f}%),净利润{report.net_profit}亿元({report.profit_growth:+.1f}%)"
return {
"ok": True,
"summary": summary,
"report_date": report.report_date.isoformat()
}
def compare_with_peers(code: str, sector: str = None) -> Dict[str, Any]:
"""同行对比
Args:
code: 股票代码
sector: 行业(可选)
Returns:
对比结果
"""
with get_session() as s:
# 获取目标股票最新财报
target = s.execute(
select(FinancialReport)
.where(FinancialReport.code == code)
.order_by(desc(FinancialReport.report_date))
.limit(1)
).scalar_one_or_none()
if not target:
return {"ok": False, "msg": "暂无财报数据"}
# 获取同行业股票(简化:随机选取)
peers = s.execute(
select(FinancialReport)
.where(
and_(
FinancialReport.code != code,
FinancialReport.report_date == target.report_date
)
)
.limit(20)
).scalars().all()
if not peers:
return {"ok": False, "msg": "暂无同行数据"}
# 计算行业均值
industry_avg = {
"roe": np.mean([p.roe for p in peers]),
"gross_margin": np.mean([p.gross_margin for p in peers]),
"revenue_growth": np.mean([p.revenue_growth for p in peers]),
"profit_growth": np.mean([p.profit_growth for p in peers]),
"debt_ratio": np.mean([p.debt_ratio for p in peers])
}
# 计算差异
comparison = {
"roe": {
"value": target.roe,
"industry_avg": round(industry_avg["roe"], 2),
"diff": round(target.roe - industry_avg["roe"], 2),
"better": target.roe > industry_avg["roe"]
},
"gross_margin": {
"value": target.gross_margin,
"industry_avg": round(industry_avg["gross_margin"], 2),
"diff": round(target.gross_margin - industry_avg["gross_margin"], 2),
"better": target.gross_margin > industry_avg["gross_margin"]
},
"revenue_growth": {
"value": target.revenue_growth,
"industry_avg": round(industry_avg["revenue_growth"], 2),
"diff": round(target.revenue_growth - industry_avg["revenue_growth"], 2),
"better": target.revenue_growth > industry_avg["revenue_growth"]
},
"profit_growth": {
"value": target.profit_growth,
"industry_avg": round(industry_avg["profit_growth"], 2),
"diff": round(target.profit_growth - industry_avg["profit_growth"], 2),
"better": target.profit_growth > industry_avg["profit_growth"]
},
"debt_ratio": {
"value": target.debt_ratio,
"industry_avg": round(industry_avg["debt_ratio"], 2),
"diff": round(target.debt_ratio - industry_avg["debt_ratio"], 2),
"better": target.debt_ratio < industry_avg["debt_ratio"] # 负债率越低越好
}
}
# 综合评分
better_count = sum(1 for v in comparison.values() if v["better"])
return {
"ok": True,
"code": code,
"name": target.name,
"report_date": target.report_date.isoformat(),
"comparison": comparison,
"better_count": better_count,
"total_metrics": len(comparison),
"conclusion": "优于行业" if better_count >= 3 else ("持平行业" if better_count == 2 else "弱于行业")
}
def detect_abnormalities(code: str) -> Dict[str, Any]:
"""财报异常预警
Args:
code: 股票代码
Returns:
异常预警
"""
with get_session() as s:
# 获取最近2期财报
reports = s.execute(
select(FinancialReport)
.where(FinancialReport.code == code)
.order_by(desc(FinancialReport.report_date))
.limit(2)
).scalars().all()
if len(reports) < 2:
return {"ok": False, "msg": "数据不足"}
current, previous = reports[0], reports[1]
warnings = []
# 1. 存货激增
if current.inventory > 0 and previous.inventory > 0:
inventory_growth = (current.inventory / previous.inventory - 1) * 100
if inventory_growth > 50:
warnings.append({
"type": "存货激增",
"severity": "high",
"description": f"存货增长{inventory_growth:.1f}%,可能存在滞销风险",
"current": current.inventory,
"previous": previous.inventory
})
# 2. 应收账款占比过高
receivable_ratio = current.receivable / current.revenue * 100 if current.revenue > 0 else 0
if receivable_ratio > 50:
warnings.append({
"type": "应收账款占比过高",
"severity": "medium",
"description": f"应收账款占营收{receivable_ratio:.1f}%,回款压力较大",
"ratio": round(receivable_ratio, 2)
})
# 3. 毛利率大幅下降
if current.gross_margin > 0 and previous.gross_margin > 0:
margin_change = current.gross_margin - previous.gross_margin
if margin_change < -5:
warnings.append({
"type": "毛利率大幅下降",
"severity": "high",
"description": f"毛利率下降{abs(margin_change):.1f}个百分点,盈利能力恶化",
"current": current.gross_margin,
"previous": previous.gross_margin
})
# 4. 资产负债率过高
if current.debt_ratio > 70:
warnings.append({
"type": "资产负债率过高",
"severity": "medium",
"description": f"资产负债率{current.debt_ratio}%,财务风险较高",
"value": current.debt_ratio
})
# 5. 增收不增利
if current.revenue_growth > 10 and current.profit_growth < 0:
warnings.append({
"type": "增收不增利",
"severity": "high",
"description": f"营收增长{current.revenue_growth:.1f}%,但净利润下降{abs(current.profit_growth):.1f}%",
"revenue_growth": current.revenue_growth,
"profit_growth": current.profit_growth
})
return {
"ok": True,
"code": code,
"name": current.name,
"report_date": current.report_date.isoformat(),
"warnings": warnings,
"risk_level": "" if any(w["severity"] == "high" for w in warnings) else ("" if warnings else "")
}
def get_report_calendar(days: int = 30) -> Dict[str, Any]:
"""财报发布日历
Args:
days: 未来N天
Returns:
日历数据
"""
today = dt.date.today()
end_date = today + dt.timedelta(days=days)
with get_session() as s:
reports = s.execute(
select(FinancialReport)
.where(
and_(
FinancialReport.publish_date >= today,
FinancialReport.publish_date <= end_date
)
)
.order_by(FinancialReport.publish_date)
).scalars().all()
if not reports:
return {"ok": False, "msg": "暂无即将发布的财报"}
# 按日期分组
calendar = defaultdict(list)
for r in reports:
calendar[r.publish_date.isoformat()].append({
"code": r.code,
"name": r.name,
"report_date": r.report_date.isoformat(),
"report_type": r.report_type
})
return {
"ok": True,
"days": days,
"total": len(reports),
"calendar": dict(calendar)
}
def get_top_reports(metric: str = "roe", limit: int = 20) -> Dict[str, Any]:
"""获取财报排行榜
Args:
metric: 排序指标roe/gross_margin/revenue_growth
limit: 返回数量
Returns:
排行榜
"""
with get_session() as s:
# 获取最新一期的所有财报
latest_date = s.execute(
select(func.max(FinancialReport.report_date))
).scalar()
if not latest_date:
return {"ok": False, "msg": "暂无数据"}
# 根据指标排序
order_field = getattr(FinancialReport, metric)
reports = s.execute(
select(FinancialReport)
.where(FinancialReport.report_date == latest_date)
.order_by(desc(order_field))
.limit(limit)
).scalars().all()
results = []
for r in reports:
results.append({
"code": r.code,
"name": r.name,
"roe": r.roe,
"gross_margin": r.gross_margin,
"revenue_growth": r.revenue_growth,
"profit_growth": r.profit_growth,
metric: getattr(r, metric)
})
return {
"ok": True,
"metric": metric,
"report_date": latest_date.isoformat(),
"rankings": results
}