273 lines
7.9 KiB
Markdown
273 lines
7.9 KiB
Markdown
# 统一股票分析调用规范
|
||
|
||
## 📌 核心原则
|
||
|
||
**所有涉及股票分析的功能必须使用统一的分析函数和数据结构!**
|
||
|
||
---
|
||
|
||
## 🎯 适用场景
|
||
|
||
✅ 以下所有场景必须遵循此规范:
|
||
|
||
- 首页单股分析
|
||
- 首页批量分析
|
||
- 持仓批量分析(UI触发)
|
||
- 持仓定时分析(自动触发)
|
||
- 智策板块中的个股分析
|
||
- 智瞰龙虎中的个股分析
|
||
- 主力选股中的个股分析
|
||
- **任何未来新增的股票分析功能**
|
||
|
||
---
|
||
|
||
## ✅ 正确做法
|
||
|
||
### 1. 调用统一分析函数
|
||
|
||
```python
|
||
from app import analyze_single_stock_for_batch
|
||
|
||
result = analyze_single_stock_for_batch(
|
||
symbol="600519.SH",
|
||
period="1y",
|
||
enabled_analysts_config={
|
||
'technical': True,
|
||
'fundamental': True,
|
||
'fund_flow': True,
|
||
'risk': True,
|
||
'sentiment': False,
|
||
'news': False
|
||
},
|
||
selected_model="deepseek-chat"
|
||
)
|
||
```
|
||
|
||
### 2. 使用统一字段名
|
||
|
||
```python
|
||
# 提取分析结果
|
||
final_decision = result["final_decision"]
|
||
stock_info = result["stock_info"]
|
||
|
||
# 使用正确的字段名
|
||
rating = final_decision.get("rating", "未知") # ✅
|
||
confidence = final_decision.get("confidence_level", "N/A") # ✅
|
||
entry_range = final_decision.get("entry_range", "N/A") # ✅
|
||
take_profit = final_decision.get("take_profit", "N/A") # ✅
|
||
stop_loss = final_decision.get("stop_loss", "N/A") # ✅
|
||
target_price = final_decision.get("target_price", "N/A") # ✅
|
||
advice = final_decision.get("advice", "") # ✅
|
||
```
|
||
|
||
### 3. 统一数据解析
|
||
|
||
```python
|
||
import re
|
||
|
||
# 解析进场区间(格式如"10.5-12.3")
|
||
entry_range = final_decision.get("entry_range", "")
|
||
entry_min, entry_max = None, None
|
||
if entry_range and isinstance(entry_range, str) and "-" in entry_range:
|
||
try:
|
||
parts = entry_range.split("-")
|
||
entry_min = float(parts[0].strip())
|
||
entry_max = float(parts[1].strip())
|
||
except:
|
||
pass
|
||
|
||
# 解析止盈止损(提取数字,如"15.8元" → 15.8)
|
||
take_profit_str = final_decision.get("take_profit", "")
|
||
take_profit = None
|
||
if take_profit_str:
|
||
try:
|
||
numbers = re.findall(r'\d+\.?\d*', str(take_profit_str))
|
||
if numbers:
|
||
take_profit = float(numbers[0])
|
||
except:
|
||
pass
|
||
|
||
# 止损位同理
|
||
stop_loss_str = final_decision.get("stop_loss", "")
|
||
stop_loss = None
|
||
if stop_loss_str:
|
||
try:
|
||
numbers = re.findall(r'\d+\.?\d*', str(stop_loss_str))
|
||
if numbers:
|
||
stop_loss = float(numbers[0])
|
||
except:
|
||
pass
|
||
```
|
||
|
||
### 4. 统一结果展示
|
||
|
||
```python
|
||
# 评级颜色标识
|
||
if "强烈买入" in rating or "买入" in rating:
|
||
rating_color = "🟢"
|
||
elif "卖出" in rating:
|
||
rating_color = "🔴"
|
||
else:
|
||
rating_color = "🟡"
|
||
|
||
# UI展示(Streamlit示例)
|
||
with st.expander(f"{rating_color} {code} - {rating} (信心度: {confidence})"):
|
||
col1, col2 = st.columns(2)
|
||
|
||
with col1:
|
||
st.markdown("**进出场位置**")
|
||
st.write(f"进场区间: {entry_range}")
|
||
st.write(f"目标价: {target_price}")
|
||
|
||
with col2:
|
||
st.markdown("**风控位置**")
|
||
st.write(f"止盈位: {take_profit}")
|
||
st.write(f"止损位: {stop_loss}")
|
||
|
||
if advice:
|
||
st.markdown("**投资建议**")
|
||
st.info(advice)
|
||
```
|
||
|
||
---
|
||
|
||
## ❌ 禁止行为
|
||
|
||
### 1. 不要直接调用 ai_agents
|
||
|
||
```python
|
||
# ❌ 错误做法
|
||
from ai_agents import StockAnalysisAgents
|
||
agents = StockAnalysisAgents()
|
||
result = agents.technical_analyst_agent(...) # 禁止!
|
||
```
|
||
|
||
### 2. 不要使用废弃字段名
|
||
|
||
```python
|
||
# ❌ 错误的字段名
|
||
rating = final_decision.get("investment_rating") # 已废弃
|
||
confidence = final_decision.get("confidence") # 已废弃
|
||
positions = final_decision.get("entry_exit_positions") # 已废弃
|
||
entry_min = positions.get("entry_zone_min") # 已废弃
|
||
```
|
||
|
||
### 3. 不要重复实现分析逻辑
|
||
|
||
```python
|
||
# ❌ 错误做法
|
||
from stock_data import StockDataFetcher
|
||
from ai_agents import StockAnalysisAgents
|
||
|
||
fetcher = StockDataFetcher()
|
||
agents = StockAnalysisAgents()
|
||
|
||
# 自己获取数据
|
||
stock_data = fetcher.get_stock_data(symbol)
|
||
indicators = fetcher.calculate_technical_indicators(stock_data)
|
||
|
||
# 自己调用分析师
|
||
result = agents.technical_analyst_agent(...) # 禁止!
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 字段对照表
|
||
|
||
| 正确字段名 | 废弃字段名 | 数据类型 | 示例值 |
|
||
|-----------|-----------|---------|-------|
|
||
| `rating` | `investment_rating` | string | "买入", "持有", "卖出" |
|
||
| `confidence_level` | `confidence` | string/number | "8/10", "N/A" |
|
||
| `entry_range` | `entry_exit_positions["entry_zone_min/max"]` | string | "10.5-12.3" |
|
||
| `take_profit` | `entry_exit_positions["take_profit"]` | string | "止盈: 15.8元" |
|
||
| `stop_loss` | `entry_exit_positions["stop_loss"]` | string | "止损: 9.2元" |
|
||
| `target_price` | - | string | "目标价: 18.5元" |
|
||
| `advice` | `summary` | string | "建议买入..." |
|
||
|
||
---
|
||
|
||
## 🔍 代码审查检查清单
|
||
|
||
提交涉及股票分析的代码时,请确认:
|
||
|
||
- [ ] 使用了 `app.analyze_single_stock_for_batch()` 而非直接调用 `ai_agents`
|
||
- [ ] 使用了正确的字段名(`rating`, `confidence_level`, `entry_range`等)
|
||
- [ ] 没有使用废弃字段名(`investment_rating`, `entry_exit_positions`等)
|
||
- [ ] 数据解析逻辑与规范一致(split("-"), re.findall())
|
||
- [ ] UI展示格式与其他模块保持一致
|
||
- [ ] 通知推送使用相同的数据结构
|
||
|
||
---
|
||
|
||
## 📚 参考代码
|
||
|
||
### 推荐参考
|
||
|
||
1. **`portfolio_manager.py`** - 完整的分析调用和数据保存示例
|
||
2. **`portfolio_ui.py`** - UI展示和数据解析示例
|
||
3. **`portfolio_scheduler.py`** - 监测同步和通知推送示例
|
||
4. **`notification_service.py`** - 通知内容构建示例
|
||
|
||
### 设计文档
|
||
|
||
- **`openspec/changes/add-portfolio-scheduled-analysis/design.md`** - Decision 4: 统一股票分析调用规范
|
||
- **`openspec/changes/add-portfolio-scheduled-analysis/specs/stock-analysis/spec.md`** - Requirement: 统一股票分析调用规范
|
||
|
||
---
|
||
|
||
## 💡 好处
|
||
|
||
遵循此规范可以获得:
|
||
|
||
1. ✅ **维护成本降低**:只需维护一个分析函数
|
||
2. ✅ **测试成本降低**:只需测试一个分析流程
|
||
3. ✅ **Bug修复效率**:一处修复,全局生效
|
||
4. ✅ **新功能快速开发**:直接复用,无需重写
|
||
5. ✅ **用户体验一致**:所有场景看到的结果格式一致
|
||
6. ✅ **代码复用最大化**:避免重复代码
|
||
7. ✅ **自动兼容优化**:首页分析的优化自动应用到所有场景
|
||
|
||
---
|
||
|
||
## ⚠️ 违规后果
|
||
|
||
不遵循此规范可能导致:
|
||
|
||
- ❌ 字段名不一致,UI显示"未知"或错误
|
||
- ❌ 数据解析失败,监测同步失败
|
||
- ❌ 通知推送格式错误
|
||
- ❌ 代码重复,维护困难
|
||
- ❌ Bug修复需要改多处
|
||
- ❌ 用户体验不一致
|
||
|
||
---
|
||
|
||
## 🆘 常见问题
|
||
|
||
### Q: 为什么必须使用统一函数?
|
||
A: 确保所有场景使用相同的AI模型、数据源、分析流程,避免结果不一致。首页分析的任何优化都会自动应用到所有场景。
|
||
|
||
### Q: 为什么不能直接调用 ai_agents?
|
||
A: `app.analyze_single_stock_for_batch()` 已经封装了完整的数据获取、分析、错误处理流程。直接调用 ai_agents 会导致代码重复和逻辑不一致。
|
||
|
||
### Q: 旧代码使用了废弃字段怎么办?
|
||
A: 必须修改!参考 `portfolio_manager.py` 中的正确用法,使用新的字段名。
|
||
|
||
### Q: 如何解析字符串字段(如进场区间)?
|
||
A: 参考本文档"统一数据解析"章节的代码示例,使用 `split("-")` 和正则表达式。
|
||
|
||
### Q: 新功能可以不遵循此规范吗?
|
||
A: **不可以!** 所有涉及股票分析的功能都必须遵循此规范,这是强制要求。
|
||
|
||
---
|
||
|
||
## 📝 更新日志
|
||
|
||
- **2024-10-20**: 初始版本,基于持仓定时分析功能的实践总结
|
||
- **规范来源**: OpenSpec - `add-portfolio-scheduled-analysis` - Decision 4
|
||
|
||
---
|
||
|
||
**遵循规范,代码更优!** 🎉
|
||
|