提交AI模型使用阿里qwen的版本

This commit is contained in:
OliverPanda 2025-09-12 21:39:03 +08:00
parent a438acdbbd
commit 07eb2156aa
29 changed files with 2451 additions and 201 deletions

394
GUIDE.md Normal file
View File

@ -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项目的完整架构和逻辑梳理帮助开发者深入理解项目的设计思路和实现细节。

View File

@ -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

View File

@ -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
View File

@ -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()

View File

@ -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. 建议在虚拟环境中安装这些依赖

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 3050% upside.
- **Dividend yield of 2%** adds floor support in a low-yield environment.
> **Quote**: “The stock is priced for collapse, but the fundamentals dont 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 dont 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 Analysts 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**. Thats not a dying business—its a **distressed asset** with **real intrinsic value**.
#### ✅ Downside Is Limited
Even if the company doesnt 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—its 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 whats already priced in.
> **Quote from Risky Analyst**: “When sentiment is uniformly negative and no one wants to touch a stock, thats 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 isnt a momentum stock; its 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**: “Its a calculated way to play a potential turnaround.”
> **My Response**: Then play it—**with discipline and structure**.
---
### 🛠️ Traders Refactored Plan: **Buy with Discipline**
#### 1. **Initial Entry Strategy**
- Start with a **small initial position (23% 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
Ive 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, Im not reacting to sentiment. Im 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 Traders Reflection**: “Cheap doesnt always mean broken, and value doesnt always move on a timeline.”
---
### 📌 Final Word
Harbin Pharmaceutical isnt a momentum stock. Its not a moonshot. Its 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.**

View File

@ -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**

View File

@ -0,0 +1,89 @@
Alright, lets 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, Ive landed on a decision—and its 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 24 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 isnt 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 Im Going with the Bull Side
Yes, the bear makes a lot of sense. And if I were a pure momentum trader, Id be selling. But Im a value investor at heart—and Ive made mistakes in the past by selling too early on turnaround stories. Ive sold companies at a slight discount, only to watch them rebound 50% or more because I panicked at the short-term noise.
This isnt one of those cases where everything is falling apart. Harbin Pharmaceutical is **not burning cash**. Its **profitable**, **cash-flow positive**, and **trading below book value**. Thats not a speculative stock—its 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 thats not a reason to sell—its 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. Thats not a turnaround overnight, but its a **step in the right direction**.
---
### 🧭 Rationale: This Is a Value Opportunity, Not a Momentum Bet
Heres 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** (23% 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 Im Applying Here
Ive sold too early before on similar names—companies that looked cheap, had real assets, but were out of favor. I let the noise win. Ive learned that **cheap doesnt always mean broken**, and **value doesnt always move on a timeline**.
This time, Im not reacting to short-term weakness. Im 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
Im not saying this is a home run. Im saying its a **calculated value play** with **limited downside** and **visible upside**. If youre 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.**

View File

@ -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")
```

View File

@ -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.

View File

@ -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 companys 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 companys 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.

View File

@ -0,0 +1 @@
FINAL TRANSACTION PROPOSAL: **BUY**

View File

@ -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", # PRAWReddit 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", # 主题:金融投资
],
)

View File

@ -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)"""

View File

@ -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 # 返回过滤后的数据

View File

@ -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, # 是否使用在线工具(获取实时数据)
}

View File

@ -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)

View File

@ -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"
]

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

View File

@ -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,
}

130
离线模式_简化版.py Normal file
View File

@ -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()

106
项目结构说明.md Normal file
View File

@ -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. 首次运行可能需要下载大量数据