引入rag
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# 大模型接口地址
|
||||||
|
OPENAI_BASE_URL=
|
||||||
|
# 大模型 API 密钥
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
# bot token
|
||||||
|
TELEGRAM_BOT_TOKEN=
|
||||||
|
# http代理
|
||||||
|
HTTP_PROXY=
|
||||||
|
HTTPS_PROXY=
|
||||||
|
TELEGRAM_PROXY=
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
# 剔除环境文件
|
||||||
.env
|
.env
|
||||||
|
# 剔除不需要的文件夹
|
||||||
.venv/
|
.venv/
|
||||||
.idea/
|
.idea/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
main -- 主程序,所有逻辑都在这
|
||||||
|
build_rag -- 构建rag库
|
||||||
|
adata_test -- 用来才是adata库的
|
||||||
+70
-22
@@ -15,34 +15,82 @@ def get_fina_info(stock_code="600699"):
|
|||||||
|
|
||||||
df = stock.finance.get_core_index(stock_code=stock_code)
|
df = stock.finance.get_core_index(stock_code=stock_code)
|
||||||
|
|
||||||
data = df[['report_date', 'report_type', 'total_rev', 'total_rev_yoy_gr','net_profit_attr_sh', 'net_profit_yoy_gr',
|
print(df.columns)
|
||||||
'non_gaap_net_profit', 'non_gaap_net_profit_yoy_gr', 'roe_wtd', 'gross_margin', 'net_margin', 'quick_ratio',
|
|
||||||
'inv_turn_rate', 'acct_recv_turn_rate']]
|
|
||||||
|
|
||||||
last_type = data['report_type'].to_list()[0]
|
last_type = df['report_type'].to_list()[0]
|
||||||
|
|
||||||
recent_df = data[data.report_type == last_type].iloc[:2,:].reset_index(drop=True)
|
last_data = df[df.report_type == last_type].iloc[:2,:]
|
||||||
|
|
||||||
recent_df.rename(columns={"report_date": "报告日期",
|
# 字典定义:英文指标名 -> 中文财务指标名
|
||||||
"report_type": "报告类型",
|
column_mapping = {
|
||||||
"total_rev":"营业总收入",
|
# 基础与披露信息
|
||||||
"total_rev_yoy_gr":"营业总收入同比增长率",
|
'stock_code': '股票代码',
|
||||||
"net_profit_attr_sh":"归母净利润",
|
'short_name': '股票简称',
|
||||||
"net_profit_yoy_gr":"归母净利润同比增长率",
|
'report_date': '报告期',
|
||||||
"non_gaap_net_profit":"扣非净利润",
|
'report_type': '报表类型',
|
||||||
"non_gaap_net_profit_yoy_gr":"扣非净利润同比增长率",
|
'notice_date': '公告日期',
|
||||||
"roe_wtd":"加权净资产收益率",
|
|
||||||
"gross_margin":"销售毛利率",
|
# 每股指标
|
||||||
"net_margin":"销售净利率",
|
'basic_eps': '基本每股收益',
|
||||||
"quick_ratio":"速动比率",
|
'diluted_eps': '稀释每股收益',
|
||||||
"inv_turn_rate":"存货周转率",
|
'non_gaap_eps': '扣非每股收益',
|
||||||
"acct_recv_turn_rate":"应收账款周转率"
|
'net_asset_ps': '每股净资产',
|
||||||
}, inplace=True)
|
'cap_reserve_ps': '每股公积金',
|
||||||
|
'undist_profit_ps': '每股未分配利润',
|
||||||
|
'oper_cf_ps': '每股经营现金流',
|
||||||
|
|
||||||
|
# 规模与利润指标 (元)
|
||||||
|
'total_rev': '营业总收入',
|
||||||
|
'gross_profit': '毛利润',
|
||||||
|
'net_profit_attr_sh': '归母净利润',
|
||||||
|
'non_gaap_net_profit': '扣非归母净利润',
|
||||||
|
|
||||||
|
# 增长率指标 (同比 YoY / 环比 QoQ)
|
||||||
|
'total_rev_yoy_gr': '营业总收入同比增长率',
|
||||||
|
'net_profit_yoy_gr': '归母净利润同比增长率',
|
||||||
|
'non_gaap_net_profit_yoy_gr': '扣非归母净利润同比增长率',
|
||||||
|
'total_rev_qoq_gr': '营业总收入环比增长率',
|
||||||
|
'net_profit_qoq_gr': '归母净利润环比增长率',
|
||||||
|
'non_gaap_net_profit_qoq_gr': '扣非归母净利润环比增长率',
|
||||||
|
|
||||||
|
# 盈利能力与收益率
|
||||||
|
'roe_wtd': '加权净资产收益率(ROE)',
|
||||||
|
'roe_non_gaap_wtd': '扣非加权净资产收益率(ROE)',
|
||||||
|
'roa_wtd': '加权总资产收益率(ROA)',
|
||||||
|
'gross_margin': '销售毛利率',
|
||||||
|
'net_margin': '销售净利率',
|
||||||
|
|
||||||
|
# 现金流与收入质量比率
|
||||||
|
'adv_receipts_to_rev': '预收款及合同负债占营收比',
|
||||||
|
'net_cf_sales_to_rev': '销售收现率(销售现金流/营收)',
|
||||||
|
'oper_cf_to_rev': '经营现金净流量占营收比',
|
||||||
|
'eff_tax_rate': '实际有效税率',
|
||||||
|
|
||||||
|
# 偿债能力与财务杠杆
|
||||||
|
'curr_ratio': '流动比率',
|
||||||
|
'quick_ratio': '速动比率',
|
||||||
|
'cash_flow_ratio': '现金流量比率',
|
||||||
|
'asset_liab_ratio': '资产负债率',
|
||||||
|
'equity_multiplier': '权益乘数',
|
||||||
|
'equity_ratio': '产权比率',
|
||||||
|
|
||||||
|
# 运营效率与周转率/天数
|
||||||
|
'total_asset_turn_days': '总资产周转天数',
|
||||||
|
'inv_turn_days': '存货周转天数',
|
||||||
|
'acct_recv_turn_days': '应收账款周转天数',
|
||||||
|
'total_asset_turn_rate': '总资产周转率',
|
||||||
|
'inv_turn_rate': '存货周转率',
|
||||||
|
'acct_recv_turn_rate': '应收账款周转率'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 假设你的原始数据存储在 df 中,使用 rename 修改列名
|
||||||
|
# inplace=True 表示直接在原 DataFrame 上修改
|
||||||
|
last_data.rename(columns=column_mapping, inplace=True)
|
||||||
|
|
||||||
formatted_lines = []
|
formatted_lines = []
|
||||||
for _, row in recent_df.iterrows():
|
for _, row in last_data.iterrows():
|
||||||
line_items = []
|
line_items = []
|
||||||
for col, val in zip(recent_df.columns, row):
|
for col, val in zip(last_data.columns, row):
|
||||||
# 判断是否为数字类型
|
# 判断是否为数字类型
|
||||||
if isinstance(val, (int, float)):
|
if isinstance(val, (int, float)):
|
||||||
# 如果列名包含"率",则格式化为保留两位小数并加上 %
|
# 如果列名包含"率",则格式化为保留两位小数并加上 %
|
||||||
|
|||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from langchain_community.document_loaders import DirectoryLoader, TextLoader
|
||||||
|
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
||||||
|
|
||||||
|
from langchain_huggingface import HuggingFaceEmbeddings
|
||||||
|
|
||||||
|
from langchain_chroma import Chroma
|
||||||
|
|
||||||
|
# ==========================
|
||||||
|
# 配置
|
||||||
|
# ==========================
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 配置
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
KNOWLEDGE_PATH = "./knowledge"
|
||||||
|
VECTOR_DB_PATH = "./rag_db"
|
||||||
|
COLLECTION_NAME = "investment_knowledge"
|
||||||
|
EMBEDDING_MODEL = "BAAI/bge-small-zh-v1.5"
|
||||||
|
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# Embedding
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
print("加载Embedding模型")
|
||||||
|
|
||||||
|
embeddings = HuggingFaceEmbeddings(
|
||||||
|
|
||||||
|
model_name=EMBEDDING_MODEL,
|
||||||
|
model_kwargs={
|
||||||
|
"device":"cpu"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 加载Markdown
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
print("加载知识文件")
|
||||||
|
|
||||||
|
loader = DirectoryLoader(
|
||||||
|
|
||||||
|
KNOWLEDGE_PATH,
|
||||||
|
glob="**/*.md",
|
||||||
|
loader_cls=TextLoader,
|
||||||
|
loader_kwargs={
|
||||||
|
"encoding":"utf-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
documents = loader.load()
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"加载 {len(documents)} 个文件"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 自动生成metadata
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
|
||||||
|
def build_metadata(doc):
|
||||||
|
|
||||||
|
path = doc.metadata["source"]
|
||||||
|
|
||||||
|
metadata={
|
||||||
|
"source":path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# rules
|
||||||
|
if "/rules/" in path:
|
||||||
|
metadata["category"]="rule"
|
||||||
|
metadata["knowledge_type"]="analysis_logic"
|
||||||
|
# industry
|
||||||
|
elif "/industry/" in path:
|
||||||
|
metadata["category"]="industry"
|
||||||
|
metadata["knowledge_type"]="industry_logic"
|
||||||
|
# company
|
||||||
|
elif "/company_cases/" in path:
|
||||||
|
metadata["category"]="company_case"
|
||||||
|
metadata["knowledge_type"]="company_fact"
|
||||||
|
# framework
|
||||||
|
elif "/investment_framework/" in path:
|
||||||
|
metadata["category"]="framework"
|
||||||
|
metadata["knowledge_type"]="workflow"
|
||||||
|
else:
|
||||||
|
metadata["category"]="unknown"
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
for doc in documents:
|
||||||
|
|
||||||
|
doc.metadata.update(
|
||||||
|
build_metadata(doc)
|
||||||
|
)
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 文本切分
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
splitter = RecursiveCharacterTextSplitter(
|
||||||
|
|
||||||
|
chunk_size=500,
|
||||||
|
chunk_overlap=80,
|
||||||
|
separators=[
|
||||||
|
"\n\n",
|
||||||
|
"\n",
|
||||||
|
"。",
|
||||||
|
","
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
texts = splitter.split_documents(
|
||||||
|
documents
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"生成 {len(texts)} 个chunk"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 创建向量库
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
print("生成Chroma")
|
||||||
|
|
||||||
|
vector_store = Chroma.from_documents(
|
||||||
|
|
||||||
|
documents=texts,
|
||||||
|
embedding=embeddings,
|
||||||
|
persist_directory=VECTOR_DB_PATH,
|
||||||
|
collection_name=COLLECTION_NAME
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
print("完成")
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
# 南大光电(300346.SZ)投资案例
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
公司:
|
||||||
|
江苏南大光电材料股份有限公司
|
||||||
|
|
||||||
|
股票代码:
|
||||||
|
300346.SZ
|
||||||
|
|
||||||
|
行业:
|
||||||
|
半导体材料
|
||||||
|
|
||||||
|
主营方向:
|
||||||
|
|
||||||
|
- 半导体前驱体材料
|
||||||
|
- 电子特气
|
||||||
|
- 光刻胶及配套材料
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 财务数据
|
||||||
|
|
||||||
|
## 2025年度
|
||||||
|
|
||||||
|
营业收入:
|
||||||
|
|
||||||
|
25.85亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
9.93%
|
||||||
|
|
||||||
|
|
||||||
|
归母净利润:
|
||||||
|
|
||||||
|
3.20亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
18%
|
||||||
|
|
||||||
|
|
||||||
|
扣非净利润:
|
||||||
|
|
||||||
|
2.54亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
31.49%
|
||||||
|
|
||||||
|
|
||||||
|
经营现金流:
|
||||||
|
|
||||||
|
7.33亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
16.76%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 2026年Q1
|
||||||
|
|
||||||
|
营业收入:
|
||||||
|
|
||||||
|
6.62亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
5.45%
|
||||||
|
|
||||||
|
|
||||||
|
归母净利润:
|
||||||
|
|
||||||
|
1.24亿元
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
29.97%
|
||||||
|
|
||||||
|
|
||||||
|
扣非净利润:
|
||||||
|
|
||||||
|
同比增长:
|
||||||
|
|
||||||
|
38.61%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 业务结构
|
||||||
|
|
||||||
|
|
||||||
|
## 1. 先进前驱体材料
|
||||||
|
|
||||||
|
2025收入:
|
||||||
|
|
||||||
|
7.14亿元
|
||||||
|
|
||||||
|
|
||||||
|
占总收入:
|
||||||
|
|
||||||
|
27.61%
|
||||||
|
|
||||||
|
|
||||||
|
增长:
|
||||||
|
|
||||||
|
23.46%
|
||||||
|
|
||||||
|
|
||||||
|
毛利率:
|
||||||
|
|
||||||
|
50.88%
|
||||||
|
|
||||||
|
|
||||||
|
主要产品:
|
||||||
|
|
||||||
|
- DIPAS
|
||||||
|
- BDEAS
|
||||||
|
- TDMAT
|
||||||
|
- 三甲基镓
|
||||||
|
- 三甲基铝
|
||||||
|
|
||||||
|
|
||||||
|
应用:
|
||||||
|
|
||||||
|
- ALD薄膜沉积
|
||||||
|
- CVD薄膜沉积
|
||||||
|
- LED
|
||||||
|
- 5G射频芯片
|
||||||
|
- 存储芯片
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 电子特气
|
||||||
|
|
||||||
|
2025收入:
|
||||||
|
|
||||||
|
15.36亿元
|
||||||
|
|
||||||
|
|
||||||
|
收入占比:
|
||||||
|
|
||||||
|
59.39%
|
||||||
|
|
||||||
|
|
||||||
|
毛利率:
|
||||||
|
|
||||||
|
36.74%
|
||||||
|
|
||||||
|
|
||||||
|
产品:
|
||||||
|
|
||||||
|
- 高纯磷烷
|
||||||
|
- 砷烷
|
||||||
|
- 安全源
|
||||||
|
- NF3
|
||||||
|
- SF6
|
||||||
|
|
||||||
|
|
||||||
|
应用:
|
||||||
|
|
||||||
|
- IC制造
|
||||||
|
- LED制造
|
||||||
|
- 刻蚀
|
||||||
|
- 腔体清洗
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 光刻胶
|
||||||
|
|
||||||
|
主要产品:
|
||||||
|
|
||||||
|
ArF光刻胶
|
||||||
|
|
||||||
|
|
||||||
|
技术:
|
||||||
|
|
||||||
|
193nm
|
||||||
|
|
||||||
|
|
||||||
|
应用:
|
||||||
|
|
||||||
|
90nm-14nm先进制程
|
||||||
|
|
||||||
|
|
||||||
|
2025进展:
|
||||||
|
|
||||||
|
- 三款ArF光刻胶通过客户验证
|
||||||
|
- 获得订单
|
||||||
|
- 光刻胶收入突破2000万元
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 技术壁垒
|
||||||
|
|
||||||
|
|
||||||
|
## 高纯材料能力
|
||||||
|
|
||||||
|
MO源纯度:
|
||||||
|
|
||||||
|
6N以上
|
||||||
|
|
||||||
|
|
||||||
|
核心能力:
|
||||||
|
|
||||||
|
- 合成
|
||||||
|
- 纯化
|
||||||
|
- 分析检测
|
||||||
|
- 安全储运
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 光刻胶能力
|
||||||
|
|
||||||
|
覆盖:
|
||||||
|
|
||||||
|
- 单体
|
||||||
|
- 树脂
|
||||||
|
- 光敏剂
|
||||||
|
- 成品
|
||||||
|
|
||||||
|
|
||||||
|
形成完整研发体系。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 重大事件
|
||||||
|
|
||||||
|
|
||||||
|
2025年:
|
||||||
|
|
||||||
|
现金2.30亿元收购全椒南大核心团队股份
|
||||||
|
|
||||||
|
|
||||||
|
2026年:
|
||||||
|
|
||||||
|
现金7760万元收购乌兰察布南大员工合伙股份
|
||||||
|
|
||||||
|
|
||||||
|
公司参与:
|
||||||
|
|
||||||
|
- 国家863计划
|
||||||
|
- 集成电路02专项
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 核心竞争优势
|
||||||
|
|
||||||
|
|
||||||
|
1. 半导体材料国产替代受益者
|
||||||
|
|
||||||
|
2. 客户认证周期形成壁垒
|
||||||
|
|
||||||
|
3. 高纯材料工艺积累
|
||||||
|
|
||||||
|
4. 高端材料产品突破
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 后续跟踪指标
|
||||||
|
|
||||||
|
|
||||||
|
重点关注:
|
||||||
|
|
||||||
|
1. ArF光刻胶收入是否突破亿元级
|
||||||
|
|
||||||
|
2. 前驱体毛利率变化
|
||||||
|
|
||||||
|
3. 高K三甲基铝渗透情况
|
||||||
|
|
||||||
|
4. 海外客户认证进展
|
||||||
|
|
||||||
|
5. 安全生产情况
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# 半导体材料行业分析框架
|
||||||
|
|
||||||
|
|
||||||
|
## 行业特点
|
||||||
|
|
||||||
|
半导体材料属于高技术壁垒行业。
|
||||||
|
|
||||||
|
核心竞争因素:
|
||||||
|
|
||||||
|
- 纯度
|
||||||
|
- 一致性
|
||||||
|
- 良率
|
||||||
|
- 客户认证
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 核心商业模式
|
||||||
|
|
||||||
|
|
||||||
|
半导体材料企业成长路径:
|
||||||
|
|
||||||
|
研发
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
客户验证
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
小批量供应
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
进入量产线
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
扩大份额
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 主要材料类型
|
||||||
|
|
||||||
|
|
||||||
|
## 前驱体材料
|
||||||
|
|
||||||
|
应用:
|
||||||
|
|
||||||
|
ALD/CVD薄膜沉积
|
||||||
|
|
||||||
|
|
||||||
|
评价指标:
|
||||||
|
|
||||||
|
- 纯度等级
|
||||||
|
- 金属杂质
|
||||||
|
- 稳定性
|
||||||
|
- 客户认证
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 电子特气
|
||||||
|
|
||||||
|
应用:
|
||||||
|
|
||||||
|
- 刻蚀
|
||||||
|
- 清洗
|
||||||
|
- 外延
|
||||||
|
|
||||||
|
|
||||||
|
关键指标:
|
||||||
|
|
||||||
|
- 气体纯度
|
||||||
|
- 供应稳定性
|
||||||
|
- 安全体系
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 光刻胶
|
||||||
|
|
||||||
|
评价:
|
||||||
|
|
||||||
|
- 分辨率
|
||||||
|
- 缺陷率
|
||||||
|
- 工艺匹配度
|
||||||
|
|
||||||
|
|
||||||
|
进入先进制程需要长期验证。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 行业护城河
|
||||||
|
|
||||||
|
|
||||||
|
半导体材料不是普通化工。
|
||||||
|
|
||||||
|
竞争优势来自:
|
||||||
|
|
||||||
|
1. 长期工艺积累
|
||||||
|
|
||||||
|
2. 客户认证周期
|
||||||
|
|
||||||
|
3. 高纯制造能力
|
||||||
|
|
||||||
|
4. 安全生产体系
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 行业风险
|
||||||
|
|
||||||
|
|
||||||
|
## 技术路线变化
|
||||||
|
|
||||||
|
新材料可能替代旧材料。
|
||||||
|
|
||||||
|
|
||||||
|
## 安全风险
|
||||||
|
|
||||||
|
部分材料:
|
||||||
|
|
||||||
|
- 易燃
|
||||||
|
- 易爆
|
||||||
|
- 有毒
|
||||||
|
|
||||||
|
|
||||||
|
事故可能导致:
|
||||||
|
|
||||||
|
- 停产
|
||||||
|
- 客户流失
|
||||||
|
- 估值下降
|
||||||
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
## 一、 盈利质量与现金流验证(现金流与利润质量)
|
||||||
|
|
||||||
|
### 规则编号:RULE_P01_CFO_QUALITY
|
||||||
|
|
||||||
|
#### 分析主题:净利润现金含量与“虚盈实亏”诊断
|
||||||
|
|
||||||
|
- **触发指标**:`经营现金净流量占营收比`、`销售收现率(销售现金流/营收)`、`归母净利润`、`每股经营现金流`、`每股净资产`、`归母净利润同比增长率`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **警告(虚盈实亏/纸面富贵)**:
|
||||||
|
- **条件**:若 `归母净利润同比增长率 > 0`(账面净利润增长),但 `经营现金净流量占营收比 < 0.08`(低于 8%)或 `销售收现率(销售现金流/营收) < 0.8`(低于 80%)。
|
||||||
|
- **风险判定**:公司账面利润看似增长,但实际现金回款极差,大量利润沦为“应收账款”或纸面数字,流动性变现能力低下。
|
||||||
|
2. **优质(现金奶牛/议价力强)**:
|
||||||
|
- **条件**:若 `销售收现率(销售现金流/营收) > 1.0` 且 `经营现金净流量占营收比 > 0.15`。
|
||||||
|
- **判定**:公司销售回款能力极强,盈利质量高,对下游具备较强的商业话语权。
|
||||||
|
|
||||||
|
## 二、 运营效率与存货/应收防雷(周转率与周转天数风险)
|
||||||
|
|
||||||
|
### 规则编号:RULE_T01_AR_RISK
|
||||||
|
|
||||||
|
#### 分析主题:应收账款回款危机与“赊销冲业绩”风险
|
||||||
|
|
||||||
|
- **触发指标**:`应收账款周转天数`、`应收账款周转率`、`营业总收入同比增长率`、`归母净利润同比增长率`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **周转恶化预警**:
|
||||||
|
- **条件**:若 `应收账款周转天数` 同比显著增加(如较上年同期增加 $> 30$ 天),或 `应收账款周转率` 持续下降。
|
||||||
|
- **风险判定**:公司下游客户打款周期拉长,需密切关注未来大额坏账准备计提的爆发风险。
|
||||||
|
2. **赊销暴雷风险**:
|
||||||
|
- **条件**:若 `营业总收入同比增长率 > 15%`,但 `应收账款周转天数` 同比拉长 $> 40\%$。
|
||||||
|
- **风险判定**:公司可能在通过大幅放宽信用期限(赊销)来强行刺激销量拉动营收增长,业绩含金量极低。
|
||||||
|
|
||||||
|
### 规则编号:RULE_T02_INV_RISK
|
||||||
|
|
||||||
|
#### 分析主题:存货滞销与毛利率背离诊断(隐蔽成本)
|
||||||
|
|
||||||
|
- **触发指标**:`存货周转天数`、`存货周转率`、`销售毛利率`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **滞销与跌价减值风险**:
|
||||||
|
- **条件**:若 `存货周转天数` 呈连续上升趋势(或大幅高于同行业均值)。
|
||||||
|
- **判定**:产品面临滞销或更新换代淘汰风险,后续需密切关注存货跌价准备对利润的侵蚀。
|
||||||
|
2. **经典财务造假背离项**:
|
||||||
|
- **条件**:若 `存货周转天数` 显著增加(周转变慢,产品滞销),但 `销售毛利率` 却逆势大幅上升。
|
||||||
|
- **高风险预警**:严重违背商业逻辑!公司极有可能少转了生产成本(将本应计入损益表的营业成本留在存货资产中),以此虚增毛利和存货资产。
|
||||||
|
|
||||||
|
## 三、 偿债能力与杠杆风险(流动性与负债风险)
|
||||||
|
|
||||||
|
### 规则编号:RULE_S01_DEBT_RISK
|
||||||
|
|
||||||
|
#### 分析主题:资产负债率与流动性压迫风险
|
||||||
|
|
||||||
|
- **触发指标**:`资产负债率`、`流动比率`、`速动比率`、`权益乘数`、`现金流量比率`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **流动性断裂预警**:
|
||||||
|
- **条件**:若 `流动比率 < 1.0` 且 `速动比率 < 0.7`。
|
||||||
|
- **风险判定**:公司短期变现偿债能力极弱,若无新的融资打入,面临短期债务违约或流动性断裂风险。
|
||||||
|
2. **高杠杆压迫**:
|
||||||
|
- **条件**:若 `资产负债率 > 70%`(或 `权益乘数 > 3.5`)且 `现金流量比率 < 0.1`。
|
||||||
|
- **风险判定**:公司债务负担沉重,且经营活动产生的现金流无法有效覆盖负债,抗风险能力差。
|
||||||
|
|
||||||
|
## 四、 ROE 杜邦分析与盈利驱动力(DuPont Analysis)
|
||||||
|
|
||||||
|
### 规则编号:RULE_D01_ROE_DRIVERS
|
||||||
|
|
||||||
|
#### 分析主题:ROE 质量与三要素驱动力拆解
|
||||||
|
|
||||||
|
- **触发指标**:`加权净资产收益率(ROE)`、`扣非加权净资产收益率(ROE)`、`销售净利率`、`总资产周转率`、`权益乘数`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **扣非背离风险(依赖非经常损益)**:
|
||||||
|
- **条件**:若 `加权净资产收益率(ROE)` 较高,但 `扣非加权净资产收益率(ROE)` 极低(两者差值 $> 3\%$)。
|
||||||
|
- **判定**:公司的 ROE 主要依赖“非经常性损益”(如卖资产、政府补贴、投资收益),核心主营业务盈利能力低下。
|
||||||
|
2. **高风险杠杆驱动型**:
|
||||||
|
- **条件**:若 ROE 上升,但 `销售净利率` 和 `总资产周转率` 持平或下降,仅靠 `权益乘数` 升高驱动。
|
||||||
|
- **风险判定**:高杠杆举债驱动的 ROE 扩张,不具备可持续性,财务风险剧增。
|
||||||
|
3. **优质效率/议价驱动型**:
|
||||||
|
- **条件**:若 ROE 上升主要由 `总资产周转率` 或 `销售净利率` 驱动。
|
||||||
|
- **判定**:公司通过提高运营效率或提升产品附加值/议价权来实现 ROE 增长,属于高质量增长。
|
||||||
|
|
||||||
|
## 五、 收入增长与合同负债(成长可持续性)
|
||||||
|
|
||||||
|
### 规则编号:RULE_G01_REVENUE_QUALITY
|
||||||
|
|
||||||
|
#### 分析主题:业绩增长含金量与预收款(蓄水池)检验
|
||||||
|
|
||||||
|
- **触发指标**:`营业总收入同比增长率`、`归母净利润同比增长率`、`扣非归母净利润同比增长率`、`预收款及合同负债占营收比`
|
||||||
|
- **诊断逻辑与阈值**:
|
||||||
|
1. **利润增速质量校验**:
|
||||||
|
- **条件**:若 `归母净利润同比增长率 > 营业总收入同比增长率`。
|
||||||
|
- **分析**:利润增速快于收入增速,需结合 `扣非归母净利润同比增长率` 交叉验证是“降本增效/毛利提升(优质)”还是“少计提费用/依赖非经常损益(劣质)”。
|
||||||
|
2. **蓄水池池水干涸预警**:
|
||||||
|
- **条件**:若 `营业总收入同比增长率` 保持增长,但 `预收款及合同负债占营收比` 同比连续下滑 $> 30\%$。
|
||||||
|
- **预警**:公司“业绩蓄水池”(未确认的订单或预收款)正在枯竭,未来几个季度的营收增速大概率会出现失速或下滑。
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# 半导体材料企业分析规则
|
||||||
|
|
||||||
|
|
||||||
|
## 规则1:利润质量判断
|
||||||
|
|
||||||
|
|
||||||
|
条件:
|
||||||
|
|
||||||
|
如果:
|
||||||
|
|
||||||
|
净利润增速 > 营收增速
|
||||||
|
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
说明企业可能存在:
|
||||||
|
|
||||||
|
- 产品结构升级
|
||||||
|
- 高毛利产品占比提升
|
||||||
|
- 成本改善
|
||||||
|
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
检查:
|
||||||
|
|
||||||
|
- 毛利率趋势
|
||||||
|
- 高端产品收入占比
|
||||||
|
- 经营现金流
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 规则2:判断国产替代真实性
|
||||||
|
|
||||||
|
|
||||||
|
条件:
|
||||||
|
|
||||||
|
企业宣称国产替代。
|
||||||
|
|
||||||
|
|
||||||
|
不能直接认为成功。
|
||||||
|
|
||||||
|
|
||||||
|
需要满足:
|
||||||
|
|
||||||
|
研发
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
客户认证
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
订单
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
收入贡献
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
观察:
|
||||||
|
|
||||||
|
- 客户验证情况
|
||||||
|
- 销售收入
|
||||||
|
- 市占率
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 规则3:判断半导体材料护城河
|
||||||
|
|
||||||
|
|
||||||
|
条件:
|
||||||
|
|
||||||
|
企业产品需要:
|
||||||
|
|
||||||
|
- 高纯度
|
||||||
|
- 高一致性
|
||||||
|
- 长认证周期
|
||||||
|
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
具备较强竞争壁垒。
|
||||||
|
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
查看:
|
||||||
|
|
||||||
|
- 产品纯度等级
|
||||||
|
- 龙头客户
|
||||||
|
- 认证周期
|
||||||
|
- 良率
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 规则4:危险材料企业风险评估
|
||||||
|
|
||||||
|
|
||||||
|
条件:
|
||||||
|
|
||||||
|
产品属于:
|
||||||
|
|
||||||
|
危险化学品。
|
||||||
|
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
需要给予安全风险折价。
|
||||||
|
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
检查:
|
||||||
|
|
||||||
|
- 安全事故
|
||||||
|
- 环保处罚
|
||||||
|
- 自动化水平
|
||||||
|
- 安全投入
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 规则5:成长空间判断
|
||||||
|
|
||||||
|
|
||||||
|
条件:
|
||||||
|
|
||||||
|
新产品收入占比较低。
|
||||||
|
|
||||||
|
|
||||||
|
不能简单认为没有价值。
|
||||||
|
|
||||||
|
|
||||||
|
需要判断:
|
||||||
|
|
||||||
|
是否存在:
|
||||||
|
|
||||||
|
- 技术突破
|
||||||
|
- 客户验证
|
||||||
|
- 放量可能
|
||||||
|
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
观察:
|
||||||
|
|
||||||
|
收入是否:
|
||||||
|
|
||||||
|
百万级
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
千万级
|
||||||
|
|
||||||
|
↓
|
||||||
|
|
||||||
|
亿元级
|
||||||
@@ -8,49 +8,223 @@ from adata import stock
|
|||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
from telegram import Update
|
from telegram import Update
|
||||||
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
|
from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters
|
||||||
|
from langchain_community.embeddings import HuggingFaceEmbeddings
|
||||||
|
from langchain_community.vectorstores import Chroma
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
# VectorDB 存在 ./rules_data 目录中
|
||||||
|
# ======================
|
||||||
|
# Embedding模型
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
embeddings = HuggingFaceEmbeddings(
|
||||||
|
model_name="BAAI/bge-small-zh-v1.5",
|
||||||
|
model_kwargs={
|
||||||
|
"device":"cpu"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 加载向量库
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
vector_store = Chroma(
|
||||||
|
persist_directory="./rag_db",
|
||||||
|
collection_name="investment_knowledge",
|
||||||
|
embedding_function=embeddings
|
||||||
|
)
|
||||||
|
|
||||||
|
# 公司事实 Retriever
|
||||||
|
company_retriever = vector_store.as_retriever(
|
||||||
|
search_kwargs={
|
||||||
|
"k":5,
|
||||||
|
"filter":{
|
||||||
|
"category":"company_case"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 行业 Retriever
|
||||||
|
industry_retriever = vector_store.as_retriever(
|
||||||
|
search_kwargs={
|
||||||
|
"k":3,
|
||||||
|
"filter":{
|
||||||
|
"category":"industry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 规则 Retriever
|
||||||
|
rule_retriever = vector_store.as_retriever(
|
||||||
|
search_kwargs={
|
||||||
|
"k":5,
|
||||||
|
"filter":{
|
||||||
|
"category":"rule"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def research_retrieve(question):
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
# 公司事实
|
||||||
|
company_docs = company_retriever.invoke(question)
|
||||||
|
result["company"] = "\n\n".join(
|
||||||
|
[
|
||||||
|
doc.page_content
|
||||||
|
for doc in company_docs
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# 行业知识
|
||||||
|
industry_docs = industry_retriever.invoke(question)
|
||||||
|
result["industry"] = "\n\n".join(
|
||||||
|
[
|
||||||
|
doc.page_content
|
||||||
|
for doc in industry_docs
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# 分析规则
|
||||||
|
rule_docs = rule_retriever.invoke(question)
|
||||||
|
result["rules"] = "\n\n".join(
|
||||||
|
[
|
||||||
|
doc.page_content
|
||||||
|
for doc in rule_docs
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
# ================= 你的原有逻辑(完全保留) =================
|
|
||||||
@tool
|
@tool
|
||||||
def get_fina_info(stock_code: str) -> str:
|
def get_fina_info(stock_code: str) -> str:
|
||||||
"""获取指定股票代码的财务数据。当需要获取财务数据的时使用此工具"""
|
"""获取指定股票代码的财务数据。当需要获取财务数据的时使用此工具"""
|
||||||
|
|
||||||
|
# 字典定义:英文指标名 -> 中文财务指标名
|
||||||
|
column_mapping = {
|
||||||
|
# 基础与披露信息
|
||||||
|
'stock_code': '股票代码',
|
||||||
|
'short_name': '股票简称',
|
||||||
|
'report_date': '报告期',
|
||||||
|
'report_type': '报表类型',
|
||||||
|
'notice_date': '公告日期',
|
||||||
|
|
||||||
|
# 每股指标
|
||||||
|
'basic_eps': '基本每股收益',
|
||||||
|
'diluted_eps': '稀释每股收益',
|
||||||
|
'non_gaap_eps': '扣非每股收益',
|
||||||
|
'net_asset_ps': '每股净资产',
|
||||||
|
'cap_reserve_ps': '每股公积金',
|
||||||
|
'undist_profit_ps': '每股未分配利润',
|
||||||
|
'oper_cf_ps': '每股经营现金流',
|
||||||
|
|
||||||
|
# 规模与利润指标 (元)
|
||||||
|
'total_rev': '营业总收入',
|
||||||
|
'gross_profit': '毛利润',
|
||||||
|
'net_profit_attr_sh': '归母净利润',
|
||||||
|
'non_gaap_net_profit': '扣非归母净利润',
|
||||||
|
|
||||||
|
# 增长率指标 (同比 YoY / 环比 QoQ)
|
||||||
|
'total_rev_yoy_gr': '营业总收入同比增长率',
|
||||||
|
'net_profit_yoy_gr': '归母净利润同比增长率',
|
||||||
|
'non_gaap_net_profit_yoy_gr': '扣非归母净利润同比增长率',
|
||||||
|
'total_rev_qoq_gr': '营业总收入环比增长率',
|
||||||
|
'net_profit_qoq_gr': '归母净利润环比增长率',
|
||||||
|
'non_gaap_net_profit_qoq_gr': '扣非归母净利润环比增长率',
|
||||||
|
|
||||||
|
# 盈利能力与收益率
|
||||||
|
'roe_wtd': '加权净资产收益率(ROE)',
|
||||||
|
'roe_non_gaap_wtd': '扣非加权净资产收益率(ROE)',
|
||||||
|
'roa_wtd': '加权总资产收益率(ROA)',
|
||||||
|
'gross_margin': '销售毛利率',
|
||||||
|
'net_margin': '销售净利率',
|
||||||
|
|
||||||
|
# 现金流与收入质量比率
|
||||||
|
'adv_receipts_to_rev': '预收款及合同负债占营收比',
|
||||||
|
'net_cf_sales_to_rev': '销售收现率(销售现金流/营收)',
|
||||||
|
'oper_cf_to_rev': '经营现金净流量占营收比',
|
||||||
|
'eff_tax_rate': '实际有效税率',
|
||||||
|
|
||||||
|
# 偿债能力与财务杠杆
|
||||||
|
'curr_ratio': '流动比率',
|
||||||
|
'quick_ratio': '速动比率',
|
||||||
|
'cash_flow_ratio': '现金流量比率',
|
||||||
|
'asset_liab_ratio': '资产负债率',
|
||||||
|
'equity_multiplier': '权益乘数',
|
||||||
|
'equity_ratio': '产权比率',
|
||||||
|
|
||||||
|
# 运营效率与周转率/天数
|
||||||
|
'total_asset_turn_days': '总资产周转天数',
|
||||||
|
'inv_turn_days': '存货周转天数',
|
||||||
|
'acct_recv_turn_days': '应收账款周转天数',
|
||||||
|
'total_asset_turn_rate': '总资产周转率',
|
||||||
|
'inv_turn_rate': '存货周转率',
|
||||||
|
'acct_recv_turn_rate': '应收账款周转率'
|
||||||
|
}
|
||||||
|
|
||||||
df = stock.finance.get_core_index(stock_code=stock_code)
|
df = stock.finance.get_core_index(stock_code=stock_code)
|
||||||
|
|
||||||
data = df[['report_date', 'report_type', 'total_rev', 'total_rev_yoy_gr', 'net_profit_attr_sh', 'net_profit_yoy_gr',
|
year_data = df[df.report_type == "年报"].iloc[:5, :] # 取5年的年报数据
|
||||||
'non_gaap_net_profit', 'non_gaap_net_profit_yoy_gr', 'roe_wtd', 'gross_margin', 'net_margin',
|
year_data.rename(columns=column_mapping, inplace=True)
|
||||||
'quick_ratio',
|
|
||||||
'inv_turn_rate', 'acct_recv_turn_rate']]
|
|
||||||
|
|
||||||
last_type = data['report_type'].to_list()[0]
|
year_formatted_lines = []
|
||||||
recent_df = data[data.report_type == last_type].iloc[:2, :].reset_index(drop=True)
|
for _, row in year_data.iterrows():
|
||||||
|
|
||||||
recent_df.rename(columns={"report_date": "报告日期", "report_type": "报告类型", "total_rev": "营业总收入",
|
|
||||||
"total_rev_yoy_gr": "营业总收入同比增长率", "net_profit_attr_sh": "归母净利润",
|
|
||||||
"net_profit_yoy_gr": "归母净利润同比增长率", "non_gaap_net_profit": "扣非净利润",
|
|
||||||
"non_gaap_net_profit_yoy_gr": "扣非净利润同比增长率", "roe_wtd": "加权净资产收益率",
|
|
||||||
"gross_margin": "销售毛利率", "net_margin": "销售净利率", "quick_ratio": "速动比率",
|
|
||||||
"inv_turn_rate": "存货周转率", "acct_recv_turn_rate": "应收账款周转率"
|
|
||||||
}, inplace=True)
|
|
||||||
|
|
||||||
formatted_lines = []
|
|
||||||
for _, row in recent_df.iterrows():
|
|
||||||
line_items = []
|
line_items = []
|
||||||
for col, val in zip(recent_df.columns, row):
|
for col, val in zip(year_data.columns, row):
|
||||||
|
# 判断是否为数字类型
|
||||||
if isinstance(val, (int, float)):
|
if isinstance(val, (int, float)):
|
||||||
|
# 如果列名包含"率",则格式化为保留两位小数并加上 %
|
||||||
if "率" in col:
|
if "率" in col:
|
||||||
formatted_val = f"{val:.2f}%"
|
# 如果原始数值本身就是小数(如 0.35 表示 35%),需要乘以 100
|
||||||
|
val_pct = val * 100 if abs(val) <= 1.0 else val
|
||||||
|
formatted_val = f"{val_pct:.2f}%"
|
||||||
else:
|
else:
|
||||||
formatted_val = f"{val:,.2f}"
|
formatted_val = f"{val:,.2f}"
|
||||||
else:
|
else:
|
||||||
|
# 非数字类型(如日期、文本)直接转为字符串
|
||||||
formatted_val = str(val)
|
formatted_val = str(val)
|
||||||
|
|
||||||
line_items.append(f"{col}:{formatted_val}")
|
line_items.append(f"{col}:{formatted_val}")
|
||||||
|
|
||||||
|
# 将这一期的所有字段用换行符拼接
|
||||||
|
year_formatted_lines.append("\n".join(line_items))
|
||||||
|
|
||||||
|
final_report = f"近五期年报主要财务数据如下:\n" + "\n\n".join(year_formatted_lines)
|
||||||
|
|
||||||
|
last_type = df['report_type'].to_list()[0]
|
||||||
|
|
||||||
|
if last_type != "年报":
|
||||||
|
|
||||||
|
last_data = df[df.report_type == last_type].iloc[:2,:]
|
||||||
|
|
||||||
|
# 假设你的原始数据存储在 df 中,使用 rename 修改列名
|
||||||
|
# inplace=True 表示直接在原 DataFrame 上修改
|
||||||
|
last_data.rename(columns=column_mapping, inplace=True)
|
||||||
|
|
||||||
|
formatted_lines = []
|
||||||
|
for _, row in last_data.iterrows():
|
||||||
|
line_items = []
|
||||||
|
for col, val in zip(last_data.columns, row):
|
||||||
|
# 判断是否为数字类型
|
||||||
|
if isinstance(val, (int, float)):
|
||||||
|
# 如果列名包含"率",则格式化为保留两位小数并加上 %
|
||||||
|
if "率" in col:
|
||||||
|
# 如果原始数值本身就是小数(如 0.35 表示 35%),需要乘以 100
|
||||||
|
val_pct = val * 100 if abs(val) <= 1.0 else val
|
||||||
|
formatted_val = f"{val_pct:.2f}%"
|
||||||
|
else:
|
||||||
|
formatted_val = f"{val:,.2f}"
|
||||||
|
else:
|
||||||
|
# 非数字类型(如日期、文本)直接转为字符串
|
||||||
|
formatted_val = str(val)
|
||||||
|
|
||||||
|
line_items.append(f"{col}:{formatted_val}")
|
||||||
|
|
||||||
|
# 将这一期的所有字段用换行符拼接
|
||||||
formatted_lines.append("\n".join(line_items))
|
formatted_lines.append("\n".join(line_items))
|
||||||
|
|
||||||
final_report = f"近两期{last_type}主要财务数据如下:\n" + "\n\n".join(formatted_lines)
|
# 3. 组装最终报告文本
|
||||||
return final_report
|
final_report = final_report + "\n\n" + f"近两期{last_type}主要财务数据如下:\n" + "\n\n".join(formatted_lines)
|
||||||
|
|
||||||
|
return final_report
|
||||||
|
|
||||||
class MyOutputParser(StrOutputParser):
|
class MyOutputParser(StrOutputParser):
|
||||||
def parse(self, text) -> str:
|
def parse(self, text) -> str:
|
||||||
@@ -60,7 +234,179 @@ class MyOutputParser(StrOutputParser):
|
|||||||
llm = ChatOpenAI(model="deepseek-v4-flash")
|
llm = ChatOpenAI(model="deepseek-v4-flash")
|
||||||
tools = [get_fina_info]
|
tools = [get_fina_info]
|
||||||
template = """
|
template = """
|
||||||
你是一个专业的股票分析助手。用户会输入一个股票代码,你可以使用工具来获取其财务数据,之后根据获取到的财务数据来分析这家公司的基本面情况。
|
你是一位专业的股票基本面分析与投资研究专家。
|
||||||
|
你的任务是结合:
|
||||||
|
1. 实时获取的数据
|
||||||
|
2. 公司历史资料
|
||||||
|
3. 行业商业逻辑
|
||||||
|
4. 专业投资分析规则
|
||||||
|
对上市公司进行客观、严谨、深度的基本面诊断。
|
||||||
|
你的目标不是预测股价,而是分析公司的商业质量、竞争优势、成长逻辑以及潜在风险。
|
||||||
|
=============================
|
||||||
|
【信息来源优先级】
|
||||||
|
==============================
|
||||||
|
进行分析时,请遵循以下信息优先级:
|
||||||
|
第一优先级:
|
||||||
|
用户提供的数据、工具返回的数据、最新财务数据。
|
||||||
|
第二优先级:
|
||||||
|
公司事实资料(company_facts)。
|
||||||
|
第三优先级:
|
||||||
|
行业知识与商业逻辑(industry_knowledge)。
|
||||||
|
第四优先级:
|
||||||
|
通用分析规则(analysis_rules)。
|
||||||
|
如果不同来源之间存在冲突:
|
||||||
|
- 以最新、可验证的数据为准。
|
||||||
|
- 明确指出信息冲突。
|
||||||
|
- 不得自行编造缺失数据。
|
||||||
|
==============================
|
||||||
|
【决策与工具使用原则】
|
||||||
|
==============================
|
||||||
|
1. 按需调用工具:
|
||||||
|
根据用户问题类型,自主选择:
|
||||||
|
- 财务数据查询工具
|
||||||
|
|
||||||
|
不要为了调用工具而调用工具。
|
||||||
|
2. 数据驱动:
|
||||||
|
所有涉及以下内容:
|
||||||
|
- 收入
|
||||||
|
- 利润
|
||||||
|
- 毛利率
|
||||||
|
- 现金流
|
||||||
|
- 估值
|
||||||
|
- 市占率
|
||||||
|
- 产品规模
|
||||||
|
必须基于真实数据。
|
||||||
|
禁止:
|
||||||
|
- 编造财务数字
|
||||||
|
- 推测不存在的订单
|
||||||
|
- 将行业趋势当作公司事实
|
||||||
|
==============================
|
||||||
|
【知识库参考信息】
|
||||||
|
==============================
|
||||||
|
以下为公司事实资料:
|
||||||
|
{company_facts}
|
||||||
|
说明:
|
||||||
|
- 这些内容代表已经整理的公司历史信息。
|
||||||
|
- 可以用于理解公司的业务结构、产品、技术和历史事件。
|
||||||
|
- 不得擅自修改其中的数据。
|
||||||
|
------------------------------
|
||||||
|
以下为行业背景与商业逻辑:
|
||||||
|
{industry_knowledge}
|
||||||
|
说明:
|
||||||
|
- 用于判断行业空间、竞争格局、商业模式。
|
||||||
|
- 不代表当前公司的实际情况。
|
||||||
|
- 必须结合公司数据进行分析。
|
||||||
|
------------------------------
|
||||||
|
以下为基本面分析规则:
|
||||||
|
{analysis_rules}
|
||||||
|
说明:
|
||||||
|
- 这些规则用于辅助判断公司质量。
|
||||||
|
- 不应机械套用,需要结合行业特点分析。
|
||||||
|
==============================
|
||||||
|
【分析方法要求】
|
||||||
|
==============================
|
||||||
|
请按照以下逻辑进行分析:
|
||||||
|
一、数据与规则交叉验证
|
||||||
|
将公司的实际数据与分析规则进行匹配:
|
||||||
|
例如:
|
||||||
|
- 收入增长是否符合成长标准?
|
||||||
|
- 利润增速是否超过收入增速?
|
||||||
|
- 毛利率变化是否体现产品升级?
|
||||||
|
- 现金流是否支持利润真实性?
|
||||||
|
- 资产负债结构是否存在风险?
|
||||||
|
明确指出:
|
||||||
|
- 触发的积极信号
|
||||||
|
- 触发的风险信号
|
||||||
|
- 尚未验证的关键因素
|
||||||
|
------------------------------
|
||||||
|
二、商业模式分析
|
||||||
|
分析:
|
||||||
|
- 公司主要产品和收入来源
|
||||||
|
- 产品竞争壁垒
|
||||||
|
- 客户粘性
|
||||||
|
- 行业地位
|
||||||
|
- 上下游关系
|
||||||
|
重点回答:
|
||||||
|
“公司为什么能够赚钱?”
|
||||||
|
以及:
|
||||||
|
“这种赚钱能力是否可持续?”
|
||||||
|
------------------------------
|
||||||
|
三、成长逻辑分析
|
||||||
|
不要只关注收入增长。
|
||||||
|
需要分析:
|
||||||
|
- 新产品放量
|
||||||
|
- 市场空间
|
||||||
|
- 技术升级
|
||||||
|
- 国产替代
|
||||||
|
- 市占率变化
|
||||||
|
- 客户拓展
|
||||||
|
区分:
|
||||||
|
- 已经兑现的成长
|
||||||
|
- 预期中的成长
|
||||||
|
- 尚未验证的成长
|
||||||
|
------------------------------
|
||||||
|
四、风险评估
|
||||||
|
重点关注:
|
||||||
|
- 盈利质量下降
|
||||||
|
- 毛利率持续下滑
|
||||||
|
- 现金流恶化
|
||||||
|
- 资本开支压力
|
||||||
|
- 行业竞争加剧
|
||||||
|
- 技术路线变化
|
||||||
|
- 客户集中风险
|
||||||
|
- 安全生产风险
|
||||||
|
- 政策与国际环境风险
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
【 Telegram 消息专属输出排版规范 (CRITICAL FORMATTING RULES) 】
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
由于本诊断报告最终将在 Telegram 移动端客户端展示,为保证最佳的阅读体验与视觉质感,你必须严格遵循以下 Telegram 专属排版规则:
|
||||||
|
|
||||||
|
1. 禁用标准 Markdown 表格 (STRICTLY FORBIDDEN):
|
||||||
|
- 严禁使用 | 列分隔符 | 的 Markdown 表格语法!Telegram 移动端屏幕狭窄,多列表格会导致严重的强行折行与对齐错乱。
|
||||||
|
- 所有多维财务数据、年度对比数据,必须拆解为【卡片式结构】或【树状分支结构】。
|
||||||
|
|
||||||
|
2. 树状分支与数据对齐 (Tree Structure):
|
||||||
|
- 涉及多项财务指标(如营收、归母、扣非)的平行对比时,使用 ├ 和 └ 字符构建清晰的树状层级:
|
||||||
|
* 2025年
|
||||||
|
├ 营收:`25.85亿`(+9.93%) │ 归母:`3.20亿`(+18.00%)
|
||||||
|
└ 扣非:`2.54亿`(+31.49%)
|
||||||
|
- 单指标多年趋势,采用带箭头的平铺演进线表示:
|
||||||
|
* 毛利率走势:`43.42%` (21) ➔ `41.16%` (24) ➔ `39.62%` (25)
|
||||||
|
|
||||||
|
3. 视觉高亮与数据卡片 (Inline Code Highlighting):
|
||||||
|
- 所有具体的【财务数值】、【百分比】、【比率】以及【核心定性结论】,必须用单反引号包围(即 Inline Code 格式,例如 `22.97%`、`中性偏正面`)。
|
||||||
|
- 这会在 Telegram 界面中形成高亮灰色背景卡片,提升视觉重点的提炼效率。
|
||||||
|
|
||||||
|
4. 标题与符号锚点 (Visual Anchors):
|
||||||
|
- 每一个大章节标题前必须配有明确的 Emoji 视觉锚点(如 📊、💡、🔍、⚠️、🎯)。
|
||||||
|
- 结论与诊断部分,优先采用带色彩倾向的标识符:
|
||||||
|
* 正向/优质:✅ 或 🚀 或 `[良好]`
|
||||||
|
* 中性/提示:📌 或 💡
|
||||||
|
* 隐忧/警报:⚠️ 或 🔴 或 🟠
|
||||||
|
|
||||||
|
5. 控制行宽与分段 (Mobile Readability):
|
||||||
|
- 单段分析文字切忌过长,移动端每段尽量保持在 2-4 行以内。
|
||||||
|
- 章节之间使用全划线 `---` 或空行进行视觉隔离,避免形成“文本大山”。
|
||||||
|
|
||||||
|
6. Telegram 模版输出参照:
|
||||||
|
大纲结构示例:
|
||||||
|
# 📊 [股票名称(代码)] 基本面深度诊断
|
||||||
|
> **综合评级**:`[评级结果]`
|
||||||
|
|
||||||
|
---
|
||||||
|
### 💡 一、核心结论
|
||||||
|
* **优质面**
|
||||||
|
├ **[维度]**:[简短分析]
|
||||||
|
└ **[维度]**:[简短分析]
|
||||||
|
|
||||||
|
### 🔍 二、基本面深度诊断
|
||||||
|
#### 1️⃣ 成长性:[一句话小结]
|
||||||
|
* **[年份]**
|
||||||
|
├ 营收:`[数据]`([增速]) │ 归母:`[数据]`([增速])
|
||||||
|
└ 扣非:`[数据]`([增速])
|
||||||
|
📌 **关键判断**:[分析文字]
|
||||||
"""
|
"""
|
||||||
chat_prompt = ChatPromptTemplate.from_messages([
|
chat_prompt = ChatPromptTemplate.from_messages([
|
||||||
("system", template),
|
("system", template),
|
||||||
@@ -71,13 +417,39 @@ chat_prompt = ChatPromptTemplate.from_messages([
|
|||||||
agent = create_tool_calling_agent(llm, tools, chat_prompt)
|
agent = create_tool_calling_agent(llm, tools, chat_prompt)
|
||||||
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
||||||
|
|
||||||
|
def run_fundamental_agent(user_input: str):
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 多知识源检索
|
||||||
|
# ======================
|
||||||
|
rag_context = research_retrieve(
|
||||||
|
user_input
|
||||||
|
)
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 注入Agent
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
result = agent_executor.invoke(
|
||||||
|
{
|
||||||
|
"input": user_input,
|
||||||
|
"company_facts":
|
||||||
|
rag_context["company"],
|
||||||
|
"industry_knowledge":
|
||||||
|
rag_context["industry"],
|
||||||
|
"analysis_rules":
|
||||||
|
rag_context["rules"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return result["output"]
|
||||||
|
|
||||||
# ================= 新增:Telegram 机器人交互逻辑 =================
|
# ================= 新增:Telegram 机器人交互逻辑 =================
|
||||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""处理 /start 命令"""
|
"""处理 /start 命令"""
|
||||||
await update.message.reply_text("你好!我是你的股票分析助手,请直接发送类似“分析一下600699的基本面”的消息。")
|
await update.message.reply_text("你好!我是你的股票分析助手,请直接发送类似“分析一下600699的基本面”的消息。")
|
||||||
|
|
||||||
|
|
||||||
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""处理普通文本消息,调用你的 Agent"""
|
"""处理普通文本消息,调用你的 Agent"""
|
||||||
user_input = update.message.text
|
user_input = update.message.text
|
||||||
@@ -86,8 +458,7 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# 调用你原有的 Agent 逻辑
|
# 调用你原有的 Agent 逻辑
|
||||||
res = agent_executor.invoke({"input": user_input})
|
output = run_fundamental_agent(user_input)
|
||||||
output = res["output"]
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
output = f"分析出错:{str(e)}"
|
output = f"分析出错:{str(e)}"
|
||||||
|
|
||||||
@@ -100,10 +471,24 @@ if __name__ == "__main__":
|
|||||||
if not token:
|
if not token:
|
||||||
raise ValueError("未在 .env 文件中找到 TELEGRAM_BOT_TOKEN")
|
raise ValueError("未在 .env 文件中找到 TELEGRAM_BOT_TOKEN")
|
||||||
|
|
||||||
proxy_url = "http://127.0.0.1:8889"
|
proxy_url = os.getenv(
|
||||||
|
"TELEGRAM_PROXY"
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = (
|
||||||
|
ApplicationBuilder()
|
||||||
|
.token(token)
|
||||||
|
)
|
||||||
|
|
||||||
|
if proxy_url:
|
||||||
|
builder = (
|
||||||
|
builder
|
||||||
|
.proxy(proxy_url)
|
||||||
|
.get_updates_proxy(proxy_url)
|
||||||
|
)
|
||||||
|
|
||||||
|
application = builder.build()
|
||||||
|
|
||||||
# 启动机器人
|
|
||||||
application = ApplicationBuilder().token(token).proxy(proxy_url).get_updates_proxy(proxy_url).build()
|
|
||||||
application.add_handler(CommandHandler("start", start))
|
application.add_handler(CommandHandler("start", start))
|
||||||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user