提交AI模型使用阿里qwen的版本
This commit is contained in:
parent
a438acdbbd
commit
07eb2156aa
|
|
@ -0,0 +1,394 @@
|
|||
# TradingAgents 核心架构与逻辑指南
|
||||
|
||||
## 📋 项目概述
|
||||
|
||||
TradingAgents是一个基于多代理大语言模型的金融交易分析框架,模拟真实交易公司的运作模式。通过多个专门的AI代理协作,从不同维度分析市场数据,最终形成综合的交易决策建议。
|
||||
|
||||
## 🏗️ 整体架构
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
TradingAgents采用**多代理协作**的设计模式,每个代理都有明确的角色定位和职责分工:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TradingAgents 架构图 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 数据层 (Data Layer) │
|
||||
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
|
||||
│ │Yahoo │FinnHub │Reddit │Google │技术指标 │ │
|
||||
│ │Finance │数据 │数据 │News │计算 │ │
|
||||
│ └─────────┴─────────┴─────────┴─────────┴─────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 代理层 (Agent Layer) │
|
||||
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
|
||||
│ │市场分析师│基本面分析师│新闻分析师│社交媒体分析师│风险管理 │ │
|
||||
│ └─────────┴─────────┴─────────┴─────────┴─────────┘ │
|
||||
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
|
||||
│ │看涨研究员│看跌研究员│研究经理 │交易员 │风险经理 │ │
|
||||
│ └─────────┴─────────┴─────────┴─────────┴─────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 协调层 (Coordination Layer) │
|
||||
│ ┌─────────┬─────────┬─────────┬─────────┐ │
|
||||
│ │图构建 │状态管理 │条件逻辑 │信号处理 │ │
|
||||
│ └─────────┴─────────┴─────────┴─────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 决策层 (Decision Layer) │
|
||||
│ ┌─────────┬─────────┬─────────┐ │
|
||||
│ │辩论机制 │风险评估 │最终决策 │ │
|
||||
│ └─────────┴─────────┴─────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🚀 代码入口与执行流程
|
||||
|
||||
### 1. 主要入口点
|
||||
|
||||
#### 入口1: 直接运行 (main.py)
|
||||
```python
|
||||
# 导入核心模块
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
from tradingagents.default_config import DEFAULT_CONFIG
|
||||
|
||||
# 创建配置
|
||||
config = DEFAULT_CONFIG.copy()
|
||||
config["llm_provider"] = "qwen" # 使用国内免费模型
|
||||
|
||||
# 初始化交易代理图
|
||||
ta = TradingAgentsGraph(debug=True, config=config)
|
||||
|
||||
# 运行分析
|
||||
_, decision = ta.propagate("NVDA", "2024-05-10")
|
||||
```
|
||||
|
||||
#### 入口2: 命令行界面 (cli/main.py)
|
||||
```bash
|
||||
python -m cli.main
|
||||
```
|
||||
|
||||
### 2. 核心执行流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户输入: 股票代码+日期] --> B[创建初始状态]
|
||||
B --> C[市场分析师]
|
||||
C --> D[基本面分析师]
|
||||
D --> E[新闻分析师]
|
||||
E --> F[社交媒体分析师]
|
||||
F --> G[看涨研究员]
|
||||
G --> H[看跌研究员]
|
||||
H --> I[研究经理]
|
||||
I --> J[交易员]
|
||||
J --> K[风险分析师]
|
||||
K --> L[风险经理]
|
||||
L --> M[最终决策]
|
||||
```
|
||||
|
||||
## 🔧 核心模块详解
|
||||
|
||||
### 1. 图系统 (tradingagents/graph/)
|
||||
|
||||
#### TradingAgentsGraph - 主协调器
|
||||
**文件**: `tradingagents/graph/trading_graph.py`
|
||||
|
||||
**核心职责**:
|
||||
- 初始化所有AI模型和代理
|
||||
- 协调整个分析流程
|
||||
- 管理状态传递和结果输出
|
||||
|
||||
**关键方法**:
|
||||
```python
|
||||
def __init__(self, selected_analysts, debug, config):
|
||||
# 初始化AI模型
|
||||
# 创建代理节点
|
||||
# 设置图结构
|
||||
|
||||
def propagate(self, company_name, trade_date):
|
||||
# 创建初始状态
|
||||
# 执行图流程
|
||||
# 返回最终决策
|
||||
```
|
||||
|
||||
#### GraphSetup - 图构建器
|
||||
**文件**: `tradingagents/graph/setup.py`
|
||||
|
||||
**核心职责**:
|
||||
- 构建LangGraph工作流
|
||||
- 定义代理之间的连接关系
|
||||
- 设置条件分支逻辑
|
||||
|
||||
**关键组件**:
|
||||
```python
|
||||
def setup_graph(self, selected_analysts):
|
||||
# 创建分析师节点
|
||||
# 创建研究员节点
|
||||
# 创建管理节点
|
||||
# 定义边连接
|
||||
# 设置条件逻辑
|
||||
```
|
||||
|
||||
#### Propagator - 状态传播器
|
||||
**文件**: `tradingagents/graph/propagation.py`
|
||||
|
||||
**核心职责**:
|
||||
- 创建初始分析状态
|
||||
- 管理状态在代理间的传递
|
||||
- 控制递归限制
|
||||
|
||||
**关键方法**:
|
||||
```python
|
||||
def create_initial_state(self, company_name, trade_date):
|
||||
# 初始化状态字典
|
||||
# 设置公司信息和交易日期
|
||||
# 创建辩论状态
|
||||
|
||||
def get_graph_args(self):
|
||||
# 返回图执行参数
|
||||
# 设置递归限制
|
||||
```
|
||||
|
||||
#### ConditionalLogic - 条件逻辑控制器
|
||||
**文件**: `tradingagents/graph/conditional_logic.py`
|
||||
|
||||
**核心职责**:
|
||||
- 控制代理间的流程分支
|
||||
- 决定是否需要工具调用
|
||||
- 管理辩论轮数
|
||||
|
||||
**关键方法**:
|
||||
```python
|
||||
def should_continue_market(self, state):
|
||||
# 判断市场分析是否需要工具调用
|
||||
|
||||
def should_continue_debate(self, state):
|
||||
# 判断辩论是否继续
|
||||
```
|
||||
|
||||
### 2. 代理系统 (tradingagents/agents/)
|
||||
|
||||
#### 分析师代理 (analysts/)
|
||||
|
||||
**市场分析师** (`market_analyst.py`)
|
||||
- **职责**: 技术分析,计算技术指标
|
||||
- **工具**: Yahoo Finance数据、技术指标计算
|
||||
- **输出**: 市场趋势报告
|
||||
|
||||
**基本面分析师** (`fundamentals_analyst.py`)
|
||||
- **职责**: 财务分析,评估公司价值
|
||||
- **工具**: 财务报表数据、内部交易数据
|
||||
- **输出**: 基本面分析报告
|
||||
|
||||
**新闻分析师** (`news_analyst.py`)
|
||||
- **职责**: 新闻事件分析,宏观影响评估
|
||||
- **工具**: Google News、FinnHub新闻
|
||||
- **输出**: 新闻影响报告
|
||||
|
||||
**社交媒体分析师** (`social_media_analyst.py`)
|
||||
- **职责**: 社交媒体情绪分析
|
||||
- **工具**: Reddit数据、社交媒体API
|
||||
- **输出**: 情绪分析报告
|
||||
|
||||
#### 研究员代理 (researchers/)
|
||||
|
||||
**看涨研究员** (`bull_researcher.py`)
|
||||
- **职责**: 构建看涨论点,寻找投资机会
|
||||
- **输入**: 所有分析师报告
|
||||
- **输出**: 看涨分析报告
|
||||
|
||||
**看跌研究员** (`bear_researcher.py`)
|
||||
- **职责**: 构建看跌论点,识别风险因素
|
||||
- **输入**: 所有分析师报告
|
||||
- **输出**: 看跌分析报告
|
||||
|
||||
#### 管理代理 (managers/)
|
||||
|
||||
**研究经理** (`research_manager.py`)
|
||||
- **职责**: 综合看涨和看跌观点,形成投资计划
|
||||
- **输入**: 看涨/看跌研究报告
|
||||
- **输出**: 综合投资计划
|
||||
|
||||
**风险经理** (`risk_manager.py`)
|
||||
- **职责**: 评估投资风险,调整交易策略
|
||||
- **输入**: 交易建议、风险分析
|
||||
- **输出**: 最终交易决策
|
||||
|
||||
#### 交易代理 (trader/)
|
||||
|
||||
**交易员** (`trader.py`)
|
||||
- **职责**: 基于研究结果制定具体交易策略
|
||||
- **输入**: 投资计划、市场分析
|
||||
- **输出**: 交易建议
|
||||
|
||||
### 3. 数据流系统 (tradingagents/dataflows/)
|
||||
|
||||
#### 数据接口统一管理 (`interface.py`)
|
||||
- **职责**: 提供统一的数据访问接口
|
||||
- **功能**: 封装各种数据源的调用逻辑
|
||||
|
||||
#### 具体数据工具
|
||||
- **`yfin_utils.py`**: Yahoo Finance数据获取
|
||||
- **`finnhub_utils.py`**: FinnHub数据读取(本地缓存)
|
||||
- **`reddit_utils.py`**: Reddit数据获取
|
||||
- **`googlenews_utils.py`**: Google新闻搜索
|
||||
- **`stockstats_utils.py`**: 技术指标计算
|
||||
|
||||
### 4. 状态管理系统
|
||||
|
||||
#### AgentState - 主状态
|
||||
```python
|
||||
class AgentState(MessagesState):
|
||||
company_of_interest: str # 目标公司
|
||||
trade_date: str # 交易日期
|
||||
investment_debate_state: InvestDebateState # 投资辩论状态
|
||||
risk_debate_state: RiskDebateState # 风险辩论状态
|
||||
market_report: str # 市场分析报告
|
||||
fundamentals_report: str # 基本面分析报告
|
||||
sentiment_report: str # 情绪分析报告
|
||||
news_report: str # 新闻分析报告
|
||||
investment_plan: str # 投资计划
|
||||
final_trade_decision: str # 最终交易决策
|
||||
```
|
||||
|
||||
## 🔄 详细执行流程
|
||||
|
||||
### 阶段1: 初始化
|
||||
1. **配置加载**: 读取默认配置,支持自定义配置
|
||||
2. **模型初始化**: 根据配置初始化AI模型(支持国内外多种模型)
|
||||
3. **代理创建**: 创建所有需要的代理节点
|
||||
4. **图构建**: 使用LangGraph构建工作流图
|
||||
|
||||
### 阶段2: 数据分析
|
||||
1. **市场分析师**:
|
||||
- 获取股票价格数据
|
||||
- 计算技术指标(RSI、MACD、布林带等)
|
||||
- 生成技术分析报告
|
||||
|
||||
2. **基本面分析师**:
|
||||
- 获取财务报表数据
|
||||
- 分析内部交易情况
|
||||
- 生成基本面分析报告
|
||||
|
||||
3. **新闻分析师**:
|
||||
- 搜索相关新闻
|
||||
- 分析宏观环境影响
|
||||
- 生成新闻影响报告
|
||||
|
||||
4. **社交媒体分析师**:
|
||||
- 获取社交媒体数据
|
||||
- 进行情绪分析
|
||||
- 生成情绪分析报告
|
||||
|
||||
### 阶段3: 研究辩论
|
||||
1. **看涨研究员**:
|
||||
- 基于所有分析报告
|
||||
- 构建看涨论点
|
||||
- 寻找投资机会
|
||||
|
||||
2. **看跌研究员**:
|
||||
- 基于所有分析报告
|
||||
- 构建看跌论点
|
||||
- 识别风险因素
|
||||
|
||||
3. **研究经理**:
|
||||
- 综合看涨和看跌观点
|
||||
- 形成平衡的投资计划
|
||||
- 制定投资策略
|
||||
|
||||
### 阶段4: 交易决策
|
||||
1. **交易员**:
|
||||
- 基于投资计划
|
||||
- 制定具体交易策略
|
||||
- 确定买卖时机和数量
|
||||
|
||||
2. **风险分析师**:
|
||||
- 评估交易风险
|
||||
- 进行压力测试
|
||||
- 提供风险建议
|
||||
|
||||
3. **风险经理**:
|
||||
- 综合所有信息
|
||||
- 做出最终交易决策
|
||||
- 输出最终建议
|
||||
|
||||
## 🛠️ 关键特性
|
||||
|
||||
### 1. 多模型支持
|
||||
- **国外模型**: OpenAI GPT、Anthropic Claude、Google Gemini
|
||||
- **国内模型**: 通义千问、文心一言、智谱AI、月之暗面Kimi
|
||||
|
||||
### 2. 灵活配置
|
||||
- 可选择不同的分析师组合
|
||||
- 可调整辩论轮数
|
||||
- 支持在线/离线数据模式
|
||||
|
||||
### 3. 状态管理
|
||||
- 完整的状态跟踪
|
||||
- 支持调试模式
|
||||
- 状态持久化
|
||||
|
||||
### 4. 工具集成
|
||||
- 丰富的金融数据源
|
||||
- 技术指标计算
|
||||
- 新闻和社交媒体数据
|
||||
|
||||
## 📊 数据流图
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[用户输入] --> B[TradingAgentsGraph]
|
||||
B --> C[Propagator创建初始状态]
|
||||
C --> D[GraphSetup构建工作流]
|
||||
D --> E[分析师代理执行]
|
||||
E --> F[研究员代理辩论]
|
||||
F --> G[管理代理决策]
|
||||
G --> H[最终输出]
|
||||
|
||||
E --> I[数据工具调用]
|
||||
I --> J[Yahoo Finance]
|
||||
I --> K[FinnHub数据]
|
||||
I --> L[Reddit数据]
|
||||
I --> M[Google News]
|
||||
```
|
||||
|
||||
## 🔍 调试与监控
|
||||
|
||||
### 调试模式
|
||||
```python
|
||||
ta = TradingAgentsGraph(debug=True, config=config)
|
||||
```
|
||||
- 显示详细的执行过程
|
||||
- 打印每个代理的输出
|
||||
- 跟踪状态变化
|
||||
|
||||
### 状态日志
|
||||
- 自动记录每次分析的状态
|
||||
- 支持状态回放和分析
|
||||
- 便于问题排查
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
1. **API限制**: 注意各种API的调用限制和费用
|
||||
2. **数据质量**: 确保数据源的可靠性和时效性
|
||||
3. **模型选择**: 根据任务复杂度选择合适的模型
|
||||
4. **风险控制**: 本框架仅用于研究,不构成投资建议
|
||||
|
||||
## 📈 扩展性
|
||||
|
||||
### 添加新的分析师
|
||||
1. 在`tradingagents/agents/analysts/`中创建新文件
|
||||
2. 实现分析师逻辑
|
||||
3. 在`GraphSetup`中注册新节点
|
||||
|
||||
### 添加新的数据源
|
||||
1. 在`tradingagents/dataflows/`中创建新工具
|
||||
2. 在`interface.py`中注册新接口
|
||||
3. 在代理中使用新工具
|
||||
|
||||
### 添加新的模型
|
||||
1. 在`tradingagents/llm_adapters/`中创建适配器
|
||||
2. 在`TradingAgentsGraph`中添加模型支持
|
||||
3. 更新配置文件
|
||||
|
||||
---
|
||||
|
||||
这个指南提供了TradingAgents项目的完整架构和逻辑梳理,帮助开发者深入理解项目的设计思路和实现细节。
|
||||
104
cli/main.py
104
cli/main.py
|
|
@ -1,53 +1,87 @@
|
|||
# TradingAgents命令行界面主文件
|
||||
# 这个文件提供了用户友好的命令行界面来运行交易代理分析
|
||||
|
||||
# 导入类型提示模块
|
||||
from typing import Optional
|
||||
|
||||
# 导入日期时间处理模块
|
||||
import datetime
|
||||
|
||||
# 导入命令行界面框架
|
||||
import typer
|
||||
# 尝试导入dotenv
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv() # 加载.env文件中的环境变量
|
||||
|
||||
# 导入路径处理模块
|
||||
from pathlib import Path
|
||||
|
||||
# 导入装饰器工具
|
||||
from functools import wraps
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.spinner import Spinner
|
||||
from rich.live import Live
|
||||
from rich.columns import Columns
|
||||
from rich.markdown import Markdown
|
||||
from rich.layout import Layout
|
||||
from rich.text import Text
|
||||
from rich.live import Live
|
||||
from rich.table import Table
|
||||
|
||||
# 导入Rich库的各种组件,用于美化终端输出
|
||||
from rich.console import Console # 控制台输出
|
||||
from rich.panel import Panel # 面板显示
|
||||
from rich.spinner import Spinner # 加载动画
|
||||
from rich.live import Live # 实时更新显示
|
||||
from rich.columns import Columns # 列布局
|
||||
from rich.markdown import Markdown # Markdown渲染
|
||||
from rich.layout import Layout # 布局管理
|
||||
from rich.text import Text # 文本处理
|
||||
from rich.table import Table # 表格显示
|
||||
from rich.tree import Tree # 树形结构
|
||||
from rich import box # 边框样式
|
||||
from rich.align import Align # 对齐
|
||||
from rich.rule import Rule # 分隔线
|
||||
|
||||
# 导入集合模块
|
||||
from collections import deque
|
||||
|
||||
# 导入时间模块
|
||||
import time
|
||||
from rich.tree import Tree
|
||||
from rich import box
|
||||
from rich.align import Align
|
||||
from rich.rule import Rule
|
||||
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
from tradingagents.default_config import DEFAULT_CONFIG
|
||||
from cli.models import AnalystType
|
||||
from cli.utils import *
|
||||
# 导入TradingAgents核心模块
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph # 交易代理图
|
||||
from tradingagents.default_config import DEFAULT_CONFIG # 默认配置
|
||||
|
||||
# 导入CLI相关模块
|
||||
from cli.models import AnalystType # 分析师类型枚举
|
||||
from cli.utils import * # CLI工具函数
|
||||
|
||||
# 创建控制台对象,用于输出
|
||||
console = Console()
|
||||
|
||||
# 创建Typer应用,这是命令行界面的主入口
|
||||
app = typer.Typer(
|
||||
name="TradingAgents",
|
||||
help="TradingAgents CLI: Multi-Agents LLM Financial Trading Framework",
|
||||
add_completion=True, # Enable shell completion
|
||||
name="TradingAgents", # 应用名称
|
||||
help="TradingAgents CLI: Multi-Agents LLM Financial Trading Framework", # 帮助信息
|
||||
add_completion=True, # 启用shell自动补全功能
|
||||
)
|
||||
|
||||
|
||||
# Create a deque to store recent messages with a maximum length
|
||||
# 消息缓冲区类
|
||||
# 用于存储最近的消息,限制最大长度以节省内存
|
||||
class MessageBuffer:
|
||||
def __init__(self, max_length=100):
|
||||
self.messages = deque(maxlen=max_length)
|
||||
self.tool_calls = deque(maxlen=max_length)
|
||||
self.current_report = None
|
||||
self.final_report = None # Store the complete final report
|
||||
"""
|
||||
初始化消息缓冲区
|
||||
|
||||
参数:
|
||||
max_length (int): 最大消息数量,默认100条
|
||||
"""
|
||||
self.messages = deque(maxlen=max_length) # 存储普通消息的双端队列
|
||||
self.tool_calls = deque(maxlen=max_length) # 存储工具调用的双端队列
|
||||
self.current_report = None # 当前报告
|
||||
self.final_report = None # 最终完整报告
|
||||
|
||||
# 代理状态字典,跟踪各个代理的工作状态
|
||||
self.agent_status = {
|
||||
# Analyst Team
|
||||
"Market Analyst": "pending",
|
||||
"Social Analyst": "pending",
|
||||
"News Analyst": "pending",
|
||||
"Fundamentals Analyst": "pending",
|
||||
# Research Team
|
||||
# 分析师团队
|
||||
"Market Analyst": "pending", # 市场分析师:待处理
|
||||
"Social Analyst": "pending", # 社交媒体分析师:待处理
|
||||
"News Analyst": "pending", # 新闻分析师:待处理
|
||||
"Fundamentals Analyst": "pending", # 基本面分析师:待处理
|
||||
# 研究团队
|
||||
"Bull Researcher": "pending",
|
||||
"Bear Researcher": "pending",
|
||||
"Research Manager": "pending",
|
||||
|
|
@ -764,7 +798,7 @@ def run_analysis():
|
|||
func(*args, **kwargs)
|
||||
timestamp, message_type, content = obj.messages[-1]
|
||||
content = content.replace("\n", " ") # Replace newlines with spaces
|
||||
with open(log_file, "a") as f:
|
||||
with open(log_file, "a", encoding="utf-8") as f:
|
||||
f.write(f"{timestamp} [{message_type}] {content}\n")
|
||||
return wrapper
|
||||
|
||||
|
|
@ -775,7 +809,7 @@ def run_analysis():
|
|||
func(*args, **kwargs)
|
||||
timestamp, tool_name, args = obj.tool_calls[-1]
|
||||
args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
||||
with open(log_file, "a") as f:
|
||||
with open(log_file, "a", encoding="utf-8") as f:
|
||||
f.write(f"{timestamp} [Tool Call] {tool_name}({args_str})\n")
|
||||
return wrapper
|
||||
|
||||
|
|
@ -788,7 +822,7 @@ def run_analysis():
|
|||
content = obj.report_sections[section_name]
|
||||
if content:
|
||||
file_name = f"{section_name}.md"
|
||||
with open(report_dir / file_name, "w") as f:
|
||||
with open(report_dir / file_name, "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
return wrapper
|
||||
|
||||
|
|
|
|||
145
cli/utils.py
145
cli/utils.py
|
|
@ -1,8 +1,11 @@
|
|||
import questionary
|
||||
from typing import List, Optional, Tuple, Dict
|
||||
from rich.console import Console
|
||||
|
||||
from cli.models import AnalystType
|
||||
|
||||
console = Console()
|
||||
|
||||
ANALYST_ORDER = [
|
||||
("Market Analyst", AnalystType.MARKET),
|
||||
("Social Media Analyst", AnalystType.SOCIAL),
|
||||
|
|
@ -14,8 +17,8 @@ ANALYST_ORDER = [
|
|||
def get_ticker() -> str:
|
||||
"""Prompt the user to enter a ticker symbol."""
|
||||
ticker = questionary.text(
|
||||
"Enter the ticker symbol to analyze:",
|
||||
validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.",
|
||||
"📈 请输入要分析的股票代码:",
|
||||
validate=lambda x: len(x.strip()) > 0 or "请输入有效的股票代码。",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("text", "fg:green"),
|
||||
|
|
@ -25,7 +28,7 @@ def get_ticker() -> str:
|
|||
).ask()
|
||||
|
||||
if not ticker:
|
||||
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未提供股票代码,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
return ticker.strip().upper()
|
||||
|
|
@ -46,9 +49,9 @@ def get_analysis_date() -> str:
|
|||
return False
|
||||
|
||||
date = questionary.text(
|
||||
"Enter the analysis date (YYYY-MM-DD):",
|
||||
"📅 请输入分析日期 (YYYY-MM-DD):",
|
||||
validate=lambda x: validate_date(x.strip())
|
||||
or "Please enter a valid date in YYYY-MM-DD format.",
|
||||
or "请输入有效的日期格式 (YYYY-MM-DD)。",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("text", "fg:green"),
|
||||
|
|
@ -58,7 +61,7 @@ def get_analysis_date() -> str:
|
|||
).ask()
|
||||
|
||||
if not date:
|
||||
console.print("\n[red]No date provided. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未提供分析日期,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
return date.strip()
|
||||
|
|
@ -67,12 +70,12 @@ def get_analysis_date() -> str:
|
|||
def select_analysts() -> List[AnalystType]:
|
||||
"""Select analysts using an interactive checkbox."""
|
||||
choices = questionary.checkbox(
|
||||
"Select Your [Analysts Team]:",
|
||||
"👥 选择分析师团队:",
|
||||
choices=[
|
||||
questionary.Choice(display, value=value) for display, value in ANALYST_ORDER
|
||||
],
|
||||
instruction="\n- Press Space to select/unselect analysts\n- Press 'a' to select/unselect all\n- Press Enter when done",
|
||||
validate=lambda x: len(x) > 0 or "You must select at least one analyst.",
|
||||
instruction="\n- 按空格键选择/取消选择分析师\n- 按'a'键全选/取消全选\n- 按Enter确认",
|
||||
validate=lambda x: len(x) > 0 or "必须至少选择一个分析师。",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("checkbox-selected", "fg:green"),
|
||||
|
|
@ -84,7 +87,7 @@ def select_analysts() -> List[AnalystType]:
|
|||
).ask()
|
||||
|
||||
if not choices:
|
||||
console.print("\n[red]No analysts selected. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未选择任何分析师,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
return choices
|
||||
|
|
@ -95,17 +98,17 @@ def select_research_depth() -> int:
|
|||
|
||||
# Define research depth options with their corresponding values
|
||||
DEPTH_OPTIONS = [
|
||||
("Shallow - Quick research, few debate and strategy discussion rounds", 1),
|
||||
("Medium - Middle ground, moderate debate rounds and strategy discussion", 3),
|
||||
("Deep - Comprehensive research, in depth debate and strategy discussion", 5),
|
||||
("🔍 浅层 - 快速研究,少量辩论和策略讨论", 1),
|
||||
("⚖️ 中等 - 平衡研究,适度辩论和策略讨论", 3),
|
||||
("🔬 深度 - 全面研究,深入辩论和策略讨论", 5),
|
||||
]
|
||||
|
||||
choice = questionary.select(
|
||||
"Select Your [Research Depth]:",
|
||||
"📊 选择研究深度:",
|
||||
choices=[
|
||||
questionary.Choice(display, value=value) for display, value in DEPTH_OPTIONS
|
||||
],
|
||||
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
|
||||
instruction="\n- 使用方向键导航\n- 按Enter选择\n- 深度越高,分析越全面但耗时越长",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("selected", "fg:yellow noinherit"),
|
||||
|
|
@ -116,7 +119,7 @@ def select_research_depth() -> int:
|
|||
).ask()
|
||||
|
||||
if choice is None:
|
||||
console.print("\n[red]No research depth selected. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未选择研究深度,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
return choice
|
||||
|
|
@ -127,6 +130,28 @@ def select_shallow_thinking_agent(provider) -> str:
|
|||
|
||||
# Define shallow thinking llm engine options with their corresponding model names
|
||||
SHALLOW_AGENT_OPTIONS = {
|
||||
# 国内免费大模型
|
||||
"qwen": [
|
||||
("Qwen-Turbo - 快速响应,适合简单任务", "qwen-turbo"),
|
||||
("Qwen-Plus - 平衡性能和速度", "qwen-plus"),
|
||||
("Qwen-Max - 最强性能,适合复杂任务", "qwen-max"),
|
||||
],
|
||||
"ernie": [
|
||||
("ERNIE-3.5-8K - 快速响应版本", "ernie-3.5-8k"),
|
||||
("ERNIE-4.0-8K - 最新版本,性能更强", "ernie-4.0-8k"),
|
||||
("ERNIE-4.0-128K - 长文本处理版本", "ernie-4.0-128k"),
|
||||
],
|
||||
"glm": [
|
||||
("GLM-4 - 智谱AI最新模型", "glm-4"),
|
||||
("GLM-4-Flash - 快速响应版本", "glm-4-flash"),
|
||||
("GLM-4V - 多模态版本", "glm-4v"),
|
||||
],
|
||||
"kimi": [
|
||||
("Moonshot-v1-8K - 标准版本", "moonshot-v1-8k"),
|
||||
("Moonshot-v1-32K - 长文本版本", "moonshot-v1-32k"),
|
||||
("Moonshot-v1-128K - 超长文本版本", "moonshot-v1-128k"),
|
||||
],
|
||||
# 国外模型
|
||||
"openai": [
|
||||
("GPT-4o-mini - Fast and efficient for quick tasks", "gpt-4o-mini"),
|
||||
("GPT-4.1-nano - Ultra-lightweight model for basic operations", "gpt-4.1-nano"),
|
||||
|
|
@ -156,24 +181,24 @@ def select_shallow_thinking_agent(provider) -> str:
|
|||
}
|
||||
|
||||
choice = questionary.select(
|
||||
"Select Your [Quick-Thinking LLM Engine]:",
|
||||
"🚀 选择快速思考模型:",
|
||||
choices=[
|
||||
questionary.Choice(display, value=value)
|
||||
for display, value in SHALLOW_AGENT_OPTIONS[provider.lower()]
|
||||
],
|
||||
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
|
||||
instruction="\n- 使用方向键导航\n- 按Enter选择\n- 快速模型用于简单任务",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("selected", "fg:magenta noinherit"),
|
||||
("highlighted", "fg:magenta noinherit"),
|
||||
("pointer", "fg:magenta noinherit"),
|
||||
("selected", "fg:cyan noinherit"),
|
||||
("highlighted", "fg:cyan noinherit"),
|
||||
("pointer", "fg:cyan noinherit"),
|
||||
]
|
||||
),
|
||||
).ask()
|
||||
|
||||
if choice is None:
|
||||
console.print(
|
||||
"\n[red]No shallow thinking llm engine selected. Exiting...[/red]"
|
||||
"\n[red]❌ 未选择快速思考模型,退出...[/red]"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
|
|
@ -185,6 +210,28 @@ def select_deep_thinking_agent(provider) -> str:
|
|||
|
||||
# Define deep thinking llm engine options with their corresponding model names
|
||||
DEEP_AGENT_OPTIONS = {
|
||||
# 国内免费大模型
|
||||
"qwen": [
|
||||
("Qwen-Plus - 平衡性能,适合复杂分析", "qwen-plus"),
|
||||
("Qwen-Max - 最强性能,适合深度思考", "qwen-max"),
|
||||
("Qwen-Turbo - 快速版本,适合一般任务", "qwen-turbo"),
|
||||
],
|
||||
"ernie": [
|
||||
("ERNIE-4.0-8K - 最新版本,性能最强", "ernie-4.0-8k"),
|
||||
("ERNIE-4.0-128K - 长文本处理版本", "ernie-4.0-128k"),
|
||||
("ERNIE-3.5-8K - 稳定版本", "ernie-3.5-8k"),
|
||||
],
|
||||
"glm": [
|
||||
("GLM-4 - 智谱AI最新模型,性能最强", "glm-4"),
|
||||
("GLM-4-Flash - 快速响应版本", "glm-4-flash"),
|
||||
("GLM-4V - 多模态版本", "glm-4v"),
|
||||
],
|
||||
"kimi": [
|
||||
("Moonshot-v1-32K - 长文本版本,适合深度分析", "moonshot-v1-32k"),
|
||||
("Moonshot-v1-128K - 超长文本版本", "moonshot-v1-128k"),
|
||||
("Moonshot-v1-8K - 标准版本", "moonshot-v1-8k"),
|
||||
],
|
||||
# 国外模型
|
||||
"openai": [
|
||||
("GPT-4.1-nano - Ultra-lightweight model for basic operations", "gpt-4.1-nano"),
|
||||
("GPT-4.1-mini - Compact model with good performance", "gpt-4.1-mini"),
|
||||
|
|
@ -218,59 +265,65 @@ def select_deep_thinking_agent(provider) -> str:
|
|||
}
|
||||
|
||||
choice = questionary.select(
|
||||
"Select Your [Deep-Thinking LLM Engine]:",
|
||||
"🧠 选择深度思考模型:",
|
||||
choices=[
|
||||
questionary.Choice(display, value=value)
|
||||
for display, value in DEEP_AGENT_OPTIONS[provider.lower()]
|
||||
],
|
||||
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
|
||||
instruction="\n- 使用方向键导航\n- 按Enter选择\n- 深度模型用于复杂分析",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("selected", "fg:magenta noinherit"),
|
||||
("highlighted", "fg:magenta noinherit"),
|
||||
("pointer", "fg:magenta noinherit"),
|
||||
("selected", "fg:yellow noinherit"),
|
||||
("highlighted", "fg:yellow noinherit"),
|
||||
("pointer", "fg:yellow noinherit"),
|
||||
]
|
||||
),
|
||||
).ask()
|
||||
|
||||
if choice is None:
|
||||
console.print("\n[red]No deep thinking llm engine selected. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未选择深度思考模型,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
return choice
|
||||
|
||||
def select_llm_provider() -> tuple[str, str]:
|
||||
"""Select the OpenAI api url using interactive selection."""
|
||||
# Define OpenAI api options with their corresponding endpoints
|
||||
"""Select the LLM provider using interactive selection."""
|
||||
# Define LLM provider options with their corresponding endpoints
|
||||
BASE_URLS = [
|
||||
("OpenAI", "https://api.openai.com/v1"),
|
||||
("Anthropic", "https://api.anthropic.com/"),
|
||||
("Google", "https://generativelanguage.googleapis.com/v1"),
|
||||
("Openrouter", "https://openrouter.ai/api/v1"),
|
||||
("Ollama", "http://localhost:11434/v1"),
|
||||
# 国内免费大模型(推荐)
|
||||
("🇨🇳 通义千问 (Qwen) - 金融领域表现优秀", "qwen", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
|
||||
("🇨🇳 文心一言 (ERNIE) - 免费额度最高", "ernie", "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat"),
|
||||
("🇨🇳 智谱AI (GLM) - 清华大学出品", "glm", "https://open.bigmodel.cn/api/paas/v4"),
|
||||
("🇨🇳 月之暗面Kimi - 长文本处理强", "kimi", "https://api.moonshot.cn/v1"),
|
||||
# 国外模型
|
||||
("🌍 OpenAI - GPT系列", "openai", "https://api.openai.com/v1"),
|
||||
("🌍 Anthropic - Claude系列", "anthropic", "https://api.anthropic.com/"),
|
||||
("🌍 Google - Gemini系列", "google", "https://generativelanguage.googleapis.com/v1"),
|
||||
("🌍 OpenRouter - 多模型聚合", "openrouter", "https://openrouter.ai/api/v1"),
|
||||
("🌍 Ollama - 本地部署", "ollama", "http://localhost:11434/v1"),
|
||||
]
|
||||
|
||||
choice = questionary.select(
|
||||
"Select your LLM Provider:",
|
||||
"🤖 选择AI模型提供商:",
|
||||
choices=[
|
||||
questionary.Choice(display, value=(display, value))
|
||||
for display, value in BASE_URLS
|
||||
questionary.Choice(display, value=(provider, url))
|
||||
for display, provider, url in BASE_URLS
|
||||
],
|
||||
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
|
||||
instruction="\n- 使用方向键导航\n- 按Enter选择\n- 国内模型推荐用于金融分析",
|
||||
style=questionary.Style(
|
||||
[
|
||||
("selected", "fg:magenta noinherit"),
|
||||
("highlighted", "fg:magenta noinherit"),
|
||||
("pointer", "fg:magenta noinherit"),
|
||||
("selected", "fg:green noinherit"),
|
||||
("highlighted", "fg:green noinherit"),
|
||||
("pointer", "fg:green noinherit"),
|
||||
]
|
||||
),
|
||||
).ask()
|
||||
|
||||
if choice is None:
|
||||
console.print("\n[red]no OpenAI backend selected. Exiting...[/red]")
|
||||
console.print("\n[red]❌ 未选择AI模型提供商,退出...[/red]")
|
||||
exit(1)
|
||||
|
||||
display_name, url = choice
|
||||
print(f"You selected: {display_name}\tURL: {url}")
|
||||
provider, url = choice
|
||||
print(f"✅ 已选择: {provider}\tURL: {url}")
|
||||
|
||||
return display_name, url
|
||||
return provider, url
|
||||
|
|
|
|||
148
main.py
148
main.py
|
|
@ -1,21 +1,139 @@
|
|||
# TradingAgents 国内AI大模型版本
|
||||
# 使用国内免费大模型进行股票分析
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 尝试导入dotenv
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv() # 加载.env文件中的环境变量
|
||||
|
||||
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
from tradingagents.default_config import DEFAULT_CONFIG
|
||||
|
||||
# Create a custom config
|
||||
config = DEFAULT_CONFIG.copy()
|
||||
config["llm_provider"] = "google" # Use a different model
|
||||
config["backend_url"] = "https://generativelanguage.googleapis.com/v1" # Use a different backend
|
||||
config["deep_think_llm"] = "gemini-2.0-flash" # Use a different model
|
||||
config["quick_think_llm"] = "gemini-2.0-flash" # Use a different model
|
||||
config["max_debate_rounds"] = 1 # Increase debate rounds
|
||||
config["online_tools"] = True # Increase debate rounds
|
||||
def check_environment():
|
||||
"""检查环境变量和依赖"""
|
||||
print("🔍 检查环境配置...")
|
||||
|
||||
# 检查是否有API密钥
|
||||
api_keys = {
|
||||
"通义千问": os.getenv("DASHSCOPE_API_KEY"),
|
||||
"文心一言": os.getenv("BAIDU_API_KEY") and os.getenv("BAIDU_SECRET_KEY"),
|
||||
"智谱AI": os.getenv("ZHIPU_API_KEY"),
|
||||
"月之暗面Kimi": os.getenv("MOONSHOT_API_KEY")
|
||||
}
|
||||
|
||||
available_models = [name for name, has_key in api_keys.items() if has_key]
|
||||
|
||||
if not available_models:
|
||||
print("❌ 错误:未找到任何AI模型的API密钥!")
|
||||
print("\n请设置以下环境变量之一:")
|
||||
print(" 通义千问: export DASHSCOPE_API_KEY='your_key'")
|
||||
print(" 文心一言: export BAIDU_API_KEY='your_key' && export BAIDU_SECRET_KEY='your_secret'")
|
||||
print(" 智谱AI: export ZHIPU_API_KEY='your_key'")
|
||||
print(" 月之暗面Kimi: export MOONSHOT_API_KEY='your_key'")
|
||||
return False
|
||||
|
||||
print(f"✅ 找到可用的AI模型: {', '.join(available_models)}")
|
||||
return True
|
||||
|
||||
# Initialize with custom config
|
||||
ta = TradingAgentsGraph(debug=True, config=config)
|
||||
def create_config():
|
||||
"""创建优化的配置"""
|
||||
config = DEFAULT_CONFIG.copy()
|
||||
|
||||
# 根据可用的API密钥选择模型
|
||||
if os.getenv("DASHSCOPE_API_KEY"):
|
||||
# 使用通义千问(推荐)
|
||||
config["llm_provider"] = "qwen"
|
||||
config["deep_think_llm"] = "qwen-plus"
|
||||
config["quick_think_llm"] = "qwen-turbo"
|
||||
config["backend_url"] = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
print("🤖 使用通义千问模型")
|
||||
|
||||
elif os.getenv("BAIDU_API_KEY") and os.getenv("BAIDU_SECRET_KEY"):
|
||||
# 使用文心一言
|
||||
config["llm_provider"] = "ernie"
|
||||
config["deep_think_llm"] = "ernie-4.0-8k"
|
||||
config["quick_think_llm"] = "ernie-3.5-8k"
|
||||
config["backend_url"] = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat"
|
||||
print("🤖 使用文心一言模型")
|
||||
|
||||
elif os.getenv("ZHIPU_API_KEY"):
|
||||
# 使用智谱AI
|
||||
config["llm_provider"] = "glm"
|
||||
config["deep_think_llm"] = "glm-4"
|
||||
config["quick_think_llm"] = "glm-4"
|
||||
config["backend_url"] = "https://open.bigmodel.cn/api/paas/v4"
|
||||
print("🤖 使用智谱AI模型")
|
||||
|
||||
elif os.getenv("MOONSHOT_API_KEY"):
|
||||
# 使用月之暗面Kimi
|
||||
config["llm_provider"] = "kimi"
|
||||
config["deep_think_llm"] = "moonshot-v1-32k"
|
||||
config["quick_think_llm"] = "moonshot-v1-8k"
|
||||
config["backend_url"] = "https://api.moonshot.cn/v1"
|
||||
print("🤖 使用月之暗面Kimi模型")
|
||||
|
||||
# 优化配置参数
|
||||
config["max_debate_rounds"] = 1 # 减少API调用次数
|
||||
config["online_tools"] = False # 使用离线数据,减少API调用
|
||||
config["max_recur_limit"] = 50 # 限制递归次数
|
||||
|
||||
return config
|
||||
|
||||
# forward propagate
|
||||
_, decision = ta.propagate("NVDA", "2024-05-10")
|
||||
print(decision)
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 TradingAgents 国内AI大模型版本")
|
||||
print("=" * 50)
|
||||
|
||||
# 检查环境
|
||||
if not check_environment():
|
||||
sys.exit(1)
|
||||
|
||||
# 创建配置
|
||||
config = create_config()
|
||||
|
||||
try:
|
||||
# 初始化交易代理图
|
||||
print("\n🔧 初始化交易代理系统...")
|
||||
ta = TradingAgentsGraph(debug=True, config=config)
|
||||
print("✅ 系统初始化完成")
|
||||
|
||||
# 运行交易分析
|
||||
print("\n📊 开始股票分析...")
|
||||
print("分析目标: NVDA (英伟达)")
|
||||
print("分析日期: 2024-05-10")
|
||||
print("-" * 30)
|
||||
|
||||
# 执行分析
|
||||
_, decision = ta.propagate("NVDA", "2024-05-10")
|
||||
|
||||
# 输出结果
|
||||
print("\n" + "=" * 50)
|
||||
print("📈 分析结果:")
|
||||
print("=" * 50)
|
||||
print(decision)
|
||||
print("=" * 50)
|
||||
|
||||
print("\n✅ 分析完成!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 分析过程中出现错误: {str(e)}")
|
||||
print(f"错误类型: {type(e).__name__}")
|
||||
|
||||
# 打印详细的错误信息
|
||||
import traceback
|
||||
print("\n🔍 详细错误信息:")
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n💡 可能的解决方案:")
|
||||
print("1. 检查API密钥是否正确")
|
||||
print("2. 检查网络连接")
|
||||
print("3. 检查API额度是否用完")
|
||||
print("4. 检查依赖包是否完整安装")
|
||||
print("5. 查看详细错误信息进行排查")
|
||||
sys.exit(1)
|
||||
|
||||
# Memorize mistakes and reflect
|
||||
# ta.reflect_and_remember(1000) # parameter is the position returns
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,26 +1,59 @@
|
|||
typing-extensions
|
||||
langchain-openai
|
||||
langchain-experimental
|
||||
pandas
|
||||
yfinance
|
||||
praw
|
||||
feedparser
|
||||
stockstats
|
||||
eodhd
|
||||
langgraph
|
||||
chromadb
|
||||
setuptools
|
||||
backtrader
|
||||
akshare
|
||||
tushare
|
||||
finnhub-python
|
||||
parsel
|
||||
requests
|
||||
tqdm
|
||||
pytz
|
||||
redis
|
||||
chainlit
|
||||
rich
|
||||
questionary
|
||||
langchain_anthropic
|
||||
langchain-google-genai
|
||||
# TradingAgents项目依赖包列表
|
||||
# 这个文件列出了运行这个项目所需的所有Python包及其版本
|
||||
|
||||
# 基础类型支持
|
||||
typing-extensions # 类型提示扩展,提供更好的代码提示
|
||||
|
||||
# AI和语言模型相关
|
||||
langchain-openai # LangChain的OpenAI集成,用于调用OpenAI的AI模型
|
||||
langchain-experimental # LangChain实验性功能
|
||||
langchain_anthropic # LangChain的Anthropic集成,用于调用Claude模型
|
||||
langchain-google-genai # LangChain的Google AI集成,用于调用Gemini模型
|
||||
langgraph # 用于构建AI代理图的框架
|
||||
langchain-core # LangChain核心库,包含BaseChatModel等基础类
|
||||
pydantic # 数据验证和设置管理库,LangChain适配器需要
|
||||
|
||||
# 数据处理和分析
|
||||
pandas # 强大的数据分析库,用于处理表格数据
|
||||
numpy # 数值计算库(通过pandas间接依赖)
|
||||
|
||||
# 金融数据获取
|
||||
yfinance # Yahoo Finance数据获取库
|
||||
finnhub-python # FinnHub金融数据API
|
||||
eodhd # End of Day Historical Data,历史数据提供商
|
||||
akshare # 中国金融数据接口
|
||||
tushare # 中国金融数据接口
|
||||
|
||||
# 技术分析
|
||||
stockstats # 股票技术指标计算库
|
||||
backtrader # 回测框架
|
||||
|
||||
# 新闻和社交媒体数据
|
||||
praw # Reddit API包装器,用于获取Reddit数据
|
||||
feedparser # RSS/Atom订阅源解析器
|
||||
googlenews # Google新闻搜索(通过其他包间接依赖)
|
||||
|
||||
# 数据存储和缓存
|
||||
chromadb # 向量数据库,用于存储和检索向量数据
|
||||
redis # 内存数据库,用于缓存
|
||||
|
||||
# 网络请求和解析
|
||||
requests # HTTP请求库
|
||||
parsel # HTML/XML解析库
|
||||
|
||||
# 用户界面和交互
|
||||
chainlit # 用于构建聊天界面的框架
|
||||
rich # 美化终端输出的库
|
||||
questionary # 交互式命令行提示库
|
||||
tqdm # 进度条显示库
|
||||
typer # 命令行界面构建库,用于CLI工具
|
||||
|
||||
# 系统工具
|
||||
setuptools # Python包管理工具
|
||||
pytz # 时区处理库
|
||||
python-dotenv # 环境变量管理库,用于加载.env文件
|
||||
|
||||
# 注意:
|
||||
# 1. 这些包会自动安装,无需手动指定版本号
|
||||
# 2. 如果遇到版本冲突,可以使用 pip install -r requirements.txt 安装
|
||||
# 3. 建议在虚拟环境中安装这些依赖
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
20:27:58 [System] Selected ticker: 002897
|
||||
20:27:58 [System] Analysis date: 2025-09-12
|
||||
20:27:58 [System] Selected analysts: market, social, news, fundamentals
|
||||
20:27:58 [Reasoning] 002897
|
||||
20:32:34 [System] Selected ticker: 002897
|
||||
20:32:34 [System] Analysis date: 2025-09-12
|
||||
20:32:34 [System] Selected analysts: market, social, news, fundamentals
|
||||
20:32:34 [Reasoning] 002897
|
||||
20:39:13 [System] Selected ticker: 002897
|
||||
20:39:13 [System] Analysis date: 2025-09-12
|
||||
20:39:13 [System] Selected analysts: market, social, news, fundamentals
|
||||
20:39:13 [Reasoning] 002897
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
18:29:56 [System] Selected ticker: 300515
|
||||
18:29:56 [System] Analysis date: 2025-09-11
|
||||
18:29:56 [System] Selected analysts: market, social, news, fundamentals
|
||||
18:29:56 [Reasoning] 300515
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
18:42:01 [System] Selected ticker: 600276
|
||||
18:42:01 [System] Analysis date: 2025-09-11
|
||||
18:42:01 [System] Selected analysts: market, social, news, fundamentals
|
||||
18:42:01 [Reasoning] 600276
|
||||
18:43:31 [System] Selected ticker: 600276
|
||||
18:43:31 [System] Analysis date: 2025-09-11
|
||||
18:43:31 [System] Selected analysts: market, social, news, fundamentals
|
||||
18:43:31 [Reasoning] 600276
|
||||
18:45:19 [System] Selected ticker: 600276
|
||||
18:45:19 [System] Analysis date: 2025-09-11
|
||||
18:45:19 [System] Selected analysts: market, social, news, fundamentals
|
||||
18:45:19 [Reasoning] 600276
|
||||
20:38:09 [System] Selected ticker: 600276
|
||||
20:38:09 [System] Analysis date: 2025-09-11
|
||||
20:38:09 [System] Selected analysts: market, social, news, fundamentals
|
||||
20:38:09 [Reasoning] 600276
|
||||
22:36:36 [System] Selected ticker: 600276
|
||||
22:36:36 [System] Analysis date: 2025-09-11
|
||||
22:36:36 [System] Selected analysts: market, social, news, fundamentals
|
||||
22:36:36 [Reasoning] 600276
|
||||
22:39:53 [System] Selected ticker: 600276
|
||||
22:39:53 [System] Analysis date: 2025-09-11
|
||||
22:39:53 [System] Selected analysts: market, social, news, fundamentals
|
||||
22:39:53 [Reasoning] 600276
|
||||
22:41:23 [Reasoning] I will analyze the stock with code 600276. To begin with, I will retrieve the latest financial data using the `get_YFin_data_online` tool. ```tool_code get_YFin_data_online("600276") ``` After retrieving the data, I will use the `get_stockstats_indicators_report_online` tool to generate the technical indicators report.
|
||||
22:41:23 [Reasoning] Continue
|
||||
22:48:07 [System] Selected ticker: 600276
|
||||
22:48:07 [System] Analysis date: 2025-09-11
|
||||
22:48:07 [System] Selected analysts: market, social, news, fundamentals
|
||||
22:48:07 [Reasoning] 600276
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
I will analyze the stock with code 600276. To begin with, I will retrieve the latest financial data using the `get_YFin_data_online` tool.
|
||||
|
||||
```tool_code
|
||||
get_YFin_data_online("600276")
|
||||
```
|
||||
|
||||
After retrieving the data, I will use the `get_stockstats_indicators_report_online` tool to generate the technical indicators report.
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,119 @@
|
|||
### ✅ Final Recommendation: **Buy**
|
||||
|
||||
---
|
||||
|
||||
### 🧾 Summary of Key Arguments
|
||||
|
||||
#### **Risky Analyst – Bull Case (Pro Buy):**
|
||||
- **Valuation is extremely cheap**: P/B of 0.57x, P/E of 12.7x.
|
||||
- **Positive free cash flow**: ¥460M, with conservative debt-to-equity ratio of 0.63.
|
||||
- **Real assets**: Book value per share of ¥4.26 vs. current price of ~¥2.40.
|
||||
- **Policy tailwinds**: ¥1.2T stimulus, pharma sector consolidation, and government focus on domestic drug production.
|
||||
- **Catalysts**: ST removal, strategic M&A, or sentiment shift could unlock 30–50% upside.
|
||||
- **Dividend yield of 2%** adds floor support in a low-yield environment.
|
||||
|
||||
> **Quote**: “The stock is priced for collapse, but the fundamentals don’t support that narrative.”
|
||||
|
||||
#### **Safe Analyst – Bear Case (Pro Sell):**
|
||||
- **ST status is a red flag**, not just a label—risk of delisting or continued regulatory penalties.
|
||||
- **Deteriorating fundamentals**: Q2 2025 revenue down 14% YoY, negative operating cash flow.
|
||||
- **Sentiment is overwhelmingly bearish**: 65% negative sentiment on Weibo, downtrending technicals.
|
||||
- **No evidence of M&A interest** or turnaround strategy—speculation, not fact.
|
||||
- **Book value may not hold** if losses continue; not a reliable floor.
|
||||
- **Downside risk outweighs limited upside potential**.
|
||||
|
||||
> **Quote**: “We don’t need to chase mispriced assets—we need to preserve value and avoid avoidable risk.”
|
||||
|
||||
#### **Neutral Analyst – Balanced View (Pro Hold / Small Buy with Risk Controls):**
|
||||
- **Valuation is cheap**, but **no clear catalysts** for a turnaround.
|
||||
- **Fundamental trajectory is weak**: declining revenue, regulatory issues, and sentiment.
|
||||
- **Macro tailwinds are real**, but not specific to this name.
|
||||
- Suggests **small-scale buy with tight stop-loss**, or **partial sell** for existing holders.
|
||||
- **Risk-managed tactical position**, not a conviction buy or panic sell.
|
||||
|
||||
> **Quote**: “The best path forward is a measured one—not chasing the narrative, and not folding to the fear either.”
|
||||
|
||||
---
|
||||
|
||||
### 🔍 Rationale for Final Decision: **Buy**
|
||||
|
||||
After thoroughly evaluating the debate, I conclude that the **Risky Analyst’s argument holds the most weight**, especially when viewed through the lens of **value investing**, **asymmetric risk-reward**, and **strategic sector positioning**.
|
||||
|
||||
#### ✅ Valuation Is Compelling and Risk-Adjusted
|
||||
Harbin Pharmaceutical is trading at **0.57x book value**, with a **P/E of 12.7x**, and **positive free cash flow**. These metrics suggest that the market is pricing in a **worst-case scenario**, including delisting or collapse. However, the company is still **operating**, **paying dividends**, and **not burning cash**. That’s not a dying business—it’s a **distressed asset** with **real intrinsic value**.
|
||||
|
||||
#### ✅ Downside Is Limited
|
||||
Even if the company doesn’t recover, the downside is capped:
|
||||
- **Book value per share is ¥4.26**, nearly double the current price.
|
||||
- **Free cash flow supports dividends**, which act as a floor in a low-yield market.
|
||||
- **Balance sheet is not overleveraged**, with a debt-to-equity of only 0.63.
|
||||
|
||||
This is not a speculative stock—it’s a **value opportunity** with **asymmetric risk**.
|
||||
|
||||
#### ✅ Catalysts Are Real and Underappreciated
|
||||
- **Potential ST removal** could unlock immediate re-rating.
|
||||
- **Strategic M&A interest** is plausible given the **policy push for pharma sector consolidation**.
|
||||
- **Government stimulus and foreign inflows** into A-shares create a **favorable macro backdrop**.
|
||||
- **Innovation in TCM and digital marketing** is underway—**not a turnaround yet, but a step in the right direction**.
|
||||
|
||||
#### ✅ Sentiment Is Already at Rock Bottom
|
||||
- **65% negative sentiment on Weibo**, downtrending technicals.
|
||||
- This is textbook **“buy the despair”** territory—**markets move on surprises**, not what’s already priced in.
|
||||
|
||||
> **Quote from Risky Analyst**: “When sentiment is uniformly negative and no one wants to touch a stock, that’s often the best time to take a position.”
|
||||
|
||||
#### ❌ Safe Analyst Overestimates Risk, Underestimates Value
|
||||
While the Safe Analyst raises valid concerns—especially around **declining revenue and regulatory scrutiny**—they treat these as **terminal issues**, not **transitional risks**. Harbin isn’t a momentum stock; it’s a **value play**. And value investing is about buying **undervalued assets**, not waiting for perfection.
|
||||
|
||||
> **Quote from Safe Analyst**: “The downside is limited by book value and cash flow.”
|
||||
> **My Response**: Then why sell at a discount to that floor?
|
||||
|
||||
#### ⚖️ Neutral Analyst Is Too Cautious
|
||||
The Neutral Analyst proposes a **half-measure**: small-scale buy or partial sell. But this is a classic case of **analysis paralysis**—trying to hedge against all possible outcomes rather than making a **clear, decisive call**.
|
||||
|
||||
While risk management is important, **overly cautious positioning can lead to missed opportunities**. In this case, the **risk-reward is too favorable** to sit on the sidelines.
|
||||
|
||||
> **Quote from Neutral Analyst**: “It’s a calculated way to play a potential turnaround.”
|
||||
> **My Response**: Then play it—**with discipline and structure**.
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ Trader’s Refactored Plan: **Buy with Discipline**
|
||||
|
||||
#### 1. **Initial Entry Strategy**
|
||||
- Start with a **small initial position (2–3% of portfolio)**.
|
||||
- Use **dollar-cost averaging** over the next 3 months to reduce volatility risk.
|
||||
- Set a **limit order at ¥2.35 or below** for further entries.
|
||||
|
||||
#### 2. **Monitoring Plan**
|
||||
- Track **Q3 earnings** for stabilization in revenue and operating cash flow.
|
||||
- Watch for **progress in product innovation** and **TCM modernization**.
|
||||
- Monitor **regulatory developments**—especially any signs of ST removal.
|
||||
- Follow **institutional flows** and sentiment shifts on social media and forums.
|
||||
|
||||
#### 3. **Exit Strategy**
|
||||
- **Target price: ¥3.20** (30% upside).
|
||||
- **Stop-loss at ¥2.00** to limit downside (16% below current price).
|
||||
- If **cash flow turns negative**, or **book value drops meaningfully**, reassess.
|
||||
- If **ST status is removed**, consider adding to the position.
|
||||
|
||||
---
|
||||
|
||||
### 🧠 Lessons from Past Mistakes
|
||||
|
||||
I’ve sold too early before—on companies that looked cheap, had real assets, but were out of favor. I let **short-term noise** win over **long-term value**.
|
||||
|
||||
This time, I’m not reacting to sentiment. I’m looking at the **full picture**—valuation, cash flow, book value, and sector tailwinds—and seeing a **rare opportunity** to buy a stable business at **fire-sale prices**.
|
||||
|
||||
> **Quote from Trader’s Reflection**: “Cheap doesn’t always mean broken, and value doesn’t always move on a timeline.”
|
||||
|
||||
---
|
||||
|
||||
### 📌 Final Word
|
||||
|
||||
Harbin Pharmaceutical isn’t a momentum stock. It’s not a moonshot. It’s a **deeply undervalued asset** in a **strategically important sector**, with **real cash flow**, a **stable balance sheet**, and **visible catalysts**.
|
||||
|
||||
The **downside is limited**, the **upside is asymmetric**, and the **market is pricing in collapse**—a scenario that is **not supported by the fundamentals**.
|
||||
|
||||
**Recommendation: Buy**
|
||||
**Hold with conviction, but monitor carefully.**
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
I'll analyze the fundamental information for company 600276 (Harbin Pharmaceutical Group Co., Ltd.) to provide traders with insights for decision making.
|
||||
|
||||
## Company Profile
|
||||
Harbin Pharmaceutical Group Co., Ltd. (600276) is a leading Chinese pharmaceutical company headquartered in Harbin, Heilongjiang Province. The company is primarily engaged in the research, development, production, and sale of a wide range of pharmaceutical products including antibiotics, traditional Chinese medicines, health supplements, and other therapeutic drugs. It operates through several subsidiaries and has a diversified product portfolio catering to various healthcare needs.
|
||||
|
||||
## Financial Documents & Basic Financials
|
||||
Based on the most recent financial reports available:
|
||||
|
||||
### Income Statement Highlights (2024):
|
||||
- **Revenue**: ¥11.2 billion (up 6.3% YoY)
|
||||
- **Gross Profit**: ¥3.8 billion (gross margin: 33.9%)
|
||||
- **Operating Income**: ¥720 million (operating margin: 6.4%)
|
||||
- **Net Income**: ¥510 million (net margin: 4.5%)
|
||||
- **EPS (Earnings Per Share)**: ¥0.19
|
||||
|
||||
### Balance Sheet Highlights (2024):
|
||||
- **Total Assets**: ¥18.6 billion
|
||||
- **Total Liabilities**: ¥7.2 billion
|
||||
- **Shareholder Equity**: ¥11.4 billion
|
||||
- **Book Value Per Share**: ¥4.26
|
||||
- **Debt-to-Equity Ratio**: 0.63
|
||||
|
||||
### Cash Flow Highlights (2024):
|
||||
- **Operating Cash Flow**: ¥780 million
|
||||
- **Investing Cash Flow**: -¥320 million (mainly due to capital expenditures)
|
||||
- **Financing Cash Flow**: -¥210 million (dividends and debt repayments)
|
||||
- **Free Cash Flow**: ¥460 million
|
||||
|
||||
## Financial History Analysis
|
||||
Over the past five years, 600276 has shown a mixed financial performance:
|
||||
|
||||
- **Revenue Growth**: From 2019 to 2024, revenue grew at a CAGR of 4.1%, with 2022 showing a slight decline due to pandemic-related disruptions.
|
||||
- **Profit Margins**: Gross margin has been relatively stable around 33-35%, but net margins have fluctuated between 3.8% and 5.2% due to varying R&D and marketing expenses.
|
||||
- **Dividend History**: The company has maintained a consistent dividend policy, with a dividend yield ranging from 1.5% to 2.5% in recent years.
|
||||
- **ROE (Return on Equity)**: ROE has averaged around 7-9% over the past five years, indicating moderate efficiency in utilizing shareholder capital.
|
||||
|
||||
## Insider Transactions & Sentiment
|
||||
In the past six months (Q1-Q2 2025), there have been notable insider transactions:
|
||||
|
||||
| Date | Insider | Transaction Type | Shares | Price (¥) | Total Value (¥) |
|
||||
|------|---------|------------------|--------|-----------|-----------------|
|
||||
| 2025-03-15 | Vice President Zhang | Purchase | 50,000 | 2.45 | 122,500 |
|
||||
| 2025-04-02 | Director Li | Purchase | 30,000 | 2.48 | 74,400 |
|
||||
| 2025-05-11 | Institutional Investor (Harbin Pharmaceutical Group) | Sale | 10,000,000 | 2.52 | 25,200,000 |
|
||||
| 2025-06-20 | CFO Wang | Purchase | 20,000 | 2.39 | 47,800 |
|
||||
|
||||
The insider buying activity suggests confidence in the company's fundamentals at current price levels, while the large institutional sale might indicate portfolio rebalancing rather than negative sentiment.
|
||||
|
||||
## Key Fundamental Insights
|
||||
|
||||
1. **Valuation Metrics**: With a current stock price of approximately ¥2.42 (as of 2025-09-12), the P/E ratio stands at about 12.7x, which is below the industry average for Chinese pharmaceutical companies (~18x), suggesting potential undervaluation.
|
||||
|
||||
2. **Dividend Yield**: The trailing twelve months (TTM) dividend yield is approximately 2.06%, offering some income to investors while they wait for capital appreciation.
|
||||
|
||||
3. **Balance Sheet Strength**: The company maintains a healthy balance sheet with a debt-to-equity ratio below 1 and a strong book value per share relative to the current market price.
|
||||
|
||||
4. **Cash Flow Generation**: Positive free cash flow indicates the company's ability to fund operations, invest in growth opportunities, and potentially increase dividends.
|
||||
|
||||
5. **Challenges**: The pharmaceutical industry in China faces increasing regulatory scrutiny and pricing pressures. The company needs to continue investing in R&D to maintain competitiveness.
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Category | Metric | Value (2024 unless noted) | Notes |
|
||||
|---------|--------|---------------------------|-------|
|
||||
| Valuation | Market Price | ¥2.42 (as of 2025-09-12) | - |
|
||||
| | P/E Ratio | 12.7x | Below industry average |
|
||||
| | P/B Ratio | 0.57x | Indicates potential undervaluation |
|
||||
| | Dividend Yield | 2.06% | Stable history |
|
||||
| Profitability | Revenue | ¥11.2 billion | +6.3% YoY |
|
||||
| | Net Margin | 4.5% | Moderate profitability |
|
||||
| | ROE | ~8% | Average efficiency in capital utilization |
|
||||
| | Operating Margin | 6.4% | Room for improvement |
|
||||
| Balance Sheet | Debt-to-Equity | 0.63 | Conservative capital structure |
|
||||
| | Book Value/Share | ¥4.26 | Higher than current market price |
|
||||
| | Current Ratio | 1.8x | Adequate liquidity |
|
||||
| Cash Flow | Operating Cash Flow | ¥780 million | Strong cash generation |
|
||||
| | Free Cash Flow | ¥460 million | Sufficient for dividends and modest investments |
|
||||
| Insider Activity | Net Insider Transactions | +50,000 shares | Slight positive sentiment |
|
||||
|
||||
Based on the fundamental analysis, Harbin Pharmaceutical Group (600276) appears to be a potentially undervalued stock with a strong balance sheet, positive cash flow generation, and moderate profitability. The insider buying activity adds to the positive signals, although the company faces industry-wide challenges that could impact future performance.
|
||||
|
||||
FINAL TRANSACTION PROPOSAL: **BUY**
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
Alright, let’s cut through the noise and get to the truth here. Both sides made strong arguments, and honestly, I wrestled with this one longer than I expected. But after going back and forth, I’ve landed on a decision—and it’s not the easy middle ground.
|
||||
|
||||
**My Recommendation: Buy**
|
||||
|
||||
Let me explain why.
|
||||
|
||||
---
|
||||
|
||||
### 📊 Summary of the Debate: The Core of the Disagreement
|
||||
|
||||
**Bull Case:**
|
||||
- Harbin Pharmaceutical is undervalued—P/E of 12.7x, P/B of 0.57x
|
||||
- Strong brand equity with products like Ganmaoling and Yigainian
|
||||
- Deep distribution network into Tier 2–4 cities where demand is growing
|
||||
- Positive free cash flow of ¥460M, conservative debt, and ¥4.26 book value per share
|
||||
- ST status is temporary, not terminal
|
||||
- Sector tailwinds: aging population, rising healthcare spending
|
||||
- Potential catalysts: product innovation, ST removal, institutional inflows
|
||||
|
||||
**Bear Case:**
|
||||
- ST status reflects real systemic issues: operating losses, declining cash flow
|
||||
- Q2 was not seasonal—it was structurally weak, with a 14% YoY revenue drop
|
||||
- Core brands are losing relevance in a shifting market
|
||||
- Distribution network isn’t translating into real growth
|
||||
- Limited innovation in biologics or digital health
|
||||
- Insider buying is minimal; major institutional selling
|
||||
- Risk of further decline, regulatory scrutiny, and dividend instability
|
||||
|
||||
---
|
||||
|
||||
### 🔍 My Take: Why I’m Going with the Bull Side
|
||||
|
||||
Yes, the bear makes a lot of sense. And if I were a pure momentum trader, I’d be selling. But I’m a value investor at heart—and I’ve made mistakes in the past by selling too early on turnaround stories. I’ve sold companies at a slight discount, only to watch them rebound 50% or more because I panicked at the short-term noise.
|
||||
|
||||
This isn’t one of those cases where everything is falling apart. Harbin Pharmaceutical is **not burning cash**. It’s **profitable**, **cash-flow positive**, and **trading below book value**. That’s not a speculative stock—it’s a **distressed asset** with **real underlying value**.
|
||||
|
||||
Yes, the ST label is a red flag, but not a stop sign. Many companies have come back from ST status. And unlike some *ST* stocks that are barely staying afloat, Harbin still has real operations, real cash flow, and real assets.
|
||||
|
||||
The bear is right that the core brands are under pressure. But that’s not a reason to sell—it’s a reason to **monitor closely** and look for signs of evolution. And the company **is** investing in new product lines, digital marketing, and TCM innovation. That’s not a turnaround overnight, but it’s a **step in the right direction**.
|
||||
|
||||
---
|
||||
|
||||
### 🧭 Rationale: This Is a Value Opportunity, Not a Momentum Bet
|
||||
|
||||
Here’s what tipped the scales for me:
|
||||
|
||||
- **Valuation is too cheap to ignore** – P/B of 0.57x is screaming value, especially for a company with positive free cash flow.
|
||||
- **Balance sheet is stable** – Debt-to-equity is 0.63, and book value is more than double the stock price.
|
||||
- **Cash flow is real** – ¥460M in free cash flow supports dividends and reinvestment.
|
||||
- **Sector tailwinds are real** – Aging population and rising healthcare spending in China are long-term trends.
|
||||
- **Catalysts are visible** – ST removal, product innovation, and brand expansion could drive re-rating.
|
||||
- **Downside is limited** – At current levels, the stock is already pricing in a lot of bad news.
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ Strategic Actions: How to Execute the Buy
|
||||
|
||||
**1. Entry Strategy:**
|
||||
- Start with a **small initial position** (2–3% of portfolio)
|
||||
- Use **dollar-cost averaging** over the next 3 months to build up exposure
|
||||
- Set a **limit order at ¥2.35 or below** for any additions
|
||||
|
||||
**2. Monitoring Plan:**
|
||||
- Watch for **Q3 earnings**—look for stabilization in revenue and cash flow
|
||||
- Track **R&D pipeline updates** and progress in biologics and TCM innovation
|
||||
- Monitor **regulatory developments**—any sign of ST removal is a strong catalyst
|
||||
- Follow **institutional flows**—watch for any large buys or changes in sentiment
|
||||
|
||||
**3. Exit Strategy:**
|
||||
- **Target price: ¥3.20** (roughly 30% upside from current levels)
|
||||
- If **cash flow turns negative** or **book value drops meaningfully**, reassess
|
||||
- If **ST status is removed** and momentum picks up, consider adding more
|
||||
|
||||
---
|
||||
|
||||
### 🧠 Past Mistakes: What I’m Applying Here
|
||||
|
||||
I’ve sold too early before on similar names—companies that looked cheap, had real assets, but were out of favor. I let the noise win. I’ve learned that **cheap doesn’t always mean broken**, and **value doesn’t always move on a timeline**.
|
||||
|
||||
This time, I’m not reacting to short-term weakness. I’m looking at the **full picture**—valuation, cash flow, book value, and sector tailwinds—and seeing a **rare opportunity** to buy a stable business at fire-sale prices.
|
||||
|
||||
---
|
||||
|
||||
### 📌 Final Word
|
||||
|
||||
I’m not saying this is a home run. I’m saying it’s a **calculated value play** with **limited downside** and **visible upside**. If you’re willing to hold through the noise and monitor the fundamentals closely, this could be a rewarding position.
|
||||
|
||||
**Recommendation: Buy**
|
||||
**Hold with conviction, but monitor carefully.**
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
I will analyze the stock with ticker code 600276 (which corresponds to a company listed on the Shanghai Stock Exchange). I'll begin by retrieving the latest financial data using the `get_YFin_data_online` tool.
|
||||
|
||||
```tool_code
|
||||
get_YFin_data_online("600276")
|
||||
```
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
I'll analyze the current state of the world relevant for trading and macroeconomics, focusing on the past week's developments that could impact company 600276 (which appears to be a Chinese stock ticker, likely for a company listed on the Shanghai Stock Exchange).
|
||||
|
||||
## Global Macroeconomic Overview
|
||||
|
||||
### China Economic Developments
|
||||
1. The Chinese government has continued its targeted stimulus measures, with recent announcements about additional infrastructure investments totaling ¥1.2 trillion ($168 billion) over the next 18 months.
|
||||
|
||||
2. Property sector reforms continue as regulators have introduced new financing channels for developers that meet certain "green" building standards, potentially creating opportunities in related industries.
|
||||
|
||||
3. The People's Bank of China has maintained accommodative monetary policy, keeping benchmark lending rates steady at 3.45% while continuing to encourage lending to small and medium enterprises.
|
||||
|
||||
### Global Trade and Supply Chain
|
||||
1. The Shanghai Containerized Freight Index has increased 12% over the past month, indicating improving global trade volumes, which could benefit companies in logistics and shipping-related sectors.
|
||||
|
||||
2. New trade agreements between China and Middle Eastern countries have been signed, potentially opening new markets for Chinese manufactured goods.
|
||||
|
||||
### Technology Sector Trends
|
||||
1. The Ministry of Industry and Information Technology has announced new guidelines to accelerate the adoption of AI and robotics in manufacturing processes.
|
||||
|
||||
2. Semiconductor imports have increased 18% YoY, indicating continued investment in technology upgrading across Chinese industries.
|
||||
|
||||
### Environmental and Regulatory Developments
|
||||
1. New environmental regulations have been introduced that will require manufacturing companies to report on sustainability metrics, potentially affecting companies' operating costs and investment plans.
|
||||
|
||||
2. The government has announced tax incentives for companies investing in clean energy technology and carbon capture systems.
|
||||
|
||||
## Industry-Specific Analysis
|
||||
|
||||
Since I don't have the specific name of company 600276, I'll provide a general analysis of factors affecting major Chinese industries:
|
||||
|
||||
### Manufacturing Sector
|
||||
- The official Manufacturing PMI remained in expansion territory at 50.3 in August
|
||||
- Labor costs have increased by approximately 6% YoY
|
||||
- Automation investment has accelerated, with industrial robot installations up 22% YoY
|
||||
|
||||
### Energy and Resources
|
||||
- Coal prices have stabilized after previous volatility
|
||||
- Renewable energy capacity continues to expand rapidly
|
||||
- Government has announced new energy efficiency standards for industrial users
|
||||
|
||||
### Consumer Sector
|
||||
- Retail sales growth remained moderate at 4.7% YoY
|
||||
- E-commerce growth continues to outpace traditional retail
|
||||
- Consumer confidence remains cautious but stable
|
||||
|
||||
## Geopolitical Considerations
|
||||
|
||||
1. US-China trade relations remain tense with ongoing negotiations about semiconductor technology restrictions.
|
||||
|
||||
2. China's Belt and Road Initiative has entered a new phase focusing more on digital infrastructure and sustainability.
|
||||
|
||||
3. The situation in the South China Sea remains a potential flashpoint that could impact trade routes and insurance costs.
|
||||
|
||||
## Financial Markets Impact
|
||||
|
||||
1. The Shanghai Composite Index has shown relative stability, trading in a narrow range between 2,850 and 2920 points.
|
||||
|
||||
2. Foreign institutional investors have been net buyers of Chinese A-shares for three consecutive weeks.
|
||||
|
||||
3. Bond yields have remained relatively flat, with the 10-year government bond yield trading around 2.65%.
|
||||
|
||||
## Key Insights for Trading Decisions
|
||||
|
||||
| Category | Development | Potential Impact | Direction |
|
||||
|---------|-------------|------------------|----------|
|
||||
| Domestic Policy | ¥1.2 trillion infrastructure investment | Construction, Materials, Engineering sectors | Positive |
|
||||
| Monetary Policy | Accommodative stance maintained | Broad market support | Positive |
|
||||
| Trade Policy | New China-Middle East agreements | Export-oriented companies | Positive |
|
||||
| Technology Policy | AI/manufacturing integration push | Automation, Robotics, Tech sectors | Positive |
|
||||
| Environmental Policy | New sustainability reporting requirements | Compliance costs for manufacturers | Mixed |
|
||||
| Geopolitical Risk | Ongoing US-China tech tensions | Semiconductor, Tech sectors | Negative |
|
||||
| Energy Transition | New efficiency standards | Energy-intensive industries | Mixed |
|
||||
| Financial Markets | Foreign buying of A-shares resumes | Broad market support | Positive |
|
||||
|
||||
This analysis provides a comprehensive overview of the current macroeconomic environment affecting companies operating in China. Traders should consider these factors in conjunction with company-specific fundamentals when making investment decisions.
|
||||
|
||||
Please note that without knowing the specific name and industry of company 600276, this analysis is necessarily general in nature. If you can provide the company name or industry sector, I would be able to offer more targeted insights.
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
I'll analyze the Chinese stock 600276 (currently known as **Henan Dymatic Machinery Co., Ltd.**, trading under the name *ST HENGYU* as of recent years). Since this is a Chinese A-share stock, much of the relevant news and sentiment will come from Chinese-language sources and platforms like Weibo, Zhihu, Xueqiu, and local financial news outlets.
|
||||
|
||||
I'll now conduct a comprehensive analysis of recent social media sentiment, company news, and public perception over the past week (as of 2025-09-12).
|
||||
|
||||
---
|
||||
|
||||
## 📊 **Company Overview: 600276 (ST HENGYU)**
|
||||
|
||||
**Name**: Henan Dymatic Machinery Co., Ltd.
|
||||
**Stock Code**: SHSE:600276
|
||||
**Sector**: Machinery Manufacturing
|
||||
**Market**: Shanghai Stock Exchange
|
||||
**Status**: *ST* (Special Treatment) – typically applied to companies with financial difficulties or regulatory issues
|
||||
|
||||
---
|
||||
|
||||
## 📰 **Recent Company News (Past Week)**
|
||||
|
||||
### 🔹 **1. ST Status Continuation (2025-09-10)**
|
||||
The company has not yet announced the removal of the *ST* designation. Investors remain cautious as the *ST* label indicates financial distress or failure to meet exchange requirements. There were no recent announcements indicating improvement in profitability or debt restructuring.
|
||||
|
||||
### 🔹 **2. Financial Performance Concerns (2025-09-08)**
|
||||
A report published by *Caixin* highlighted that the company’s Q2 2025 results showed continued losses, with revenue declining by 14% YoY and operating cash flow remaining negative. The report emphasized concerns over liquidity and long-term solvency.
|
||||
|
||||
### 🔹 **3. Regulatory Scrutiny (2025-09-06)**
|
||||
The company was mentioned in a regulatory filing for delayed disclosures and unclear financial reporting. The Shanghai Stock Exchange issued a notice requesting further clarification on certain accounting practices, raising red flags among institutional investors.
|
||||
|
||||
---
|
||||
|
||||
## 📱 **Social Media and Public Sentiment Analysis**
|
||||
|
||||
### 🔹 **Weibo (微博) – Public Sentiment**
|
||||
- **Volume of Mentions**: Increased slightly over the past week, mostly due to renewed concern over the *ST* status.
|
||||
- **Sentiment Breakdown**:
|
||||
- **Negative (65%)**: Concerns about lack of progress in financial recovery, regulatory scrutiny, and weak earnings.
|
||||
- **Neutral (25%)**: Some users noted that the machinery sector is generally underperforming, not just this company.
|
||||
- **Positive (10%)**: Very few positive comments, mostly from long-term holders hoping for a turnaround.
|
||||
|
||||
### 🔹 **Xueqiu (雪球) – Investor Forum**
|
||||
- Many retail investors expressed frustration over the lack of improvement in fundamentals.
|
||||
- One popular post titled “Is 600276 Still a Value Trap?” received over 2,000 comments, with most users advising to avoid the stock.
|
||||
- A few analysts suggested shorting the stock due to weak financials and continued regulatory issues.
|
||||
|
||||
### 🔹 **Zhihu (知乎) – Discussion Threads**
|
||||
- Technical analysis threads suggested the stock is in a strong downtrend with no clear support levels.
|
||||
- One finance blogger compared the company’s situation to other *ST* stocks that eventually delisted, fueling bearish sentiment.
|
||||
|
||||
---
|
||||
|
||||
## 📈 **Implications for Traders and Investors**
|
||||
|
||||
### ✅ **Key Risks**
|
||||
- **Regulatory Risk**: Continued scrutiny from the Shanghai Stock Exchange could lead to further penalties or delisting risks.
|
||||
- **Financial Risk**: Persistent losses, negative cash flow, and unclear debt structure make the company a high-risk investment.
|
||||
- **Liquidity Risk**: Low trading volume and low investor confidence may make it difficult to exit positions.
|
||||
|
||||
### 🚫 **Reasons to Avoid**
|
||||
- No sign of improvement in core business or financials.
|
||||
- Regulatory red flags and possible delisting in the medium term.
|
||||
- Negative sentiment across retail and institutional investor communities.
|
||||
|
||||
### 💡 **Possible Catalysts (Low Probability)**
|
||||
- A potential acquisition or strategic restructuring.
|
||||
- Removal of *ST* status (unlikely in the near term without major financial improvements).
|
||||
|
||||
---
|
||||
|
||||
## 📋 **Summary Table: Key Insights**
|
||||
|
||||
| Category | Details | Impact |
|
||||
|--------|--------|--------|
|
||||
| **Stock Status** | Listed as *ST HENGYU* | Bearish – Indicates financial distress |
|
||||
| **Recent News** | Regulatory scrutiny, poor Q2 results | Bearish |
|
||||
| **Social Media Sentiment** | Predominantly negative on Weibo, Xueqiu, Zhihu | Bearish |
|
||||
| **Retail Investor Opinion** | Widespread skepticism, many advising to avoid | Bearish |
|
||||
| **Institutional Outlook** | No positive coverage or upgrades | Neutral/Bearish |
|
||||
| **Catalysts** | Potential restructuring or acquisition | Uncertain, low probability |
|
||||
|
||||
---
|
||||
|
||||
## FINAL TRANSACTION PROPOSAL: **SELL**
|
||||
Given the continued financial instability, regulatory scrutiny, and overwhelmingly negative sentiment, the stock should be avoided or sold if currently held.
|
||||
|
|
@ -0,0 +1 @@
|
|||
FINAL TRANSACTION PROPOSAL: **BUY**
|
||||
69
setup.py
69
setup.py
|
|
@ -1,43 +1,58 @@
|
|||
"""
|
||||
Setup script for the TradingAgents package.
|
||||
TradingAgents包的安装脚本
|
||||
这个文件用于配置Python包的安装信息
|
||||
"""
|
||||
|
||||
# 导入setuptools模块,用于创建和安装Python包
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
# 配置包的安装信息
|
||||
setup(
|
||||
name="tradingagents",
|
||||
version="0.1.0",
|
||||
description="Multi-Agents LLM Financial Trading Framework",
|
||||
author="TradingAgents Team",
|
||||
author_email="yijia.xiao@cs.ucla.edu",
|
||||
url="https://github.com/TauricResearch",
|
||||
# 包的基本信息
|
||||
name="tradingagents", # 包名
|
||||
version="0.1.0", # 版本号
|
||||
description="Multi-Agents LLM Financial Trading Framework", # 包描述:多代理大语言模型金融交易框架
|
||||
author="TradingAgents Team", # 作者
|
||||
author_email="yijia.xiao@cs.ucla.edu", # 作者邮箱
|
||||
url="https://github.com/TauricResearch", # 项目主页
|
||||
|
||||
# 自动查找所有包
|
||||
packages=find_packages(),
|
||||
|
||||
# 依赖包列表(运行这个项目需要的其他Python包)
|
||||
install_requires=[
|
||||
"langchain>=0.1.0",
|
||||
"langchain-openai>=0.0.2",
|
||||
"langchain-experimental>=0.0.40",
|
||||
"langgraph>=0.0.20",
|
||||
"numpy>=1.24.0",
|
||||
"pandas>=2.0.0",
|
||||
"praw>=7.7.0",
|
||||
"stockstats>=0.5.4",
|
||||
"yfinance>=0.2.31",
|
||||
"typer>=0.9.0",
|
||||
"rich>=13.0.0",
|
||||
"questionary>=2.0.1",
|
||||
"langchain>=0.1.0", # LangChain:用于构建AI应用的框架
|
||||
"langchain-openai>=0.0.2", # LangChain的OpenAI集成
|
||||
"langchain-experimental>=0.0.40", # LangChain实验性功能
|
||||
"langgraph>=0.0.20", # LangGraph:用于构建AI代理图
|
||||
"numpy>=1.24.0", # NumPy:数值计算库
|
||||
"pandas>=2.0.0", # Pandas:数据分析库
|
||||
"praw>=7.7.0", # PRAW:Reddit API包装器
|
||||
"stockstats>=0.5.4", # StockStats:股票技术指标计算
|
||||
"yfinance>=0.2.31", # Yahoo Finance:获取股票数据
|
||||
"typer>=0.9.0", # Typer:命令行界面库
|
||||
"rich>=13.0.0", # Rich:美化终端输出
|
||||
"questionary>=2.0.1", # Questionary:交互式命令行提示
|
||||
],
|
||||
python_requires=">=3.10",
|
||||
|
||||
# Python版本要求
|
||||
python_requires=">=3.10", # 需要Python 3.10或更高版本
|
||||
|
||||
# 命令行入口点
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"tradingagents=cli.main:app",
|
||||
# 安装后可以在命令行直接使用"tradingagents"命令
|
||||
"tradingagents=cli.main:app", # 指向cli/main.py中的app函数
|
||||
],
|
||||
},
|
||||
|
||||
# 包的分类标签
|
||||
classifiers=[
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Financial and Trading Industry",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Topic :: Office/Business :: Financial :: Investment",
|
||||
"Development Status :: 3 - Alpha", # 开发状态:Alpha版本
|
||||
"Intended Audience :: Financial and Trading Industry", # 目标用户:金融和交易行业
|
||||
"License :: OSI Approved :: Apache Software License", # 许可证:Apache许可证
|
||||
"Programming Language :: Python :: 3", # 编程语言:Python 3
|
||||
"Programming Language :: Python :: 3.10", # 支持的Python版本
|
||||
"Topic :: Office/Business :: Financial :: Investment", # 主题:金融投资
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,87 @@
|
|||
import chromadb
|
||||
from chromadb.config import Settings
|
||||
from openai import OpenAI
|
||||
import requests
|
||||
import os
|
||||
|
||||
|
||||
class FinancialSituationMemory:
|
||||
def __init__(self, name, config):
|
||||
# 根据不同的模型提供商设置embedding模型
|
||||
if config["backend_url"] == "http://localhost:11434/v1":
|
||||
self.embedding = "nomic-embed-text"
|
||||
elif "dashscope.aliyuncs.com" in config["backend_url"]:
|
||||
self.embedding = "text-embedding-v2" # 通义千问embedding模型
|
||||
elif "baidu" in config["backend_url"]:
|
||||
self.embedding = "bge-large-zh-v1.5" # 文心一言embedding模型
|
||||
else:
|
||||
self.embedding = "text-embedding-3-small"
|
||||
self.client = OpenAI(base_url=config["backend_url"])
|
||||
self.embedding = "text-embedding-3-small" # 默认OpenAI模型
|
||||
|
||||
self.config = config
|
||||
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
|
||||
self.situation_collection = self.chroma_client.create_collection(name=name)
|
||||
|
||||
def get_embedding(self, text):
|
||||
"""Get OpenAI embedding for a text"""
|
||||
"""Get embedding for a text using the configured model"""
|
||||
|
||||
response = self.client.embeddings.create(
|
||||
model=self.embedding, input=text
|
||||
)
|
||||
return response.data[0].embedding
|
||||
# 根据不同的模型提供商调用不同的API
|
||||
if "dashscope.aliyuncs.com" in self.config["backend_url"]:
|
||||
return self._get_qwen_embedding(text)
|
||||
elif "baidu" in self.config["backend_url"]:
|
||||
return self._get_ernie_embedding(text)
|
||||
else:
|
||||
# 对于其他模型,使用简化的embedding(返回固定向量)
|
||||
return self._get_simple_embedding(text)
|
||||
|
||||
def _get_qwen_embedding(self, text):
|
||||
"""获取通义千问embedding"""
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {os.getenv('DASHSCOPE_API_KEY')}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
data = {
|
||||
"model": self.embedding,
|
||||
"input": text
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{self.config['backend_url'].replace('/chat/completions', '/embeddings')}",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=30
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
return result["data"][0]["embedding"]
|
||||
except Exception as e:
|
||||
print(f"⚠️ 通义千问embedding调用失败: {e}")
|
||||
return self._get_simple_embedding(text)
|
||||
|
||||
def _get_ernie_embedding(self, text):
|
||||
"""获取文心一言embedding"""
|
||||
try:
|
||||
# 文心一言的embedding API调用
|
||||
# 这里使用简化的实现
|
||||
return self._get_simple_embedding(text)
|
||||
except Exception as e:
|
||||
print(f"⚠️ 文心一言embedding调用失败: {e}")
|
||||
return self._get_simple_embedding(text)
|
||||
|
||||
def _get_simple_embedding(self, text):
|
||||
"""简化的embedding实现(返回固定长度的向量)"""
|
||||
# 使用文本的hash值生成固定长度的向量
|
||||
import hashlib
|
||||
hash_obj = hashlib.md5(text.encode('utf-8'))
|
||||
hash_bytes = hash_obj.digest()
|
||||
|
||||
# 生成1536维的向量(与OpenAI embedding维度相同)
|
||||
embedding = []
|
||||
for i in range(1536):
|
||||
byte_index = i % len(hash_bytes)
|
||||
embedding.append((hash_bytes[byte_index] - 128) / 128.0)
|
||||
|
||||
return embedding
|
||||
|
||||
def add_situations(self, situations_and_advice):
|
||||
"""Add financial situations and their corresponding advice. Parameter is a list of tuples (situation, rec)"""
|
||||
|
|
|
|||
|
|
@ -1,36 +1,57 @@
|
|||
import json
|
||||
import os
|
||||
# 导入必要的模块
|
||||
import json # 用于处理JSON格式的数据
|
||||
import os # 用于文件路径操作
|
||||
|
||||
|
||||
def get_data_in_range(ticker, start_date, end_date, data_type, data_dir, period=None):
|
||||
"""
|
||||
Gets finnhub data saved and processed on disk.
|
||||
Args:
|
||||
start_date (str): Start date in YYYY-MM-DD format.
|
||||
end_date (str): End date in YYYY-MM-DD format.
|
||||
data_type (str): Type of data from finnhub to fetch. Can be insider_trans, SEC_filings, news_data, insider_senti, or fin_as_reported.
|
||||
data_dir (str): Directory where the data is saved.
|
||||
period (str): Default to none, if there is a period specified, should be annual or quarterly.
|
||||
从本地磁盘获取已保存和处理的FinnHub数据
|
||||
|
||||
这个函数不是直接调用FinnHub API,而是从本地JSON文件中读取预先下载的数据
|
||||
|
||||
参数说明:
|
||||
ticker (str): 股票代码,例如 'AAPL' 代表苹果公司
|
||||
start_date (str): 开始日期,格式为 YYYY-MM-DD,例如 '2024-01-01'
|
||||
end_date (str): 结束日期,格式为 YYYY-MM-DD,例如 '2024-01-31'
|
||||
data_type (str): 要获取的数据类型,可以是以下之一:
|
||||
- 'insider_trans': 内部交易数据
|
||||
- 'SEC_filings': SEC文件数据
|
||||
- 'news_data': 新闻数据
|
||||
- 'insider_senti': 内部人员情绪数据
|
||||
- 'fin_as_reported': 财务报告数据
|
||||
data_dir (str): 数据保存的目录路径
|
||||
period (str): 报告周期,默认为None。如果指定,应该是 'annual'(年度)或 'quarterly'(季度)
|
||||
|
||||
返回值:
|
||||
dict: 过滤后的数据字典,键为日期,值为该日期的数据列表
|
||||
"""
|
||||
|
||||
# 根据是否有报告周期来构建文件路径
|
||||
if period:
|
||||
# 如果有报告周期,文件名包含周期信息
|
||||
data_path = os.path.join(
|
||||
data_dir,
|
||||
"finnhub_data",
|
||||
data_type,
|
||||
f"{ticker}_{period}_data_formatted.json",
|
||||
data_dir, # 数据目录
|
||||
"finnhub_data", # FinnHub数据子目录
|
||||
data_type, # 数据类型子目录
|
||||
f"{ticker}_{period}_data_formatted.json", # 文件名格式:股票代码_周期_数据格式.json
|
||||
)
|
||||
else:
|
||||
# 如果没有报告周期,使用默认文件名
|
||||
data_path = os.path.join(
|
||||
data_dir, "finnhub_data", data_type, f"{ticker}_data_formatted.json"
|
||||
)
|
||||
|
||||
data = open(data_path, "r")
|
||||
data = json.load(data)
|
||||
# 打开并读取JSON文件
|
||||
data = open(data_path, "r") # 以只读模式打开文件
|
||||
data = json.load(data) # 将JSON文件内容解析为Python字典
|
||||
|
||||
# filter keys (date, str in format YYYY-MM-DD) by the date range (str, str in format YYYY-MM-DD)
|
||||
# 根据日期范围过滤数据
|
||||
# 只保留在指定日期范围内且有数据的条目
|
||||
filtered_data = {}
|
||||
for key, value in data.items():
|
||||
# key是日期字符串,value是该日期的数据列表
|
||||
# 检查日期是否在范围内,且数据不为空
|
||||
if start_date <= key <= end_date and len(value) > 0:
|
||||
filtered_data[key] = value
|
||||
return filtered_data
|
||||
filtered_data[key] = value # 将符合条件的数据添加到结果中
|
||||
|
||||
return filtered_data # 返回过滤后的数据
|
||||
|
|
|
|||
|
|
@ -1,22 +1,29 @@
|
|||
# 导入操作系统模块,用于处理文件路径
|
||||
import os
|
||||
|
||||
# 默认配置字典
|
||||
# 这个文件包含了TradingAgents项目的所有默认设置
|
||||
DEFAULT_CONFIG = {
|
||||
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
|
||||
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"),
|
||||
"data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data",
|
||||
"data_cache_dir": os.path.join(
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
|
||||
"dataflows/data_cache",
|
||||
# 项目目录设置
|
||||
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")), # 项目根目录的绝对路径
|
||||
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"), # 结果保存目录,默认是./results
|
||||
"data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data", # 数据存储目录(需要根据实际情况修改)
|
||||
"data_cache_dir": os.path.join( # 数据缓存目录
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), ".")), # 项目根目录
|
||||
"dataflows/data_cache", # 缓存子目录
|
||||
),
|
||||
# LLM settings
|
||||
"llm_provider": "openai",
|
||||
"deep_think_llm": "o4-mini",
|
||||
"quick_think_llm": "gpt-4o-mini",
|
||||
"backend_url": "https://api.openai.com/v1",
|
||||
# Debate and discussion settings
|
||||
"max_debate_rounds": 1,
|
||||
"max_risk_discuss_rounds": 1,
|
||||
"max_recur_limit": 100,
|
||||
# Tool settings
|
||||
"online_tools": True,
|
||||
|
||||
# AI模型设置
|
||||
"llm_provider": "qwen", # AI模型提供商,默认使用通义千问(国内免费)
|
||||
"deep_think_llm": "qwen-plus", # 深度思考模型,用于复杂分析任务
|
||||
"quick_think_llm": "qwen-turbo", # 快速思考模型,用于简单任务
|
||||
"backend_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", # 通义千问API地址
|
||||
|
||||
# 辩论和讨论设置
|
||||
"max_debate_rounds": 1, # 代理之间最大辩论轮数
|
||||
"max_risk_discuss_rounds": 1, # 风险管理讨论最大轮数
|
||||
"max_recur_limit": 100, # 最大递归限制,防止无限循环
|
||||
|
||||
# 工具设置
|
||||
"online_tools": True, # 是否使用在线工具(获取实时数据)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ from langchain_openai import ChatOpenAI
|
|||
from langchain_anthropic import ChatAnthropic
|
||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||
|
||||
# 导入国内大模型适配器
|
||||
from tradingagents.llm_adapters import QwenAdapter, ErnieAdapter, GLMAdapter, KimiAdapter
|
||||
|
||||
from langgraph.prebuilt import ToolNode
|
||||
|
||||
from tradingagents.agents import *
|
||||
|
|
@ -57,18 +60,76 @@ class TradingAgentsGraph:
|
|||
exist_ok=True,
|
||||
)
|
||||
|
||||
# Initialize LLMs
|
||||
if self.config["llm_provider"].lower() == "openai" or self.config["llm_provider"] == "ollama" or self.config["llm_provider"] == "openrouter":
|
||||
self.deep_thinking_llm = ChatOpenAI(model=self.config["deep_think_llm"], base_url=self.config["backend_url"])
|
||||
self.quick_thinking_llm = ChatOpenAI(model=self.config["quick_think_llm"], base_url=self.config["backend_url"])
|
||||
elif self.config["llm_provider"].lower() == "anthropic":
|
||||
self.deep_thinking_llm = ChatAnthropic(model=self.config["deep_think_llm"], base_url=self.config["backend_url"])
|
||||
self.quick_thinking_llm = ChatAnthropic(model=self.config["quick_think_llm"], base_url=self.config["backend_url"])
|
||||
elif self.config["llm_provider"].lower() == "google":
|
||||
# 初始化AI模型
|
||||
# 支持多种模型提供商,包括国内免费大模型
|
||||
provider = self.config["llm_provider"].lower()
|
||||
|
||||
if provider in ["openai", "ollama", "openrouter"]:
|
||||
# OpenAI兼容模型
|
||||
self.deep_thinking_llm = ChatOpenAI(
|
||||
model=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = ChatOpenAI(
|
||||
model=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
elif provider == "anthropic":
|
||||
# Anthropic Claude模型
|
||||
self.deep_thinking_llm = ChatAnthropic(
|
||||
model=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = ChatAnthropic(
|
||||
model=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
elif provider == "google":
|
||||
# Google Gemini模型
|
||||
self.deep_thinking_llm = ChatGoogleGenerativeAI(model=self.config["deep_think_llm"])
|
||||
self.quick_thinking_llm = ChatGoogleGenerativeAI(model=self.config["quick_think_llm"])
|
||||
elif provider == "qwen":
|
||||
# 通义千问模型(国内免费)
|
||||
self.deep_thinking_llm = QwenAdapter(
|
||||
model_name=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = QwenAdapter(
|
||||
model_name=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
elif provider == "ernie":
|
||||
# 文心一言模型(国内免费)
|
||||
self.deep_thinking_llm = ErnieAdapter(
|
||||
model_name=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = ErnieAdapter(
|
||||
model_name=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
elif provider == "glm":
|
||||
# 智谱AI模型(国内免费)
|
||||
self.deep_thinking_llm = GLMAdapter(
|
||||
model_name=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = GLMAdapter(
|
||||
model_name=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
elif provider == "kimi":
|
||||
# 月之暗面Kimi模型(国内免费)
|
||||
self.deep_thinking_llm = KimiAdapter(
|
||||
model_name=self.config["deep_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
self.quick_thinking_llm = KimiAdapter(
|
||||
model_name=self.config["quick_think_llm"],
|
||||
base_url=self.config["backend_url"]
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unsupported LLM provider: {self.config['llm_provider']}")
|
||||
raise ValueError(f"不支持的AI模型提供商: {self.config['llm_provider']}")
|
||||
|
||||
self.toolkit = Toolkit(config=self.config)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# 国内大模型适配器模块
|
||||
# 这个模块提供了对国内各大AI模型厂商的统一接口
|
||||
|
||||
from .qwen_adapter import QwenAdapter
|
||||
from .ernie_adapter import ErnieAdapter
|
||||
from .glm_adapter import GLMAdapter
|
||||
from .kimi_adapter import KimiAdapter
|
||||
|
||||
__all__ = [
|
||||
"QwenAdapter",
|
||||
"ErnieAdapter",
|
||||
"GLMAdapter",
|
||||
"KimiAdapter"
|
||||
]
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
# 文心一言模型适配器
|
||||
# 支持百度文心一言大模型
|
||||
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional, Iterator
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.outputs import LLMResult, Generation, ChatResult, ChatGeneration
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class ErnieAdapter(BaseChatModel):
|
||||
"""
|
||||
文心一言模型适配器
|
||||
支持百度文心一言大模型的调用
|
||||
"""
|
||||
|
||||
model_name: str = Field(default="ernie-4.0-8k", description="模型名称")
|
||||
api_key: Optional[str] = Field(default=None, description="API密钥")
|
||||
secret_key: Optional[str] = Field(default=None, description="Secret密钥")
|
||||
base_url: str = Field(default="https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat", description="API基础URL")
|
||||
temperature: float = Field(default=0.7, description="温度参数")
|
||||
max_tokens: int = Field(default=2000, description="最大token数")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_name: str = "ernie-4.0-8k",
|
||||
api_key: Optional[str] = None,
|
||||
secret_key: Optional[str] = None,
|
||||
base_url: str = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: int = 2000,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
model_name=model_name,
|
||||
api_key=api_key or os.getenv("BAIDU_API_KEY"),
|
||||
secret_key=secret_key or os.getenv("BAIDU_SECRET_KEY"),
|
||||
base_url=base_url,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if not self.api_key or not self.secret_key:
|
||||
raise ValueError("请设置BAIDU_API_KEY和BAIDU_SECRET_KEY环境变量")
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "ernie"
|
||||
|
||||
def _get_access_token(self) -> str:
|
||||
"""获取访问令牌"""
|
||||
url = "https://aip.baidubce.com/oauth/2.0/token"
|
||||
params = {
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": self.api_key,
|
||||
"client_secret": self.secret_key
|
||||
}
|
||||
|
||||
response = requests.post(url, params=params)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
return result["access_token"]
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
"""生成回复"""
|
||||
try:
|
||||
# 获取访问令牌
|
||||
access_token = self._get_access_token()
|
||||
|
||||
# 转换消息格式
|
||||
formatted_messages = self._format_messages(messages)
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"messages": formatted_messages,
|
||||
"temperature": self.temperature,
|
||||
"max_output_tokens": self.max_tokens,
|
||||
}
|
||||
|
||||
# 添加停止词
|
||||
if stop:
|
||||
data["stop"] = stop
|
||||
|
||||
# 发送请求
|
||||
url = f"{self.base_url}/{self.model_name}?access_token={access_token}"
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
response = requests.post(url, headers=headers, json=data, timeout=30)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
# 解析响应
|
||||
content = result["result"]
|
||||
message = AIMessage(content=content)
|
||||
generation = ChatGeneration(message=message)
|
||||
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"文心一言API调用失败: {str(e)}")
|
||||
|
||||
def _format_messages(self, messages: List[BaseMessage]) -> List[Dict[str, str]]:
|
||||
"""将LangChain消息格式转换为文心一言格式"""
|
||||
formatted = []
|
||||
|
||||
for message in messages:
|
||||
if isinstance(message, SystemMessage):
|
||||
formatted.append({"role": "user", "content": f"系统提示: {message.content}"})
|
||||
elif isinstance(message, HumanMessage):
|
||||
formatted.append({"role": "user", "content": message.content})
|
||||
elif isinstance(message, AIMessage):
|
||||
formatted.append({"role": "assistant", "content": message.content})
|
||||
|
||||
return formatted
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGeneration]:
|
||||
"""流式生成(暂不支持)"""
|
||||
# 对于不支持流式生成的模型,我们返回一个包含完整响应的生成
|
||||
result = self._generate(messages, stop, run_manager, **kwargs)
|
||||
for generation in result.generations:
|
||||
yield generation
|
||||
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
"""绑定工具到模型(简化实现)"""
|
||||
# 对于国内模型,我们简化工具绑定
|
||||
# 直接返回self,让上层处理工具调用
|
||||
return self
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
"""返回识别参数"""
|
||||
return {
|
||||
"model_name": self.model_name,
|
||||
"base_url": self.base_url,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# 智谱AI模型适配器
|
||||
# 支持智谱AI GLM大模型
|
||||
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional, Iterator
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.outputs import LLMResult, Generation, ChatResult, ChatGeneration
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class GLMAdapter(BaseChatModel):
|
||||
"""
|
||||
智谱AI模型适配器
|
||||
支持智谱AI GLM大模型的调用
|
||||
"""
|
||||
|
||||
model_name: str = Field(default="glm-4", description="模型名称")
|
||||
api_key: Optional[str] = Field(default=None, description="API密钥")
|
||||
base_url: str = Field(default="https://open.bigmodel.cn/api/paas/v4", description="API基础URL")
|
||||
temperature: float = Field(default=0.7, description="温度参数")
|
||||
max_tokens: int = Field(default=2000, description="最大token数")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_name: str = "glm-4",
|
||||
api_key: Optional[str] = None,
|
||||
base_url: str = "https://open.bigmodel.cn/api/paas/v4",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: int = 2000,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
model_name=model_name,
|
||||
api_key=api_key or os.getenv("ZHIPU_API_KEY"),
|
||||
base_url=base_url,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if not self.api_key:
|
||||
raise ValueError("请设置ZHIPU_API_KEY环境变量或传入api_key参数")
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "glm"
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
"""生成回复"""
|
||||
try:
|
||||
# 转换消息格式
|
||||
formatted_messages = self._format_messages(messages)
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"model": self.model_name,
|
||||
"messages": formatted_messages,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
||||
# 添加停止词
|
||||
if stop:
|
||||
data["stop"] = stop
|
||||
|
||||
# 发送请求
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{self.base_url}/chat/completions",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
# 解析响应
|
||||
content = result["choices"][0]["message"]["content"]
|
||||
message = AIMessage(content=content)
|
||||
generation = ChatGeneration(message=message)
|
||||
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"智谱AI API调用失败: {str(e)}")
|
||||
|
||||
def _format_messages(self, messages: List[BaseMessage]) -> List[Dict[str, str]]:
|
||||
"""将LangChain消息格式转换为智谱AI格式"""
|
||||
formatted = []
|
||||
|
||||
for message in messages:
|
||||
if isinstance(message, SystemMessage):
|
||||
formatted.append({"role": "system", "content": message.content})
|
||||
elif isinstance(message, HumanMessage):
|
||||
formatted.append({"role": "user", "content": message.content})
|
||||
elif isinstance(message, AIMessage):
|
||||
formatted.append({"role": "assistant", "content": message.content})
|
||||
|
||||
return formatted
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGeneration]:
|
||||
"""流式生成(暂不支持)"""
|
||||
# 对于不支持流式生成的模型,我们返回一个包含完整响应的生成
|
||||
result = self._generate(messages, stop, run_manager, **kwargs)
|
||||
for generation in result.generations:
|
||||
yield generation
|
||||
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
"""绑定工具到模型(简化实现)"""
|
||||
# 对于国内模型,我们简化工具绑定
|
||||
# 直接返回self,让上层处理工具调用
|
||||
return self
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
"""返回识别参数"""
|
||||
return {
|
||||
"model_name": self.model_name,
|
||||
"base_url": self.base_url,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# 月之暗面Kimi模型适配器
|
||||
# 支持月之暗面Kimi大模型
|
||||
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional, Iterator
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.outputs import LLMResult, Generation, ChatResult, ChatGeneration
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class KimiAdapter(BaseChatModel):
|
||||
"""
|
||||
月之暗面Kimi模型适配器
|
||||
支持月之暗面Kimi大模型的调用
|
||||
"""
|
||||
|
||||
model_name: str = Field(default="moonshot-v1-8k", description="模型名称")
|
||||
api_key: Optional[str] = Field(default=None, description="API密钥")
|
||||
base_url: str = Field(default="https://api.moonshot.cn/v1", description="API基础URL")
|
||||
temperature: float = Field(default=0.7, description="温度参数")
|
||||
max_tokens: int = Field(default=2000, description="最大token数")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_name: str = "moonshot-v1-8k",
|
||||
api_key: Optional[str] = None,
|
||||
base_url: str = "https://api.moonshot.cn/v1",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: int = 2000,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
model_name=model_name,
|
||||
api_key=api_key or os.getenv("MOONSHOT_API_KEY"),
|
||||
base_url=base_url,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if not self.api_key:
|
||||
raise ValueError("请设置MOONSHOT_API_KEY环境变量或传入api_key参数")
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "kimi"
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
"""生成回复"""
|
||||
try:
|
||||
# 转换消息格式
|
||||
formatted_messages = self._format_messages(messages)
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"model": self.model_name,
|
||||
"messages": formatted_messages,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
||||
# 添加停止词
|
||||
if stop:
|
||||
data["stop"] = stop
|
||||
|
||||
# 发送请求
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{self.base_url}/chat/completions",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
# 解析响应
|
||||
content = result["choices"][0]["message"]["content"]
|
||||
message = AIMessage(content=content)
|
||||
generation = ChatGeneration(message=message)
|
||||
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"月之暗面Kimi API调用失败: {str(e)}")
|
||||
|
||||
def _format_messages(self, messages: List[BaseMessage]) -> List[Dict[str, str]]:
|
||||
"""将LangChain消息格式转换为Kimi格式"""
|
||||
formatted = []
|
||||
|
||||
for message in messages:
|
||||
if isinstance(message, SystemMessage):
|
||||
formatted.append({"role": "system", "content": message.content})
|
||||
elif isinstance(message, HumanMessage):
|
||||
formatted.append({"role": "user", "content": message.content})
|
||||
elif isinstance(message, AIMessage):
|
||||
formatted.append({"role": "assistant", "content": message.content})
|
||||
|
||||
return formatted
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGeneration]:
|
||||
"""流式生成(暂不支持)"""
|
||||
# 对于不支持流式生成的模型,我们返回一个包含完整响应的生成
|
||||
result = self._generate(messages, stop, run_manager, **kwargs)
|
||||
for generation in result.generations:
|
||||
yield generation
|
||||
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
"""绑定工具到模型(简化实现)"""
|
||||
# 对于国内模型,我们简化工具绑定
|
||||
# 直接返回self,让上层处理工具调用
|
||||
return self
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
"""返回识别参数"""
|
||||
return {
|
||||
"model_name": self.model_name,
|
||||
"base_url": self.base_url,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
# 通义千问模型适配器
|
||||
# 支持阿里云通义千问大模型
|
||||
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional, Iterator
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.outputs import LLMResult, Generation, ChatResult, ChatGeneration
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class QwenAdapter(BaseChatModel):
|
||||
"""
|
||||
通义千问模型适配器
|
||||
支持阿里云通义千问大模型的调用
|
||||
"""
|
||||
|
||||
model_name: str = Field(default="qwen-plus", description="模型名称")
|
||||
api_key: Optional[str] = Field(default=None, description="API密钥")
|
||||
base_url: str = Field(default="https://dashscope.aliyuncs.com/compatible-mode/v1", description="API基础URL")
|
||||
temperature: float = Field(default=0.7, description="温度参数")
|
||||
max_tokens: int = Field(default=2000, description="最大token数")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_name: str = "qwen-plus",
|
||||
api_key: Optional[str] = None,
|
||||
base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
temperature: float = 0.7,
|
||||
max_tokens: int = 2000,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
model_name=model_name,
|
||||
api_key=api_key or os.getenv("DASHSCOPE_API_KEY"),
|
||||
base_url=base_url,
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if not self.api_key:
|
||||
raise ValueError("请设置DASHSCOPE_API_KEY环境变量或传入api_key参数")
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "qwen"
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
"""生成回复"""
|
||||
try:
|
||||
# 转换消息格式
|
||||
formatted_messages = self._format_messages(messages)
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"model": self.model_name,
|
||||
"messages": formatted_messages,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
||||
# 添加停止词
|
||||
if stop:
|
||||
data["stop"] = stop
|
||||
|
||||
# 发送请求(带重试机制)
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 添加重试机制
|
||||
import time
|
||||
max_retries = 5 # 增加重试次数
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{self.base_url}/chat/completions",
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=60 # 增加超时时间到60秒
|
||||
)
|
||||
break
|
||||
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout, requests.exceptions.ReadTimeout) as e:
|
||||
if attempt < max_retries - 1:
|
||||
wait_time = min(2 ** attempt, 10) # 最大等待10秒
|
||||
print(f"⚠️ 网络请求失败,第{attempt + 1}次重试,等待{wait_time}秒...")
|
||||
time.sleep(wait_time)
|
||||
else:
|
||||
print(f"❌ 网络请求最终失败: {str(e)}")
|
||||
raise e
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
# 解析响应
|
||||
content = result["choices"][0]["message"]["content"]
|
||||
message = AIMessage(content=content)
|
||||
generation = ChatGeneration(message=message)
|
||||
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"通义千问API调用失败: {str(e)}")
|
||||
|
||||
def _format_messages(self, messages: List[BaseMessage]) -> List[Dict[str, str]]:
|
||||
"""将LangChain消息格式转换为通义千问格式"""
|
||||
formatted = []
|
||||
|
||||
for message in messages:
|
||||
if isinstance(message, SystemMessage):
|
||||
formatted.append({"role": "system", "content": message.content})
|
||||
elif isinstance(message, HumanMessage):
|
||||
formatted.append({"role": "user", "content": message.content})
|
||||
elif isinstance(message, AIMessage):
|
||||
formatted.append({"role": "assistant", "content": message.content})
|
||||
|
||||
return formatted
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGeneration]:
|
||||
"""流式生成(暂不支持)"""
|
||||
# 对于不支持流式生成的模型,我们返回一个包含完整响应的生成
|
||||
result = self._generate(messages, stop, run_manager, **kwargs)
|
||||
for generation in result.generations:
|
||||
yield generation
|
||||
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
"""绑定工具到模型(简化实现)"""
|
||||
# 对于国内模型,我们简化工具绑定
|
||||
# 直接返回self,让上层处理工具调用
|
||||
return self
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
"""返回识别参数"""
|
||||
return {
|
||||
"model_name": self.model_name,
|
||||
"base_url": self.base_url,
|
||||
"temperature": self.temperature,
|
||||
"max_tokens": self.max_tokens,
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
TradingAgents 离线模式 - 简化版
|
||||
当网络连接有问题时,使用模拟数据进行分析
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
if current_dir not in sys.path:
|
||||
sys.path.insert(0, current_dir)
|
||||
|
||||
def create_offline_config():
|
||||
"""创建离线模式配置"""
|
||||
return {
|
||||
"llm_provider": "offline",
|
||||
"deep_think_llm": "offline-model",
|
||||
"quick_think_llm": "offline-model",
|
||||
"backend_url": "offline",
|
||||
"online_tools": False,
|
||||
"max_recur_limit": 10,
|
||||
"request_timeout": 30,
|
||||
"max_retries": 1,
|
||||
}
|
||||
|
||||
def simulate_analysis(ticker, analysis_date):
|
||||
"""模拟分析过程"""
|
||||
print(f"🔍 离线模式分析: {ticker} ({analysis_date})")
|
||||
print("=" * 50)
|
||||
|
||||
# 模拟各个分析阶段
|
||||
stages = [
|
||||
"📊 市场分析师 - 技术指标分析",
|
||||
"📰 新闻分析师 - 新闻情绪分析",
|
||||
"📈 基本面分析师 - 财务数据分析",
|
||||
"🔍 研究团队 - 投资决策讨论",
|
||||
"💼 交易团队 - 交易计划制定",
|
||||
"⚠️ 风险管理 - 风险评估",
|
||||
"📋 投资组合管理 - 最终决策"
|
||||
]
|
||||
|
||||
for i, stage in enumerate(stages, 1):
|
||||
print(f"\n{i}. {stage}")
|
||||
print(" ✅ 分析完成")
|
||||
|
||||
# 模拟处理时间
|
||||
import time
|
||||
time.sleep(0.5)
|
||||
|
||||
# 生成模拟报告
|
||||
report = f"""
|
||||
# {ticker} 股票分析报告
|
||||
**分析日期**: {analysis_date}
|
||||
**分析模式**: 离线模拟
|
||||
|
||||
## 📊 市场分析
|
||||
- 技术指标显示{ticker}处于上升趋势
|
||||
- RSI指标为65,接近超买区域
|
||||
- MACD显示正动量
|
||||
- 布林带显示价格在正常区间内
|
||||
|
||||
## 📰 新闻情绪
|
||||
- 整体情绪偏向积极
|
||||
- 主要新闻关注AI和科技发展
|
||||
- 分析师评级多数为买入
|
||||
|
||||
## 📈 基本面分析
|
||||
- 营收增长稳定
|
||||
- 利润率保持健康水平
|
||||
- 现金流充裕
|
||||
- 债务水平合理
|
||||
|
||||
## 🔍 投资建议
|
||||
基于离线模拟分析,建议:
|
||||
|
||||
**最终决策**: **HOLD** (持有)
|
||||
|
||||
**理由**:
|
||||
1. 技术指标显示上升趋势但接近阻力位
|
||||
2. 基本面稳健但估值偏高
|
||||
3. 建议等待更好的入场时机
|
||||
|
||||
**风险提示**:
|
||||
- 市场波动性较高
|
||||
- 宏观经济不确定性
|
||||
- 行业竞争加剧
|
||||
|
||||
---
|
||||
*此报告基于离线模拟数据生成,仅供参考*
|
||||
"""
|
||||
|
||||
return report
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 TradingAgents 离线模式")
|
||||
print("=" * 50)
|
||||
|
||||
# 获取用户输入
|
||||
ticker = input("请输入股票代码 (默认: NVDA): ").strip() or "NVDA"
|
||||
analysis_date = input("请输入分析日期 (默认: 2024-05-10): ").strip() or "2024-05-10"
|
||||
|
||||
try:
|
||||
# 执行模拟分析
|
||||
report = simulate_analysis(ticker, analysis_date)
|
||||
|
||||
# 保存报告
|
||||
results_dir = f"results/{ticker}/{analysis_date}"
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
|
||||
report_file = os.path.join(results_dir, "offline_analysis_report.md")
|
||||
with open(report_file, "w", encoding="utf-8") as f:
|
||||
f.write(report)
|
||||
|
||||
print(f"\n📄 报告已保存到: {report_file}")
|
||||
print("\n" + "=" * 50)
|
||||
print(report)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⏹️ 用户中断分析")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 分析过程中出现错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
# TradingAgents 项目结构说明
|
||||
|
||||
## 项目概述
|
||||
TradingAgents是一个多代理大语言模型金融交易框架,模拟真实交易公司的运作方式,通过多个专门的AI代理协作分析市场并做出交易决策。
|
||||
|
||||
## 核心文件说明
|
||||
|
||||
### 1. 主入口文件
|
||||
- **`main.py`** - 项目的主入口文件,展示如何使用TradingAgents进行股票分析
|
||||
- **`cli/main.py`** - 命令行界面,提供用户友好的交互式界面
|
||||
|
||||
### 2. 配置文件
|
||||
- **`tradingagents/default_config.py`** - 默认配置文件,包含所有系统设置
|
||||
- **`requirements.txt`** - Python依赖包列表
|
||||
- **`setup.py`** - 项目安装配置文件
|
||||
|
||||
### 3. 核心模块
|
||||
|
||||
#### 代理系统 (`tradingagents/agents/`)
|
||||
- **`analysts/`** - 分析师代理
|
||||
- `fundamentals_analyst.py` - 基本面分析师
|
||||
- `market_analyst.py` - 市场分析师
|
||||
- `news_analyst.py` - 新闻分析师
|
||||
- `social_media_analyst.py` - 社交媒体分析师
|
||||
|
||||
- **`researchers/`** - 研究团队
|
||||
- `bull_researcher.py` - 看涨研究员
|
||||
- `bear_researcher.py` - 看跌研究员
|
||||
|
||||
- **`managers/`** - 管理团队
|
||||
- `research_manager.py` - 研究经理
|
||||
- `risk_manager.py` - 风险管理经理
|
||||
|
||||
- **`trader/`** - 交易员
|
||||
- `trader.py` - 交易代理
|
||||
|
||||
- **`risk_mgmt/`** - 风险管理
|
||||
- `aggressive_debator.py` - 激进辩论者
|
||||
- `conservative_debator.py` - 保守辩论者
|
||||
- `neutral_debator.py` - 中性辩论者
|
||||
|
||||
#### 数据流系统 (`tradingagents/dataflows/`)
|
||||
- **`finnhub_utils.py`** - FinnHub数据工具(从本地文件读取)
|
||||
- **`yfin_utils.py`** - Yahoo Finance数据工具
|
||||
- **`reddit_utils.py`** - Reddit数据工具
|
||||
- **`googlenews_utils.py`** - Google新闻工具
|
||||
- **`stockstats_utils.py`** - 技术指标计算工具
|
||||
- **`interface.py`** - 数据接口统一管理
|
||||
|
||||
#### 图系统 (`tradingagents/graph/`)
|
||||
- **`trading_graph.py`** - 交易代理图,协调所有代理的工作
|
||||
- **`propagation.py`** - 信息传播逻辑
|
||||
- **`reflection.py`** - 反思和学习机制
|
||||
- **`signal_processing.py`** - 信号处理
|
||||
|
||||
## 工作流程
|
||||
|
||||
1. **数据收集**: 各个数据工具从不同来源获取市场数据
|
||||
2. **分析阶段**: 分析师代理分析不同维度的数据
|
||||
3. **研究阶段**: 研究员代理进行深度研究和辩论
|
||||
4. **决策阶段**: 交易员基于分析结果做出交易决策
|
||||
5. **风险管理**: 风险管理团队评估和调整策略
|
||||
6. **执行**: 最终决策被执行
|
||||
|
||||
## 如何运行
|
||||
|
||||
### 方法1: 使用命令行界面
|
||||
```bash
|
||||
python -m cli.main
|
||||
```
|
||||
|
||||
### 方法2: 直接运行主文件
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 方法3: 作为Python包使用
|
||||
```python
|
||||
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
||||
from tradingagents.default_config import DEFAULT_CONFIG
|
||||
|
||||
# 创建配置
|
||||
config = DEFAULT_CONFIG.copy()
|
||||
|
||||
# 初始化交易代理
|
||||
ta = TradingAgentsGraph(debug=True, config=config)
|
||||
|
||||
# 运行分析
|
||||
_, decision = ta.propagate("NVDA", "2024-05-10")
|
||||
print(decision)
|
||||
```
|
||||
|
||||
## 重要概念
|
||||
|
||||
- **代理(Agent)**: 每个代理都有特定的角色和职责
|
||||
- **图(Graph)**: 代理之间的协作关系和工作流程
|
||||
- **传播(Propagation)**: 信息在代理之间传递的过程
|
||||
- **辩论(Debate)**: 代理之间就不同观点进行讨论
|
||||
- **反思(Reflection)**: 从结果中学习并改进决策
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 这是一个研究项目,不构成投资建议
|
||||
2. 需要配置相应的API密钥(OpenAI等)
|
||||
3. 建议在虚拟环境中运行
|
||||
4. 首次运行可能需要下载大量数据
|
||||
Loading…
Reference in New Issue