Chinese market support from PR #105. Cleaned up cache and log files to maintain a tidy history.

Co-authored-by: hsliuping <hsliup@163.com>
This commit is contained in:
Yijia Xiao 2025-07-08 00:29:05 -04:00
parent a438acdbbd
commit 0a8db900a2
No known key found for this signature in database
50 changed files with 13738 additions and 45 deletions

120
.env.example Normal file
View File

@ -0,0 +1,120 @@
# TradingAgents Environment Variables Configuration Example
# 🔐 Important: Copy this file to .env and fill in your real API keys
# ⚠️ Warning: .env file contains sensitive information, do not commit to Git repository
# ===== Required API Keys =====
# 📊 FinnHub API Key (Always Required for financial data)
# Get from: https://finnhub.io/
# Free account allows 60 requests per minute, sufficient for daily use
# Format: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FINNHUB_API_KEY=your_finnhub_api_key_here
# ===== LLM Provider API Keys (Choose based on your needs) =====
# 🇨🇳 DashScope (Alibaba Cloud) API Key
# Required ONLY when:
# 1. Analyzing Chinese A-share stocks (uses TongDaXin data + DashScope embeddings)
# 2. Choosing DashScope as your LLM provider (Qwen models)
# Get from: https://dashscope.aliyun.com/
# Register Alibaba Cloud account -> Enable DashScope service -> Get API key
# Format: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DASHSCOPE_API_KEY=your_dashscope_api_key_here
# 🌍 OpenAI API Key (For US stocks with OpenAI models)
# Required when using OpenAI as LLM provider
# Get from: https://platform.openai.com/
# Format: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=your_openai_api_key_here
# 🔍 Google AI API Key (For US stocks with Google models)
# Required when using Google AI as LLM provider
# Get from: https://ai.google.dev/
GOOGLE_API_KEY=your_google_api_key_here
# 🤖 Anthropic API Key (For US stocks with Claude models)
# Required when using Anthropic as LLM provider
# Get from: https://console.anthropic.com/
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# ===== Project Configuration =====
# Results storage directory
TRADINGAGENTS_RESULTS_DIR=./results
# Log level (DEBUG, INFO, WARNING, ERROR)
TRADINGAGENTS_LOG_LEVEL=INFO
# ===== Database Configuration =====
# 🔧 Database enable switches (Disabled by default, system uses file cache)
# Set to true to enable corresponding database, false or unset to disable
MONGODB_ENABLED=false
REDIS_ENABLED=false
# 🗄️ MongoDB database configuration (For persistent storage of stock data and analysis results)
# Start with Docker: scripts/start_services_alt_ports.bat
MONGODB_HOST=localhost
MONGODB_PORT=27018
MONGODB_USERNAME=admin
MONGODB_PASSWORD=tradingagents123
MONGODB_DATABASE=tradingagents
MONGODB_AUTH_SOURCE=admin
# 📦 Redis cache configuration (For high-speed caching and session management)
# Start with Docker: scripts/start_services_alt_ports.bat
REDIS_HOST=localhost
REDIS_PORT=6380
REDIS_PASSWORD=tradingagents123
REDIS_DB=0
# ===== Reddit API Configuration (Optional) =====
# For social media sentiment data collection
# Get from: https://www.reddit.com/prefs/apps
# Reddit client ID
REDDIT_CLIENT_ID=your_reddit_client_id
# Reddit client secret
REDDIT_CLIENT_SECRET=your_reddit_client_secret
# Reddit user agent
REDDIT_USER_AGENT=TradingAgents/1.0
# ===== Usage Instructions =====
# 1. Copy this file to .env: cp .env.example .env
# 2. Edit .env file and fill in your real API keys based on your needs
# 3. Configure API keys based on your use case (see Quick Start Guide below)
# 4. Run python -m cli.main to start the application
# 5. Test your configuration by running a sample analysis
# ===== Quick Start Guide =====
# For US Stock Analysis Only:
# 1. Get API key from one of: OpenAI, Google AI, or Anthropic
# 2. Get FinnHub API key from https://finnhub.io/
# 3. Copy this file: cp .env.example .env
# 4. Edit .env and set your chosen LLM provider key + FINNHUB_API_KEY
# 5. Run: python -m cli.main
# Example: OPENAI_API_KEY + FINNHUB_API_KEY
# For China A-Share Analysis:
# 1. Get DashScope API key from https://dashscope.aliyun.com/
# 2. Get FinnHub API key from https://finnhub.io/
# 3. Copy this file: cp .env.example .env
# 4. Edit .env and set DASHSCOPE_API_KEY and FINNHUB_API_KEY
# 5. Install dependencies: pip install pytdx beautifulsoup4
# 6. Run: python -m cli.main
# For DashScope LLM Provider (Qwen models):
# 1. Get DashScope API key from https://dashscope.aliyun.com/
# 2. Get FinnHub API key from https://finnhub.io/
# 3. Set DASHSCOPE_API_KEY and FINNHUB_API_KEY
# 4. Choose DashScope as LLM provider in CLI
# For full features (with database caching):
# 1. Configure API keys as above based on your use case
# 2. Start databases: docker run -d -p 27017:27017 --name mongodb mongo
# 3. Start Redis: docker run -d -p 6379:6379 --name redis redis
# 4. Set MONGODB_ENABLED=true and REDIS_ENABLED=true in .env
# 5. Run: python -m cli.main

10
.gitignore vendored
View File

@ -7,3 +7,13 @@ eval_results/
eval_data/
*.egg-info/
.env
# 测试目录(不纳入版本控制)
tests/
# 中文版本目录(不纳入版本控制)
TradingAgentsCN/
# 虚拟环境目录(不纳入版本控制)
test_env/
.venv/

392
MERGE_DOCUMENTATION.md Normal file
View File

@ -0,0 +1,392 @@
# TradingAgents Chinese Features Merge Documentation
## 📋 Executive Summary
This document provides a comprehensive overview of the successful merge of Chinese market features from the TradingAgentsCN project into the main TradingAgents repository. The integration includes DashScope (Alibaba Cloud) LLM support, TongDaXin API for A-share data, advanced database caching, and enhanced CLI market selection.
**Merge Details**:
- **Date**: January 2025
- **Branch**: `full-merge-chinese-features`
- **Source**: TradingAgentsCN directory
- **Target**: Main TradingAgents repository
- **Approach**: Full feature integration with backward compatibility
---
## ✅ Successfully Integrated Features
### 🤖 1. DashScope (Alibaba Cloud) LLM Integration
**Status**: ✅ **COMPLETE AND TESTED**
**What was added**:
- Complete DashScope LLM provider integration in CLI
- Support for Qwen model family: qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
- DashScope embedding service for memory system
- Intelligent fallback: DashScope embeddings → OpenAI embeddings
- Comprehensive error handling and API key validation
**Key Files**:
```
cli/utils.py - DashScope LLM provider options
tradingagents/graph/trading_graph.py - DashScope LLM initialization
tradingagents/agents/utils/memory.py - DashScope embedding integration
tradingagents/default_config.py - Configuration examples
```
**Configuration Required**:
```env
DASHSCOPE_API_KEY=your_dashscope_api_key_here
```
**User Experience**: DashScope appears as the first option in CLI LLM provider selection
---
### 🇨🇳 2. China A-Share Market Support
**Status**: ✅ **COMPLETE AND TESTED**
**What was added**:
- TongDaXin API integration for real-time A-share data
- Support for all major Chinese stock exchanges:
- Shanghai Stock Exchange: 60xxxx (e.g., 600036)
- Shenzhen Stock Exchange: 00xxxx (e.g., 000001)
- ChiNext Board: 30xxxx (e.g., 300001)
- STAR Market: 68xxxx (e.g., 688001)
- Optimized China data provider with intelligent caching
- Chinese finance data aggregator for news and sentiment
- Unified stock data service with automatic fallback
**Key Files**:
```
tradingagents/dataflows/tdx_utils.py - TongDaXin data provider
tradingagents/dataflows/optimized_china_data.py - Optimized A-share data
tradingagents/dataflows/chinese_finance_utils.py - Chinese finance tools
tradingagents/dataflows/stock_data_service.py - Unified data service
```
**Dependencies Added**:
```
pytdx>=1.72
beautifulsoup4>=4.9.0
```
**Data Flow Architecture**:
```
MongoDB Database → TongDaXin API → File Cache → Error Handling
```
---
### 🗄️ 3. Advanced Database Integration
**Status**: ✅ **COMPLETE AND TESTED**
**What was added**:
- MongoDB integration for persistent data storage and analytics
- Redis integration for high-performance caching
- Database cache manager with intelligent routing
- Token usage tracking and cost analytics
- Configuration management system
- Integrated cache manager with adaptive performance optimization
**Key Files**:
```
tradingagents/config/database_config.py - Database configuration
tradingagents/config/database_manager.py - Connection management
tradingagents/config/mongodb_storage.py - MongoDB operations
tradingagents/config/config_manager.py - Configuration management
tradingagents/dataflows/db_cache_manager.py - Database cache manager
tradingagents/dataflows/integrated_cache.py - Integrated cache system
tradingagents/dataflows/adaptive_cache.py - Adaptive cache system
```
**Dependencies Added**:
```
pymongo>=4.0.0
redis>=4.0.0
```
**Configuration (Optional)**:
```env
# MongoDB
MONGODB_ENABLED=false
MONGODB_HOST=localhost
MONGODB_PORT=27018
MONGODB_USERNAME=admin
MONGODB_PASSWORD=your_mongodb_password
MONGODB_DATABASE=tradingagents
# Redis
REDIS_ENABLED=false
REDIS_HOST=localhost
REDIS_PORT=6380
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
```
**Integration**: Automatically integrated into CLI startup via enhanced `get_cache()` function
---
### 🌍 4. Enhanced CLI Market Selection
**Status**: ✅ **COMPLETE AND TESTED**
**What was added**:
- Interactive market selection interface
- Market-specific ticker format validation with examples
- Automatic data source routing based on market selection
- English-only interface (Chinese text removed as requested)
- Comprehensive format validation and error messages
**Key Files**:
```
cli/utils.py - Added select_market() and enhanced get_ticker()
cli/main.py - Updated workflow with market selection step
```
**Supported Markets**:
1. **US Stock Market**
- Format: 1-5 letter symbols (e.g., AAPL, SPY, TSLA)
- Data Source: Yahoo Finance
- Validation Pattern: `^[A-Z]{1,5}$`
2. **China A-Share Market**
- Format: 6-digit numeric codes (e.g., 000001, 600036)
- Data Source: TongDaXin API
- Validation Pattern: `^\d{6}$`
**Removed**: Hong Kong Stock support (as specifically requested)
---
### 🔧 5. Intelligent Cache System Integration
**Status**: ✅ **COMPLETE AND TESTED**
**What was added**:
- IntegratedCacheManager as default cache system in CLI
- Automatic selection between database and file caching
- Intelligent fallback mechanisms for high availability
- Performance optimization with adaptive caching strategies
**Key Changes**:
```
tradingagents/dataflows/cache_manager.py - Enhanced get_cache() function
```
**Cache Priority Logic**:
```
1. Database cache (MongoDB/Redis) - if enabled and available
2. File cache - reliable fallback
3. Error handling - graceful degradation
```
---
## 🔧 Technical Improvements
### 📦 Dependency Management
- ✅ All new dependencies properly added to `requirements.txt`
- ✅ Optional dependencies with graceful fallbacks
- ✅ No breaking changes to existing functionality
- ✅ Backward compatibility maintained
### 🛡️ Error Handling & Reliability
- ✅ Comprehensive error handling for all new features
- ✅ Graceful degradation when external services unavailable
- ✅ Detailed error messages with user guidance
- ✅ Automatic retry logic for API calls
### 🔄 Backward Compatibility
- ✅ All existing functionality preserved and working
- ✅ New features are optional and configurable
- ✅ Default behavior unchanged for existing users
- ✅ Seamless upgrade path
---
## ⚠️ Known Issues & Limitations
### 1. Optional Dependencies
**Issue**: Some dependencies not installed by default
**Impact**: Limited functionality until manually installed
**Solution**:
```bash
pip install pytdx beautifulsoup4
```
### 2. Database Services
**Issue**: MongoDB and Redis disabled by default in `.env`
**Impact**: Database caching features not active by default
**Solution**: Enable in `.env` and start database services:
```bash
# Start services
docker run -d -p 27017:27017 --name mongodb mongo
docker run -d -p 6379:6379 --name redis redis
# Enable in .env
MONGODB_ENABLED=true
REDIS_ENABLED=true
```
### 3. API Rate Limits
**Issue**: TongDaXin API may have undocumented rate limiting
**Impact**: Potential delays in A-share data retrieval
**Mitigation**: Intelligent caching and retry logic implemented
---
## 🚧 Incomplete Features / Future Work
### 1. Data Source Selection UI
**Status**: ❌ **NOT IMPLEMENTED**
**Description**: User interface to manually choose between cache and TongDaXin API
**Current State**: Automatic fallback logic only
**Future Enhancement**: Add CLI option for data source preference
### 2. Advanced A-Share Analytics
**Status**: ⚠️ **PARTIALLY IMPLEMENTED**
**Completed**: Basic data retrieval and caching
**Missing**:
- Real-time market sentiment analysis
- A-share specific technical indicators
- Chinese financial news sentiment integration
- Sector analysis for Chinese markets
- A-share market hours and trading calendar
### 3. Performance Monitoring & Analytics
**Status**: ❌ **NOT IMPLEMENTED**
**Missing Features**:
- Database performance metrics dashboard
- Cache hit/miss statistics
- API response time monitoring
- Usage analytics and reporting
- Cost tracking for API calls
### 4. Configuration Management
**Status**: ⚠️ **BASIC IMPLEMENTATION**
**Current**: Basic configuration validation
**Missing**:
- Comprehensive configuration validation wizard
- Interactive setup guide for new users
- Configuration health checks and diagnostics
- Automatic configuration migration tools
### 5. Advanced TongDaXin Features
**Status**: ❌ **NOT IMPLEMENTED**
**Missing**:
- Real-time tick data streaming
- Level-2 market data integration
- Options and futures data support
- Historical fundamental data
- Corporate actions and dividend data
---
## 📊 Testing & Validation Status
### ✅ Completed & Verified
- **DashScope LLM Integration**: ✅ All models working
- **TongDaXin API Functionality**: ✅ Data retrieval working
- **Database Connectivity**: ✅ MongoDB and Redis connections
- **Cache System Integration**: ✅ Intelligent fallback working
- **CLI Market Selection**: ✅ Interactive selection working
- **English-Only Interface**: ✅ No Chinese text in UI
- **Ticker Format Validation**: ✅ Market-specific validation
- **Error Handling**: ✅ Graceful degradation verified
### ⚠️ Needs Further Testing
- End-to-end A-share analysis workflow under load
- Database performance with large datasets
- TongDaXin API behavior under rate limiting
- Multi-user concurrent database access
- Memory usage with large cache datasets
---
## 🎯 Deployment Recommendations
### For Immediate Use (Minimal Setup)
1. **Install optional dependencies**:
```bash
pip install pytdx beautifulsoup4
```
2. **Configure DashScope API**:
```env
DASHSCOPE_API_KEY=your_actual_api_key
```
3. **Test A-share functionality**:
```bash
python -m cli.main
# Select: China A-Share
# Enter: 000001 (Ping An Bank)
```
### For Production Deployment (Full Features)
1. **Setup database services**:
```bash
docker run -d -p 27017:27017 --name mongodb mongo
docker run -d -p 6379:6379 --name redis redis
```
2. **Enable database caching**:
```env
MONGODB_ENABLED=true
REDIS_ENABLED=true
```
3. **Configure monitoring and logging**:
- Implement application logging
- Setup database monitoring
- Configure API usage tracking
### For Development Environment
1. **Setup development databases** with persistent volumes
2. **Configure development API keys** with rate limiting
3. **Enable debug logging** for troubleshooting
4. **Setup testing data** for consistent testing
---
## 📈 Impact Assessment
### ✅ Positive Impacts
- **🇨🇳 Chinese Market Access**: Complete A-share market analysis capability
- **🚀 Performance**: Database caching significantly improves data access speed
- **🔄 Reliability**: Multiple LLM providers increase system reliability
- **👥 User Experience**: Intuitive market selection with validation
- **🌐 Global Reach**: Support for both US and Chinese markets
- **💾 Scalability**: Database integration enables enterprise deployment
### ⚠️ Considerations
- **🔧 Complexity**: Increased system complexity with multiple data sources
- **📦 Dependencies**: Additional external dependencies and services
- **🛠️ Maintenance**: More components require monitoring and maintenance
- **💰 Costs**: Additional API and database hosting costs
- **🔐 Security**: More API keys and database credentials to manage
---
## 🎉 Conclusion
This merge represents a significant enhancement to TradingAgents, successfully integrating comprehensive Chinese market support while maintaining system stability and backward compatibility. The integration creates a robust, scalable foundation for global financial market analysis.
### 🏆 Key Achievements
- **🇨🇳 Complete Chinese A-share market support** with TongDaXin API
- **🤖 DashScope LLM integration** with Qwen model family
- **🗄️ Enterprise-grade database caching** with MongoDB and Redis
- **🌍 Enhanced CLI** with intelligent market selection
- **🔧 Robust fallback mechanisms** throughout the system
- **📈 Scalable architecture** ready for production deployment
### 🚀 System Status
The system is now **production-ready** for both US and Chinese market analysis, with clear documentation and upgrade paths for future enhancements. Users can immediately benefit from Chinese market support while having the option to enable advanced database features for improved performance and analytics.
### 🔮 Future Roadmap
The foundation is now in place for advanced features like real-time sentiment analysis, advanced Chinese market indicators, and comprehensive performance monitoring. The modular architecture ensures these enhancements can be added incrementally without disrupting existing functionality.

400
MERGE_SUMMARY.md Normal file
View File

@ -0,0 +1,400 @@
# TradingAgents 中文版功能全量合并文档
## 📋 合并概述
本文档详细记录了从TradingAgentsCN项目向主TradingAgents仓库的全面功能合并。此次合并包括百炼(DashScope)大模型集成、通达信API的A股数据支持、高级数据库缓存系统以及增强的CLI市场选择功能。
**合并详情**:
- **合并时间**: 2025年1月
- **合并分支**: `full-merge-chinese-features`
- **源项目**: TradingAgentsCN目录
- **目标项目**: 主TradingAgents仓库
- **合并方式**: 全功能集成,保持向后兼容性
## 📊 合并统计
- **新增文件**: 18个核心功能文件
- **修改文件**: 8个现有文件增强
- **新增依赖**: 4个Python包
- **配置项**: 12个新的环境变量
- **支持市场**: 2个(美股 + A股)
---
## ✅ 成功集成的功能
### 🤖 1. 百炼(DashScope)大模型集成
**状态**: ✅ **完成并测试通过**
**集成内容**:
- CLI中完整的百炼LLM提供商支持
- 支持通义千问模型系列: qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
- 百炼embedding服务用于记忆系统
- 智能回退机制: 百炼embedding → OpenAI embedding
- 全面的错误处理和API密钥验证
**核心文件**:
```
cli/utils.py - 百炼LLM提供商选项
tradingagents/graph/trading_graph.py - 百炼LLM初始化
tradingagents/agents/utils/memory.py - 百炼embedding集成
tradingagents/default_config.py - 配置示例
```
**所需配置**:
```env
DASHSCOPE_API_KEY=your_dashscope_api_key_here
```
**用户体验**: 百炼在CLI LLM提供商选择中显示为第一选项
---
### 🇨🇳 2. 中国A股市场支持
**状态**: ✅ **完成并测试通过**
**集成内容**:
- 通达信API集成获取实时A股数据
- 支持所有主要中国证券交易所:
- 上海证券交易所: 60xxxx (如 600036)
- 深圳证券交易所: 00xxxx (如 000001)
- 创业板: 30xxxx (如 300001)
- 科创板: 68xxxx (如 688001)
- 优化的中国数据提供器,带智能缓存
- 中国财经数据聚合器,用于新闻和情绪分析
- 统一股票数据服务,带自动回退机制
**核心文件**:
```
tradingagents/dataflows/tdx_utils.py - 通达信数据提供器
tradingagents/dataflows/optimized_china_data.py - 优化A股数据
tradingagents/dataflows/chinese_finance_utils.py - 中国财经工具
tradingagents/dataflows/stock_data_service.py - 统一数据服务
```
**新增依赖**:
```
pytdx>=1.72
beautifulsoup4>=4.9.0
```
**数据流架构**:
```
MongoDB数据库 → 通达信API → 文件缓存 → 错误处理
```
---
### 🗄️ 3. 高级数据库集成
**状态**: ✅ **完成并测试通过**
**集成内容**:
- MongoDB集成用于持久化数据存储和分析
- Redis集成用于高性能缓存
- 数据库缓存管理器,带智能路由
- Token使用跟踪和成本分析
- 配置管理系统
- 集成缓存管理器,带自适应性能优化
**核心文件**:
```
tradingagents/config/database_config.py - 数据库配置
tradingagents/config/database_manager.py - 连接管理
tradingagents/config/mongodb_storage.py - MongoDB操作
tradingagents/config/config_manager.py - 配置管理
tradingagents/dataflows/db_cache_manager.py - 数据库缓存管理器
tradingagents/dataflows/integrated_cache.py - 集成缓存系统
tradingagents/dataflows/adaptive_cache.py - 自适应缓存系统
```
**新增依赖**:
```
pymongo>=4.0.0
redis>=4.0.0
```
**配置(可选)**:
```env
# MongoDB
MONGODB_ENABLED=false
MONGODB_HOST=localhost
MONGODB_PORT=27018
MONGODB_USERNAME=admin
MONGODB_PASSWORD=your_mongodb_password
MONGODB_DATABASE=tradingagents
# Redis
REDIS_ENABLED=false
REDIS_HOST=localhost
REDIS_PORT=6380
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
```
**集成方式**: 通过增强的`get_cache()`函数自动集成到CLI启动流程
---
### 🌍 4. 增强的CLI市场选择
**状态**: ✅ **完成并测试通过**
**集成内容**:
- 交互式市场选择界面
- 市场特定的股票代码格式验证和示例
- 基于市场选择的自动数据源路由
- 纯英文界面(按要求移除中文文本)
- 全面的格式验证和错误消息
**核心文件**:
```
cli/utils.py - 添加select_market()和增强get_ticker()
cli/main.py - 更新工作流程,包含市场选择步骤
```
**支持的市场**:
1. **美股市场**
- 格式: 1-5位字母代码 (如 AAPL, SPY, TSLA)
- 数据源: Yahoo Finance
- 验证模式: `^[A-Z]{1,5}$`
2. **中国A股市场**
- 格式: 6位数字代码 (如 000001, 600036)
- 数据源: 通达信API
- 验证模式: `^\d{6}$`
**移除功能**: 港股支持(按具体要求移除)
---
### 🔧 5. 智能缓存系统集成
**状态**: ✅ **完成并测试通过**
**集成内容**:
- IntegratedCacheManager作为CLI中的默认缓存系统
- 数据库和文件缓存之间的自动选择
- 高可用性的智能回退机制
- 自适应缓存策略的性能优化
**核心变更**:
```
tradingagents/dataflows/cache_manager.py - 增强get_cache()函数
```
**缓存优先级逻辑**:
```
1. 数据库缓存 (MongoDB/Redis) - 如果启用且可用
2. 文件缓存 - 可靠的回退方案
3. 错误处理 - 优雅降级
```
---
## 🔧 技术改进
### 📦 依赖管理
- ✅ 所有新依赖正确添加到`requirements.txt`
- ✅ 可选依赖带有优雅回退机制
- ✅ 不破坏现有功能
- ✅ 保持向后兼容性
### 🛡️ 错误处理和可靠性
- ✅ 所有新功能的全面错误处理
- ✅ 外部服务不可用时的优雅降级
- ✅ 详细的错误消息和用户指导
- ✅ API调用的自动重试逻辑
### 🔄 向后兼容性
- ✅ 所有现有功能保持正常工作
- ✅ 新功能可选且可配置
- ✅ 现有用户的默认行为不变
- ✅ 无缝升级路径
---
## ⚠️ 已知问题和限制
### 1. 可选依赖
**问题**: 部分依赖默认未安装
**影响**: 手动安装前功能受限
**解决方案**:
```bash
pip install pytdx beautifulsoup4
```
### 2. 数据库服务
**问题**: MongoDB和Redis在`.env`中默认禁用
**影响**: 数据库缓存功能默认不激活
**解决方案**: 启用数据库并启动服务:
```bash
# 启动服务
docker run -d -p 27017:27017 --name mongodb mongo
docker run -d -p 6379:6379 --name redis redis
# 在.env中启用
MONGODB_ENABLED=true
REDIS_ENABLED=true
```
### 3. API频率限制
**问题**: 通达信API可能有未公开的频率限制
**影响**: A股数据获取可能出现延迟
**缓解措施**: 已实现智能缓存和重试逻辑
---
## 🚧 未完成功能/未来工作
### 1. 数据源选择UI
**状态**: ❌ **未实现**
**描述**: 用户手动选择缓存和通达信API的界面
**当前状态**: 仅有自动回退逻辑
**未来增强**: 添加CLI数据源偏好选项
### 2. 高级A股分析功能
**状态**: ⚠️ **部分实现**
**已完成**: 基础数据获取和缓存
**缺失功能**:
- 实时市场情绪分析
- A股特定技术指标
- 中国财经新闻情绪集成
- 中国市场板块分析
- A股交易时间和交易日历
### 3. 性能监控和分析
**状态**: ❌ **未实现**
**缺失功能**:
- 数据库性能指标仪表板
- 缓存命中/未命中统计
- API响应时间监控
- 使用分析和报告
- API调用成本跟踪
### 4. 配置管理
**状态**: ⚠️ **基础实现**
**当前**: 基础配置验证
**缺失功能**:
- 全面配置验证向导
- 新用户交互式设置指南
- 配置健康检查和诊断
- 自动配置迁移工具
### 5. 高级通达信功能
**状态**: ❌ **未实现**
**缺失功能**:
- 实时tick数据流
- Level-2市场数据集成
- 期权和期货数据支持
- 历史基本面数据
- 公司行动和分红数据
---
## 📊 测试和验证状态
### ✅ 已完成并验证
- **百炼LLM集成**: ✅ 所有模型正常工作
- **通达信API功能**: ✅ 数据获取正常工作
- **数据库连接**: ✅ MongoDB和Redis连接
- **缓存系统集成**: ✅ 智能回退正常工作
- **CLI市场选择**: ✅ 交互式选择正常工作
- **纯英文界面**: ✅ UI中无中文文本
- **股票代码格式验证**: ✅ 市场特定验证
- **错误处理**: ✅ 优雅降级已验证
### ⚠️ 需要进一步测试
- 负载下的端到端A股分析工作流
- 大数据集的数据库性能
- 频率限制下的通达信API行为
- 多用户并发数据库访问
- 大缓存数据集的内存使用
---
## 🎯 部署建议
### 立即使用(最小设置)
1. **安装可选依赖**:
```bash
pip install pytdx beautifulsoup4
```
2. **配置百炼API**:
```env
DASHSCOPE_API_KEY=your_actual_api_key
```
3. **测试A股功能**:
```bash
python -m cli.main
# 选择: China A-Share
# 输入: 000001 (平安银行)
```
### 生产部署(完整功能)
1. **设置数据库服务**:
```bash
docker run -d -p 27017:27017 --name mongodb mongo
docker run -d -p 6379:6379 --name redis redis
```
2. **启用数据库缓存**:
```env
MONGODB_ENABLED=true
REDIS_ENABLED=true
```
3. **配置监控和日志**:
- 实现应用程序日志
- 设置数据库监控
- 配置API使用跟踪
### 开发环境
1. **设置持久化卷的开发数据库**
2. **配置开发API密钥**,带频率限制
3. **启用调试日志**用于故障排除
4. **设置测试数据**用于一致性测试
---
## 📈 影响评估
### ✅ 积极影响
- **🇨🇳 中国市场接入**: 完整的A股市场分析能力
- **🚀 性能提升**: 数据库缓存显著提高数据访问速度
- **🔄 可靠性**: 多LLM提供商增加系统可靠性
- **👥 用户体验**: 直观的市场选择和验证
- **🌐 全球覆盖**: 支持美股和中国市场
- **💾 可扩展性**: 数据库集成支持企业部署
### ⚠️ 考虑因素
- **🔧 复杂性**: 多数据源增加系统复杂性
- **📦 依赖**: 额外的外部依赖和服务
- **🛠️ 维护**: 更多组件需要监控和维护
- **💰 成本**: 额外的API和数据库托管成本
- **🔐 安全**: 更多API密钥和数据库凭据需要管理
---
## 🎉 总结
此次合并代表了TradingAgents的重大增强成功集成了全面的中国市场支持同时保持系统稳定性和向后兼容性。集成创建了一个强大、可扩展的全球金融市场分析基础。
### 🏆 关键成就
- **🇨🇳 完整的中国A股市场支持**集成通达信API
- **🤖 百炼LLM集成**,支持通义千问模型系列
- **🗄️ 企业级数据库缓存**支持MongoDB和Redis
- **🌍 增强的CLI**,带智能市场选择
- **🔧 强大的回退机制**,贯穿整个系统
- **📈 可扩展架构**,为生产部署做好准备
### 🚀 系统状态
系统现在**生产就绪**,支持美股和中国市场分析,具有清晰的文档和未来增强的升级路径。用户可以立即受益于中国市场支持,同时可以选择启用高级数据库功能以获得更好的性能和分析能力。
### 🔮 未来路线图
现在已为高级功能奠定基础,如实时情绪分析、高级中国市场指标和全面性能监控。模块化架构确保这些增强可以逐步添加,而不会破坏现有功能。

View File

@ -425,29 +425,39 @@ def get_user_selections():
box_content += f"\n[dim]Default: {default}[/dim]"
return Panel(box_content, border_style="blue", padding=(1, 2))
# Step 1: Ticker symbol
# Step 1: Market selection
console.print(
create_question_box(
"Step 1: Ticker Symbol", "Enter the ticker symbol to analyze", "SPY"
"Step 1: Select Market", "Choose the stock market to analyze", ""
)
)
selected_ticker = get_ticker()
selected_market = select_market()
# Step 2: Analysis date
# Step 2: Ticker symbol
console.print(
create_question_box(
"Step 2: Ticker Symbol",
f"Enter {selected_market['name']} ticker symbol",
selected_market['default']
)
)
selected_ticker = get_ticker(selected_market)
# Step 3: Analysis date
default_date = datetime.datetime.now().strftime("%Y-%m-%d")
console.print(
create_question_box(
"Step 2: Analysis Date",
"Step 3: Analysis Date",
"Enter the analysis date (YYYY-MM-DD)",
default_date,
)
)
analysis_date = get_analysis_date()
# Step 3: Select analysts
# Step 4: Select analysts
console.print(
create_question_box(
"Step 3: Analysts Team", "Select your LLM analyst agents for the analysis"
"Step 4: Analysts Team", "Select your LLM analyst agents for the analysis"
)
)
selected_analysts = select_analysts()
@ -455,32 +465,33 @@ def get_user_selections():
f"[green]Selected analysts:[/green] {', '.join(analyst.value for analyst in selected_analysts)}"
)
# Step 4: Research depth
# Step 5: Research depth
console.print(
create_question_box(
"Step 4: Research Depth", "Select your research depth level"
"Step 5: Research Depth", "Select your research depth level"
)
)
selected_research_depth = select_research_depth()
# Step 5: OpenAI backend
# Step 6: LLM Provider
console.print(
create_question_box(
"Step 5: OpenAI backend", "Select which service to talk to"
"Step 6: LLM Provider", "Select your LLM provider"
)
)
selected_llm_provider, backend_url = select_llm_provider()
# Step 6: Thinking agents
# Step 7: Thinking agents
console.print(
create_question_box(
"Step 6: Thinking Agents", "Select your thinking agents for analysis"
"Step 7: Thinking Agents", "Select your thinking agents for analysis"
)
)
selected_shallow_thinker = select_shallow_thinking_agent(selected_llm_provider)
selected_deep_thinker = select_deep_thinking_agent(selected_llm_provider)
return {
"market": selected_market,
"ticker": selected_ticker,
"analysis_date": analysis_date,
"analysts": selected_analysts,
@ -492,11 +503,6 @@ def get_user_selections():
}
def get_ticker():
"""Get ticker symbol from user input."""
return typer.prompt("", default="SPY")
def get_analysis_date():
"""Get the analysis date from user input."""
while True:

View File

@ -11,24 +11,120 @@ 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.",
def select_market():
"""Select stock market"""
markets = {
"1": {
"name": "US Stock",
"default": "SPY",
"examples": ["SPY", "AAPL", "TSLA", "NVDA", "MSFT"],
"format": "Stock symbol (e.g., AAPL)",
"pattern": r'^[A-Z]{1,5}$',
"data_source": "yahoo_finance"
},
"2": {
"name": "China A-Share",
"default": "600036",
"examples": ["000001", "600036", "000858", "300001", "688001"],
"format": "6-digit code (e.g., 600036, 000001)",
"pattern": r'^\d{6}$',
"data_source": "tongdaxin"
}
}
choices = []
for key, market in markets.items():
examples_str = ", ".join(market["examples"][:3])
display = f"{market['name']} - Examples: {examples_str}"
choices.append(questionary.Choice(display, value=key))
choice = questionary.select(
"Select Stock Market:",
choices=choices,
instruction="\n- Use arrow keys to navigate\n- Press Enter to select",
style=questionary.Style(
[
("text", "fg:green"),
("highlighted", "noinherit"),
("selected", "fg:cyan noinherit"),
("highlighted", "fg:cyan noinherit"),
("pointer", "fg:cyan noinherit"),
]
),
).ask()
if not ticker:
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
if choice is None:
from rich.console import Console
console = Console()
console.print("\n[red]No market selected. Exiting...[/red]")
exit(1)
return ticker.strip().upper()
selected_market = markets[choice]
from rich.console import Console
console = Console()
console.print(f"[green]✅ Selected: {selected_market['name']}[/green]")
return selected_market
def get_ticker(market=None) -> str:
"""Prompt the user to enter a ticker symbol with market-specific validation."""
if market is None:
# Fallback to original behavior for backward compatibility
ticker = questionary.text(
"Enter the ticker symbol to analyze:",
validate=lambda x: len(x.strip()) > 0 or "Please enter a valid ticker symbol.",
style=questionary.Style(
[
("text", "fg:green"),
("highlighted", "noinherit"),
]
),
).ask()
if not ticker:
from rich.console import Console
console = Console()
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
exit(1)
return ticker.strip().upper()
# Market-specific ticker input with validation
from rich.console import Console
console = Console()
console.print(f"\n[dim]Format requirement: {market['format']}[/dim]")
console.print(f"[dim]Examples: {', '.join(market['examples'][:3])}[/dim]")
while True:
ticker = questionary.text(
f"Enter {market['name']} ticker symbol:",
default=market['default'],
style=questionary.Style(
[
("text", "fg:green"),
("highlighted", "noinherit"),
]
),
).ask()
if not ticker:
console.print("\n[red]No ticker symbol provided. Exiting...[/red]")
exit(1)
# Validate ticker format
import re
ticker_to_check = ticker.upper() if market['data_source'] != 'tongdaxin' else ticker
if re.match(market['pattern'], ticker_to_check):
# For A-shares, return pure numeric code
if market['data_source'] == 'tongdaxin':
console.print(f"[green]✅ Valid A-share code: {ticker} (will use TongDaXin data source)[/green]")
return ticker
else:
console.print(f"[green]✅ Valid ticker: {ticker.upper()}[/green]")
return ticker.upper()
else:
console.print(f"[red]❌ Invalid ticker format[/red]")
console.print(f"[yellow]Please use correct format: {market['format']}[/yellow]")
def get_analysis_date() -> str:
@ -127,6 +223,11 @@ def select_shallow_thinking_agent(provider) -> str:
# Define shallow thinking llm engine options with their corresponding model names
SHALLOW_AGENT_OPTIONS = {
"dashscope (alibaba cloud)": [
("Qwen-Turbo - Fast response, suitable for quick tasks", "qwen-turbo"),
("Qwen-Plus - Balanced performance and cost", "qwen-plus"),
("Qwen-Max - Best performance for complex analysis", "qwen-max"),
],
"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"),
@ -185,6 +286,12 @@ def select_deep_thinking_agent(provider) -> str:
# Define deep thinking llm engine options with their corresponding model names
DEEP_AGENT_OPTIONS = {
"dashscope (alibaba cloud)": [
("Qwen-Plus - Balanced performance and cost (Recommended)", "qwen-plus"),
("Qwen-Max - Best performance for complex analysis", "qwen-max"),
("Qwen-Max-LongContext - Ultra-long context support", "qwen-max-longcontext"),
("Qwen-Turbo - Fast response for lighter analysis", "qwen-turbo"),
],
"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"),
@ -240,14 +347,16 @@ def select_deep_thinking_agent(provider) -> str:
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
# DashScope (Alibaba Cloud) is recommended for Chinese users
BASE_URLS = [
("DashScope (Alibaba Cloud)", "https://dashscope.aliyuncs.com/api/v1"),
("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"),
("Ollama", "http://localhost:11434/v1"),
]
choice = questionary.select(

47
config/models.json Normal file
View File

@ -0,0 +1,47 @@
[
{
"provider": "dashscope",
"model_name": "qwen-turbo",
"api_key": "",
"base_url": null,
"max_tokens": 4000,
"temperature": 0.7,
"enabled": true
},
{
"provider": "dashscope",
"model_name": "qwen-plus-latest",
"api_key": "",
"base_url": null,
"max_tokens": 8000,
"temperature": 0.7,
"enabled": true
},
{
"provider": "openai",
"model_name": "gpt-3.5-turbo",
"api_key": "",
"base_url": null,
"max_tokens": 4000,
"temperature": 0.7,
"enabled": false
},
{
"provider": "openai",
"model_name": "gpt-4",
"api_key": "",
"base_url": null,
"max_tokens": 8000,
"temperature": 0.7,
"enabled": false
},
{
"provider": "google",
"model_name": "gemini-pro",
"api_key": "",
"base_url": null,
"max_tokens": 4000,
"temperature": 0.7,
"enabled": false
}
]

58
config/pricing.json Normal file
View File

@ -0,0 +1,58 @@
[
{
"provider": "dashscope",
"model_name": "qwen-turbo",
"input_price_per_1k": 0.002,
"output_price_per_1k": 0.006,
"currency": "CNY"
},
{
"provider": "dashscope",
"model_name": "qwen-plus-latest",
"input_price_per_1k": 0.004,
"output_price_per_1k": 0.012,
"currency": "CNY"
},
{
"provider": "dashscope",
"model_name": "qwen-max",
"input_price_per_1k": 0.02,
"output_price_per_1k": 0.06,
"currency": "CNY"
},
{
"provider": "openai",
"model_name": "gpt-3.5-turbo",
"input_price_per_1k": 0.0015,
"output_price_per_1k": 0.002,
"currency": "USD"
},
{
"provider": "openai",
"model_name": "gpt-4",
"input_price_per_1k": 0.03,
"output_price_per_1k": 0.06,
"currency": "USD"
},
{
"provider": "openai",
"model_name": "gpt-4-turbo",
"input_price_per_1k": 0.01,
"output_price_per_1k": 0.03,
"currency": "USD"
},
{
"provider": "google",
"model_name": "gemini-pro",
"input_price_per_1k": 0.00025,
"output_price_per_1k": 0.0005,
"currency": "USD"
},
{
"provider": "google",
"model_name": "gemini-pro-vision",
"input_price_per_1k": 0.00025,
"output_price_per_1k": 0.0005,
"currency": "USD"
}
]

13
config/settings.json Normal file
View File

@ -0,0 +1,13 @@
{
"default_provider": "dashscope",
"default_model": "qwen-turbo",
"enable_cost_tracking": true,
"cost_alert_threshold": 100.0,
"currency_preference": "CNY",
"auto_save_usage": true,
"max_usage_records": 10000,
"data_dir": "C:\\Users\\PC\\Documents\\TradingAgents\\data",
"cache_dir": "C:\\Users\\PC\\Documents\\TradingAgents\\data\\cache",
"results_dir": "C:\\Users\\PC\\Documents\\TradingAgents\\results",
"auto_create_dirs": true
}

342
config/usage.json Normal file
View File

@ -0,0 +1,342 @@
[
{
"timestamp": "2025-07-06T01:27:53.221525",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 995,
"output_tokens": 960,
"cost": 0.0,
"session_id": "dashscope_2571",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:28:32.717975",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 296,
"output_tokens": 1219,
"cost": 0.0,
"session_id": "dashscope_2425",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:33:12.391161",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 995,
"output_tokens": 2000,
"cost": 0.0,
"session_id": "dashscope_8735",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:33:52.234907",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 296,
"output_tokens": 1137,
"cost": 0.0,
"session_id": "dashscope_3412",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:34:45.569135",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3418,
"output_tokens": 1629,
"cost": 0.0,
"session_id": "dashscope_7942",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:35:33.311327",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 6698,
"output_tokens": 1510,
"cost": 0.0,
"session_id": "dashscope_5949",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:36:04.980288",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3385,
"output_tokens": 1034,
"cost": 0.0,
"session_id": "dashscope_3044",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:36:16.075312",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1221,
"output_tokens": 343,
"cost": 0.0,
"session_id": "dashscope_8727",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:36:39.358955",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3816,
"output_tokens": 697,
"cost": 0.0,
"session_id": "dashscope_5037",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:37:06.369288",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 5197,
"output_tokens": 880,
"cost": 0.0,
"session_id": "dashscope_1332",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:37:35.911409",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 6939,
"output_tokens": 872,
"cost": 0.0,
"session_id": "dashscope_9043",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:38:22.823185",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3784,
"output_tokens": 1375,
"cost": 0.0,
"session_id": "dashscope_6470",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:38:23.655366",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1453,
"output_tokens": 2,
"cost": 0.0,
"session_id": "dashscope_2763",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:54:39.131950",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 995,
"output_tokens": 1028,
"cost": 0.0,
"session_id": "dashscope_9952",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:55:32.797129",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1309,
"output_tokens": 1286,
"cost": 0.0,
"session_id": "dashscope_7587",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:56:28.654801",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3903,
"output_tokens": 1867,
"cost": 0.0,
"session_id": "dashscope_2776",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:56:59.435956",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3399,
"output_tokens": 1014,
"cost": 0.0,
"session_id": "dashscope_8436",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:57:00.760673",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1201,
"output_tokens": 9,
"cost": 0.0,
"session_id": "dashscope_9520",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:57:18.693685",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1373,
"output_tokens": 639,
"cost": 0.0,
"session_id": "dashscope_9265",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:57:48.090320",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 2638,
"output_tokens": 862,
"cost": 0.0,
"session_id": "dashscope_2116",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:58:09.368979",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 4344,
"output_tokens": 717,
"cost": 0.0,
"session_id": "dashscope_3118",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:58:48.717170",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3533,
"output_tokens": 1342,
"cost": 0.0,
"session_id": "dashscope_7964",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T01:58:49.403888",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1420,
"output_tokens": 1,
"cost": 0.0,
"session_id": "dashscope_8751",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:19:18.587771",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1004,
"output_tokens": 857,
"cost": 0.0,
"session_id": "dashscope_8011",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:19:39.128858",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 301,
"output_tokens": 795,
"cost": 0.0,
"session_id": "dashscope_7608",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:20:10.260163",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1932,
"output_tokens": 1235,
"cost": 0.0,
"session_id": "dashscope_4353",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:20:59.393383",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 4424,
"output_tokens": 1757,
"cost": 0.0,
"session_id": "dashscope_1838",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:21:26.495076",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3238,
"output_tokens": 974,
"cost": 0.0,
"session_id": "dashscope_8486",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:21:37.129257",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1166,
"output_tokens": 369,
"cost": 0.0,
"session_id": "dashscope_8640",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:21:58.378592",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 2356,
"output_tokens": 796,
"cost": 0.0,
"session_id": "dashscope_7747",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:22:17.692252",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3935,
"output_tokens": 696,
"cost": 0.0,
"session_id": "dashscope_9352",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:22:43.276489",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 5309,
"output_tokens": 909,
"cost": 0.0,
"session_id": "dashscope_409",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:23:17.386666",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 3676,
"output_tokens": 1158,
"cost": 0.0,
"session_id": "dashscope_3412",
"analysis_type": "stock_analysis"
},
{
"timestamp": "2025-07-06T02:23:17.988476",
"provider": "dashscope",
"model_name": "qwen-plus",
"input_tokens": 1236,
"output_tokens": 2,
"cost": 0.0,
"session_id": "dashscope_3169",
"analysis_type": "stock_analysis"
}
]

145
docs/README.md Normal file
View File

@ -0,0 +1,145 @@
# TradingAgents Documentation
## 📚 Documentation Structure
This documentation is organized into language-specific directories to serve different user communities:
### 🇺🇸 English Documentation (`en-US/`)
**Status**: ✅ Included in version control
Contains comprehensive guides for English-speaking users:
- **Configuration Guide** (`configuration_guide.md`) - Detailed instructions for modifying system configurations and agent prompts
- **Quick Reference** (`quick_reference.md`) - Quick lookup card for common modifications and file locations
- **Prompt Templates** (`prompt_templates.md`) - Ready-to-use prompt templates for various agent roles
### 🇨🇳 Chinese Documentation (`zh-CN/`)
**Status**: ✅ Included in version control
Contains comprehensive guides in Chinese for Chinese-speaking users:
- **主文档** (`README.md`) - 中文版系统概览和快速开始
- **配置指南** (`configuration_guide.md`) - 详细的配置修改和新功能设置指南
- **架构指南** (`architecture_guide.md`) - 系统架构和技术实现详解
- **快速开始指南** (`quick_start_guide.md`) - 5分钟快速设置和使用教程
- **快速参考** (`quick_reference.md`) - 新手友好的快速查找卡片
- **提示词模板库** (`prompt_templates.md`) - 可直接使用的提示词模板
## 🎯 Quick Start
### For English Users
Navigate to [`en-US/`](en-US/) directory for:
- System configuration instructions
- Prompt customization guides
- Template libraries
- Troubleshooting tips
### For Chinese Users
Navigate to [`zh-CN/`](zh-CN/) directory for:
- 系统配置说明
- 中国A股市场功能
- 百炼(DashScope)集成指南
- 数据库配置说明
- 提示词定制指南
- 架构技术文档
- 故障排除技巧
## 📖 Available Guides
| Guide | English | Chinese | Description |
|-------|---------|---------|-------------|
| **Main Documentation** | [📖 View](en-US/) | [📖 查看](zh-CN/README.md) | System overview and quick start |
| **Configuration Guide** | [📖 View](en-US/configuration_guide.md) | [📖 查看](zh-CN/configuration_guide.md) | Complete guide for modifying configurations and new features |
| **Architecture Guide** | [🏗️ View](en-US/architecture_guide.md) | [🏗️ 查看](zh-CN/architecture_guide.md) | System architecture and technical implementation |
| **Quick Start Guide** | [🚀 View](en-US/quick_start_guide.md) | [🚀 查看](zh-CN/quick_start_guide.md) | 5-minute setup and usage tutorial |
| **Quick Reference** | [📋 View](en-US/quick_reference.md) | [📋 查看](zh-CN/quick_reference.md) | Quick lookup for common modifications |
| **Prompt Templates** | [🎯 View](en-US/prompt_templates.md) | [🎯 查看](zh-CN/prompt_templates.md) | Ready-to-use prompt templates |
## 🔧 Key Topics Covered
### Configuration Management
- LLM provider settings (DashScope, OpenAI, Google, Anthropic)
- **DashScope (Alibaba Cloud)**: Full support for Qwen model series ⭐ **Recommended for Chinese users**
- **Current Setup**: DashScope as primary option with intelligent fallback
- Market selection and data sources
- **US Stock Market**: Yahoo Finance integration
- **China A-Share Market**: TongDaXin API integration ⭐ **New Feature**
- Database and caching systems
- **MongoDB**: Persistent data storage
- **Redis**: High-performance caching
- **Intelligent Cache**: Automatic fallback mechanisms
- Debate and discussion parameters
- API configuration and limits
### Agent Customization
- Market Analyst prompts
- Fundamentals Analyst prompts
- News and Social Media Analyst prompts
- Bull/Bear Researcher prompts
- Trader decision prompts
- Reflection system prompts
### Advanced Features
- **Multi-market support**: US stocks and China A-shares
- **Database integration**: MongoDB and Redis for enterprise deployment
- **Intelligent caching**: Adaptive cache management with fallback
- **Multi-LLM support**: DashScope, OpenAI, Google, Anthropic
- **TongDaXin integration**: Real-time A-share data access
- Risk management templates
- Performance optimization
- Custom prompt creation
- Environment-specific configurations
## 🚀 Getting Started
1. **Choose Your Language**: Select the appropriate documentation directory
2. **Start with Quick Reference**: Get familiar with key file locations
3. **Read Configuration Guide**: Understand the system architecture
4. **Use Prompt Templates**: Copy and customize templates for your needs
5. **Test Changes**: Always test modifications in a safe environment
## 🛠️ Development Workflow
### For Contributors
1. **English Documentation**:
- Modify files in `en-US/` directory
- Commit changes to version control
- These will be available to all users
2. **Chinese Documentation**:
- Modify files in `zh-CN/` directory
- Keep changes local (not committed)
- Use for local development and testing
### Version Control Policy
- ✅ **Include**: `en-US/` directory and all English documentation
- ✅ **Include**: `zh-CN/` directory and all Chinese documentation
- ✅ **Include**: This README file for navigation
- 🎯 **Rationale**: Both language versions provide value to the global community
## 📝 Contributing
When contributing to documentation:
1. **Update English docs** for features that should be shared with the international community
2. **Update Chinese docs** for features that benefit Chinese-speaking users
3. **Maintain consistency** between language versions when possible
4. **Test all examples** before documenting them
5. **Consider localization** - some features may be more relevant to specific regions
## 🔗 Related Resources
- **Project Repository**: Main TradingAgents codebase
- **Configuration Files**: `tradingagents/default_config.py`, `main.py`
- **Agent Files**: `tradingagents/agents/` directory
- **Test Files**: `tests/` directory (local only)
## 📞 Support
For questions about:
- **Configuration**: See Configuration Guide
- **Prompts**: See Prompt Templates
- **Quick Help**: See Quick Reference
- **Issues**: Submit to project repository
---
💡 **Note**: This documentation structure allows for both community sharing (English) and local customization (Chinese) while maintaining clean version control.

View File

@ -0,0 +1,376 @@
# TradingAgents System Architecture Guide
## 📖 Overview
This document provides a comprehensive overview of the TradingAgents system architecture, including the integration of Chinese market features, database systems, and multi-LLM support. The architecture is designed for scalability, reliability, and global market coverage.
## 🏗️ High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ TradingAgents System │
├─────────────────────────────────────────────────────────────────┤
│ CLI Interface (Market Selection + Configuration) │
├─────────────────────────────────────────────────────────────────┤
│ Multi-Agent Framework │
│ ├── Market Analyst ├── Fundamentals Analyst │
│ ├── News Analyst ├── Bull/Bear Researchers │
│ └── Trader Agent └── Risk Management │
├─────────────────────────────────────────────────────────────────┤
│ Multi-LLM Provider Layer │
│ ├── DashScope (Qwen) ├── OpenAI (GPT) │
│ ├── Google (Gemini) └── Anthropic (Claude) │
├─────────────────────────────────────────────────────────────────┤
│ Data Layer │
│ ├── US Market (Yahoo Finance) │
│ ├── China A-Share (TongDaXin API) │
│ └── Financial News & Social Media │
├─────────────────────────────────────────────────────────────────┤
│ Caching & Storage Layer │
│ ├── MongoDB (Persistent Storage) │
│ ├── Redis (High-Performance Cache) │
│ └── File Cache (Fallback) │
└─────────────────────────────────────────────────────────────────┘
```
## 🎯 Core Components
### 1. CLI Interface Layer
#### Market Selection System
- **Interactive Market Selection**: US Stock vs China A-Share
- **Format Validation**: Market-specific ticker validation
- **Data Source Routing**: Automatic routing based on market selection
- **English-Only Interface**: Internationalization-ready
**Key Files**:
```
cli/main.py - Main CLI application
cli/utils.py - Market selection and validation utilities
```
**Flow**:
```
User Input → Market Selection → Ticker Validation → Data Source Assignment
```
### 2. Multi-Agent Framework
#### Agent Hierarchy
```
TradingAgentsGraph
├── Analyst Team
│ ├── MarketAnalyst (Technical Analysis)
│ ├── FundamentalsAnalyst (Financial Analysis)
│ └── NewsAnalyst (Sentiment Analysis)
├── Research Team
│ ├── BullResearcher (Positive Sentiment)
│ └── BearResearcher (Risk Analysis)
├── Trading Team
│ ├── TraderAgent (Decision Making)
│ └── RiskManager (Risk Assessment)
└── Reflection System
└── ReflectionAgent (Quality Control)
```
**Key Files**:
```
tradingagents/graph/trading_graph.py - Main agent orchestration
tradingagents/agents/analysts/ - Analyst implementations
tradingagents/agents/researchers/ - Research team
tradingagents/agents/trader/ - Trading decisions
```
### 3. Multi-LLM Provider Layer
#### Provider Architecture
```
LLM Request → Provider Router → Specific Adapter → API Call → Response
```
#### Supported Providers
1. **DashScope (Alibaba Cloud)**
- Models: qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
- Optimized for Chinese language
- Primary choice for Chinese users
2. **OpenAI**
- Models: GPT-4o, GPT-4o-mini, o1, o3, o4-mini
- Global standard for English content
3. **Google AI**
- Models: Gemini 2.0 Flash, Gemini 2.5 Flash
- Advanced reasoning capabilities
4. **Anthropic**
- Models: Claude 3.5 Haiku, Claude 3.5 Sonnet, Claude 4
- Strong analytical capabilities
**Key Files**:
```
tradingagents/graph/trading_graph.py - LLM initialization
tradingagents/agents/utils/memory.py - Embedding services
cli/utils.py - Provider selection
```
#### Intelligent Fallback System
```
Primary Provider (DashScope)
↓ (if unavailable)
Secondary Provider (OpenAI)
↓ (if unavailable)
Tertiary Provider (Google/Anthropic)
↓ (if all fail)
Error Handling & User Notification
```
### 4. Data Layer Architecture
#### Multi-Market Data Sources
**US Stock Market**:
```
Yahoo Finance API → Data Validation → Cache Storage → Agent Consumption
```
**China A-Share Market**:
```
TongDaXin API → Data Optimization → Cache Storage → Agent Consumption
```
#### Data Flow Architecture
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Data Request │ -> │ Source Router │ -> │ Data Provider │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Cache Manager │ <- Data Processor <- Raw Data
└─────────────────┘ └──────────────────┘ └─────────────────┘
```
**Key Files**:
```
tradingagents/dataflows/interface.py - Data interface
tradingagents/dataflows/tdx_utils.py - TongDaXin integration
tradingagents/dataflows/optimized_china_data.py - China data optimization
tradingagents/dataflows/chinese_finance_utils.py - Chinese finance tools
tradingagents/dataflows/stock_data_service.py - Unified data service
```
#### Supported Chinese Exchanges
- **Shanghai Stock Exchange**: 60xxxx (e.g., 600036 - China Merchants Bank)
- **Shenzhen Stock Exchange**: 00xxxx (e.g., 000001 - Ping An Bank)
- **ChiNext Board**: 30xxxx (e.g., 300001 - Technology stocks)
- **STAR Market**: 68xxxx (e.g., 688001 - Innovation companies)
### 5. Caching & Storage Layer
#### Three-Tier Cache Architecture
**Tier 1: Redis (High-Performance Cache)**
```
Memory-based → Sub-millisecond access → Real-time data
```
**Tier 2: MongoDB (Persistent Storage)**
```
Document-based → Structured storage → Historical data & analytics
```
**Tier 3: File Cache (Fallback)**
```
File-based → Reliable fallback → Always available
```
#### Cache Management Flow
```
Data Request
Redis Check (Tier 1)
↓ (if miss)
MongoDB Check (Tier 2)
↓ (if miss)
File Cache Check (Tier 3)
↓ (if miss)
External API Call
Store in All Tiers
```
**Key Files**:
```
tradingagents/dataflows/cache_manager.py - Cache coordination
tradingagents/dataflows/db_cache_manager.py - Database cache
tradingagents/dataflows/integrated_cache.py - Integrated cache system
tradingagents/dataflows/adaptive_cache.py - Adaptive cache strategies
tradingagents/config/database_manager.py - Database connections
tradingagents/config/mongodb_storage.py - MongoDB operations
```
#### Database Schema Design
**MongoDB Collections**:
```
stock_data - Historical stock prices and volumes
analysis_results - Agent analysis outputs
token_usage - LLM API usage tracking
cache_metadata - Cache management information
user_sessions - User interaction history
```
**Redis Key Patterns**:
```
stock:{symbol}:{date} - Daily stock data
analysis:{symbol}:{timestamp} - Analysis results
news:{symbol}:{date} - News sentiment data
cache:meta:{key} - Cache metadata
```
## 🔄 Data Flow Patterns
### 1. Analysis Workflow
```
User Input (CLI)
Market Selection & Validation
Data Retrieval (Multi-source)
Agent Analysis (Multi-LLM)
Result Aggregation
Output Generation
Cache Storage
```
### 2. Cache Workflow
```
Data Request
Cache Key Generation
Tier 1 (Redis) Check
↓ (if miss)
Tier 2 (MongoDB) Check
↓ (if miss)
Tier 3 (File) Check
↓ (if miss)
External API Call
Multi-tier Storage
Response to User
```
### 3. Error Handling Workflow
```
Component Failure
Error Detection
Fallback Activation
Alternative Path
User Notification (if needed)
Graceful Degradation
```
## 🛡️ Reliability & Scalability Features
### High Availability Design
- **Multi-LLM Fallback**: Automatic provider switching
- **Multi-tier Caching**: Redundant data storage
- **Graceful Degradation**: System continues with reduced functionality
- **Error Recovery**: Automatic retry mechanisms
### Scalability Features
- **Database Clustering**: MongoDB replica sets
- **Cache Scaling**: Redis clustering support
- **Load Balancing**: Multiple API endpoints
- **Horizontal Scaling**: Stateless agent design
### Performance Optimization
- **Intelligent Caching**: Adaptive cache strategies
- **Connection Pooling**: Database connection management
- **Async Processing**: Non-blocking operations
- **Data Compression**: Efficient storage formats
## 🔧 Configuration Management
### Environment-Based Configuration
```
.env File → Environment Variables → Runtime Configuration
```
### Configuration Hierarchy
```
1. Environment Variables (.env)
2. Default Configuration (default_config.py)
3. Runtime Overrides (main.py)
4. Dynamic Configuration (config.py)
```
### Configuration Categories
- **API Keys**: LLM providers and data sources
- **Database Settings**: MongoDB and Redis configuration
- **Cache Settings**: Cache TTL and strategies
- **Market Settings**: Supported markets and exchanges
- **Agent Settings**: Model selection and parameters
## 📊 Monitoring & Analytics
### System Metrics
- **API Usage**: Token consumption and costs
- **Cache Performance**: Hit rates and response times
- **Database Performance**: Query times and storage usage
- **Error Rates**: Failure rates by component
### Business Metrics
- **Analysis Quality**: Agent performance metrics
- **User Engagement**: Usage patterns and preferences
- **Market Coverage**: Supported symbols and exchanges
- **Response Times**: End-to-end analysis duration
## 🚀 Deployment Architecture
### Development Environment
```
Local Machine → File Cache → Single LLM Provider → Basic Features
```
### Production Environment
```
Application Server → Redis Cluster → MongoDB Replica Set → Multi-LLM → Full Features
```
### Cloud Deployment Options
- **Database**: MongoDB Atlas, Redis Cloud
- **Application**: Docker containers, Kubernetes
- **Load Balancing**: Application load balancers
- **Monitoring**: Application performance monitoring
## 🔮 Future Architecture Enhancements
### Planned Improvements
- **Microservices Architecture**: Service decomposition
- **Event-Driven Architecture**: Async message processing
- **Machine Learning Pipeline**: Automated model training
- **Real-time Streaming**: Live market data processing
- **Global CDN**: Distributed cache network
### Extensibility Points
- **New Market Support**: Additional exchanges and regions
- **New LLM Providers**: Additional AI services
- **Custom Agents**: User-defined analysis agents
- **Plugin System**: Third-party integrations
- **API Gateway**: External service access
---
This architecture provides a robust, scalable foundation for global financial market analysis while maintaining flexibility for future enhancements and integrations.

View File

@ -0,0 +1,699 @@
# TradingAgents Configuration and Prompt Modification Guide
## 📖 Overview
This document provides a comprehensive guide for new users to modify configurations and customize prompts in the TradingAgents project. Through this guide, you will learn:
- How to modify system configuration parameters
- How to configure multi-market support (US stocks and China A-shares)
- How to setup database integration (MongoDB and Redis)
- How to configure multiple LLM providers (DashScope, OpenAI, Google, Anthropic)
- How to customize prompts for various agents
- How to add new features and configurations
## 🌟 New Features Overview
### 🇨🇳 China A-Share Market Support
- **TongDaXin API Integration**: Real-time A-share data access
- **Market Selection**: Interactive CLI market selection
- **Exchange Support**: Shanghai, Shenzhen, ChiNext, STAR Market
- **Intelligent Caching**: Optimized data retrieval and storage
### 🤖 DashScope (Alibaba Cloud) Integration
- **Qwen Model Series**: qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
- **Embedding Service**: DashScope embeddings for memory system
- **Intelligent Fallback**: Automatic fallback to OpenAI when unavailable
### 🗄️ Database Integration
- **MongoDB**: Persistent data storage and analytics
- **Redis**: High-performance caching
- **Adaptive Cache**: Intelligent cache management with automatic fallback
## 🔧 Configuration File Locations and Descriptions
### 1. Main Configuration Files
#### 📁 `tradingagents/default_config.py`
**Purpose**: Core configuration file defining all default parameters
```python
DEFAULT_CONFIG = {
# Directory configuration
"project_dir": "Project root directory path",
"results_dir": "Results output directory",
"data_dir": "Data storage directory",
"data_cache_dir": "Cache directory",
# LLM model configuration
"llm_provider": "dashscope", # LLM provider: "dashscope", "openai", "google", "anthropic"
"deep_think_llm": "qwen-plus", # Deep thinking model
"quick_think_llm": "qwen-turbo", # Quick thinking model
"backend_url": "https://dashscope.aliyuncs.com/api/v1", # API backend URL
# Debate and discussion settings
"max_debate_rounds": 1, # Maximum debate rounds
"max_risk_discuss_rounds": 1, # Maximum risk discussion rounds
"max_recur_limit": 100, # Maximum recursion limit
# Tool settings
"online_tools": True, # Whether to use online tools
}
```
**Modification Method**:
1. Directly edit the `tradingagents/default_config.py` file
2. Modify the corresponding configuration values
3. Restart the application for changes to take effect
#### 📁 `main.py`
**Purpose**: Runtime configuration override, allows temporary parameter adjustments without modifying default config
```python
# Create custom configuration
config = DEFAULT_CONFIG.copy()
config["llm_provider"] = "google" # Use Google models
config["backend_url"] = "https://generativelanguage.googleapis.com/v1"
config["deep_think_llm"] = "gemini-2.0-flash" # Deep thinking model
config["quick_think_llm"] = "gemini-2.0-flash" # Quick thinking model
config["max_debate_rounds"] = 2 # Increase debate rounds
config["online_tools"] = True # Enable online tools
```
**Modification Method**:
1. Edit the config section in `main.py`
2. Add or modify configuration items to override
3. Save and run
### 2. Dynamic Configuration Management
#### 📁 `tradingagents/dataflows/config.py`
**Purpose**: Provides dynamic configuration get/set functionality
```python
# Get current configuration
config = get_config()
# Dynamically modify configuration
set_config({
"llm_provider": "anthropic",
"max_debate_rounds": 3
})
```
## 🌟 New Features Configuration
### 1. Environment Variables Configuration (`.env`)
#### 📁 `.env` File Setup
**Purpose**: Configure API keys and database settings
**Required API Keys**:
**For US Stock Analysis**:
```env
# Choose one LLM provider
OPENAI_API_KEY=your_openai_api_key_here
# OR
GOOGLE_API_KEY=your_google_api_key_here
# OR
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# FinnHub - Required for financial data
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**For China A-Share Analysis**:
```env
# DashScope - Required for Chinese stock analysis
DASHSCOPE_API_KEY=your_dashscope_api_key_here
# FinnHub - Required for financial data
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**For DashScope LLM Provider**:
```env
# DashScope - Required for Qwen models
DASHSCOPE_API_KEY=your_dashscope_api_key_here
# FinnHub - Required for financial data
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**Optional API Keys**:
```env
# OpenAI - Optional fallback
OPENAI_API_KEY=your_openai_api_key_here
# Google AI - For Gemini models
GOOGLE_API_KEY=your_google_api_key_here
# Anthropic - For Claude models
ANTHROPIC_API_KEY=your_anthropic_api_key_here
```
**Database Configuration (Optional)**:
```env
# MongoDB - For persistent data storage
MONGODB_ENABLED=false
MONGODB_HOST=localhost
MONGODB_PORT=27018
MONGODB_USERNAME=admin
MONGODB_PASSWORD=your_mongodb_password
MONGODB_DATABASE=tradingagents
# Redis - For high-performance caching
REDIS_ENABLED=false
REDIS_HOST=localhost
REDIS_PORT=6380
REDIS_PASSWORD=your_redis_password
REDIS_DB=0
```
### 2. Market Selection Configuration
#### 📁 CLI Market Selection
**Purpose**: Configure supported markets and data sources
**Supported Markets**:
1. **US Stock Market**
- Format: 1-5 letter symbols (e.g., AAPL, SPY)
- Data Source: Yahoo Finance
- Validation: `^[A-Z]{1,5}$`
2. **China A-Share Market**
- Format: 6-digit codes (e.g., 000001, 600036)
- Data Source: TongDaXin API
- Validation: `^\d{6}$`
- Exchanges: Shanghai (60xxxx), Shenzhen (00xxxx), ChiNext (30xxxx), STAR (68xxxx)
**Configuration in Code**:
```python
# Market-specific configuration
market_config = {
"us_stock": {
"data_source": "yahoo_finance",
"pattern": r'^[A-Z]{1,5}$'
},
"china_a_share": {
"data_source": "tongdaxin",
"pattern": r'^\d{6}$'
}
}
```
### 3. Database Integration Configuration
#### 📁 MongoDB Configuration
**Purpose**: Persistent data storage and analytics
**Setup Steps**:
1. **Start MongoDB**:
```bash
docker run -d -p 27017:27017 --name mongodb mongo
```
2. **Enable in .env**:
```env
MONGODB_ENABLED=true
```
3. **Configuration Options**:
```python
mongodb_config = {
"host": "localhost",
"port": 27018,
"database": "tradingagents",
"username": "admin",
"password": "your_password"
}
```
#### 📁 Redis Configuration
**Purpose**: High-performance caching
**Setup Steps**:
1. **Start Redis**:
```bash
docker run -d -p 6379:6379 --name redis redis
```
2. **Enable in .env**:
```env
REDIS_ENABLED=true
```
3. **Configuration Options**:
```python
redis_config = {
"host": "localhost",
"port": 6380,
"password": "your_password",
"db": 0
}
```
### 4. LLM Provider Configuration
#### 📁 DashScope (Alibaba Cloud) Configuration
**Purpose**: Chinese-optimized LLM provider
**Supported Models**:
- `qwen-turbo`: Fast response, suitable for quick tasks
- `qwen-plus`: Balanced performance and cost (Recommended)
- `qwen-max`: Best performance for complex analysis
- `qwen-max-longcontext`: Ultra-long context support
**Configuration Example**:
```python
dashscope_config = {
"llm_provider": "dashscope",
"deep_think_llm": "qwen-plus",
"quick_think_llm": "qwen-turbo",
"backend_url": "https://dashscope.aliyuncs.com/api/v1"
}
```
**API Key Setup**:
1. Visit: https://dashscope.aliyun.com/
2. Register Alibaba Cloud account
3. Enable DashScope service
4. Get API key
5. Set in .env: `DASHSCOPE_API_KEY=your_key`
#### 📁 Multi-LLM Fallback Configuration
**Purpose**: Intelligent fallback between LLM providers
**Fallback Priority**:
1. Primary: DashScope (if configured)
2. Secondary: OpenAI (if configured)
3. Tertiary: Google AI (if configured)
4. Fallback: Anthropic (if configured)
**Configuration**:
```python
fallback_config = {
"primary_provider": "dashscope",
"fallback_providers": ["openai", "google", "anthropic"],
"auto_fallback": True,
"retry_attempts": 3
}
```
## 🤖 Agent Prompt Modification Guide
### 1. Analyst Prompts
#### 📁 Market Analyst (`tradingagents/agents/analysts/market_analyst.py`)
**Location**: `system_message` variable at lines 24-50
**Current Prompt**:
```python
system_message = (
"""You are a trading assistant tasked with analyzing financial markets.
Your role is to select the **most relevant indicators** for a given market
condition or trading strategy from the following list..."""
)
```
**Modification Example**:
```python
system_message = (
"""You are a professional market analyst specializing in financial market analysis.
Your task is to select the most relevant indicators from the following list,
providing analysis for specific market conditions or trading strategies.
Goal: Choose up to 8 indicators that provide complementary insights without redundancy..."""
)
```
#### 📁 Fundamentals Analyst (`tradingagents/agents/analysts/fundamentals_analyst.py`)
**Location**: `system_message` variable at lines 23-26
**Key Modification Points**:
- Analysis depth requirements
- Report format requirements
- Focus financial metrics
#### 📁 News Analyst (`tradingagents/agents/analysts/news_analyst.py`)
**Location**: `system_message` variable at lines 20-23
**Key Modification Points**:
- News source preferences
- Analysis time range
- Types of news to focus on
#### 📁 Social Media Analyst (`tradingagents/agents/analysts/social_media_analyst.py`)
**Location**: `system_message` variable at lines 19-22
**Key Modification Points**:
- Sentiment analysis depth
- Social media platform preferences
- Sentiment weight settings
### 2. Researcher Prompts
#### 📁 Bull Researcher (`tradingagents/agents/researchers/bull_researcher.py`)
**Location**: `prompt` variable at lines 25-43
**Current Prompt Structure**:
```python
prompt = f"""You are a Bull Analyst advocating for investing in the stock.
Key points to focus on:
- Growth Potential: Highlight market opportunities, revenue projections, and scalability
- Competitive Advantages: Emphasize unique products, strong branding, or market dominance
- Positive Indicators: Use financial health, industry trends, and recent positive news as evidence
- Bear Counterpoints: Critically analyze bear arguments with specific data and sound reasoning
"""
```
**Modification Suggestions**:
- Adjust analysis focus
- Modify argumentation strategy
- Customize rebuttal logic
#### 📁 Bear Researcher (`tradingagents/agents/researchers/bear_researcher.py`)
**Key Modification Points**:
- Risk identification focus
- Pessimistic scenario analysis
- Strategy for countering bull arguments
### 3. Trader Prompts
#### 📁 Trader (`tradingagents/agents/trader/trader.py`)
**Location**: System message in `messages` array at lines 30-36
**Current Prompt**:
```python
{
"role": "system",
"content": f"""You are a trading agent analyzing market data to make
investment decisions. Based on your analysis, provide a specific
recommendation to buy, sell, or hold. End with a firm decision and
always conclude your response with 'FINAL TRANSACTION PROPOSAL:
**BUY/HOLD/SELL**' to confirm your recommendation.""",
}
```
**Modification Example**:
```python
{
"role": "system",
"content": f"""You are a professional trading agent responsible for analyzing
market data and making investment decisions.
Decision Requirements:
1. Provide detailed analysis reasoning
2. Consider risk management
3. Must end with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**'
Historical Lessons: {past_memory_str}""",
}
```
### 4. Risk Management Prompts
#### 📁 Conservative Debater (`tradingagents/agents/risk_mgmt/conservative_debator.py`)
#### 📁 Aggressive Debater (`tradingagents/agents/risk_mgmt/aggresive_debator.py`)
#### 📁 Neutral Debater (`tradingagents/agents/risk_mgmt/neutral_debator.py`)
**Key Modification Points**:
- Risk tolerance settings
- Debate style adjustments
- Decision weight allocation
### 5. Reflection System Prompts
#### 📁 Reflection System (`tradingagents/graph/reflection.py`)
**Location**: `_get_reflection_prompt` method at lines 15-47
**Current Prompt Structure**:
```python
return """
You are an expert financial analyst tasked with reviewing trading
decisions/analysis and providing a comprehensive, step-by-step analysis.
1. Reasoning: Analyze whether each trading decision was correct
2. Improvement: Propose revisions for incorrect decisions
3. Summary: Summarize lessons learned from successes and failures
4. Query: Extract key insights into concise sentences
"""
```
## 🎯 Prompt Modification Best Practices
### 1. Pre-modification Preparation
1. **Backup Original Files**:
```bash
cp tradingagents/agents/trader/trader.py tradingagents/agents/trader/trader.py.backup
```
2. **Understand Agent Roles**: Ensure modifications align with expected agent functionality
3. **Prepare Test Environment**: Validate modifications in test environment
### 2. Prompt Modification Techniques
#### 🔍 **Structured Prompts**
```python
system_message = f"""
Role Definition: You are a {role_name}
Main Tasks:
1. {task_1}
2. {task_2}
3. {task_3}
Analysis Requirements:
- Depth: {analysis_depth}
- Format: {output_format}
- Focus: {focus_areas}
Output Format:
{output_template}
Constraints:
- {constraint_1}
- {constraint_2}
"""
```
#### ⚙️ **Parameterized Prompts**
```python
def create_analyst_prompt(
role="Market Analyst",
analysis_depth="Detailed",
time_horizon="1 week",
risk_tolerance="Moderate",
output_language="English"
):
return f"""
You are a professional {role}, please analyze based on the following parameters:
Analysis Depth: {analysis_depth}
Time Horizon: {time_horizon}
Risk Preference: {risk_tolerance}
Output Language: {output_language}
Please provide corresponding market analysis and investment recommendations based on these parameters.
"""
```
### 3. Common Modification Scenarios
#### 📈 **Adjusting Analysis Focus**
```python
# Original: General market analysis
system_message = "Analyze overall market trends..."
# Modified: Focus on specific industry
system_message = "Analyze technology stock market trends, focusing on AI, semiconductor, and cloud computing industries..."
```
#### 🎯 **Modifying Decision Style**
```python
# Original: Conservative
"provide conservative investment recommendations..."
# Modified: Aggressive
"provide aggressive growth-oriented investment recommendations with higher risk tolerance..."
```
## 🔧 New Configuration Items
### 1. Cache Configuration (`tradingagents/dataflows/cache_manager.py`)
```python
# Add new cache configuration in cache_manager.py
self.cache_config = {
'us_stock_data': {
'ttl_hours': 2, # US stock data cached for 2 hours
'description': 'US stock historical data'
},
'china_stock_data': {
'ttl_hours': 1, # A-share data cached for 1 hour
'description': 'A-share historical data'
},
# Add new cache type
'crypto_data': {
'ttl_hours': 0.5, # Crypto data cached for 30 minutes
'description': 'Cryptocurrency data'
}
}
```
### 2. API Configuration
```python
# Add new API configuration in default_config.py
DEFAULT_CONFIG = {
# Existing configuration...
# New API configuration
"api_keys": {
"finnhub": "your_finnhub_api_key",
"alpha_vantage": "your_alpha_vantage_key",
"polygon": "your_polygon_key"
},
# API limit configuration
"api_limits": {
"finnhub_calls_per_minute": 60,
"alpha_vantage_calls_per_minute": 5,
"polygon_calls_per_minute": 100
}
}
```
## 🚀 Quick Start Examples
### 1. Switch to Google Models
```python
# Edit main.py
config = DEFAULT_CONFIG.copy()
config["llm_provider"] = "google"
config["backend_url"] = "https://generativelanguage.googleapis.com/v1"
config["deep_think_llm"] = "gemini-2.0-flash"
config["quick_think_llm"] = "gemini-2.0-flash"
```
#### 🚀 Supported Google Models
**Fast Thinking Models (Quick Analysis)**:
- `gemini-2.0-flash-lite` - Cost efficiency and low latency
- `gemini-2.0-flash` - Next generation features, speed, and thinking ⭐ **Recommended**
- `gemini-2.5-flash-preview-05-20` - Adaptive thinking, cost efficiency
**Deep Thinking Models (Complex Analysis)**:
- `gemini-2.0-flash-lite` - Cost efficiency and low latency
- `gemini-2.0-flash` - Next generation features, speed, and thinking ⭐ **Current Default**
- `gemini-2.5-flash-preview-05-20` - Adaptive thinking, cost efficiency
- `gemini-2.5-pro-preview-06-05` - Professional-grade performance
#### 🔑 Google API Key Setup
**Method 1: Environment Variable (Recommended)**
```bash
export GOOGLE_API_KEY="your_google_api_key_here"
```
**Method 2: In Code**
```python
import os
os.environ["GOOGLE_API_KEY"] = "your_google_api_key_here"
```
**Method 3: .env File**
```
# Create .env file in project root
GOOGLE_API_KEY=your_google_api_key_here
```
#### 📋 Model Selection Examples
**High Performance Setup**:
```python
config["deep_think_llm"] = "gemini-2.5-pro-preview-06-05" # Best reasoning
config["quick_think_llm"] = "gemini-2.0-flash" # Fast response
```
**Cost-Optimized Setup**:
```python
config["deep_think_llm"] = "gemini-2.0-flash-lite" # Economical
config["quick_think_llm"] = "gemini-2.0-flash-lite" # Economical
```
**Balanced Setup (Current Default)**:
```python
config["deep_think_llm"] = "gemini-2.0-flash" # Good performance
config["quick_think_llm"] = "gemini-2.0-flash" # Good speed
```
### 2. Add Risk Control
```python
# Edit tradingagents/agents/trader/trader.py
messages = [
{
"role": "system",
"content": f"""You are a professional trading agent with strict risk control awareness.
Trading Principles:
1. Risk first, returns second
2. Strict stop-loss, protect capital
3. Diversified investment, reduce risk
4. Data-driven, rational decisions
Decision Process:
1. Analyze market trends and technical indicators
2. Assess fundamental and news impact
3. Calculate risk-reward ratio
4. Set stop-loss and take-profit points
5. Make final trading decision
Output Requirements:
- Must include risk assessment
- Must set stop-loss points
- Must end with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**'
Historical Experience: {past_memory_str}""",
},
context,
]
```
## 📝 Important Notes
1. **Backup Important**: Always backup original files before modification
2. **Test Validation**: Validate modifications in test environment
3. **Version Control**: Use Git to manage configuration changes
4. **Documentation Updates**: Update related documentation promptly
5. **Team Collaboration**: Sync configuration changes with team members
## 🔗 Quick File Index
| Function | File Path | Description |
|----------|-----------|-------------|
| Main Config | `tradingagents/default_config.py` | System default configuration |
| Runtime Config | `main.py` | Runtime configuration override |
| Dynamic Config | `tradingagents/dataflows/config.py` | Configuration management interface |
| Market Analyst | `tradingagents/agents/analysts/market_analyst.py` | Technical analysis prompts |
| Fundamentals Analyst | `tradingagents/agents/analysts/fundamentals_analyst.py` | Fundamental analysis prompts |
| News Analyst | `tradingagents/agents/analysts/news_analyst.py` | News analysis prompts |
| Social Media Analyst | `tradingagents/agents/analysts/social_media_analyst.py` | Sentiment analysis prompts |
| Bull Researcher | `tradingagents/agents/researchers/bull_researcher.py` | Bull analysis prompts |
| Bear Researcher | `tradingagents/agents/researchers/bear_researcher.py` | Bear analysis prompts |
| Trader | `tradingagents/agents/trader/trader.py` | Trading decision prompts |
| Reflection System | `tradingagents/graph/reflection.py` | Reflection analysis prompts |
| Cache Config | `tradingagents/dataflows/cache_manager.py` | Cache management configuration |
Through this guide, you should be able to easily modify the TradingAgents project's configuration and prompts to meet your specific needs.

View File

@ -0,0 +1,517 @@
# TradingAgents Prompt Template Library
## 📚 Overview
This document provides prompt templates for various roles in the TradingAgents project. You can copy and use them directly or modify them according to your needs.
## 🚀 Google Model Integration
TradingAgents fully supports Google Gemini models. The current configuration uses:
- **Deep Thinking**: `gemini-2.0-flash` - For complex analysis and reasoning
- **Quick Thinking**: `gemini-2.0-flash` - For fast responses and simple tasks
**Available Models**:
- `gemini-2.0-flash-lite` - Cost-efficient, low latency
- `gemini-2.0-flash` - Balanced performance ⭐ **Current Default**
- `gemini-2.5-flash-preview-05-20` - Advanced adaptive thinking
- `gemini-2.5-pro-preview-06-05` - Professional-grade performance
**Setup**: Ensure `GOOGLE_API_KEY` environment variable is set.
## 🎯 Analyst Prompt Templates
### 1. Market Analyst - Professional Version
```python
system_message = (
"""You are a professional market analyst specializing in stock market technical indicator analysis. Your task is to select the most relevant indicators (up to 8) from the following list to provide analysis for specific market conditions or trading strategies.
Technical Indicator Categories:
📈 Moving Averages:
- close_50_sma: 50-day Simple Moving Average - Medium-term trend indicator for identifying trend direction and dynamic support/resistance
- close_200_sma: 200-day Simple Moving Average - Long-term trend benchmark for confirming overall market trend and golden/death cross setups
- close_10_ema: 10-day Exponential Moving Average - Short-term trend response for capturing quick momentum changes and potential entry points
📊 MACD Related Indicators:
- macd: MACD Line - Calculates momentum via EMA differences, look for crossovers and divergence as trend change signals
- macds: MACD Signal Line - EMA smoothing of MACD line, use crossovers with MACD line to trigger trades
- macdh: MACD Histogram - Shows gap between MACD line and signal, visualize momentum strength and spot early divergence
⚡ Momentum Indicators:
- rsi: Relative Strength Index - Measures momentum to flag overbought/oversold conditions, apply 70/30 thresholds and watch for divergence
📏 Volatility Indicators:
- boll: Bollinger Middle Band - 20-day SMA serving as Bollinger Bands basis, acts as dynamic benchmark for price movement
- boll_ub: Bollinger Upper Band - Typically 2 standard deviations above middle, signals potential overbought conditions and breakout zones
- boll_lb: Bollinger Lower Band - Typically 2 standard deviations below middle, indicates potential oversold conditions
- atr: Average True Range - Measures volatility for setting stop-loss levels and adjusting position sizes based on current market volatility
📊 Volume Indicators:
- vwma: Volume Weighted Moving Average - Confirms trends by integrating price action with volume data
Analysis Requirements:
1. Select indicators that provide diverse and complementary information, avoid redundancy
2. Briefly explain why these indicators are suitable for the given market environment
3. Use exact indicator names for tool calls
4. Call get_YFin_data first to retrieve CSV data needed for indicator generation
5. Write detailed and nuanced trend observation reports, avoid simply stating "trends are mixed"
6. Append a Markdown table at the end of the report to organize key points in an organized and easy-to-read format
Please provide professional, detailed market analysis."""
)
```
### 2. Fundamentals Analyst - Professional Version
```python
system_message = (
"""You are a professional fundamental research analyst specializing in company fundamental information analysis. Your task is to write a comprehensive report on the company's fundamental information over the past week.
Analysis Scope:
📊 Financial Document Analysis: Balance sheet, income statement, cash flow statement
🏢 Company Profile: Business model, competitive advantages, management quality
💰 Basic Financial Metrics: PE, PB, ROE, ROA, gross margin, net margin
📈 Financial Historical Trends: Revenue growth, profit growth, debt level changes
👥 Insider Sentiment: Management and insider buying/selling behavior
💼 Insider Transactions: Trading records of major shareholders and executives
Analysis Requirements:
1. Provide as much detail as possible to help traders make informed decisions
2. Don't simply state "trends are mixed", provide detailed and nuanced analysis insights
3. Focus on key financial metric changes that may affect stock prices
4. Analyze potential implications of insider behavior
5. Assess company's financial health and future prospects
6. Append a Markdown table at the end of the report to organize key points in an organized and easy-to-read format
Please write a professional, comprehensive fundamental analysis report."""
)
```
### 3. News Analyst - Professional Version
```python
system_message = (
"""You are a professional news research analyst specializing in analyzing recent news and trends over the past week. Your task is to write a comprehensive report on the current state of the world relevant to trading and macroeconomics.
Analysis Scope:
🌍 Global Macroeconomic News: Central bank policies, inflation data, GDP growth, employment data
📈 Financial Market Dynamics: Stock market performance, bond yields, currency changes, commodity prices
🏛️ Policy Impact: Monetary policy, fiscal policy, regulatory changes, trade policy
🏭 Industry Trends: Technology, energy, finance, consumer, healthcare and other key industry dynamics
⚡ Breaking Events: Geopolitical events, natural disasters, major corporate events
News Sources:
- EODHD news data
- Finnhub news data
- Google news search
- Reddit discussion hotspots
Analysis Requirements:
1. Provide detailed and nuanced analysis insights, avoid simply stating "trends are mixed"
2. Focus on important news events that may affect markets
3. Analyze potential market impact and trading opportunities of news events
4. Identify changing trends in market sentiment
5. Assess macroeconomic environment impact on different asset classes
6. Append a Markdown table at the end of the report to organize key points in an organized and easy-to-read format
Please write a professional, comprehensive news analysis report."""
)
```
### 4. Social Media Analyst - Professional Version
```python
system_message = (
"""You are a professional social media sentiment analyst specializing in analyzing investor sentiment and discussion hotspots on social media platforms. Your task is to write a comprehensive report on specific stock sentiment and discussions on social media.
Analysis Scope:
📱 Social Media Platforms: Reddit, Twitter, StockTwits, etc.
💭 Sentiment Analysis: Distribution and trend changes of positive, negative, and neutral sentiment
🔥 Hot Topics: Most discussed topics and keywords
👥 User Behavior: Retail investor opinions and behavior patterns
📊 Sentiment Indicators: Fear & Greed Index, bull/bear ratios, discussion volume changes
Key Focus Areas:
- Investor views on company fundamentals
- Reactions to latest earnings and news
- Technical analysis opinions and price predictions
- Risk factors and concerns
- Institutional vs retail investor opinion differences
Analysis Requirements:
1. Quantify sentiment trend changes, provide specific data support
2. Identify key sentiment turning points that may affect stock prices
3. Analyze correlation between social media sentiment and actual stock performance
4. Don't simply state "sentiment is mixed", provide detailed sentiment analysis
5. Assess reliability and potential bias of social media sentiment
6. Append a Markdown table at the end of the report to organize key points in an organized and easy-to-read format
Please write a professional, in-depth social media sentiment analysis report."""
)
```
## 🔬 Researcher Prompt Templates
### 1. Bull Researcher - Professional Version
```python
prompt = f"""You are a professional bull analyst responsible for building a strong case for investing in the stock. Your task is to construct a strong, evidence-based case emphasizing growth potential, competitive advantages, and positive market indicators.
🎯 Key Focus Areas:
📈 Growth Potential:
- Highlight company's market opportunities, revenue projections, and scalability
- Analyze growth drivers from new products, new markets, new technologies
- Assess management's execution capability and strategic planning
🏆 Competitive Advantages:
- Emphasize factors like unique products, strong branding, or dominant market positioning
- Analyze moats: technological barriers, network effects, economies of scale
- Assess company's relative competitive position in the industry
📊 Positive Indicators:
- Use financial health, industry trends, and recent positive news as evidence
- Analyze valuation attractiveness and upside potential
- Identify catalyst events and positive factors
🛡️ Bear Counterpoints:
- Critically analyze bear arguments with specific data and sound reasoning
- Thoroughly address concerns and show why bull perspective holds stronger merit
- Provide alternative explanations and risk mitigation measures
💬 Debate Style:
- Present arguments in conversational style, directly engaging with bear analyst's points
- Debate effectively rather than just listing data
- Maintain professional but persuasive tone
Available Resources:
- Market research report: {market_research_report}
- Social media sentiment report: {sentiment_report}
- Latest world affairs news: {news_report}
- Company fundamentals report: {fundamentals_report}
- Debate conversation history: {history}
- Last bear argument: {current_response}
- Reflections from similar situations and lessons learned: {past_memory_str}
Use this information to deliver a compelling bull argument, refute bear concerns, and engage in dynamic debate that demonstrates the strengths of the bull position. You must also address reflections and learn from past lessons and mistakes.
Please provide professional, persuasive bull analysis and debate."""
```
### 2. Bear Researcher - Professional Version
```python
prompt = f"""You are a professional bear analyst responsible for identifying risks and potential issues with investing in the stock. Your task is to construct an evidence-based cautious case emphasizing risk factors, valuation concerns, and negative market indicators.
🎯 Key Focus Areas:
⚠️ Risk Factors:
- Identify potential risks in business model, industry, or macroeconomic environment
- Analyze competitive threats, technological disruption, regulatory risks
- Assess management risks and corporate governance issues
💰 Valuation Concerns:
- Analyze whether current valuation is excessive compared to historical and peer comparisons
- Identify bubble signs and unreasonable market expectations
- Assess downside risks and potential valuation corrections
📉 Negative Indicators:
- Use financial deterioration, industry headwinds, and negative news as evidence
- Analyze technical indicators showing weakness signals
- Identify potential catalyst risk events
🛡️ Bull Counterpoints:
- Question bull arguments with specific data and sound reasoning
- Point out blind spots and excessive optimism in bull analysis
- Provide more conservative scenario analysis
💬 Debate Style:
- Present arguments in conversational style, directly engaging with bull analyst's points
- Maintain rational and objective approach, avoid excessive pessimism
- Provide strong rebuttals based on facts
Available Resources:
- Market research report: {market_research_report}
- Social media sentiment report: {sentiment_report}
- Latest world affairs news: {news_report}
- Company fundamentals report: {fundamentals_report}
- Debate conversation history: {history}
- Last bull argument: {current_response}
- Reflections from similar situations and lessons learned: {past_memory_str}
Use this information to provide convincing bear arguments, question bull optimistic expectations, and engage in dynamic debate that demonstrates the reasonableness of the bear position. You must also address reflections and learn from past lessons and mistakes.
Please provide professional, rational bear analysis and debate."""
```
## 💼 Trader Prompt Templates
### 1. Conservative Trader
```python
messages = [
{
"role": "system",
"content": f"""You are a professional conservative trading agent with risk control as the top priority. Based on comprehensive analysis from the team of analysts, you need to make prudent investment decisions.
🛡️ Risk Control Principles:
1. Risk first, returns second - Never risk more than you can afford to lose
2. Strict stop-loss, protect capital - Set clear stop-loss points and execute strictly
3. Diversified investment, reduce risk - Avoid over-concentration in single investments
4. Data-driven, rational decisions - Base decisions on objective analysis, not emotions
📊 Decision Framework:
1. Risk Assessment: Evaluate potential losses and probabilities
2. Return Analysis: Calculate risk-adjusted expected returns
3. Position Management: Determine appropriate investment proportions
4. Exit Strategy: Set stop-loss and take-profit points
📋 Must Include Elements:
- Risk level assessment (Low/Medium/High)
- Specific stop-loss points
- Recommended maximum position ratio
- Detailed risk warnings
💭 Decision Considerations:
- Current market environment and volatility
- Company fundamental stability
- Technical indicator confirmation signals
- Macroeconomic and industry risks
- Historical experience and lessons: {past_memory_str}
Based on comprehensive analysis, provide prudent investment recommendations. Must end your response with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**' to confirm your recommendation.
Please provide professional, cautious trading decision analysis.""",
},
context,
]
```
### 2. Aggressive Trader
```python
messages = [
{
"role": "system",
"content": f"""You are a professional aggressive trading agent focused on capturing high-return opportunities. Based on comprehensive analysis from the team of analysts, you need to make proactive investment decisions.
🚀 Growth-Oriented Principles:
1. Returns priority, moderate risk - Pursue high-return opportunities, accept corresponding risks
2. Trend following, momentum investing - Identify and follow strong trends
3. Quick action, seize opportunities - Act decisively within opportunity windows
4. Data-driven, flexible adjustment - Quickly adjust strategies based on market changes
📈 Decision Framework:
1. Opportunity Identification: Look for high-return potential investment opportunities
2. Momentum Analysis: Assess price and volume momentum
3. Catalyst Assessment: Identify factors that may drive stock prices
4. Timing: Choose optimal entry and exit timing
📋 Must Include Elements:
- Return potential assessment (Conservative/Optimistic/Aggressive)
- Key catalyst factors
- Recommended target price levels
- Momentum confirmation signals
💭 Decision Considerations:
- Technical breakouts and momentum signals
- Fundamental improvement catalysts
- Market sentiment and capital flows
- Industry rotation and thematic investment opportunities
- Historical success experience: {past_memory_str}
Based on comprehensive analysis, provide proactive investment recommendations. Must end your response with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**' to confirm your recommendation.
Please provide professional, proactive trading decision analysis.""",
},
context,
]
```
### 3. Quantitative Trader
```python
messages = [
{
"role": "system",
"content": f"""You are a professional quantitative trading agent making systematic investment decisions based on data and models. You rely on objective quantitative indicators and statistical analysis to make trading decisions.
📊 Quantitative Analysis Framework:
1. Technical Indicator Quantification: Numerical analysis of RSI, MACD, Bollinger Bands and other indicators
2. Statistical Arbitrage: Statistical significance of price deviations from mean
3. Momentum Factors: Quantitative measurement of price and volume momentum
4. Risk Models: VaR, Sharpe ratio, maximum drawdown and other risk indicators
🔢 Decision Model:
- Multi-factor scoring model: Technical (40%) + Fundamental (30%) + Sentiment (20%) + Macro (10%)
- Signal Strength: Strong Buy (>80 points) | Buy (60-80) | Hold (40-60) | Sell (20-40) | Strong Sell (<20)
- Confidence Level: Based on historical backtesting and statistical significance
📈 Quantitative Indicator Weights:
Technical Indicators:
- RSI Divergence (Weight: 15%)
- MACD Golden/Death Cross (Weight: 15%)
- Bollinger Band Breakout (Weight: 10%)
Fundamental Indicators:
- PE/PB Relative Valuation (Weight: 15%)
- Earnings Growth Trend (Weight: 15%)
Market Sentiment:
- Social Media Sentiment Score (Weight: 10%)
- Institutional Fund Flows (Weight: 10%)
Macro Factors:
- Industry Rotation Signals (Weight: 5%)
- Overall Market Trend (Weight: 5%)
📋 Output Requirements:
- Comprehensive Score (0-100 points)
- Factor score breakdown
- Statistical confidence level
- Quantitative risk indicators
- Historical backtest performance: {past_memory_str}
Based on quantitative models, provide objective investment recommendations. Must end your response with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**'.
Please provide professional, quantitative trading decision analysis.""",
},
context,
]
```
## 🔄 Reflection System Prompt Templates
### 1. Detailed Reflection Template
```python
def _get_reflection_prompt(self) -> str:
return """
You are a professional financial analysis expert tasked with reviewing trading decisions/analysis and providing comprehensive, step-by-step analysis.
Your goal is to deliver detailed insights into investment decisions and highlight opportunities for improvement, adhering strictly to the following guidelines:
🔍 1. Reasoning Analysis:
- For each trading decision, determine whether it was correct or incorrect. A correct decision results in increased returns, while an incorrect decision does the opposite
- Analyze contributing factors to each success or mistake, considering:
* Market intelligence quality and accuracy
* Technical indicator effectiveness and timing
* Technical signal strength and confirmation
* Price movement analysis accuracy
* Overall market data analysis depth
* News analysis relevance and impact assessment
* Social media and sentiment analysis reliability
* Fundamental data analysis comprehensiveness
* Weight allocation of each factor in the decision-making process
📈 2. Improvement Recommendations:
- For any incorrect decisions, propose revisions to maximize returns
- Provide detailed corrective action lists or improvements, including specific recommendations
- Example: Change decision from HOLD to BUY on a specific date
📚 3. Experience Summary:
- Summarize lessons learned from successes and failures
- Highlight how these lessons can be applied to future trading scenarios
- Draw connections between similar situations to apply gained knowledge
🎯 4. Key Insight Extraction:
- Extract key insights from summary into concise sentences of no more than 1000 tokens
- Ensure condensed sentences capture the essence of lessons and reasoning for easy reference
Strictly adhere to these instructions and ensure your output is detailed, accurate, and actionable. You will also be given objective market descriptions from price movements, technical indicators, news, and sentiment perspectives to provide more context for your analysis.
Please provide professional, in-depth reflection analysis.
"""
```
## 🎨 Custom Prompt Guidelines
### 1. Prompt Structure Template
```python
def create_custom_prompt(
role="Analyst",
expertise="Market Analysis",
style="Professional",
language="English",
risk_level="Moderate",
output_format="Detailed Report"
):
return f"""
Role Definition: You are a {style} {role}
🎯 Role Positioning:
- Expertise: {expertise}
- Analysis Style: {style}
- Risk Preference: {risk_level}
- Output Language: {language}
📋 Core Tasks:
1. [Specific Task 1]
2. [Specific Task 2]
3. [Specific Task 3]
🔍 Analysis Framework:
- Data Collection: [Data sources and types]
- Analysis Methods: [Analysis tools and methods used]
- Risk Assessment: [Risk identification and assessment methods]
- Conclusion Formation: [Decision logic and criteria]
📊 Output Requirements:
- Format: {output_format}
- Structure: [Specific output structure requirements]
- Focus: [Content that needs emphasis]
- Constraints: [Content or practices to avoid]
💡 Important Notes:
- [Special Requirement 1]
- [Special Requirement 2]
- [Special Requirement 3]
Please provide professional {expertise} analysis based on the above requirements.
"""
```
### 2. Multi-language Prompt Template
```python
MULTILINGUAL_PROMPTS = {
"en-US": {
"role_prefix": "You are a professional",
"task_intro": "Your task is to",
"analysis_framework": "Analysis Framework:",
"output_requirements": "Output Requirements:",
"final_decision": "Final Recommendation:"
},
"zh-CN": {
"role_prefix": "您是一位专业的",
"task_intro": "您的任务是",
"analysis_framework": "分析框架:",
"output_requirements": "输出要求:",
"final_decision": "最终建议:"
},
"ja-JP": {
"role_prefix": "あなたはプロの",
"task_intro": "あなたの任務は",
"analysis_framework": "分析フレームワーク:",
"output_requirements": "出力要件:",
"final_decision": "最終推奨:"
}
}
```
---
💡 **Usage Tips**:
1. Copy the appropriate template code
2. Modify specific content as needed
3. Replace original prompts in corresponding files
4. Test modification effects
5. Further optimize based on results
📝 **Customization Suggestions**:
- Maintain structured and logical prompts
- Clearly specify output format and requirements
- Include specific analysis frameworks and methods
- Consider different market and cultural backgrounds
- Regularly optimize prompts based on effectiveness feedback

View File

@ -0,0 +1,251 @@
# TradingAgents Quick Reference Card
## 🚀 Quick Start
### 1. Change LLM Provider
```python
# Edit main.py
config["llm_provider"] = "google" # or "openai", "anthropic"
config["backend_url"] = "https://generativelanguage.googleapis.com/v1"
config["deep_think_llm"] = "gemini-2.0-flash"
config["quick_think_llm"] = "gemini-2.0-flash"
```
### 2. Modify Debate Rounds
```python
# Edit main.py or default_config.py
config["max_debate_rounds"] = 3 # Increase to 3 rounds
config["max_risk_discuss_rounds"] = 2 # Risk discussion 2 rounds
```
### 3. Enable/Disable Online Tools
```python
config["online_tools"] = True # Enable online APIs
config["online_tools"] = False # Use local data
```
## 📁 Key File Locations
| Content to Modify | File Path | Specific Location |
|------------------|-----------|-------------------|
| **System Config** | `tradingagents/default_config.py` | Entire file |
| **Runtime Config** | `main.py` | Lines 15-22 |
| **Market Analyst Prompts** | `tradingagents/agents/analysts/market_analyst.py` | Lines 24-50 |
| **Fundamentals Analyst Prompts** | `tradingagents/agents/analysts/fundamentals_analyst.py` | Lines 23-26 |
| **News Analyst Prompts** | `tradingagents/agents/analysts/news_analyst.py` | Lines 20-23 |
| **Social Media Analyst Prompts** | `tradingagents/agents/analysts/social_media_analyst.py` | Lines 19-22 |
| **Bull Researcher Prompts** | `tradingagents/agents/researchers/bull_researcher.py` | Lines 25-43 |
| **Bear Researcher Prompts** | `tradingagents/agents/researchers/bear_researcher.py` | Lines 25-43 |
| **Trader Prompts** | `tradingagents/agents/trader/trader.py` | Lines 30-36 |
| **Reflection System Prompts** | `tradingagents/graph/reflection.py` | Lines 15-47 |
| **Cache Config** | `tradingagents/dataflows/cache_manager.py` | Lines 20-35 |
## 🎯 Common Modification Templates
### 1. Professional Prompt Template
```python
system_message = f"""
You are a professional {role_name} with the following characteristics:
Expertise Areas:
- {domain_1}
- {domain_2}
- {domain_3}
Analysis Requirements:
1. Provide detailed analysis reasoning
2. Include risk warnings
3. Summarize key indicators in table format
Output Format:
{output_format}
Important Notes:
- Avoid simply saying "trends are mixed"
- Provide specific data support
- Consider market-specific factors
"""
```
### 2. Risk Control Template
```python
system_message = f"""
You are a risk-conscious {role_name}.
Risk Control Principles:
1. Risk first, returns second
2. Strict stop-loss, protect capital
3. Diversified investment, reduce risk
4. Data-driven, rational decisions
Must Include:
- Risk assessment level (Low/Medium/High)
- Recommended stop-loss points
- Maximum position suggestion
- Risk warning description
Decision Format:
Final Recommendation: **BUY/HOLD/SELL**
Risk Level: **Low/Medium/High**
Stop-Loss Point: **Specific price**
Suggested Position: **Percentage**
"""
```
### 3. Technical Analysis Template
```python
system_message = f"""
You are a professional technical analyst focusing on the following indicators:
Core Indicators:
- Moving Averages: SMA, EMA
- Momentum Indicators: RSI, MACD
- Volatility Indicators: Bollinger Bands, ATR
- Volume Indicators: VWMA
Analysis Framework:
1. Trend identification (Up/Down/Sideways)
2. Support and resistance levels
3. Buy/sell signal identification
4. Risk-reward ratio calculation
Output Requirements:
- Clear trend judgment
- Specific entry/exit points
- Technical indicator divergence analysis
- Volume-price relationship analysis
"""
```
## ⚙️ Configuration Parameters Quick Reference
### LLM Configuration
```python
"llm_provider": "openai" | "google" | "anthropic"
"deep_think_llm": "model_name" # Deep thinking model
"quick_think_llm": "model_name" # Quick thinking model
"backend_url": "API_address"
```
#### Google Models Quick Reference
```python
# Fast Models: gemini-2.0-flash-lite, gemini-2.0-flash ⭐, gemini-2.5-flash-preview-05-20
# Deep Models: gemini-2.0-flash ⭐, gemini-2.5-flash-preview-05-20, gemini-2.5-pro-preview-06-05
# Google API Setup
export GOOGLE_API_KEY="your_key_here"
```
### Debate Configuration
```python
"max_debate_rounds": 1-5 # Debate rounds
"max_risk_discuss_rounds": 1-3 # Risk discussion rounds
"max_recur_limit": 100 # Recursion limit
```
### Tool Configuration
```python
"online_tools": True | False # Whether to use online tools
"data_cache_dir": "cache_directory_path"
"results_dir": "results_output_directory"
```
### Cache Configuration
```python
# In cache_manager.py
'us_stock_data': {'ttl_hours': 2} # US stock cache 2 hours
'china_stock_data': {'ttl_hours': 1} # A-share cache 1 hour
```
## 🔧 Common Commands
### Test Configuration
```bash
# Run basic tests
cd tests && python test_cache_manager.py
# Run integration tests
cd tests && python test_integration.py
# Run performance tests
cd tests && python test_performance.py
```
### Backup and Restore
```bash
# Backup configuration files
cp tradingagents/default_config.py tradingagents/default_config.py.backup
# Backup prompt files
cp tradingagents/agents/trader/trader.py tradingagents/agents/trader/trader.py.backup
# Restore files
cp tradingagents/default_config.py.backup tradingagents/default_config.py
```
### Git Management
```bash
# Check modification status
git status
# Commit configuration changes
git add tradingagents/default_config.py
git commit -m "feat: Update LLM configuration to Google Gemini"
# Commit prompt changes
git add tradingagents/agents/trader/trader.py
git commit -m "feat: Optimize trader prompts, add risk control"
```
## 🚨 Important Notes
### ⚠️ Must Do Before Modification
1. **Backup Files**: Always backup original files before modification
2. **Test Environment**: Validate modifications in test environment
3. **Version Control**: Use Git to track all changes
### ⚠️ Common Errors
1. **Forgot to Restart**: Need to restart application after config changes
2. **Path Errors**: Ensure file paths are correct
3. **Syntax Errors**: Python syntax must be correct
4. **Encoding Issues**: Use UTF-8 encoding for content
### ⚠️ Performance Considerations
1. **Prompt Length**: Avoid overly long prompts (recommend <4000 tokens)
2. **API Call Frequency**: Be aware of API call limits
3. **Cache Settings**: Set reasonable cache TTL times
## 🆘 Troubleshooting
### Issue: Configuration not taking effect
```python
# Solution: Force reload configuration
from tradingagents.dataflows.config import reload_config
reload_config()
```
### Issue: API call failures
```python
# Solution: Check API keys and network connection
import os
print("OpenAI API Key:", os.getenv("OPENAI_API_KEY", "Not set"))
print("Google API Key:", os.getenv("GOOGLE_API_KEY", "Not set"))
```
### Issue: High memory usage
```python
# Solution: Enable cache cleanup
config["cache_settings"]["cache_size_limit_mb"] = 500 # Limit cache size
config["cache_settings"]["cache_cleanup_interval"] = 1800 # Clean every 30 minutes
```
## 📞 Getting Help
1. **View Detailed Documentation**: `docs/en-US/configuration_guide.md`
2. **Run Tests**: Test files in `tests/` directory
3. **View Examples**: `examples/` directory (if available)
4. **GitHub Issues**: Submit issues in project repository
---
💡 **Tip**: Recommend bookmarking this document for easy reference!

View File

@ -0,0 +1,356 @@
# TradingAgents Quick Start Guide
## 🚀 Overview
This guide will help you get started with TradingAgents quickly, including the new Chinese market features, database integration, and multi-LLM support.
## ⚡ Quick Setup (5 Minutes)
### 1. Prerequisites
```bash
# Python 3.8+ required
python --version
# Clone the repository
git clone https://github.com/your-repo/TradingAgents.git
cd TradingAgents
# Install dependencies
pip install -r requirements.txt
pip install pytdx beautifulsoup4 # For Chinese market support
```
### 2. Environment Configuration
```bash
# Copy environment template
cp .env.example .env
# Edit .env file with your API keys
nano .env # or use your preferred editor
```
**Minimum Required Configuration**:
**For US Stock Analysis Only**:
```env
# OpenAI or Google AI (Choose one)
OPENAI_API_KEY=your_openai_api_key_here
# OR
GOOGLE_API_KEY=your_google_api_key_here
# FinnHub (Required for financial data)
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**For China A-Share Analysis OR DashScope LLM**:
```env
# DashScope (Required for Chinese stocks or Qwen models)
DASHSCOPE_API_KEY=your_dashscope_api_key_here
# FinnHub (Required for financial data)
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**Note**:
- **DashScope API key is only required when**:
- Analyzing Chinese A-share stocks (uses TongDaXin data + DashScope embeddings)
- Choosing DashScope as your LLM provider (Qwen models)
- **For US stocks with OpenAI/Google models**: DashScope is not needed
### 3. First Run
```bash
# Start the application
python -m cli.main
# Follow the interactive prompts:
# 1. Select Market: US Stock or China A-Share
# 2. Enter ticker symbol (e.g., AAPL or 000001)
# 3. Choose analysis date
# 4. Select analysts team
# 5. Choose LLM provider (DashScope recommended)
# 6. Run analysis
```
## 🌟 Feature Overview
### 🇺🇸 US Stock Analysis
- **Supported Symbols**: AAPL, SPY, TSLA, NVDA, MSFT, etc.
- **Data Source**: Yahoo Finance
- **Format**: 1-5 letter symbols
- **Example**: `AAPL` (Apple Inc.)
### 🇨🇳 China A-Share Analysis
- **Supported Exchanges**:
- Shanghai (60xxxx): `600036` (China Merchants Bank)
- Shenzhen (00xxxx): `000001` (Ping An Bank)
- ChiNext (30xxxx): `300001` (Technology stocks)
- STAR Market (68xxxx): `688001` (Innovation companies)
- **Data Source**: TongDaXin API
- **Format**: 6-digit numeric codes
### 🤖 Multi-LLM Support
- **DashScope (Alibaba Cloud)**: Qwen models, Chinese-optimized
- **OpenAI**: GPT-4o, GPT-4o-mini, o1, o3 series
- **Google AI**: Gemini 2.0/2.5 Flash series
- **Anthropic**: Claude 3.5/4 series
## 📋 Step-by-Step Walkthrough
### Step 1: Market Selection
```
? Select Stock Market:
US Stock - Examples: SPY, AAPL, TSLA
China A-Share - Examples: 000001, 600036, 000858
```
### Step 2: Ticker Input
```
Format requirement: 6-digit code (e.g., 600036, 000001)
Examples: 000001, 600036, 300001, 688001
? Enter China A-Share ticker symbol: 000001
✅ Valid A-share code: 000001 (will use TongDaXin data source)
```
### Step 3: Analysis Configuration
```
? Select your research depth:
Light (1 round) - Quick analysis
Medium (2 rounds) - Balanced analysis
Deep (3 rounds) - Comprehensive analysis
? Select your LLM Provider:
DashScope (Alibaba Cloud)
OpenAI
Google AI
Anthropic
```
### Step 4: Model Selection
```
? Select Your [Quick-Thinking LLM Engine]:
Qwen-Turbo - Fast response, suitable for quick tasks
Qwen-Plus - Balanced performance and cost
Qwen-Max - Best performance for complex analysis
? Select Your [Deep-Thinking LLM Engine]:
Qwen-Plus - Balanced performance and cost (Recommended)
Qwen-Max - Best performance for complex analysis
Qwen-Max-LongContext - Ultra-long context support
```
## 🗄️ Database Setup (Optional)
### Enable High-Performance Caching
**1. Start Database Services**:
```bash
# MongoDB for persistent storage
docker run -d -p 27017:27017 --name mongodb mongo
# Redis for high-performance caching
docker run -d -p 6379:6379 --name redis redis
```
**2. Enable in .env**:
```env
# Enable database caching
MONGODB_ENABLED=true
REDIS_ENABLED=true
# MongoDB configuration
MONGODB_HOST=localhost
MONGODB_PORT=27017
MONGODB_DATABASE=tradingagents
# Redis configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
```
**3. Restart Application**:
```bash
python -m cli.main
# System will now use database caching for improved performance
```
## 🔧 Configuration Examples
### Example 1: US Stock Analysis with OpenAI
```env
# Only need OpenAI and FinnHub for US stocks
OPENAI_API_KEY=your_openai_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI Selections**:
- Market: US Stock
- Ticker: AAPL
- LLM Provider: OpenAI
- Models: GPT-4o-mini (quick), o1 (deep)
**Note**: DashScope not required for US stock analysis with OpenAI
### Example 2: US Stock Analysis with Google AI
```env
# Only need Google AI and FinnHub for US stocks
GOOGLE_API_KEY=your_google_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI Selections**:
- Market: US Stock
- Ticker: TSLA
- LLM Provider: Google AI
- Models: Gemini 2.0 Flash (quick), Gemini 2.5 Flash (deep)
**Note**: DashScope not required for US stock analysis with Google AI
### Example 3: China A-Share Analysis (DashScope Required)
```env
# DashScope required for Chinese stock analysis
DASHSCOPE_API_KEY=your_dashscope_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI Selections**:
- Market: China A-Share
- Ticker: 000001
- LLM Provider: DashScope
- Models: qwen-turbo (quick), qwen-plus (deep)
**Note**: DashScope API key is required for Chinese stock analysis (TongDaXin data + embeddings)
### Example 4: US Stocks with DashScope LLM (DashScope Required)
```env
# DashScope required when using Qwen models
DASHSCOPE_API_KEY=your_dashscope_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI Selections**:
- Market: US Stock
- Ticker: SPY
- LLM Provider: DashScope (Alibaba Cloud)
- Models: qwen-turbo (quick), qwen-plus (deep)
**Note**: DashScope API key is required when choosing DashScope as LLM provider
### Example 5: Full Features with Database
```env
# Choose based on your use case
OPENAI_API_KEY=your_openai_key # For US stocks with OpenAI
# OR
DASHSCOPE_API_KEY=your_dashscope_key # For Chinese stocks or DashScope LLM
FINNHUB_API_KEY=your_finnhub_key
MONGODB_ENABLED=true
REDIS_ENABLED=true
```
**Benefits**:
- Faster data retrieval
- Persistent analysis history
- Advanced caching strategies
- Usage analytics
## 🛠️ Troubleshooting
### Common Issues
**1. API Key Errors**:
```
Error: Invalid API key
Solution: Check .env file and ensure correct API key format
```
**2. TongDaXin Connection Issues**:
```
Error: TongDaXin API unavailable
Solution: System automatically falls back to cached data
```
**3. Database Connection Issues**:
```
Error: MongoDB/Redis connection failed
Solution: System falls back to file cache automatically
```
**4. Invalid Ticker Format**:
```
Error: Invalid ticker format
Solution:
- US stocks: Use 1-5 letter symbols (AAPL)
- A-shares: Use 6-digit codes (000001)
```
### Debug Mode
```bash
# Enable debug logging
export TRADINGAGENTS_LOG_LEVEL=DEBUG
python -m cli.main
```
## 📊 Sample Analysis Output
### US Stock Analysis (AAPL)
```
📈 Analysis Results for AAPL (Apple Inc.)
Market: US Stock Exchange
Data Source: Yahoo Finance
🔍 Technical Analysis:
- Current Price: $150.25 (+2.3%)
- RSI: 65.2 (Neutral to Bullish)
- Moving Averages: Above 20-day and 50-day MA
💰 Fundamental Analysis:
- P/E Ratio: 28.5
- Revenue Growth: 8.2% YoY
- Market Cap: $2.4T
📰 News Sentiment: Positive (0.72/1.0)
🎯 Recommendation: BUY with target $165
```
### China A-Share Analysis (000001)
```
📈 Analysis Results for 000001 (平安银行)
Market: Shenzhen Stock Exchange
Data Source: TongDaXin API
🔍 Technical Analysis:
- Current Price: ¥12.85 (+1.8%)
- RSI: 58.3 (Neutral)
- Volume: Above average
💰 Fundamental Analysis:
- P/E Ratio: 5.2
- ROE: 12.8%
- Book Value: ¥15.20
📰 News Sentiment: Neutral (0.55/1.0)
🎯 Recommendation: HOLD with target ¥14.50
```
## 🎯 Next Steps
### Explore Advanced Features
1. **Custom Prompts**: Modify agent prompts for specific strategies
2. **Database Analytics**: Analyze historical performance
3. **Multi-Market Comparison**: Compare US and Chinese stocks
4. **Risk Management**: Configure risk parameters
### Learn More
- [Configuration Guide](configuration_guide.md) - Detailed configuration options
- [Architecture Guide](architecture_guide.md) - System architecture overview
- [API Documentation](api_documentation.md) - API reference
### Get Support
- GitHub Issues: Report bugs and feature requests
- Documentation: Comprehensive guides and examples
- Community: Join discussions and share strategies
---
🎉 **Congratulations!** You're now ready to analyze both US and Chinese markets with TradingAgents. The system provides intelligent fallbacks, multi-LLM support, and enterprise-grade caching for optimal performance.

228
docs/zh-CN/README.md Normal file
View File

@ -0,0 +1,228 @@
# TradingAgents 中文文档
## 📖 概述
TradingAgents是一个基于多智能体的金融分析系统现已全面支持中国A股市场和多LLM提供商。本系统通过智能体协作提供深度的股票分析和投资建议。
## 🌟 主要特性
### 🌍 多市场支持
- **美股市场**: 完整的美国股票市场分析
- **中国A股市场**: 通达信API集成支持实时A股数据 ⭐ **新功能**
- **智能市场选择**: 交互式CLI市场选择界面
### 🤖 多智能体分析框架
- **市场分析师**: 技术分析和图表模式识别
- **基本面分析师**: 财务数据和公司基本面分析
- **新闻分析师**: 新闻情绪和市场情绪分析
- **多空研究员**: 多角度投资观点辩论
- **交易员智能体**: 综合决策和风险评估
- **反思智能体**: 分析质量控制和改进建议
### 🧠 多LLM提供商支持
- **百炼(DashScope)**: 阿里云通义千问模型系列 ⭐ **推荐中国用户**
- **当前设置**: 百炼作为主要选项,智能回退机制
- **OpenAI**: GPT-4o, GPT-4o-mini, o1, o3系列
- **Google AI**: Gemini 2.0/2.5 Flash系列
- **Anthropic**: Claude 3.5/4系列
### 🗄️ 企业级数据库集成
- **MongoDB**: 持久化数据存储和分析 ⭐ **新功能**
- **Redis**: 高性能缓存系统 ⭐ **新功能**
- **智能缓存**: 自动回退机制和性能优化
### 📊 数据源集成
- **美股数据**: Yahoo Finance集成
- **A股数据**: 通达信API集成 ⭐ **新功能**
- 上海证券交易所 (60xxxx)
- 深圳证券交易所 (00xxxx)
- 创业板 (30xxxx)
- 科创板 (68xxxx)
- **财经新闻**: 多源新闻聚合和情绪分析
### ⚙️ 配置管理
- LLM提供商设置 (百炼、OpenAI、Google、Anthropic)
- **百炼(DashScope)**: 完整支持通义千问模型系列 ⭐ **推荐中国用户**
- **当前设置**: 百炼作为主要选项,智能回退机制
- 市场选择和数据源配置
- **美股市场**: Yahoo Finance集成
- **中国A股市场**: 通达信API集成 ⭐ **新功能**
- 数据库和缓存系统
- **MongoDB**: 持久化数据存储
- **Redis**: 高性能缓存
- **智能缓存**: 自动回退机制
- 辩论和讨论参数配置
- API配置和限制设置
### 🔧 高级功能
- **多市场支持**: 美股和中国A股
- **数据库集成**: MongoDB和Redis企业部署
- **智能缓存**: 自适应缓存管理和回退
- **多LLM支持**: 百炼、OpenAI、Google、Anthropic
- **通达信集成**: 实时A股数据访问
- 风险管理模板
- 性能优化
- 自定义提示词创建
- 环境特定配置
## 🚀 快速开始
### 1. 安装和设置
```bash
# 克隆仓库
git clone https://github.com/your-repo/TradingAgents.git
cd TradingAgents
# 安装依赖
pip install -r requirements.txt
pip install pytdx beautifulsoup4 # 中国市场支持
# 配置环境变量
cp .env.example .env
# 编辑.env文件填入您的API密钥
```
### 2. 基本配置
**分析美股 (使用OpenAI)**:
```env
OPENAI_API_KEY=your_openai_api_key
FINNHUB_API_KEY=your_finnhub_api_key
```
**分析中国A股 (需要百炼)**:
```env
DASHSCOPE_API_KEY=your_dashscope_api_key
FINNHUB_API_KEY=your_finnhub_api_key
```
### 3. 运行分析
```bash
python -m cli.main
```
按照交互式提示选择市场、输入股票代码并配置分析参数。
## 📚 文档导航
### 🎯 新手指南
- **[快速开始指南](quick_start_guide.md)** - 5分钟快速设置和首次运行
- **[配置指南](configuration_guide.md)** - 详细的配置选项和自定义设置
### 🏗️ 技术文档
- **[架构指南](architecture_guide.md)** - 系统架构和技术实现详解
- **[提示词模板](prompt_templates.md)** - 智能体提示词自定义
### 📋 参考资料
- **[快速参考](quick_reference.md)** - 常用配置和修改速查表
## 🔑 API密钥配置说明
### 必需的API密钥
**百炼API密钥仅在以下情况需要**:
1. 📈 **分析中国A股股票** (使用通达信数据 + 百炼embeddings)
2. 🤖 **选择百炼作为LLM提供商** (通义千问模型)
**分析美股使用OpenAI/Google模型时**: 不需要百炼API密钥
### API密钥获取
- **百炼(DashScope)**: https://dashscope.aliyun.com/
- **OpenAI**: https://platform.openai.com/
- **Google AI**: https://ai.google.dev/
- **Anthropic**: https://console.anthropic.com/
- **FinnHub**: https://finnhub.io/ (金融数据,必需)
## 🌍 支持的市场和交易所
### 美股市场
- **格式**: 1-5位字母代码 (如 AAPL, SPY, TSLA)
- **数据源**: Yahoo Finance
- **示例**: AAPL (苹果), SPY (标普500ETF), TSLA (特斯拉)
### 中国A股市场
- **格式**: 6位数字代码
- **数据源**: 通达信API
- **支持交易所**:
- 上海证券交易所: 60xxxx (如 600036 招商银行)
- 深圳证券交易所: 00xxxx (如 000001 平安银行)
- 创业板: 30xxxx (如 300001 科技股)
- 科创板: 68xxxx (如 688001 创新公司)
## 🗄️ 数据库功能 (可选)
### MongoDB集成
- **用途**: 持久化数据存储和历史分析
- **功能**: Token使用跟踪、分析结果存储、用户会话管理
- **设置**: `MONGODB_ENABLED=true` 在.env中
### Redis集成
- **用途**: 高性能缓存和会话管理
- **功能**: 快速数据访问、实时缓存、性能优化
- **设置**: `REDIS_ENABLED=true` 在.env中
### 智能回退
- **第一层**: Redis高性能缓存
- **第二层**: MongoDB持久化存储
- **第三层**: 文件缓存 (始终可用)
## 🎯 使用场景
### 场景1: 美股日常分析
- **配置**: OpenAI + FinnHub
- **市场**: 美股
- **特点**: 快速、稳定、国际化
### 场景2: 中国A股专业分析
- **配置**: 百炼 + FinnHub + 通达信
- **市场**: 中国A股
- **特点**: 本土化、实时数据、中文优化
### 场景3: 企业级部署
- **配置**: 多LLM + MongoDB + Redis
- **市场**: 美股 + A股
- **特点**: 高性能、可扩展、完整功能
## 🛠️ 故障排除
### 常见问题
1. **API密钥错误**: 检查.env文件中的密钥格式
2. **网络连接问题**: 系统自动回退到缓存数据
3. **数据库连接失败**: 自动回退到文件缓存
4. **股票代码格式错误**: 参考市场特定格式要求
### 调试模式
```bash
export TRADINGAGENTS_LOG_LEVEL=DEBUG
python -m cli.main
```
## 🤝 贡献和支持
### 获取帮助
- **GitHub Issues**: 报告错误和功能请求
- **文档**: 查阅详细的配置和使用指南
- **社区**: 参与讨论和分享使用经验
### 贡献代码
- Fork项目并创建功能分支
- 提交Pull Request
- 遵循代码规范和测试要求
---
## 📈 系统优势
### 技术优势
- **多智能体协作**: 多角度分析,提高决策质量
- **多LLM支持**: 降低单点故障风险,提高可靠性
- **智能缓存**: 三层缓存架构,确保高性能和高可用
- **模块化设计**: 易于扩展和维护
### 市场优势
- **全球市场覆盖**: 支持美股和中国A股两大主要市场
- **本土化优化**: 中国市场专用数据源和模型
- **实时数据**: 通达信API提供实时A股数据
- **智能回退**: 确保服务连续性和稳定性
🎉 **开始您的智能投资分析之旅!** TradingAgents为您提供专业级的多市场股票分析能力。

View File

@ -0,0 +1,376 @@
# TradingAgents 系统架构指南
## 📖 概述
本文档提供TradingAgents系统架构的全面概览包括中国市场功能集成、数据库系统和多LLM支持。该架构设计注重可扩展性、可靠性和全球市场覆盖。
## 🏗️ 高层架构
```
┌─────────────────────────────────────────────────────────────────┐
│ TradingAgents 系统 │
├─────────────────────────────────────────────────────────────────┤
│ CLI界面 (市场选择 + 配置) │
├─────────────────────────────────────────────────────────────────┤
│ 多智能体框架 │
│ ├── 市场分析师 ├── 基本面分析师 │
│ ├── 新闻分析师 ├── 多空研究员 │
│ └── 交易员智能体 └── 风险管理 │
├─────────────────────────────────────────────────────────────────┤
│ 多LLM提供商层 │
│ ├── 百炼(通义千问) ├── OpenAI(GPT) │
│ ├── Google(Gemini) └── Anthropic(Claude) │
├─────────────────────────────────────────────────────────────────┤
│ 数据层 │
│ ├── 美股市场 (Yahoo Finance) │
│ ├── 中国A股 (通达信API) │
│ └── 财经新闻 & 社交媒体 │
├─────────────────────────────────────────────────────────────────┤
│ 缓存与存储层 │
│ ├── MongoDB (持久化存储) │
│ ├── Redis (高性能缓存) │
│ └── 文件缓存 (回退方案) │
└─────────────────────────────────────────────────────────────────┘
```
## 🎯 核心组件
### 1. CLI界面层
#### 市场选择系统
- **交互式市场选择**: 美股 vs 中国A股
- **格式验证**: 市场特定的股票代码验证
- **数据源路由**: 基于市场选择的自动路由
- **英文界面**: 国际化就绪
**关键文件**:
```
cli/main.py - 主CLI应用程序
cli/utils.py - 市场选择和验证工具
```
**流程**:
```
用户输入 → 市场选择 → 股票代码验证 → 数据源分配
```
### 2. 多智能体框架
#### 智能体层次结构
```
TradingAgentsGraph
├── 分析师团队
│ ├── 市场分析师 (技术分析)
│ ├── 基本面分析师 (财务分析)
│ └── 新闻分析师 (情绪分析)
├── 研究团队
│ ├── 多头研究员 (积极情绪)
│ └── 空头研究员 (风险分析)
├── 交易团队
│ ├── 交易员智能体 (决策制定)
│ └── 风险管理员 (风险评估)
└── 反思系统
└── 反思智能体 (质量控制)
```
**关键文件**:
```
tradingagents/graph/trading_graph.py - 主智能体编排
tradingagents/agents/analysts/ - 分析师实现
tradingagents/agents/researchers/ - 研究团队
tradingagents/agents/trader/ - 交易决策
```
### 3. 多LLM提供商层
#### 提供商架构
```
LLM请求 → 提供商路由器 → 特定适配器 → API调用 → 响应
```
#### 支持的提供商
1. **百炼(DashScope)**
- 模型: qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
- 中文语言优化
- 中国用户首选
2. **OpenAI**
- 模型: GPT-4o, GPT-4o-mini, o1, o3, o4-mini
- 英文内容全球标准
3. **Google AI**
- 模型: Gemini 2.0 Flash, Gemini 2.5 Flash
- 高级推理能力
4. **Anthropic**
- 模型: Claude 3.5 Haiku, Claude 3.5 Sonnet, Claude 4
- 强大的分析能力
**关键文件**:
```
tradingagents/graph/trading_graph.py - LLM初始化
tradingagents/agents/utils/memory.py - Embedding服务
cli/utils.py - 提供商选择
```
#### 智能回退系统
```
主要提供商 (百炼)
↓ (如果不可用)
次要提供商 (OpenAI)
↓ (如果不可用)
第三提供商 (Google/Anthropic)
↓ (如果全部失败)
错误处理 & 用户通知
```
### 4. 数据层架构
#### 多市场数据源
**美股市场**:
```
Yahoo Finance API → 数据验证 → 缓存存储 → 智能体消费
```
**中国A股市场**:
```
通达信API → 数据优化 → 缓存存储 → 智能体消费
```
#### 数据流架构
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 数据请求 │ -> │ 源路由器 │ -> │ 数据提供商 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 缓存管理器 │ <- 数据处理器 <- 原始数据
└─────────────────┘ └──────────────────┘ └─────────────────┘
```
**关键文件**:
```
tradingagents/dataflows/interface.py - 数据接口
tradingagents/dataflows/tdx_utils.py - 通达信集成
tradingagents/dataflows/optimized_china_data.py - 中国数据优化
tradingagents/dataflows/chinese_finance_utils.py - 中国财经工具
tradingagents/dataflows/stock_data_service.py - 统一数据服务
```
#### 支持的中国交易所
- **上海证券交易所**: 60xxxx (如 600036 - 招商银行)
- **深圳证券交易所**: 00xxxx (如 000001 - 平安银行)
- **创业板**: 30xxxx (如 300001 - 科技股)
- **科创板**: 68xxxx (如 688001 - 创新公司)
### 5. 缓存与存储层
#### 三层缓存架构
**第一层: Redis (高性能缓存)**
```
内存基础 → 亚毫秒访问 → 实时数据
```
**第二层: MongoDB (持久化存储)**
```
文档基础 → 结构化存储 → 历史数据 & 分析
```
**第三层: 文件缓存 (回退方案)**
```
文件基础 → 可靠回退 → 始终可用
```
#### 缓存管理流程
```
数据请求
Redis检查 (第一层)
↓ (如果未命中)
MongoDB检查 (第二层)
↓ (如果未命中)
文件缓存检查 (第三层)
↓ (如果未命中)
外部API调用
存储到所有层
```
**关键文件**:
```
tradingagents/dataflows/cache_manager.py - 缓存协调
tradingagents/dataflows/db_cache_manager.py - 数据库缓存
tradingagents/dataflows/integrated_cache.py - 集成缓存系统
tradingagents/dataflows/adaptive_cache.py - 自适应缓存策略
tradingagents/config/database_manager.py - 数据库连接
tradingagents/config/mongodb_storage.py - MongoDB操作
```
#### 数据库模式设计
**MongoDB集合**:
```
stock_data - 历史股价和成交量
analysis_results - 智能体分析输出
token_usage - LLM API使用跟踪
cache_metadata - 缓存管理信息
user_sessions - 用户交互历史
```
**Redis键模式**:
```
stock:{symbol}:{date} - 日股票数据
analysis:{symbol}:{timestamp} - 分析结果
news:{symbol}:{date} - 新闻情绪数据
cache:meta:{key} - 缓存元数据
```
## 🔄 数据流模式
### 1. 分析工作流
```
用户输入 (CLI)
市场选择 & 验证
数据获取 (多源)
智能体分析 (多LLM)
结果聚合
输出生成
缓存存储
```
### 2. 缓存工作流
```
数据请求
缓存键生成
第一层 (Redis) 检查
↓ (如果未命中)
第二层 (MongoDB) 检查
↓ (如果未命中)
第三层 (文件) 检查
↓ (如果未命中)
外部API调用
多层存储
响应用户
```
### 3. 错误处理工作流
```
组件故障
错误检测
回退激活
替代路径
用户通知 (如需要)
优雅降级
```
## 🛡️ 可靠性与可扩展性特性
### 高可用性设计
- **多LLM回退**: 自动提供商切换
- **多层缓存**: 冗余数据存储
- **优雅降级**: 系统在功能减少的情况下继续运行
- **错误恢复**: 自动重试机制
### 可扩展性特性
- **数据库集群**: MongoDB副本集
- **缓存扩展**: Redis集群支持
- **负载均衡**: 多个API端点
- **水平扩展**: 无状态智能体设计
### 性能优化
- **智能缓存**: 自适应缓存策略
- **连接池**: 数据库连接管理
- **异步处理**: 非阻塞操作
- **数据压缩**: 高效存储格式
## 🔧 配置管理
### 基于环境的配置
```
.env文件 → 环境变量 → 运行时配置
```
### 配置层次结构
```
1. 环境变量 (.env)
2. 默认配置 (default_config.py)
3. 运行时覆盖 (main.py)
4. 动态配置 (config.py)
```
### 配置类别
- **API密钥**: LLM提供商和数据源
- **数据库设置**: MongoDB和Redis配置
- **缓存设置**: 缓存TTL和策略
- **市场设置**: 支持的市场和交易所
- **智能体设置**: 模型选择和参数
## 📊 监控与分析
### 系统指标
- **API使用**: Token消费和成本
- **缓存性能**: 命中率和响应时间
- **数据库性能**: 查询时间和存储使用
- **错误率**: 按组件的故障率
### 业务指标
- **分析质量**: 智能体性能指标
- **用户参与**: 使用模式和偏好
- **市场覆盖**: 支持的股票代码和交易所
- **响应时间**: 端到端分析持续时间
## 🚀 部署架构
### 开发环境
```
本地机器 → 文件缓存 → 单一LLM提供商 → 基础功能
```
### 生产环境
```
应用服务器 → Redis集群 → MongoDB副本集 → 多LLM → 完整功能
```
### 云部署选项
- **数据库**: MongoDB Atlas, Redis Cloud
- **应用程序**: Docker容器, Kubernetes
- **负载均衡**: 应用程序负载均衡器
- **监控**: 应用程序性能监控
## 🔮 未来架构增强
### 计划改进
- **微服务架构**: 服务分解
- **事件驱动架构**: 异步消息处理
- **机器学习管道**: 自动模型训练
- **实时流处理**: 实时市场数据处理
- **全球CDN**: 分布式缓存网络
### 扩展点
- **新市场支持**: 额外的交易所和地区
- **新LLM提供商**: 额外的AI服务
- **自定义智能体**: 用户定义的分析智能体
- **插件系统**: 第三方集成
- **API网关**: 外部服务访问
---
该架构为全球金融市场分析提供了强大、可扩展的基础,同时保持了未来增强和集成的灵活性。

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,517 @@
# TradingAgents 提示词模板库
## 📚 概述
本文档提供了TradingAgents项目中各种角色的提示词模板您可以直接复制使用或根据需要进行修改。
## 🚀 Google模型集成
TradingAgents完全支持Google Gemini模型。当前配置使用
- **深度思考**: `gemini-2.0-flash` - 用于复杂分析和推理
- **快速思考**: `gemini-2.0-flash` - 用于快速响应和简单任务
**可用模型**
- `gemini-2.0-flash-lite` - 成本效率高,低延迟
- `gemini-2.0-flash` - 平衡性能 ⭐ **当前默认**
- `gemini-2.5-flash-preview-05-20` - 高级自适应思考
- `gemini-2.5-pro-preview-06-05` - 专业级性能
**设置**: 确保设置了`GOOGLE_API_KEY`环境变量。
## 🎯 分析师提示词模板
### 1. 市场分析师 - 中文版
```python
system_message = (
"""您是一位专业的中文市场分析师专门分析股票市场技术指标。您的任务是从以下指标列表中选择最相关的指标最多8个为特定的市场条件或交易策略提供分析。
技术指标分类:
📈 移动平均线类:
- close_50_sma: 50日简单移动平均线 - 中期趋势指标,用于识别趋势方向和动态支撑阻力
- close_200_sma: 200日简单移动平均线 - 长期趋势基准,确认整体市场趋势和金叉死叉设置
- close_10_ema: 10日指数移动平均线 - 短期趋势响应,捕捉快速动量变化和潜在入场点
📊 MACD相关指标
- macd: MACD主线 - 通过EMA差值计算动量寻找交叉和背离作为趋势变化信号
- macds: MACD信号线 - MACD线的EMA平滑与MACD线交叉触发交易信号
- macdh: MACD柱状图 - 显示MACD线与信号线的差距可视化动量强度和早期背离
⚡ 动量指标:
- rsi: 相对强弱指数 - 测量动量以标记超买超卖条件应用70/30阈值并观察背离
📏 波动率指标:
- boll: 布林带中轨 - 20日SMA作为布林带基础充当价格运动的动态基准
- boll_ub: 布林带上轨 - 通常为中轨上方2个标准差信号潜在超买条件和突破区域
- boll_lb: 布林带下轨 - 通常为中轨下方2个标准差指示潜在超卖条件
- atr: 平均真实波幅 - 测量波动率,用于设置止损水平和根据当前市场波动调整仓位
📊 成交量指标:
- vwma: 成交量加权移动平均线 - 结合价格行为和成交量数据确认趋势
分析要求:
1. 选择提供多样化和互补信息的指标,避免冗余
2. 简要解释为什么这些指标适合给定的市场环境
3. 使用确切的指标名称进行工具调用
4. 首先调用get_YFin_data获取生成指标所需的CSV数据
5. 撰写详细且细致的趋势观察报告,避免简单地说"趋势混合"
6. 在报告末尾添加Markdown表格来组织关键要点使其有条理且易于阅读
请用中文提供专业、详细的市场分析。"""
)
```
### 2. 基本面分析师 - 中文版
```python
system_message = (
"""您是一位专业的基本面研究分析师,专门分析公司的基本面信息。您的任务是撰写一份关于公司过去一周基本面信息的综合报告。
分析范围包括:
📊 财务文档分析:资产负债表、利润表、现金流量表
🏢 公司概况:业务模式、竞争优势、管理层质量
💰 基本财务指标PE、PB、ROE、ROA、毛利率、净利率
📈 财务历史趋势:收入增长、利润增长、债务水平变化
👥 内部人士情绪:管理层和内部人士的买卖行为
💼 内部人士交易:重要股东和高管的交易记录
分析要求:
1. 提供尽可能详细的信息,帮助交易者做出明智决策
2. 不要简单地说"趋势混合",要提供详细和细致的分析洞察
3. 重点关注可能影响股价的关键财务指标变化
4. 分析内部人士行为的潜在含义
5. 评估公司的财务健康状况和未来前景
6. 在报告末尾添加Markdown表格来组织关键要点使其有条理且易于阅读
请用中文撰写专业、全面的基本面分析报告。"""
)
```
### 3. 新闻分析师 - 中文版
```python
system_message = (
"""您是一位专业的新闻研究分析师,专门分析过去一周的新闻和趋势。您的任务是撰写一份关于当前世界状况的综合报告,重点关注与交易和宏观经济相关的内容。
分析范围:
🌍 全球宏观经济新闻央行政策、通胀数据、GDP增长、就业数据
📈 金融市场动态:股市表现、债券收益率、汇率变化、商品价格
🏛️ 政策影响:货币政策、财政政策、监管变化、贸易政策
🏭 行业趋势:科技、能源、金融、消费、医疗等重点行业动态
⚡ 突发事件:地缘政治事件、自然灾害、重大公司事件
新闻来源:
- EODHD新闻数据
- Finnhub新闻数据
- Google新闻搜索
- Reddit讨论热点
分析要求:
1. 提供详细和细致的分析洞察,避免简单地说"趋势混合"
2. 重点关注可能影响市场的重要新闻事件
3. 分析新闻事件的潜在市场影响和交易机会
4. 识别市场情绪的变化趋势
5. 评估宏观经济环境对不同资产类别的影响
6. 在报告末尾添加Markdown表格来组织关键要点使其有条理且易于阅读
请用中文撰写专业、全面的新闻分析报告。"""
)
```
### 4. 社交媒体分析师 - 中文版
```python
system_message = (
"""您是一位专业的社交媒体情绪分析师,专门分析社交媒体平台上的投资者情绪和讨论热点。您的任务是撰写一份关于特定股票在社交媒体上情绪和讨论的综合报告。
分析范围:
📱 社交媒体平台Reddit、Twitter、StockTwits、雪球等
💭 情绪分析:正面、负面、中性情绪的分布和变化趋势
🔥 热门话题:最受关注的讨论主题和关键词
👥 用户行为:散户投资者的观点和行为模式
📊 情绪指标:恐惧贪婪指数、看涨看跌比例、讨论量变化
重点关注:
- 投资者对公司基本面的看法
- 对最新财报和新闻的反应
- 技术分析观点和价格预测
- 风险因素和担忧点
- 机构投资者vs散户投资者的观点差异
分析要求:
1. 量化情绪变化趋势,提供具体的数据支持
2. 识别可能影响股价的关键情绪转折点
3. 分析社交媒体情绪与实际股价表现的相关性
4. 不要简单地说"情绪混合",要提供详细的情绪分析
5. 评估社交媒体情绪的可靠性和潜在偏差
6. 在报告末尾添加Markdown表格来组织关键要点使其有条理且易于阅读
请用中文撰写专业、深入的社交媒体情绪分析报告。"""
)
```
## 🔬 研究员提示词模板
### 1. 多头研究员 - 中文版
```python
prompt = f"""您是一位专业的多头分析师,负责为投资该股票建立强有力的论证。您的任务是构建一个基于证据的强有力案例,强调增长潜力、竞争优势和积极的市场指标。
🎯 重点关注领域:
📈 增长潜力:
- 突出公司的市场机会、收入预测和可扩展性
- 分析新产品、新市场、新技术的增长驱动因素
- 评估管理层的执行能力和战略规划
🏆 竞争优势:
- 强调独特产品、强势品牌或主导市场地位等因素
- 分析护城河:技术壁垒、网络效应、规模经济
- 评估公司在行业中的相对竞争地位
📊 积极指标:
- 使用财务健康状况、行业趋势和最新正面新闻作为证据
- 分析估值吸引力和上涨空间
- 识别催化剂事件和积极因素
🛡️ 反驳空头观点:
- 用具体数据和合理推理批判性分析空头论点
- 彻底解决担忧并展示为什么多头观点具有更强的优势
- 提供替代解释和风险缓解措施
💬 辩论风格:
- 以对话式风格呈现论点,直接与空头分析师的观点交锋
- 有效辩论而不仅仅是列举数据
- 保持专业但有说服力的语调
可用资源:
- 市场研究报告:{market_research_report}
- 社交媒体情绪报告:{sentiment_report}
- 最新世界事务新闻:{news_report}
- 公司基本面报告:{fundamentals_report}
- 辩论历史记录:{history}
- 最后的空头论点:{current_response}
- 类似情况的反思和经验教训:{past_memory_str}
请使用这些信息提供令人信服的多头论点,反驳空头的担忧,并进行动态辩论,展示多头立场的优势。您还必须处理反思并从过去的经验教训和错误中学习。
请用中文进行专业、有说服力的多头分析和辩论。"""
```
### 2. 空头研究员 - 中文版
```python
prompt = f"""您是一位专业的空头分析师,负责识别投资该股票的风险和潜在问题。您的任务是构建一个基于证据的谨慎案例,强调风险因素、估值担忧和负面市场指标。
🎯 重点关注领域:
⚠️ 风险因素:
- 识别业务模式、行业或宏观经济的潜在风险
- 分析竞争威胁、技术颠覆、监管风险
- 评估管理层风险和公司治理问题
💰 估值担忧:
- 分析当前估值是否过高,与历史和同行比较
- 识别泡沫迹象和不合理的市场预期
- 评估下行风险和潜在的估值修正
📉 负面指标:
- 使用财务恶化、行业逆风和负面新闻作为证据
- 分析技术指标显示的弱势信号
- 识别可能的催化剂风险事件
🛡️ 反驳多头观点:
- 用具体数据和合理推理质疑多头论点
- 指出多头分析中的盲点和过度乐观
- 提供更保守的情景分析
💬 辩论风格:
- 以对话式风格呈现论点,直接与多头分析师的观点交锋
- 保持理性和客观,避免过度悲观
- 基于事实进行有力反驳
可用资源:
- 市场研究报告:{market_research_report}
- 社交媒体情绪报告:{sentiment_report}
- 最新世界事务新闻:{news_report}
- 公司基本面报告:{fundamentals_report}
- 辩论历史记录:{history}
- 最后的多头论点:{current_response}
- 类似情况的反思和经验教训:{past_memory_str}
请使用这些信息提供令人信服的空头论点,质疑多头的乐观预期,并进行动态辩论,展示空头立场的合理性。您还必须处理反思并从过去的经验教训和错误中学习。
请用中文进行专业、理性的空头分析和辩论。"""
```
## 💼 交易员提示词模板
### 1. 保守型交易员
```python
messages = [
{
"role": "system",
"content": f"""您是一位专业的保守型交易智能体,以风险控制为第一要务。基于团队分析师的综合分析,您需要做出谨慎的投资决策。
🛡️ 风险控制原则:
1. 风险第一,收益第二 - 永远不要冒险超过可承受的损失
2. 严格止损,保护本金 - 设定明确的止损点并严格执行
3. 分散投资,降低风险 - 避免过度集中在单一投资
4. 基于数据,理性决策 - 依据客观分析而非情绪
📊 决策框架:
1. 风险评估:评估潜在损失和概率
2. 收益分析:计算风险调整后的预期收益
3. 仓位管理:确定合适的投资比例
4. 退出策略:设定止损和止盈点
📋 必须包含的要素:
- 风险等级评估(低/中/高)
- 具体的止损点位
- 建议的最大仓位比例
- 详细的风险提示
💭 决策考虑因素:
- 当前市场环境和波动性
- 公司基本面的稳定性
- 技术指标的确认信号
- 宏观经济和行业风险
- 历史经验和教训:{past_memory_str}
请基于综合分析提供谨慎的投资建议,必须以'最终交易建议: **买入/持有/卖出**'结束您的回应,以确认您的建议。
请用中文提供专业、谨慎的交易决策分析。""",
},
context,
]
```
### 2. 激进型交易员
```python
messages = [
{
"role": "system",
"content": f"""您是一位专业的激进型交易智能体,专注于捕捉高收益机会。基于团队分析师的综合分析,您需要做出积极的投资决策。
🚀 增长导向原则:
1. 收益优先,适度风险 - 追求高收益机会,接受相应风险
2. 趋势跟随,动量投资 - 识别并跟随强势趋势
3. 快速行动,抓住机会 - 在机会窗口内果断行动
4. 数据驱动,灵活调整 - 基于市场变化快速调整策略
📈 决策框架:
1. 机会识别:寻找高收益潜力的投资机会
2. 动量分析:评估价格和成交量动量
3. 催化剂评估:识别可能推动股价的因素
4. 时机把握:选择最佳的进入和退出时机
📋 必须包含的要素:
- 收益潜力评估(保守/乐观/激进)
- 关键催化剂因素
- 建议的目标价位
- 动量确认信号
💭 决策考虑因素:
- 技术突破和动量信号
- 基本面改善的催化剂
- 市场情绪和资金流向
- 行业轮动和主题投资机会
- 历史成功经验:{past_memory_str}
请基于综合分析提供积极的投资建议,必须以'最终交易建议: **买入/持有/卖出**'结束您的回应,以确认您的建议。
请用中文提供专业、积极的交易决策分析。""",
},
context,
]
```
### 3. 量化交易员
```python
messages = [
{
"role": "system",
"content": f"""您是一位专业的量化交易智能体,基于数据和模型进行系统化投资决策。您依赖客观的量化指标和统计分析来做出交易决策。
📊 量化分析框架:
1. 技术指标量化RSI、MACD、布林带等指标的数值分析
2. 统计套利:价格偏离均值的统计显著性
3. 动量因子:价格和成交量动量的量化测量
4. 风险模型VaR、夏普比率、最大回撤等风险指标
🔢 决策模型:
- 多因子评分模型:技术面(40%) + 基本面(30%) + 情绪面(20%) + 宏观面(10%)
- 信号强度:强买入(>80分) | 买入(60-80分) | 持有(40-60分) | 卖出(20-40分) | 强卖出(<20分)
- 置信度:基于历史回测和统计显著性
📈 量化指标权重:
技术指标:
- RSI背离 (权重: 15%)
- MACD金叉死叉 (权重: 15%)
- 布林带突破 (权重: 10%)
基本面指标:
- PE/PB相对估值 (权重: 15%)
- 盈利增长趋势 (权重: 15%)
市场情绪:
- 社交媒体情绪得分 (权重: 10%)
- 机构资金流向 (权重: 10%)
宏观因素:
- 行业轮动信号 (权重: 5%)
- 市场整体趋势 (权重: 5%)
📋 输出要求:
- 综合评分0-100分
- 各因子得分明细
- 统计置信度
- 量化风险指标
- 历史回测表现:{past_memory_str}
请基于量化模型提供客观的投资建议,必须以'最终交易建议: **买入/持有/卖出**'结束您的回应。
请用中文提供专业、量化的交易决策分析。""",
},
context,
]
```
## 🔄 反思系统提示词模板
### 1. 详细反思模板
```python
def _get_reflection_prompt(self) -> str:
return """
您是一位专业的金融分析专家,负责审查交易决策/分析并提供全面的逐步分析。
您的目标是对投资决策提供详细洞察,并突出改进机会,严格遵循以下准则:
🔍 1. 推理分析:
- 对于每个交易决策,判断其是否正确。正确的决策会带来收益增加,错误的决策则相反
- 分析每个成功或错误的贡献因素,考虑:
* 市场情报质量和准确性
* 技术指标的有效性和时机
* 技术信号的强度和确认
* 价格走势分析的准确性
* 整体市场数据分析的深度
* 新闻分析的相关性和影响评估
* 社交媒体和情绪分析的可靠性
* 基本面数据分析的全面性
* 在决策过程中各因素的权重分配
📈 2. 改进建议:
- 对于任何错误决策,提出修正方案以最大化收益
- 提供详细的纠正措施或改进清单,包括具体建议
- 例如:在特定日期将决策从持有改为买入
📚 3. 经验总结:
- 总结从成功和失败中学到的经验教训
- 突出这些经验如何适用于未来的交易场景
- 在相似情况之间建立联系,以应用所获得的知识
🎯 4. 关键洞察提取:
- 将总结中的关键洞察提取为不超过1000个token的简洁句子
- 确保浓缩的句子捕捉到经验教训和推理的精髓,便于参考
严格遵循这些指示,确保您的输出详细、准确且可操作。您还将获得市场的客观描述,从价格走势、技术指标、新闻和情绪角度为您的分析提供更多背景。
请用中文提供专业、深入的反思分析。
"""
```
## 🎨 自定义提示词指南
### 1. 提示词结构模板
```python
def create_custom_prompt(
role="分析师",
expertise="市场分析",
style="专业",
language="中文",
risk_level="中等",
output_format="详细报告"
):
return f"""
您是一位{style}的{role},专精于{expertise}。
🎯 角色定位:
- 专业领域:{expertise}
- 分析风格:{style}
- 风险偏好:{risk_level}
- 输出语言:{language}
📋 核心任务:
1. [具体任务1]
2. [具体任务2]
3. [具体任务3]
🔍 分析框架:
- 数据收集:[数据来源和类型]
- 分析方法:[使用的分析工具和方法]
- 风险评估:[风险识别和评估方法]
- 结论形成:[决策逻辑和标准]
📊 输出要求:
- 格式:{output_format}
- 结构:[具体的输出结构要求]
- 重点:[需要重点关注的内容]
- 限制:[需要避免的内容或做法]
💡 注意事项:
- [特殊要求1]
- [特殊要求2]
- [特殊要求3]
请基于以上要求提供专业的{expertise}分析。
"""
```
### 2. 多语言提示词模板
```python
MULTILINGUAL_PROMPTS = {
"zh-CN": {
"role_prefix": "您是一位专业的",
"task_intro": "您的任务是",
"analysis_framework": "分析框架:",
"output_requirements": "输出要求:",
"final_decision": "最终建议:"
},
"en-US": {
"role_prefix": "You are a professional",
"task_intro": "Your task is to",
"analysis_framework": "Analysis Framework:",
"output_requirements": "Output Requirements:",
"final_decision": "Final Recommendation:"
},
"ja-JP": {
"role_prefix": "あなたはプロの",
"task_intro": "あなたの任務は",
"analysis_framework": "分析フレームワーク:",
"output_requirements": "出力要件:",
"final_decision": "最終推奨:"
}
}
```
---
💡 **使用提示**:
1. 复制相应的模板代码
2. 根据需要修改具体内容
3. 在对应的文件中替换原有提示词
4. 测试修改效果
5. 根据结果进一步优化
📝 **自定义建议**:
- 保持提示词的结构化和逻辑性
- 明确指定输出格式和要求
- 包含具体的分析框架和方法
- 考虑不同市场和文化背景
- 定期根据效果反馈优化提示词

View File

@ -0,0 +1,259 @@
# TradingAgents 快速参考卡片
## 🚀 快速开始
### 1. 修改LLM提供商
```python
# 编辑 main.py
config["llm_provider"] = "google" # 或 "openai", "anthropic"
config["backend_url"] = "https://generativelanguage.googleapis.com/v1"
config["deep_think_llm"] = "gemini-2.0-flash"
config["quick_think_llm"] = "gemini-2.0-flash"
```
### 2. 修改辩论轮数
```python
# 编辑 main.py 或 default_config.py
config["max_debate_rounds"] = 3 # 增加到3轮
config["max_risk_discuss_rounds"] = 2 # 风险讨论2轮
```
### 3. 启用/禁用在线工具
```python
config["online_tools"] = True # 启用在线API
config["online_tools"] = False # 使用本地数据
```
## 📁 关键文件位置
| 需要修改的内容 | 文件路径 | 具体位置 |
|---------------|----------|----------|
| **系统配置** | `tradingagents/default_config.py` | 整个文件 |
| **运行时配置** | `main.py` | 第15-22行 |
| **市场分析师提示词** | `tradingagents/agents/analysts/market_analyst.py` | 第24-50行 |
| **基本面分析师提示词** | `tradingagents/agents/analysts/fundamentals_analyst.py` | 第23-26行 |
| **新闻分析师提示词** | `tradingagents/agents/analysts/news_analyst.py` | 第20-23行 |
| **社媒分析师提示词** | `tradingagents/agents/analysts/social_media_analyst.py` | 第19-22行 |
| **多头研究员提示词** | `tradingagents/agents/researchers/bull_researcher.py` | 第25-43行 |
| **空头研究员提示词** | `tradingagents/agents/researchers/bear_researcher.py` | 第25-43行 |
| **交易员提示词** | `tradingagents/agents/trader/trader.py` | 第30-36行 |
| **反思系统提示词** | `tradingagents/graph/reflection.py` | 第15-47行 |
| **缓存配置** | `tradingagents/dataflows/cache_manager.py` | 第20-35行 |
## 🎯 常用修改模板
### 1. 中文化提示词模板
```python
system_message = f"""
您是一位专业的{role_name},具有以下特点:
专业领域:
- {domain_1}
- {domain_2}
- {domain_3}
分析要求:
1. 使用中文进行分析
2. 提供详细的分析理由
3. 包含风险提示
4. 以表格形式总结关键指标
输出格式:
{output_format}
注意事项:
- 避免简单地说"趋势混合"
- 提供具体的数据支持
- 考虑中国市场特色因素
"""
```
### 2. 风险控制模板
```python
system_message = f"""
您是一位风险意识强烈的{role_name}。
风险控制原则:
1. 风险第一,收益第二
2. 严格止损,保护本金
3. 分散投资,降低风险
4. 基于数据,理性决策
必须包含:
- 风险评估等级(低/中/高)
- 建议止损点位
- 最大仓位建议
- 风险提示说明
决策格式:
最终建议: **买入/持有/卖出**
风险等级: **低/中/高**
止损点位: **具体价格**
建议仓位: **百分比**
"""
```
### 3. 技术分析专用模板
```python
system_message = f"""
您是一位专业的技术分析师,专注于以下指标:
核心指标:
- 移动平均线SMA、EMA
- 动量指标RSI、MACD
- 波动率指标布林带、ATR
- 成交量指标VWMA
分析框架:
1. 趋势识别(上升/下降/横盘)
2. 支撑阻力位确定
3. 买卖信号识别
4. 风险收益比计算
输出要求:
- 明确的趋势判断
- 具体的进出场点位
- 技术指标背离分析
- 量价关系分析
"""
```
## ⚙️ 配置参数速查
### LLM配置
```python
"llm_provider": "openai" | "google" | "anthropic"
"deep_think_llm": "模型名称" # 深度思考模型
"quick_think_llm": "模型名称" # 快速思考模型
"backend_url": "API地址"
```
#### Google模型快速参考
```python
# 快速模型: gemini-2.0-flash-lite, gemini-2.0-flash ⭐, gemini-2.5-flash-preview-05-20
# 深度模型: gemini-2.0-flash ⭐, gemini-2.5-flash-preview-05-20, gemini-2.5-pro-preview-06-05
# Google API设置
export GOOGLE_API_KEY="your_key_here"
```
### 辩论配置
```python
"max_debate_rounds": 1-5 # 辩论轮数
"max_risk_discuss_rounds": 1-3 # 风险讨论轮数
"max_recur_limit": 100 # 递归限制
```
### 工具配置
```python
"online_tools": True | False # 是否使用在线工具
"data_cache_dir": "缓存目录路径"
"results_dir": "结果输出目录"
```
### 缓存配置
```python
# 在cache_manager.py中
'us_stock_data': {'ttl_hours': 2} # 美股缓存2小时
'china_stock_data': {'ttl_hours': 1} # A股缓存1小时
```
## 🔧 常用命令
### 测试配置
```bash
# 运行基础测试
cd tests && python test_cache_manager.py
# 运行集成测试
cd tests && python test_integration.py
# 运行性能测试
cd tests && python test_performance.py
```
### 备份与恢复
```bash
# 备份配置文件
cp tradingagents/default_config.py tradingagents/default_config.py.backup
# 备份提示词文件
cp tradingagents/agents/trader/trader.py tradingagents/agents/trader/trader.py.backup
# 恢复文件
cp tradingagents/default_config.py.backup tradingagents/default_config.py
```
### Git管理
```bash
# 查看修改状态
git status
# 提交配置更改
git add tradingagents/default_config.py
git commit -m "feat: 更新LLM配置为Google Gemini"
# 提交提示词更改
git add tradingagents/agents/trader/trader.py
git commit -m "feat: 优化交易员提示词,增加风险控制"
```
## 🚨 注意事项
### ⚠️ 修改前必做
1. **备份文件**: 修改前务必备份原文件
2. **测试环境**: 在测试环境中验证修改效果
3. **版本控制**: 使用Git跟踪所有更改
### ⚠️ 常见错误
1. **忘记重启**: 修改配置后需要重启应用
2. **路径错误**: 确保文件路径正确
3. **语法错误**: Python语法必须正确
4. **编码问题**: 中文内容使用UTF-8编码
### ⚠️ 性能考虑
1. **提示词长度**: 避免过长的提示词(建议<4000 tokens
2. **API调用频率**: 注意API调用限制
3. **缓存设置**: 合理设置缓存TTL时间
## 🆘 故障排除
### 问题:配置不生效
```python
# 解决方案:强制重新加载配置
from tradingagents.dataflows.config import reload_config
reload_config()
```
### 问题:中文显示乱码
```python
# 解决方案确保文件编码为UTF-8
# 在文件开头添加编码声明
# -*- coding: utf-8 -*-
```
### 问题API调用失败
```python
# 解决方案检查API密钥和网络连接
import os
print("OpenAI API Key:", os.getenv("OPENAI_API_KEY", "未设置"))
print("Google API Key:", os.getenv("GOOGLE_API_KEY", "未设置"))
```
### 问题:内存使用过高
```python
# 解决方案:启用缓存清理
config["cache_settings"]["cache_size_limit_mb"] = 500 # 限制缓存大小
config["cache_settings"]["cache_cleanup_interval"] = 1800 # 30分钟清理一次
```
## 📞 获取帮助
1. **查看详细文档**: `docs/configuration_guide.md`
2. **运行测试**: `tests/` 目录下的测试文件
3. **查看示例**: `examples/` 目录(如果有)
4. **GitHub Issues**: 在项目仓库提交问题
---
💡 **提示**: 建议将此文档保存为书签,方便随时查阅!

View File

@ -0,0 +1,334 @@
# TradingAgents 快速开始指南
## 🚀 概述
本指南将帮助您快速开始使用TradingAgents包括新的中国市场功能、数据库集成和多LLM支持。
## ⚡ 快速设置 (5分钟)
### 1. 前置条件
```bash
# 需要Python 3.8+
python --version
# 克隆仓库
git clone https://github.com/your-repo/TradingAgents.git
cd TradingAgents
# 安装依赖
pip install -r requirements.txt
pip install pytdx beautifulsoup4 # 中国市场支持
```
### 2. 环境配置
```bash
# 复制环境模板
cp .env.example .env
# 编辑.env文件填入您的API密钥
nano .env # 或使用您喜欢的编辑器
```
**最小必需配置**:
**仅分析美股时**:
```env
# OpenAI或Google AI (选择一个)
OPENAI_API_KEY=your_openai_api_key_here
# 或者
GOOGLE_API_KEY=your_google_api_key_here
# FinnHub (金融数据必需)
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**分析中国A股或使用百炼LLM时**:
```env
# 百炼 (中国股票或通义千问模型必需)
DASHSCOPE_API_KEY=your_dashscope_api_key_here
# FinnHub (金融数据必需)
FINNHUB_API_KEY=your_finnhub_api_key_here
```
**注意**:
- **百炼API密钥仅在以下情况需要**:
- 分析中国A股股票 (使用通达信数据 + 百炼embeddings)
- 选择百炼作为LLM提供商 (通义千问模型)
- **分析美股使用OpenAI/Google模型时**: 不需要百炼
### 3. 首次运行
```bash
# 启动应用程序
python -m cli.main
# 按照交互式提示操作:
# 1. 选择市场: 美股或中国A股
# 2. 输入股票代码 (如 AAPL 或 000001)
# 3. 选择分析日期
# 4. 选择分析师团队
# 5. 选择LLM提供商 (推荐百炼)
# 6. 运行分析
```
## 🌟 功能概览
### 🇺🇸 美股分析
- **支持代码**: AAPL, SPY, TSLA, NVDA, MSFT 等
- **数据源**: Yahoo Finance
- **格式**: 1-5位字母代码
- **示例**: `AAPL` (苹果公司)
### 🇨🇳 中国A股分析
- **支持交易所**:
- 上交所 (60xxxx): `600036` (招商银行)
- 深交所 (00xxxx): `000001` (平安银行)
- 创业板 (30xxxx): `300001` (科技股)
- 科创板 (68xxxx): `688001` (创新公司)
- **数据源**: 通达信API
- **格式**: 6位数字代码
### 🤖 多LLM支持
- **百炼(DashScope)**: 通义千问模型,中文优化
- **OpenAI**: GPT-4o, GPT-4o-mini, o1, o3系列
- **Google AI**: Gemini 2.0/2.5 Flash系列
- **Anthropic**: Claude 3.5/4系列
## 📋 分步操作演示
### 步骤1: 市场选择
```
? Select Stock Market:
US Stock - Examples: SPY, AAPL, TSLA
China A-Share - Examples: 000001, 600036, 000858
```
### 步骤2: 股票代码输入
```
格式要求: 6位数字代码 (如 600036, 000001)
示例: 000001, 600036, 300001, 688001
? Enter China A-Share ticker symbol: 000001
✅ Valid A-share code: 000001 (will use TongDaXin data source)
```
### 步骤3: 分析配置
```
? Select your research depth:
Light (1 round) - 快速分析
Medium (2 rounds) - 平衡分析
Deep (3 rounds) - 深度分析
? Select your LLM Provider:
DashScope (Alibaba Cloud)
OpenAI
Google AI
Anthropic
```
### 步骤4: 模型选择
```
? Select Your [Quick-Thinking LLM Engine]:
Qwen-Turbo - 快速响应,适合快速任务
Qwen-Plus - 平衡性能和成本
Qwen-Max - 复杂分析的最佳性能
? Select Your [Deep-Thinking LLM Engine]:
Qwen-Plus - 平衡性能和成本 (推荐)
Qwen-Max - 复杂分析的最佳性能
Qwen-Max-LongContext - 超长上下文支持
```
## 🗄️ 数据库设置 (可选)
### 启用高性能缓存
**1. 启动数据库服务**:
```bash
# MongoDB用于持久化存储
docker run -d -p 27017:27017 --name mongodb mongo
# Redis用于高性能缓存
docker run -d -p 6379:6379 --name redis redis
```
**2. 在.env中启用**:
```env
# 启用数据库缓存
MONGODB_ENABLED=true
REDIS_ENABLED=true
# MongoDB配置
MONGODB_HOST=localhost
MONGODB_PORT=27017
MONGODB_DATABASE=tradingagents
# Redis配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
```
**3. 重启应用程序**:
```bash
python -m cli.main
# 系统现在将使用数据库缓存以提高性能
```
## 🔧 配置示例
### 示例1: 使用OpenAI分析美股
```env
OPENAI_API_KEY=your_openai_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI选择**:
- 市场: 美股
- 股票代码: AAPL
- LLM提供商: OpenAI
- 模型: GPT-4o-mini (快速), o1 (深度)
**注意**: 使用OpenAI分析美股时不需要百炼
### 示例2: 使用Google AI分析美股
```env
GOOGLE_API_KEY=your_google_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI选择**:
- 市场: 美股
- 股票代码: TSLA
- LLM提供商: Google AI
- 模型: Gemini 2.0 Flash (快速), Gemini 2.5 Flash (深度)
**注意**: 使用Google AI分析美股时不需要百炼
### 示例3: 中国A股分析 (需要百炼)
```env
DASHSCOPE_API_KEY=your_dashscope_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI选择**:
- 市场: 中国A股
- 股票代码: 000001
- LLM提供商: 百炼
- 模型: qwen-turbo (快速), qwen-plus (深度)
**注意**: 中国股票分析需要百炼API密钥 (通达信数据 + embeddings)
### 示例4: 使用百炼LLM分析美股 (需要百炼)
```env
DASHSCOPE_API_KEY=your_dashscope_key
FINNHUB_API_KEY=your_finnhub_key
```
**CLI选择**:
- 市场: 美股
- 股票代码: SPY
- LLM提供商: 百炼(阿里云)
- 模型: qwen-turbo (快速), qwen-plus (深度)
**注意**: 选择百炼作为LLM提供商时需要百炼API密钥
## 🛠️ 故障排除
### 常见问题
**1. API密钥错误**:
```
错误: Invalid API key
解决方案: 检查.env文件并确保API密钥格式正确
```
**2. 通达信连接问题**:
```
错误: TongDaXin API unavailable
解决方案: 系统自动回退到缓存数据
```
**3. 数据库连接问题**:
```
错误: MongoDB/Redis connection failed
解决方案: 系统自动回退到文件缓存
```
**4. 股票代码格式错误**:
```
错误: Invalid ticker format
解决方案:
- 美股: 使用1-5位字母代码 (AAPL)
- A股: 使用6位数字代码 (000001)
```
### 调试模式
```bash
# 启用调试日志
export TRADINGAGENTS_LOG_LEVEL=DEBUG
python -m cli.main
```
## 📊 示例分析输出
### 美股分析 (AAPL)
```
📈 AAPL (苹果公司) 分析结果
市场: 美国证券交易所
数据源: Yahoo Finance
🔍 技术分析:
- 当前价格: $150.25 (+2.3%)
- RSI: 65.2 (中性偏多)
- 移动平均线: 高于20日和50日均线
💰 基本面分析:
- 市盈率: 28.5
- 营收增长: 8.2% 同比
- 市值: $2.4万亿
📰 新闻情绪: 积极 (0.72/1.0)
🎯 建议: 买入,目标价 $165
```
### 中国A股分析 (000001)
```
📈 000001 (平安银行) 分析结果
市场: 深圳证券交易所
数据源: 通达信API
🔍 技术分析:
- 当前价格: ¥12.85 (+1.8%)
- RSI: 58.3 (中性)
- 成交量: 高于平均水平
💰 基本面分析:
- 市盈率: 5.2
- ROE: 12.8%
- 账面价值: ¥15.20
📰 新闻情绪: 中性 (0.55/1.0)
🎯 建议: 持有,目标价 ¥14.50
```
## 🎯 下一步
### 探索高级功能
1. **自定义提示词**: 修改智能体提示词以适应特定策略
2. **数据库分析**: 分析历史性能
3. **多市场比较**: 比较美股和中国股票
4. **风险管理**: 配置风险参数
### 了解更多
- [配置指南](configuration_guide.md) - 详细配置选项
- [架构指南](architecture_guide.md) - 系统架构概览
- [API文档](api_documentation.md) - API参考
### 获取支持
- GitHub Issues: 报告错误和功能请求
- 文档: 全面的指南和示例
- 社区: 加入讨论和分享策略
---
🎉 **恭喜!** 您现在已经准备好使用TradingAgents分析美股和中国市场了。系统提供智能回退、多LLM支持和企业级缓存以获得最佳性能。

View File

@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""
DashScope (Alibaba Cloud) Configuration Example
阿里云百炼模型配置示例
This example shows how to configure TradingAgents to use DashScope models.
这个示例展示如何配置TradingAgents使用阿里云百炼模型
"""
import os
import sys
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from tradingagents.default_config import DEFAULT_CONFIG
def create_dashscope_config():
"""
Create configuration for DashScope models
创建百炼模型配置
"""
# Copy default config
config = DEFAULT_CONFIG.copy()
# Configure for DashScope
config.update({
# LLM Provider Settings
"llm_provider": "dashscope",
"backend_url": "https://dashscope.aliyuncs.com/api/v1",
# Model Selection
# 模型选择 - 根据需要调整
"deep_think_llm": "qwen-plus", # For complex analysis 复杂分析
"quick_think_llm": "qwen-turbo", # For quick tasks 快速任务
# Optional: Reduce rounds for faster execution
# 可选:减少轮次以加快执行速度
"max_debate_rounds": 1,
"max_risk_discuss_rounds": 1,
# Enable online tools
"online_tools": True,
})
return config
def check_dashscope_setup():
"""
Check if DashScope is properly configured
检查百炼配置是否正确
"""
print("🔍 Checking DashScope Configuration")
print("🔍 检查百炼配置")
print("=" * 50)
# Check API key
api_key = os.getenv('DASHSCOPE_API_KEY')
if api_key:
print(f"✅ DASHSCOPE_API_KEY: {api_key[:10]}...")
else:
print("❌ DASHSCOPE_API_KEY not found in environment variables")
print("❌ 环境变量中未找到 DASHSCOPE_API_KEY")
print("\n💡 To fix this:")
print("💡 解决方法:")
print("1. Get API key from: https://dashscope.aliyun.com/")
print("1. 从以下网址获取API密钥: https://dashscope.aliyun.com/")
print("2. Add to .env file: DASHSCOPE_API_KEY=your_key_here")
print("2. 添加到.env文件: DASHSCOPE_API_KEY=your_key_here")
return False
# Check DashScope package
try:
import dashscope
print("✅ dashscope package installed")
print("✅ dashscope包已安装")
except ImportError:
print("❌ dashscope package not installed")
print("❌ dashscope包未安装")
print("\n💡 To install:")
print("💡 安装方法:")
print("pip install dashscope")
return False
# Check adapter
try:
from tradingagents.llm_adapters.dashscope_adapter import ChatDashScope
print("✅ DashScope adapter available")
print("✅ 百炼适配器可用")
except ImportError:
print("❌ DashScope adapter not available")
print("❌ 百炼适配器不可用")
return False
print("\n🎉 DashScope configuration is ready!")
print("🎉 百炼配置已就绪!")
return True
def test_dashscope_connection():
"""
Test connection to DashScope
测试百炼连接
"""
print("\n🧪 Testing DashScope Connection")
print("🧪 测试百炼连接")
print("=" * 50)
try:
from tradingagents.llm_adapters.dashscope_adapter import ChatDashScope
from langchain_core.messages import HumanMessage
# Create model instance
llm = ChatDashScope(
model="qwen-turbo",
temperature=0.1,
max_tokens=100
)
# Test simple query
test_message = HumanMessage(content="Hello, please respond with 'DashScope connection successful!'")
response = llm.invoke([test_message])
print(f"✅ Connection successful!")
print(f"✅ 连接成功!")
print(f"📝 Response: {response.content}")
print(f"📝 响应: {response.content}")
return True
except Exception as e:
print(f"❌ Connection failed: {str(e)}")
print(f"❌ 连接失败: {str(e)}")
return False
def main():
"""
Main function to demonstrate DashScope configuration
主函数演示百炼配置
"""
print("🚀 DashScope Configuration Example")
print("🚀 百炼配置示例")
print("=" * 50)
# Check setup
if not check_dashscope_setup():
print("\n❌ Please fix the configuration issues above")
print("❌ 请修复上述配置问题")
return
# Test connection
if not test_dashscope_connection():
print("\n❌ Connection test failed")
print("❌ 连接测试失败")
return
# Show configuration
config = create_dashscope_config()
print(f"\n📋 DashScope Configuration:")
print(f"📋 百炼配置:")
print(f" Provider: {config['llm_provider']}")
print(f" Deep Think Model: {config['deep_think_llm']}")
print(f" Quick Think Model: {config['quick_think_llm']}")
print(f" Backend URL: {config['backend_url']}")
print(f"\n💡 Usage Example:")
print(f"💡 使用示例:")
print(f"""
from tradingagents.graph.trading_graph import TradingAgentsGraph
# Create config
config = create_dashscope_config()
# Initialize trading graph
ta = TradingAgentsGraph(config)
# Run analysis
result, decision = ta.propagate("AAPL", "2024-01-15")
print(result)
""")
print(f"\n🎯 Available DashScope Models:")
print(f"🎯 可用的百炼模型:")
models = {
"qwen-turbo": "Fast response, suitable for daily conversations",
"qwen-plus": "Balanced performance and cost",
"qwen-max": "Best performance",
"qwen-max-longcontext": "Supports ultra-long context"
}
for model, description in models.items():
print(f"{model}: {description}")
if __name__ == "__main__":
main()

View File

@ -32,3 +32,29 @@ dependencies = [
"typing-extensions>=4.14.0",
"yfinance>=0.2.63",
]
[project.optional-dependencies]
chinese = [
"pytdx",
"dashscope",
]
database = [
"pymongo>=4.0.0",
]
visualization = [
"streamlit",
"plotly",
]
development = [
"pytest",
"black",
"flake8",
]
all = [
"pytdx",
"dashscope",
"pymongo>=4.0.0",
"streamlit",
"plotly",
]

20
requirements-optional.txt Normal file
View File

@ -0,0 +1,20 @@
# Optional Dependencies for TradingAgents
# Install specific groups as needed:
# pip install -r requirements-optional.txt
# Chinese dependencies
pytdx # TongDaXin API for Chinese stock real-time data
dashscope # Alibaba Cloud LLM support
# Database dependencies
pymongo # MongoDB database support for token usage storage
# Visualization dependencies
streamlit # Web app framework
plotly # Interactive plotting
# Development dependencies
pytest # Testing framework
black # Code formatter
flake8 # Code linter

View File

@ -24,3 +24,10 @@ rich
questionary
langchain_anthropic
langchain-google-genai
# New dependencies from Chinese version
dashscope
streamlit
plotly
pytdx # TongDaXin API for Chinese stock real-time data
pymongo # MongoDB database support for token usage storage

View File

@ -0,0 +1,154 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
def create_china_market_analyst(llm, toolkit):
"""创建中国市场分析师"""
def china_market_analyst_node(state):
current_date = state["trade_date"]
ticker = state["company_of_interest"]
# 中国股票分析工具
tools = [
toolkit.get_china_stock_data,
toolkit.get_china_market_overview,
toolkit.get_YFin_data, # 备用数据源
]
system_message = (
"""您是一位专业的中国股市分析师专门分析A股、港股等中国资本市场。您具备深厚的中国股市知识和丰富的本土投资经验。
您的专业领域包括
1. **A股市场分析**: 深度理解A股的独特性包括涨跌停制度T+1交易融资融券等
2. **中国经济政策**: 熟悉货币政策财政政策对股市的影响机制
3. **行业板块轮动**: 掌握中国特色的板块轮动规律和热点切换
4. **监管环境**: 了解证监会政策退市制度注册制等监管变化
5. **市场情绪**: 理解中国投资者的行为特征和情绪波动
分析重点
- **技术面分析**: 使用通达信数据进行精确的技术指标分析
- **基本面分析**: 结合中国会计准则和财报特点进行分析
- **政策面分析**: 评估政策变化对个股和板块的影响
- **资金面分析**: 分析北向资金融资融券大宗交易等资金流向
- **市场风格**: 判断当前是成长风格还是价值风格占优
中国股市特色考虑
- 涨跌停板限制对交易策略的影响
- ST股票的特殊风险和机会
- 科创板创业板的差异化分析
- 国企改革混改等主题投资机会
- 中美关系地缘政治对中概股的影响
请基于通达信API提供的实时数据和技术指标结合中国股市的特殊性撰写专业的中文分析报告
确保在报告末尾附上Markdown表格总结关键发现和投资建议"""
)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"您是一位专业的AI助手与其他分析师协作进行股票分析。"
" 使用提供的工具获取和分析数据。"
" 如果您无法完全回答,没关系;其他分析师会补充您的分析。"
" 专注于您的专业领域,提供高质量的分析见解。"
" 您可以访问以下工具:{tool_names}\n{system_message}"
"当前分析日期:{current_date},分析标的:{ticker}。请用中文撰写所有分析内容。",
),
MessagesPlaceholder(variable_name="messages"),
]
)
prompt = prompt.partial(system_message=system_message)
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
prompt = prompt.partial(current_date=current_date)
prompt = prompt.partial(ticker=ticker)
chain = prompt | llm.bind_tools(tools)
result = chain.invoke(state["messages"])
report = ""
if len(result.tool_calls) == 0:
report = result.content
return {
"messages": [result],
"china_market_report": report,
"sender": "ChinaMarketAnalyst",
}
return china_market_analyst_node
def create_china_stock_screener(llm, toolkit):
"""创建中国股票筛选器"""
def china_stock_screener_node(state):
current_date = state["trade_date"]
tools = [
toolkit.get_china_market_overview,
]
system_message = (
"""您是一位专业的中国股票筛选专家负责从A股市场中筛选出具有投资价值的股票。
筛选维度包括
1. **基本面筛选**:
- 财务指标ROEROA净利润增长率营收增长率
- 估值指标PEPBPEGPS比率
- 财务健康资产负债率流动比率速动比率
2. **技术面筛选**:
- 趋势指标均线系统MACDKDJ
- 动量指标RSI威廉指标CCI
- 成交量指标量价关系换手率
3. **市场面筛选**:
- 资金流向主力资金净流入北向资金偏好
- 机构持仓基金重仓社保持仓QFII持仓
- 市场热度概念板块活跃度题材炒作程度
4. **政策面筛选**:
- 政策受益国家政策扶持行业
- 改革红利国企改革混改标的
- 监管影响监管政策变化的影响
筛选策略
- **价值投资**: 低估值高分红稳定增长
- **成长投资**: 高增长新兴行业技术创新
- **主题投资**: 政策驱动事件催化概念炒作
- **周期投资**: 经济周期行业周期季节性
请基于当前市场环境和政策背景提供专业的股票筛选建议"""
)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"您是一位专业的股票筛选专家。"
" 使用提供的工具分析市场概况。"
" 您可以访问以下工具:{tool_names}\n{system_message}"
"当前日期:{current_date}。请用中文撰写分析内容。",
),
MessagesPlaceholder(variable_name="messages"),
]
)
prompt = prompt.partial(system_message=system_message)
prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
prompt = prompt.partial(current_date=current_date)
chain = prompt | llm.bind_tools(tools)
result = chain.invoke(state["messages"])
return {
"messages": [result],
"stock_screening_report": result.content,
"sender": "ChinaStockScreener",
}
return china_stock_screener_node

View File

@ -1,25 +1,114 @@
import chromadb
from chromadb.config import Settings
from openai import OpenAI
import os
# Import DashScope if available
try:
import dashscope
from dashscope import TextEmbedding
DASHSCOPE_AVAILABLE = True
except ImportError:
DASHSCOPE_AVAILABLE = False
dashscope = None
TextEmbedding = None
class FinancialSituationMemory:
def __init__(self, name, config):
if config["backend_url"] == "http://localhost:11434/v1":
self.config = config
self.llm_provider = config.get("llm_provider", "openai").lower()
# Configure embedding model and client based on LLM provider
if (self.llm_provider == "dashscope" or
"dashscope" in self.llm_provider or
"alibaba" in self.llm_provider):
# Check if DashScope is available and configured
dashscope_key = os.getenv('DASHSCOPE_API_KEY')
openai_key = os.getenv('OPENAI_API_KEY')
if DASHSCOPE_AVAILABLE and dashscope_key:
# Use DashScope embeddings
self.embedding = "text-embedding-v3"
self.client = None # DashScope doesn't need OpenAI client
dashscope.api_key = dashscope_key
print("✅ Using DashScope embeddings")
elif openai_key:
# Fallback to OpenAI embeddings
print("⚠️ DashScope not available or not configured, falling back to OpenAI embeddings")
self.embedding = "text-embedding-3-small"
self.client = OpenAI(base_url=config.get("backend_url", "https://api.openai.com/v1"))
else:
# No valid API keys available
raise ValueError(
"No valid API keys found. For DashScope provider, please set either:\n"
"1. DASHSCOPE_API_KEY (preferred for DashScope embeddings)\n"
"2. OPENAI_API_KEY (fallback for OpenAI embeddings)\n"
"Install dashscope package: pip install dashscope"
)
elif self.llm_provider == "google":
# Google AI uses DashScope embedding if available, otherwise OpenAI
dashscope_key = os.getenv('DASHSCOPE_API_KEY')
openai_key = os.getenv('OPENAI_API_KEY')
if dashscope_key and DASHSCOPE_AVAILABLE:
self.embedding = "text-embedding-v3"
self.client = None
dashscope.api_key = dashscope_key
print("💡 Google AI using DashScope embedding service")
elif openai_key:
self.embedding = "text-embedding-3-small"
self.client = OpenAI(base_url=config.get("backend_url", "https://api.openai.com/v1"))
print("⚠️ Google AI falling back to OpenAI embedding service")
else:
raise ValueError(
"No valid API keys found for Google AI embeddings. Please set either:\n"
"1. DASHSCOPE_API_KEY (preferred)\n"
"2. OPENAI_API_KEY (fallback)"
)
elif config["backend_url"] == "http://localhost:11434/v1":
self.embedding = "nomic-embed-text"
self.client = OpenAI(base_url=config["backend_url"])
else:
self.embedding = "text-embedding-3-small"
self.client = OpenAI(base_url=config["backend_url"])
self.client = OpenAI(base_url=config["backend_url"])
self.chroma_client = chromadb.Client(Settings(allow_reset=True))
self.situation_collection = self.chroma_client.create_collection(name=name)
# Try to get existing collection, create new one if it doesn't exist
try:
self.situation_collection = self.chroma_client.get_collection(name=name)
except Exception:
# Collection doesn't exist, create new one
self.situation_collection = self.chroma_client.create_collection(name=name)
def get_embedding(self, text):
"""Get OpenAI embedding for a text"""
response = self.client.embeddings.create(
model=self.embedding, input=text
)
return response.data[0].embedding
"""Get embedding for a text using the configured provider"""
if ((self.llm_provider == "dashscope" or
"dashscope" in self.llm_provider or
"alibaba" in self.llm_provider or
(self.llm_provider == "google" and self.client is None)) and
DASHSCOPE_AVAILABLE and self.client is None):
# Use DashScope embedding model
try:
response = TextEmbedding.call(
model=self.embedding,
input=text
)
if response.status_code == 200:
return response.output['embeddings'][0]['embedding']
else:
raise Exception(f"DashScope embedding error: {response.code} - {response.message}")
except Exception as e:
raise Exception(f"Error getting DashScope embedding: {str(e)}")
else:
# Use OpenAI-compatible embedding model
response = self.client.embeddings.create(
model=self.embedding, input=text
)
return response.data[0].embedding
def add_situations(self, situations_and_advice):
"""Add financial situations and their corresponding advice. Parameter is a list of tuples (situation, rec)"""
@ -45,7 +134,7 @@ class FinancialSituationMemory:
)
def get_memories(self, current_situation, n_matches=1):
"""Find matching recommendations using OpenAI embeddings"""
"""Find matching recommendations using embeddings"""
query_embedding = self.get_embedding(current_situation)
results = self.situation_collection.query(

View File

@ -0,0 +1,295 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
股票数据API接口
提供便捷的股票数据获取接口支持完整的降级机制
"""
import sys
import os
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
# 添加dataflows目录到路径
dataflows_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'dataflows')
if dataflows_path not in sys.path:
sys.path.append(dataflows_path)
try:
from stock_data_service import get_stock_data_service
SERVICE_AVAILABLE = True
except ImportError as e:
print(f"⚠️ 股票数据服务不可用: {e}")
SERVICE_AVAILABLE = False
def get_stock_info(stock_code: str) -> Dict[str, Any]:
"""
获取单个股票的基础信息
Args:
stock_code: 股票代码 '000001'
Returns:
Dict: 股票基础信息
Example:
>>> info = get_stock_info('000001')
>>> print(info['name']) # 平安银行
"""
if not SERVICE_AVAILABLE:
return {
'error': '股票数据服务不可用',
'code': stock_code,
'suggestion': '请检查服务配置'
}
service = get_stock_data_service()
result = service.get_stock_basic_info(stock_code)
if result is None:
return {
'error': f'未找到股票{stock_code}的信息',
'code': stock_code,
'suggestion': '请检查股票代码是否正确'
}
return result
def get_all_stocks() -> List[Dict[str, Any]]:
"""
获取所有股票的基础信息
Returns:
List[Dict]: 所有股票的基础信息列表
Example:
>>> stocks = get_all_stocks()
>>> print(f"共有{len(stocks)}只股票")
"""
if not SERVICE_AVAILABLE:
return [{
'error': '股票数据服务不可用',
'suggestion': '请检查服务配置'
}]
service = get_stock_data_service()
result = service.get_stock_basic_info()
if result is None or (isinstance(result, dict) and 'error' in result):
return [{
'error': '无法获取股票列表',
'suggestion': '请检查网络连接和数据库配置'
}]
return result if isinstance(result, list) else [result]
def get_stock_data(stock_code: str, start_date: str = None, end_date: str = None) -> str:
"""
获取股票历史数据带降级机制
Args:
stock_code: 股票代码
start_date: 开始日期格式YYYY-MM-DD默认为30天前
end_date: 结束日期格式YYYY-MM-DD默认为今天
Returns:
str: 股票数据的字符串表示或错误信息
Example:
>>> data = get_stock_data('000001', '2024-01-01', '2024-01-31')
>>> print(data)
"""
if not SERVICE_AVAILABLE:
return "❌ 股票数据服务不可用,请检查服务配置"
# 设置默认日期
if end_date is None:
end_date = datetime.now().strftime('%Y-%m-%d')
if start_date is None:
start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
service = get_stock_data_service()
return service.get_stock_data_with_fallback(stock_code, start_date, end_date)
def search_stocks(keyword: str) -> List[Dict[str, Any]]:
"""
根据关键词搜索股票
Args:
keyword: 搜索关键词股票代码或名称的一部分
Returns:
List[Dict]: 匹配的股票信息列表
Example:
>>> results = search_stocks('平安')
>>> for stock in results:
... print(f"{stock['code']}: {stock['name']}")
"""
all_stocks = get_all_stocks()
if not all_stocks or (len(all_stocks) == 1 and 'error' in all_stocks[0]):
return all_stocks
# 搜索匹配的股票
matches = []
keyword_lower = keyword.lower()
for stock in all_stocks:
if 'error' in stock:
continue
code = stock.get('code', '').lower()
name = stock.get('name', '').lower()
if keyword_lower in code or keyword_lower in name:
matches.append(stock)
return matches
def get_market_summary() -> Dict[str, Any]:
"""
获取市场概览信息
Returns:
Dict: 市场统计信息
Example:
>>> summary = get_market_summary()
>>> print(f"沪市股票数量: {summary['shanghai_count']}")
"""
all_stocks = get_all_stocks()
if not all_stocks or (len(all_stocks) == 1 and 'error' in all_stocks[0]):
return {
'error': '无法获取市场数据',
'suggestion': '请检查网络连接和数据库配置'
}
# 统计市场信息
shanghai_count = 0
shenzhen_count = 0
category_stats = {}
for stock in all_stocks:
if 'error' in stock:
continue
market = stock.get('market', '')
category = stock.get('category', '未知')
if market == '上海':
shanghai_count += 1
elif market == '深圳':
shenzhen_count += 1
category_stats[category] = category_stats.get(category, 0) + 1
return {
'total_count': len([s for s in all_stocks if 'error' not in s]),
'shanghai_count': shanghai_count,
'shenzhen_count': shenzhen_count,
'category_stats': category_stats,
'data_source': all_stocks[0].get('source', 'unknown') if all_stocks else 'unknown',
'updated_at': datetime.now().isoformat()
}
def check_service_status() -> Dict[str, Any]:
"""
检查服务状态
Returns:
Dict: 服务状态信息
Example:
>>> status = check_service_status()
>>> print(f"MongoDB状态: {status['mongodb_status']}")
"""
if not SERVICE_AVAILABLE:
return {
'service_available': False,
'error': '股票数据服务不可用',
'suggestion': '请检查服务配置和依赖'
}
service = get_stock_data_service()
# 检查MongoDB状态
mongodb_status = 'disconnected'
if service.db_manager and service.db_manager.mongodb_db:
try:
# 尝试执行一个简单的查询来测试连接
service.db_manager.mongodb_db.list_collection_names()
mongodb_status = 'connected'
except Exception:
mongodb_status = 'error'
# 检查通达信API状态
tdx_status = 'unavailable'
if service.tdx_provider:
try:
# 尝试获取一个股票名称来测试API
test_name = service.tdx_provider._get_stock_name('000001')
if test_name and test_name != '000001':
tdx_status = 'available'
else:
tdx_status = 'limited'
except Exception:
tdx_status = 'error'
return {
'service_available': True,
'mongodb_status': mongodb_status,
'tdx_api_status': tdx_status,
'enhanced_fetcher_available': hasattr(service, '_get_from_tdx_api'),
'fallback_available': True,
'checked_at': datetime.now().isoformat()
}
# 便捷的别名函数
get_stock = get_stock_info # 别名
get_stocks = get_all_stocks # 别名
search = search_stocks # 别名
status = check_service_status # 别名
if __name__ == '__main__':
# 简单的命令行测试
print("🔍 股票数据API测试")
print("=" * 50)
# 检查服务状态
print("\n📊 服务状态检查:")
status_info = check_service_status()
for key, value in status_info.items():
print(f" {key}: {value}")
# 测试获取单个股票信息
print("\n🏢 获取平安银行信息:")
stock_info = get_stock_info('000001')
if 'error' not in stock_info:
print(f" 代码: {stock_info.get('code')}")
print(f" 名称: {stock_info.get('name')}")
print(f" 市场: {stock_info.get('market')}")
print(f" 类别: {stock_info.get('category')}")
print(f" 数据源: {stock_info.get('source')}")
else:
print(f" 错误: {stock_info.get('error')}")
# 测试搜索功能
print("\n🔍 搜索'平安'相关股票:")
search_results = search_stocks('平安')
for i, stock in enumerate(search_results[:3]): # 只显示前3个结果
if 'error' not in stock:
print(f" {i+1}. {stock.get('code')}: {stock.get('name')}")
# 测试市场概览
print("\n📈 市场概览:")
summary = get_market_summary()
if 'error' not in summary:
print(f" 总股票数: {summary.get('total_count')}")
print(f" 沪市股票: {summary.get('shanghai_count')}")
print(f" 深市股票: {summary.get('shenzhen_count')}")
print(f" 数据源: {summary.get('data_source')}")
else:
print(f" 错误: {summary.get('error')}")

View File

@ -0,0 +1,13 @@
"""
配置管理模块
"""
from .config_manager import config_manager, token_tracker, ModelConfig, PricingConfig, UsageRecord
__all__ = [
'config_manager',
'token_tracker',
'ModelConfig',
'PricingConfig',
'UsageRecord'
]

View File

@ -0,0 +1,574 @@
#!/usr/bin/env python3
"""
配置管理器
管理API密钥模型配置费率设置等
"""
import json
import os
from datetime import datetime
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, asdict
from pathlib import Path
from dotenv import load_dotenv
try:
from .mongodb_storage import MongoDBStorage
MONGODB_AVAILABLE = True
except ImportError:
MONGODB_AVAILABLE = False
MongoDBStorage = None
@dataclass
class ModelConfig:
"""模型配置"""
provider: str # 供应商dashscope, openai, google, etc.
model_name: str # 模型名称
api_key: str # API密钥
base_url: Optional[str] = None # 自定义API地址
max_tokens: int = 4000 # 最大token数
temperature: float = 0.7 # 温度参数
enabled: bool = True # 是否启用
@dataclass
class PricingConfig:
"""定价配置"""
provider: str # 供应商
model_name: str # 模型名称
input_price_per_1k: float # 输入token价格每1000个token
output_price_per_1k: float # 输出token价格每1000个token
currency: str = "CNY" # 货币单位
@dataclass
class UsageRecord:
"""使用记录"""
timestamp: str # 时间戳
provider: str # 供应商
model_name: str # 模型名称
input_tokens: int # 输入token数
output_tokens: int # 输出token数
cost: float # 成本
session_id: str # 会话ID
analysis_type: str # 分析类型
class ConfigManager:
"""配置管理器"""
def __init__(self, config_dir: str = "config"):
self.config_dir = Path(config_dir)
self.config_dir.mkdir(exist_ok=True)
self.models_file = self.config_dir / "models.json"
self.pricing_file = self.config_dir / "pricing.json"
self.usage_file = self.config_dir / "usage.json"
self.settings_file = self.config_dir / "settings.json"
# 加载.env文件保持向后兼容
self._load_env_file()
# 初始化MongoDB存储如果可用
self.mongodb_storage = None
self._init_mongodb_storage()
self._init_default_configs()
def _load_env_file(self):
"""加载.env文件保持向后兼容"""
# 尝试从项目根目录加载.env文件
project_root = Path(__file__).parent.parent.parent
env_file = project_root / ".env"
if env_file.exists():
load_dotenv(env_file, override=True)
def _get_env_api_key(self, provider: str) -> str:
"""从环境变量获取API密钥"""
env_key_map = {
"dashscope": "DASHSCOPE_API_KEY",
"openai": "OPENAI_API_KEY",
"google": "GOOGLE_API_KEY",
"anthropic": "ANTHROPIC_API_KEY",
"deepseek": "DEEPSEEK_API_KEY"
}
env_key = env_key_map.get(provider.lower())
if env_key:
return os.getenv(env_key, "")
return ""
def _init_mongodb_storage(self):
"""初始化MongoDB存储"""
if not MONGODB_AVAILABLE:
return
# 检查是否启用MongoDB存储
use_mongodb = os.getenv("USE_MONGODB_STORAGE", "false").lower() == "true"
if not use_mongodb:
return
try:
connection_string = os.getenv("MONGODB_CONNECTION_STRING")
database_name = os.getenv("MONGODB_DATABASE_NAME", "tradingagents")
self.mongodb_storage = MongoDBStorage(
connection_string=connection_string,
database_name=database_name
)
if self.mongodb_storage.is_connected():
print("✅ MongoDB存储已启用")
else:
self.mongodb_storage = None
print("⚠️ MongoDB连接失败将使用JSON文件存储")
except Exception as e:
print(f"❌ MongoDB初始化失败: {e}")
self.mongodb_storage = None
def _init_default_configs(self):
"""初始化默认配置"""
# 默认模型配置
if not self.models_file.exists():
default_models = [
ModelConfig(
provider="dashscope",
model_name="qwen-turbo",
api_key="",
max_tokens=4000,
temperature=0.7
),
ModelConfig(
provider="dashscope",
model_name="qwen-plus-latest",
api_key="",
max_tokens=8000,
temperature=0.7
),
ModelConfig(
provider="openai",
model_name="gpt-3.5-turbo",
api_key="",
max_tokens=4000,
temperature=0.7,
enabled=False
),
ModelConfig(
provider="openai",
model_name="gpt-4",
api_key="",
max_tokens=8000,
temperature=0.7,
enabled=False
),
ModelConfig(
provider="google",
model_name="gemini-pro",
api_key="",
max_tokens=4000,
temperature=0.7,
enabled=False
)
]
self.save_models(default_models)
# 默认定价配置
if not self.pricing_file.exists():
default_pricing = [
# 阿里百炼定价 (人民币)
PricingConfig("dashscope", "qwen-turbo", 0.002, 0.006, "CNY"),
PricingConfig("dashscope", "qwen-plus-latest", 0.004, 0.012, "CNY"),
PricingConfig("dashscope", "qwen-max", 0.02, 0.06, "CNY"),
# OpenAI定价 (美元)
PricingConfig("openai", "gpt-3.5-turbo", 0.0015, 0.002, "USD"),
PricingConfig("openai", "gpt-4", 0.03, 0.06, "USD"),
PricingConfig("openai", "gpt-4-turbo", 0.01, 0.03, "USD"),
# Google定价 (美元)
PricingConfig("google", "gemini-pro", 0.00025, 0.0005, "USD"),
PricingConfig("google", "gemini-pro-vision", 0.00025, 0.0005, "USD"),
]
self.save_pricing(default_pricing)
# 默认设置
if not self.settings_file.exists():
# 导入默认数据目录配置
import os
default_data_dir = os.path.join(os.path.expanduser("~"), "Documents", "TradingAgents", "data")
default_settings = {
"default_provider": "dashscope",
"default_model": "qwen-turbo",
"enable_cost_tracking": True,
"cost_alert_threshold": 100.0, # 成本警告阈值
"currency_preference": "CNY",
"auto_save_usage": True,
"max_usage_records": 10000,
"data_dir": default_data_dir, # 数据目录配置
"cache_dir": os.path.join(default_data_dir, "cache"), # 缓存目录
"results_dir": os.path.join(os.path.expanduser("~"), "Documents", "TradingAgents", "results"), # 结果目录
"auto_create_dirs": True # 自动创建目录
}
self.save_settings(default_settings)
def load_models(self) -> List[ModelConfig]:
"""加载模型配置,优先使用.env中的API密钥"""
try:
with open(self.models_file, 'r', encoding='utf-8') as f:
data = json.load(f)
models = [ModelConfig(**item) for item in data]
# 合并.env中的API密钥优先级更高
for model in models:
env_api_key = self._get_env_api_key(model.provider)
if env_api_key:
model.api_key = env_api_key
# 如果.env中有API密钥自动启用该模型
if not model.enabled:
model.enabled = True
return models
except Exception as e:
print(f"加载模型配置失败: {e}")
return []
def save_models(self, models: List[ModelConfig]):
"""保存模型配置"""
try:
data = [asdict(model) for model in models]
with open(self.models_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存模型配置失败: {e}")
def load_pricing(self) -> List[PricingConfig]:
"""加载定价配置"""
try:
with open(self.pricing_file, 'r', encoding='utf-8') as f:
data = json.load(f)
return [PricingConfig(**item) for item in data]
except Exception as e:
print(f"加载定价配置失败: {e}")
return []
def save_pricing(self, pricing: List[PricingConfig]):
"""保存定价配置"""
try:
data = [asdict(price) for price in pricing]
with open(self.pricing_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存定价配置失败: {e}")
def load_usage_records(self) -> List[UsageRecord]:
"""加载使用记录"""
try:
if not self.usage_file.exists():
return []
with open(self.usage_file, 'r', encoding='utf-8') as f:
data = json.load(f)
return [UsageRecord(**item) for item in data]
except Exception as e:
print(f"加载使用记录失败: {e}")
return []
def save_usage_records(self, records: List[UsageRecord]):
"""保存使用记录"""
try:
data = [asdict(record) for record in records]
with open(self.usage_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存使用记录失败: {e}")
def add_usage_record(self, provider: str, model_name: str, input_tokens: int,
output_tokens: int, session_id: str, analysis_type: str = "stock_analysis"):
"""添加使用记录"""
# 计算成本
cost = self.calculate_cost(provider, model_name, input_tokens, output_tokens)
record = UsageRecord(
timestamp=datetime.now().isoformat(),
provider=provider,
model_name=model_name,
input_tokens=input_tokens,
output_tokens=output_tokens,
cost=cost,
session_id=session_id,
analysis_type=analysis_type
)
# 优先使用MongoDB存储
if self.mongodb_storage and self.mongodb_storage.is_connected():
success = self.mongodb_storage.save_usage_record(record)
if success:
return record
else:
print("⚠️ MongoDB保存失败回退到JSON文件存储")
# 回退到JSON文件存储
records = self.load_usage_records()
records.append(record)
# 限制记录数量
settings = self.load_settings()
max_records = settings.get("max_usage_records", 10000)
if len(records) > max_records:
records = records[-max_records:]
self.save_usage_records(records)
return record
def calculate_cost(self, provider: str, model_name: str, input_tokens: int, output_tokens: int) -> float:
"""计算使用成本"""
pricing_configs = self.load_pricing()
for pricing in pricing_configs:
if pricing.provider == provider and pricing.model_name == model_name:
input_cost = (input_tokens / 1000) * pricing.input_price_per_1k
output_cost = (output_tokens / 1000) * pricing.output_price_per_1k
return round(input_cost + output_cost, 6)
return 0.0
def load_settings(self) -> Dict[str, Any]:
"""加载设置,合并.env中的配置"""
try:
with open(self.settings_file, 'r', encoding='utf-8') as f:
settings = json.load(f)
except Exception as e:
print(f"加载设置失败: {e}")
settings = {}
# 合并.env中的其他配置
env_settings = {
"finnhub_api_key": os.getenv("FINNHUB_API_KEY", ""),
"reddit_client_id": os.getenv("REDDIT_CLIENT_ID", ""),
"reddit_client_secret": os.getenv("REDDIT_CLIENT_SECRET", ""),
"reddit_user_agent": os.getenv("REDDIT_USER_AGENT", ""),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", ""),
"log_level": os.getenv("TRADINGAGENTS_LOG_LEVEL", "INFO"),
"data_dir": os.getenv("TRADINGAGENTS_DATA_DIR", ""), # 数据目录环境变量
"cache_dir": os.getenv("TRADINGAGENTS_CACHE_DIR", ""), # 缓存目录环境变量
}
# 只有当环境变量存在且不为空时才覆盖
for key, value in env_settings.items():
if value:
settings[key] = value
return settings
def get_env_config_status(self) -> Dict[str, Any]:
"""获取.env配置状态"""
return {
"env_file_exists": (Path(__file__).parent.parent.parent / ".env").exists(),
"api_keys": {
"dashscope": bool(os.getenv("DASHSCOPE_API_KEY")),
"openai": bool(os.getenv("OPENAI_API_KEY")),
"google": bool(os.getenv("GOOGLE_API_KEY")),
"anthropic": bool(os.getenv("ANTHROPIC_API_KEY")),
"finnhub": bool(os.getenv("FINNHUB_API_KEY")),
},
"other_configs": {
"reddit_configured": bool(os.getenv("REDDIT_CLIENT_ID") and os.getenv("REDDIT_CLIENT_SECRET")),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"),
"log_level": os.getenv("TRADINGAGENTS_LOG_LEVEL", "INFO"),
}
}
def save_settings(self, settings: Dict[str, Any]):
"""保存设置"""
try:
with open(self.settings_file, 'w', encoding='utf-8') as f:
json.dump(settings, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存设置失败: {e}")
def get_enabled_models(self) -> List[ModelConfig]:
"""获取启用的模型"""
models = self.load_models()
return [model for model in models if model.enabled and model.api_key]
def get_model_by_name(self, provider: str, model_name: str) -> Optional[ModelConfig]:
"""根据名称获取模型配置"""
models = self.load_models()
for model in models:
if model.provider == provider and model.model_name == model_name:
return model
return None
def get_usage_statistics(self, days: int = 30) -> Dict[str, Any]:
"""获取使用统计"""
# 优先使用MongoDB获取统计
if self.mongodb_storage and self.mongodb_storage.is_connected():
try:
# 从MongoDB获取基础统计
stats = self.mongodb_storage.get_usage_statistics(days)
# 获取供应商统计
provider_stats = self.mongodb_storage.get_provider_statistics(days)
if stats:
stats["provider_stats"] = provider_stats
stats["records_count"] = stats.get("total_requests", 0)
return stats
except Exception as e:
print(f"⚠️ MongoDB统计获取失败回退到JSON文件: {e}")
# 回退到JSON文件统计
records = self.load_usage_records()
# 过滤最近N天的记录
from datetime import datetime, timedelta
cutoff_date = datetime.now() - timedelta(days=days)
recent_records = []
for record in records:
try:
record_date = datetime.fromisoformat(record.timestamp)
if record_date >= cutoff_date:
recent_records.append(record)
except:
continue
# 统计数据
total_cost = sum(record.cost for record in recent_records)
total_input_tokens = sum(record.input_tokens for record in recent_records)
total_output_tokens = sum(record.output_tokens for record in recent_records)
# 按供应商统计
provider_stats = {}
for record in recent_records:
if record.provider not in provider_stats:
provider_stats[record.provider] = {
"cost": 0,
"input_tokens": 0,
"output_tokens": 0,
"requests": 0
}
provider_stats[record.provider]["cost"] += record.cost
provider_stats[record.provider]["input_tokens"] += record.input_tokens
provider_stats[record.provider]["output_tokens"] += record.output_tokens
provider_stats[record.provider]["requests"] += 1
return {
"period_days": days,
"total_cost": round(total_cost, 4),
"total_input_tokens": total_input_tokens,
"total_output_tokens": total_output_tokens,
"total_requests": len(recent_records),
"provider_stats": provider_stats,
"records_count": len(recent_records)
}
def get_data_dir(self) -> str:
"""获取数据目录路径"""
settings = self.load_settings()
data_dir = settings.get("data_dir")
if not data_dir:
# 如果没有配置,使用默认路径
data_dir = os.path.join(os.path.expanduser("~"), "Documents", "TradingAgents", "data")
return data_dir
def set_data_dir(self, data_dir: str):
"""设置数据目录路径"""
settings = self.load_settings()
settings["data_dir"] = data_dir
# 同时更新缓存目录
settings["cache_dir"] = os.path.join(data_dir, "cache")
self.save_settings(settings)
# 如果启用自动创建目录,则创建目录
if settings.get("auto_create_dirs", True):
self.ensure_directories_exist()
def ensure_directories_exist(self):
"""确保必要的目录存在"""
settings = self.load_settings()
directories = [
settings.get("data_dir"),
settings.get("cache_dir"),
settings.get("results_dir"),
os.path.join(settings.get("data_dir", ""), "finnhub_data"),
os.path.join(settings.get("data_dir", ""), "finnhub_data", "news_data"),
os.path.join(settings.get("data_dir", ""), "finnhub_data", "insider_sentiment"),
os.path.join(settings.get("data_dir", ""), "finnhub_data", "insider_transactions")
]
for directory in directories:
if directory and not os.path.exists(directory):
try:
os.makedirs(directory, exist_ok=True)
print(f"✅ 创建目录: {directory}")
except Exception as e:
print(f"❌ 创建目录失败 {directory}: {e}")
class TokenTracker:
"""Token使用跟踪器"""
def __init__(self, config_manager: ConfigManager):
self.config_manager = config_manager
def track_usage(self, provider: str, model_name: str, input_tokens: int,
output_tokens: int, session_id: str = None, analysis_type: str = "stock_analysis"):
"""跟踪Token使用"""
if session_id is None:
session_id = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# 检查是否启用成本跟踪
settings = self.config_manager.load_settings()
if not settings.get("enable_cost_tracking", True):
return None
# 添加使用记录
record = self.config_manager.add_usage_record(
provider=provider,
model_name=model_name,
input_tokens=input_tokens,
output_tokens=output_tokens,
session_id=session_id,
analysis_type=analysis_type
)
# 检查成本警告
self._check_cost_alert(record.cost)
return record
def _check_cost_alert(self, current_cost: float):
"""检查成本警告"""
settings = self.config_manager.load_settings()
threshold = settings.get("cost_alert_threshold", 100.0)
# 获取今日总成本
today_stats = self.config_manager.get_usage_statistics(1)
total_today = today_stats["total_cost"]
if total_today >= threshold:
print(f"⚠️ 成本警告: 今日成本已达到 ¥{total_today:.4f},超过阈值 ¥{threshold}")
def get_session_cost(self, session_id: str) -> float:
"""获取会话成本"""
records = self.config_manager.load_usage_records()
session_cost = sum(record.cost for record in records if record.session_id == session_id)
return session_cost
def estimate_cost(self, provider: str, model_name: str, estimated_input_tokens: int,
estimated_output_tokens: int) -> float:
"""估算成本"""
return self.config_manager.calculate_cost(
provider, model_name, estimated_input_tokens, estimated_output_tokens
)
# 全局配置管理器实例
config_manager = ConfigManager()
token_tracker = TokenTracker(config_manager)

View File

@ -0,0 +1,119 @@
#!/usr/bin/env python3
"""
数据库配置管理模块
统一管理MongoDB和Redis的连接配置
"""
import os
from typing import Dict, Any, Optional
class DatabaseConfig:
"""数据库配置管理类"""
@staticmethod
def get_mongodb_config() -> Dict[str, Any]:
"""
获取MongoDB配置
Returns:
Dict[str, Any]: MongoDB配置字典
Raises:
ValueError: 当必要的配置未设置时
"""
connection_string = os.getenv('MONGODB_CONNECTION_STRING')
if not connection_string:
raise ValueError(
"MongoDB连接字符串未配置。请设置环境变量 MONGODB_CONNECTION_STRING\n"
"例如: MONGODB_CONNECTION_STRING=mongodb://localhost:27017/"
)
return {
'connection_string': connection_string,
'database': os.getenv('MONGODB_DATABASE', 'tradingagents'),
'auth_source': os.getenv('MONGODB_AUTH_SOURCE', 'admin')
}
@staticmethod
def get_redis_config() -> Dict[str, Any]:
"""
获取Redis配置
Returns:
Dict[str, Any]: Redis配置字典
Raises:
ValueError: 当必要的配置未设置时
"""
# 优先使用连接字符串
connection_string = os.getenv('REDIS_CONNECTION_STRING')
if connection_string:
return {
'connection_string': connection_string,
'database': int(os.getenv('REDIS_DATABASE', 0))
}
# 使用分离的配置参数
host = os.getenv('REDIS_HOST')
port = os.getenv('REDIS_PORT')
if not host or not port:
raise ValueError(
"Redis连接配置未完整设置。请设置以下环境变量之一\n"
"1. REDIS_CONNECTION_STRING=redis://localhost:6379/0\n"
"2. REDIS_HOST + REDIS_PORT (例如: REDIS_HOST=localhost, REDIS_PORT=6379)"
)
return {
'host': host,
'port': int(port),
'password': os.getenv('REDIS_PASSWORD'),
'database': int(os.getenv('REDIS_DATABASE', 0))
}
@staticmethod
def validate_config() -> Dict[str, bool]:
"""
验证数据库配置是否完整
Returns:
Dict[str, bool]: 验证结果
"""
result = {
'mongodb_valid': False,
'redis_valid': False
}
try:
DatabaseConfig.get_mongodb_config()
result['mongodb_valid'] = True
except ValueError:
pass
try:
DatabaseConfig.get_redis_config()
result['redis_valid'] = True
except ValueError:
pass
return result
@staticmethod
def get_config_status() -> str:
"""
获取配置状态的友好描述
Returns:
str: 配置状态描述
"""
validation = DatabaseConfig.validate_config()
if validation['mongodb_valid'] and validation['redis_valid']:
return "✅ 所有数据库配置正常"
elif validation['mongodb_valid']:
return "⚠️ MongoDB配置正常Redis配置缺失"
elif validation['redis_valid']:
return "⚠️ Redis配置正常MongoDB配置缺失"
else:
return "❌ 数据库配置缺失,请检查环境变量"

View File

@ -0,0 +1,360 @@
#!/usr/bin/env python3
"""
智能数据库管理器
自动检测MongoDB和Redis可用性提供降级方案
使用项目现有的.env配置
"""
import logging
import os
from pathlib import Path
from typing import Dict, Any, Optional, Tuple
class DatabaseManager:
"""智能数据库管理器"""
def __init__(self):
self.logger = logging.getLogger(__name__)
# 加载.env配置
self._load_env_config()
# 数据库连接状态
self.mongodb_available = False
self.redis_available = False
self.mongodb_client = None
self.redis_client = None
# 检测数据库可用性
self._detect_databases()
# 初始化连接
self._initialize_connections()
self.logger.info(f"数据库管理器初始化完成 - MongoDB: {self.mongodb_available}, Redis: {self.redis_available}")
def _load_env_config(self):
"""从.env文件加载配置"""
# 尝试加载python-dotenv
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
self.logger.info("python-dotenv未安装直接读取环境变量")
# 读取启用开关
self.mongodb_enabled = os.getenv("MONGODB_ENABLED", "false").lower() == "true"
self.redis_enabled = os.getenv("REDIS_ENABLED", "false").lower() == "true"
# 从环境变量读取MongoDB配置
self.mongodb_config = {
"enabled": self.mongodb_enabled,
"host": os.getenv("MONGODB_HOST", "localhost"),
"port": int(os.getenv("MONGODB_PORT", "27017")),
"username": os.getenv("MONGODB_USERNAME"),
"password": os.getenv("MONGODB_PASSWORD"),
"database": os.getenv("MONGODB_DATABASE", "tradingagents"),
"auth_source": os.getenv("MONGODB_AUTH_SOURCE", "admin"),
"timeout": 2000
}
# 从环境变量读取Redis配置
self.redis_config = {
"enabled": self.redis_enabled,
"host": os.getenv("REDIS_HOST", "localhost"),
"port": int(os.getenv("REDIS_PORT", "6379")),
"password": os.getenv("REDIS_PASSWORD"),
"db": int(os.getenv("REDIS_DB", "0")),
"timeout": 2
}
self.logger.info(f"MongoDB启用: {self.mongodb_enabled}")
self.logger.info(f"Redis启用: {self.redis_enabled}")
if self.mongodb_enabled:
self.logger.info(f"MongoDB配置: {self.mongodb_config['host']}:{self.mongodb_config['port']}")
if self.redis_enabled:
self.logger.info(f"Redis配置: {self.redis_config['host']}:{self.redis_config['port']}")
def _detect_mongodb(self) -> Tuple[bool, str]:
"""检测MongoDB是否可用"""
# 首先检查是否启用
if not self.mongodb_enabled:
return False, "MongoDB未启用 (MONGODB_ENABLED=false)"
try:
import pymongo
from pymongo import MongoClient
# 构建连接参数
connect_kwargs = {
"host": self.mongodb_config["host"],
"port": self.mongodb_config["port"],
"serverSelectionTimeoutMS": self.mongodb_config["timeout"],
"connectTimeoutMS": self.mongodb_config["timeout"]
}
# 如果有用户名和密码,添加认证
if self.mongodb_config["username"] and self.mongodb_config["password"]:
connect_kwargs.update({
"username": self.mongodb_config["username"],
"password": self.mongodb_config["password"],
"authSource": self.mongodb_config["auth_source"]
})
client = MongoClient(**connect_kwargs)
# 测试连接
client.server_info()
client.close()
return True, "MongoDB连接成功"
except ImportError:
return False, "pymongo未安装"
except Exception as e:
return False, f"MongoDB连接失败: {str(e)}"
def _detect_redis(self) -> Tuple[bool, str]:
"""检测Redis是否可用"""
# 首先检查是否启用
if not self.redis_enabled:
return False, "Redis未启用 (REDIS_ENABLED=false)"
try:
import redis
# 构建连接参数
connect_kwargs = {
"host": self.redis_config["host"],
"port": self.redis_config["port"],
"db": self.redis_config["db"],
"socket_timeout": self.redis_config["timeout"],
"socket_connect_timeout": self.redis_config["timeout"]
}
# 如果有密码,添加密码
if self.redis_config["password"]:
connect_kwargs["password"] = self.redis_config["password"]
client = redis.Redis(**connect_kwargs)
# 测试连接
client.ping()
return True, "Redis连接成功"
except ImportError:
return False, "redis未安装"
except Exception as e:
return False, f"Redis连接失败: {str(e)}"
def _detect_databases(self):
"""检测所有数据库"""
self.logger.info("开始检测数据库可用性...")
# 检测MongoDB
mongodb_available, mongodb_msg = self._detect_mongodb()
self.mongodb_available = mongodb_available
if mongodb_available:
self.logger.info(f"✅ MongoDB: {mongodb_msg}")
else:
self.logger.info(f"❌ MongoDB: {mongodb_msg}")
# 检测Redis
redis_available, redis_msg = self._detect_redis()
self.redis_available = redis_available
if redis_available:
self.logger.info(f"✅ Redis: {redis_msg}")
else:
self.logger.info(f"❌ Redis: {redis_msg}")
# 更新配置
self._update_config_based_on_detection()
def _update_config_based_on_detection(self):
"""根据检测结果更新配置"""
# 确定缓存后端
if self.redis_available:
self.primary_backend = "redis"
elif self.mongodb_available:
self.primary_backend = "mongodb"
else:
self.primary_backend = "file"
self.logger.info(f"主要缓存后端: {self.primary_backend}")
def _initialize_connections(self):
"""初始化数据库连接"""
# 初始化MongoDB连接
if self.mongodb_available:
try:
import pymongo
# 构建连接参数
connect_kwargs = {
"host": self.mongodb_config["host"],
"port": self.mongodb_config["port"],
"serverSelectionTimeoutMS": self.mongodb_config["timeout"]
}
# 如果有用户名和密码,添加认证
if self.mongodb_config["username"] and self.mongodb_config["password"]:
connect_kwargs.update({
"username": self.mongodb_config["username"],
"password": self.mongodb_config["password"],
"authSource": self.mongodb_config["auth_source"]
})
self.mongodb_client = pymongo.MongoClient(**connect_kwargs)
self.logger.info("MongoDB客户端初始化成功")
except Exception as e:
self.logger.error(f"MongoDB客户端初始化失败: {e}")
self.mongodb_available = False
# 初始化Redis连接
if self.redis_available:
try:
import redis
# 构建连接参数
connect_kwargs = {
"host": self.redis_config["host"],
"port": self.redis_config["port"],
"db": self.redis_config["db"],
"socket_timeout": self.redis_config["timeout"]
}
# 如果有密码,添加密码
if self.redis_config["password"]:
connect_kwargs["password"] = self.redis_config["password"]
self.redis_client = redis.Redis(**connect_kwargs)
self.logger.info("Redis客户端初始化成功")
except Exception as e:
self.logger.error(f"Redis客户端初始化失败: {e}")
self.redis_available = False
def get_mongodb_client(self):
"""获取MongoDB客户端"""
if self.mongodb_available and self.mongodb_client:
return self.mongodb_client
return None
def get_redis_client(self):
"""获取Redis客户端"""
if self.redis_available and self.redis_client:
return self.redis_client
return None
def is_mongodb_available(self) -> bool:
"""检查MongoDB是否可用"""
return self.mongodb_available
def is_redis_available(self) -> bool:
"""检查Redis是否可用"""
return self.redis_available
def is_database_available(self) -> bool:
"""检查是否有任何数据库可用"""
return self.mongodb_available or self.redis_available
def get_cache_backend(self) -> str:
"""获取当前缓存后端"""
return self.primary_backend
def get_config(self) -> Dict[str, Any]:
"""获取配置信息"""
return {
"mongodb": self.mongodb_config,
"redis": self.redis_config,
"primary_backend": self.primary_backend,
"mongodb_available": self.mongodb_available,
"redis_available": self.redis_available
}
def get_status_report(self) -> Dict[str, Any]:
"""获取状态报告"""
return {
"database_available": self.is_database_available(),
"mongodb": {
"available": self.mongodb_available,
"host": self.mongodb_config["host"],
"port": self.mongodb_config["port"]
},
"redis": {
"available": self.redis_available,
"host": self.redis_config["host"],
"port": self.redis_config["port"]
},
"cache_backend": self.get_cache_backend(),
"fallback_enabled": True # 总是启用降级
}
def get_cache_stats(self) -> Dict[str, Any]:
"""获取缓存统计信息"""
stats = {
"mongodb_available": self.mongodb_available,
"redis_available": self.redis_available,
"redis_keys": 0,
"redis_memory": "N/A"
}
# Redis统计
if self.redis_available and self.redis_client:
try:
info = self.redis_client.info()
stats["redis_keys"] = self.redis_client.dbsize()
stats["redis_memory"] = info.get("used_memory_human", "N/A")
except Exception as e:
self.logger.error(f"获取Redis统计失败: {e}")
return stats
def cache_clear_pattern(self, pattern: str) -> int:
"""清理匹配模式的缓存"""
cleared_count = 0
if self.redis_available and self.redis_client:
try:
keys = self.redis_client.keys(pattern)
if keys:
cleared_count += self.redis_client.delete(*keys)
except Exception as e:
self.logger.error(f"Redis缓存清理失败: {e}")
return cleared_count
# 全局数据库管理器实例
_database_manager = None
def get_database_manager() -> DatabaseManager:
"""获取全局数据库管理器实例"""
global _database_manager
if _database_manager is None:
_database_manager = DatabaseManager()
return _database_manager
def is_mongodb_available() -> bool:
"""检查MongoDB是否可用"""
return get_database_manager().is_mongodb_available()
def is_redis_available() -> bool:
"""检查Redis是否可用"""
return get_database_manager().is_redis_available()
def get_cache_backend() -> str:
"""获取当前缓存后端"""
return get_database_manager().get_cache_backend()
def get_mongodb_client():
"""获取MongoDB客户端"""
return get_database_manager().get_mongodb_client()
def get_redis_client():
"""获取Redis客户端"""
return get_database_manager().get_redis_client()

View File

@ -0,0 +1,285 @@
#!/usr/bin/env python3
"""
MongoDB存储适配器
用于将token使用记录存储到MongoDB数据库
"""
import os
from datetime import datetime
from typing import Dict, List, Optional, Any
from dataclasses import asdict
from .config_manager import UsageRecord
try:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
MONGODB_AVAILABLE = True
except ImportError:
MONGODB_AVAILABLE = False
MongoClient = None
class MongoDBStorage:
"""MongoDB存储适配器"""
def __init__(self, connection_string: str = None, database_name: str = "tradingagents"):
if not MONGODB_AVAILABLE:
raise ImportError("pymongo is not installed. Please install it with: pip install pymongo")
# 修复硬编码问题 - 如果没有提供连接字符串且环境变量也未设置,则抛出错误
self.connection_string = connection_string or os.getenv("MONGODB_CONNECTION_STRING")
if not self.connection_string:
raise ValueError(
"MongoDB连接字符串未配置。请通过以下方式之一进行配置\n"
"1. 设置环境变量 MONGODB_CONNECTION_STRING\n"
"2. 在初始化时传入 connection_string 参数\n"
"例如: MONGODB_CONNECTION_STRING=mongodb://localhost:27017/"
)
self.database_name = database_name
self.collection_name = "token_usage"
self.client = None
self.db = None
self.collection = None
self._connected = False
# 尝试连接
self._connect()
def _connect(self):
"""连接到MongoDB"""
try:
self.client = MongoClient(
self.connection_string,
serverSelectionTimeoutMS=5000 # 5秒超时
)
# 测试连接
self.client.admin.command('ping')
self.db = self.client[self.database_name]
self.collection = self.db[self.collection_name]
# 创建索引以提高查询性能
self._create_indexes()
self._connected = True
print(f"✅ MongoDB连接成功: {self.database_name}.{self.collection_name}")
except (ConnectionFailure, ServerSelectionTimeoutError) as e:
print(f"❌ MongoDB连接失败: {e}")
print("将使用本地JSON文件存储")
self._connected = False
except Exception as e:
print(f"❌ MongoDB初始化失败: {e}")
self._connected = False
def _create_indexes(self):
"""创建数据库索引"""
try:
# 创建复合索引
self.collection.create_index([
("timestamp", -1), # 按时间倒序
("provider", 1),
("model_name", 1)
])
# 创建会话ID索引
self.collection.create_index("session_id")
# 创建分析类型索引
self.collection.create_index("analysis_type")
except Exception as e:
print(f"创建MongoDB索引失败: {e}")
def is_connected(self) -> bool:
"""检查是否连接到MongoDB"""
return self._connected
def save_usage_record(self, record: UsageRecord) -> bool:
"""保存单个使用记录到MongoDB"""
if not self._connected:
return False
try:
# 转换为字典格式
record_dict = asdict(record)
# 添加MongoDB特有的字段
record_dict['_created_at'] = datetime.now()
# 插入记录
result = self.collection.insert_one(record_dict)
if result.inserted_id:
return True
else:
print("MongoDB插入失败未返回插入ID")
return False
except Exception as e:
print(f"保存记录到MongoDB失败: {e}")
return False
def load_usage_records(self, limit: int = 10000, days: int = None) -> List[UsageRecord]:
"""从MongoDB加载使用记录"""
if not self._connected:
return []
try:
# 构建查询条件
query = {}
if days:
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=days)
query['timestamp'] = {'$gte': cutoff_date.isoformat()}
# 查询记录,按时间倒序
cursor = self.collection.find(query).sort('timestamp', -1).limit(limit)
records = []
for doc in cursor:
# 移除MongoDB特有的字段
doc.pop('_id', None)
doc.pop('_created_at', None)
# 转换为UsageRecord对象
try:
record = UsageRecord(**doc)
records.append(record)
except Exception as e:
print(f"解析记录失败: {e}, 记录: {doc}")
continue
return records
except Exception as e:
print(f"从MongoDB加载记录失败: {e}")
return []
def get_usage_statistics(self, days: int = 30) -> Dict[str, Any]:
"""从MongoDB获取使用统计"""
if not self._connected:
return {}
try:
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=days)
# 聚合查询
pipeline = [
{
'$match': {
'timestamp': {'$gte': cutoff_date.isoformat()}
}
},
{
'$group': {
'_id': None,
'total_cost': {'$sum': '$cost'},
'total_input_tokens': {'$sum': '$input_tokens'},
'total_output_tokens': {'$sum': '$output_tokens'},
'total_requests': {'$sum': 1}
}
}
]
result = list(self.collection.aggregate(pipeline))
if result:
stats = result[0]
return {
'period_days': days,
'total_cost': round(stats.get('total_cost', 0), 4),
'total_input_tokens': stats.get('total_input_tokens', 0),
'total_output_tokens': stats.get('total_output_tokens', 0),
'total_requests': stats.get('total_requests', 0)
}
else:
return {
'period_days': days,
'total_cost': 0,
'total_input_tokens': 0,
'total_output_tokens': 0,
'total_requests': 0
}
except Exception as e:
print(f"获取MongoDB统计失败: {e}")
return {}
def get_provider_statistics(self, days: int = 30) -> Dict[str, Dict[str, Any]]:
"""按供应商获取统计信息"""
if not self._connected:
return {}
try:
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=days)
# 按供应商聚合
pipeline = [
{
'$match': {
'timestamp': {'$gte': cutoff_date.isoformat()}
}
},
{
'$group': {
'_id': '$provider',
'cost': {'$sum': '$cost'},
'input_tokens': {'$sum': '$input_tokens'},
'output_tokens': {'$sum': '$output_tokens'},
'requests': {'$sum': 1}
}
}
]
results = list(self.collection.aggregate(pipeline))
provider_stats = {}
for result in results:
provider = result['_id']
provider_stats[provider] = {
'cost': round(result.get('cost', 0), 4),
'input_tokens': result.get('input_tokens', 0),
'output_tokens': result.get('output_tokens', 0),
'requests': result.get('requests', 0)
}
return provider_stats
except Exception as e:
print(f"获取供应商统计失败: {e}")
return {}
def cleanup_old_records(self, days: int = 90) -> int:
"""清理旧记录"""
if not self._connected:
return 0
try:
from datetime import timedelta
cutoff_date = datetime.now() - timedelta(days=days)
result = self.collection.delete_many({
'timestamp': {'$lt': cutoff_date.isoformat()}
})
deleted_count = result.deleted_count
if deleted_count > 0:
print(f"清理了 {deleted_count} 条超过 {days} 天的记录")
return deleted_count
except Exception as e:
print(f"清理旧记录失败: {e}")
return 0
def close(self):
"""关闭MongoDB连接"""
if self.client:
self.client.close()
self._connected = False
print("MongoDB连接已关闭")

View File

@ -0,0 +1,383 @@
#!/usr/bin/env python3
"""
自适应缓存系统
根据数据库可用性自动选择最佳缓存策略
"""
import os
import json
import pickle
import hashlib
import logging
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, Optional, Union
import pandas as pd
from ..config.database_manager import get_database_manager
class AdaptiveCacheSystem:
"""自适应缓存系统"""
def __init__(self, cache_dir: str = "data/cache"):
self.logger = logging.getLogger(__name__)
# 获取数据库管理器
self.db_manager = get_database_manager()
# 设置缓存目录
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(parents=True, exist_ok=True)
# 获取配置
self.config = self.db_manager.get_config()
self.cache_config = self.config["cache"]
# 初始化缓存后端
self.primary_backend = self.cache_config["primary_backend"]
self.fallback_enabled = self.cache_config["fallback_enabled"]
self.logger.info(f"自适应缓存系统初始化 - 主要后端: {self.primary_backend}")
def _get_cache_key(self, symbol: str, start_date: str = "", end_date: str = "",
data_source: str = "default", data_type: str = "stock_data") -> str:
"""生成缓存键"""
key_data = f"{symbol}_{start_date}_{end_date}_{data_source}_{data_type}"
return hashlib.md5(key_data.encode()).hexdigest()
def _get_ttl_seconds(self, symbol: str, data_type: str = "stock_data") -> int:
"""获取TTL秒数"""
# 判断市场类型
if len(symbol) == 6 and symbol.isdigit():
market = "china"
else:
market = "us"
# 获取TTL配置
ttl_key = f"{market}_{data_type}"
ttl_seconds = self.cache_config["ttl_settings"].get(ttl_key, 7200)
return ttl_seconds
def _is_cache_valid(self, cache_time: datetime, ttl_seconds: int) -> bool:
"""检查缓存是否有效"""
if cache_time is None:
return False
expiry_time = cache_time + timedelta(seconds=ttl_seconds)
return datetime.now() < expiry_time
def _save_to_file(self, cache_key: str, data: Any, metadata: Dict) -> bool:
"""保存到文件缓存"""
try:
cache_file = self.cache_dir / f"{cache_key}.pkl"
cache_data = {
'data': data,
'metadata': metadata,
'timestamp': datetime.now(),
'backend': 'file'
}
with open(cache_file, 'wb') as f:
pickle.dump(cache_data, f)
self.logger.debug(f"文件缓存保存成功: {cache_key}")
return True
except Exception as e:
self.logger.error(f"文件缓存保存失败: {e}")
return False
def _load_from_file(self, cache_key: str) -> Optional[Dict]:
"""从文件缓存加载"""
try:
cache_file = self.cache_dir / f"{cache_key}.pkl"
if not cache_file.exists():
return None
with open(cache_file, 'rb') as f:
cache_data = pickle.load(f)
self.logger.debug(f"文件缓存加载成功: {cache_key}")
return cache_data
except Exception as e:
self.logger.error(f"文件缓存加载失败: {e}")
return None
def _save_to_redis(self, cache_key: str, data: Any, metadata: Dict, ttl_seconds: int) -> bool:
"""保存到Redis缓存"""
redis_client = self.db_manager.get_redis_client()
if not redis_client:
return False
try:
cache_data = {
'data': data,
'metadata': metadata,
'timestamp': datetime.now().isoformat(),
'backend': 'redis'
}
serialized_data = pickle.dumps(cache_data)
redis_client.setex(cache_key, ttl_seconds, serialized_data)
self.logger.debug(f"Redis缓存保存成功: {cache_key}")
return True
except Exception as e:
self.logger.error(f"Redis缓存保存失败: {e}")
return False
def _load_from_redis(self, cache_key: str) -> Optional[Dict]:
"""从Redis缓存加载"""
redis_client = self.db_manager.get_redis_client()
if not redis_client:
return None
try:
serialized_data = redis_client.get(cache_key)
if not serialized_data:
return None
cache_data = pickle.loads(serialized_data)
# 转换时间戳
if isinstance(cache_data['timestamp'], str):
cache_data['timestamp'] = datetime.fromisoformat(cache_data['timestamp'])
self.logger.debug(f"Redis缓存加载成功: {cache_key}")
return cache_data
except Exception as e:
self.logger.error(f"Redis缓存加载失败: {e}")
return None
def _save_to_mongodb(self, cache_key: str, data: Any, metadata: Dict, ttl_seconds: int) -> bool:
"""保存到MongoDB缓存"""
mongodb_client = self.db_manager.get_mongodb_client()
if not mongodb_client:
return False
try:
db = mongodb_client.tradingagents
collection = db.cache
# 序列化数据
if isinstance(data, pd.DataFrame):
serialized_data = data.to_json()
data_type = 'dataframe'
else:
serialized_data = pickle.dumps(data).hex()
data_type = 'pickle'
cache_doc = {
'_id': cache_key,
'data': serialized_data,
'data_type': data_type,
'metadata': metadata,
'timestamp': datetime.now(),
'expires_at': datetime.now() + timedelta(seconds=ttl_seconds),
'backend': 'mongodb'
}
collection.replace_one({'_id': cache_key}, cache_doc, upsert=True)
self.logger.debug(f"MongoDB缓存保存成功: {cache_key}")
return True
except Exception as e:
self.logger.error(f"MongoDB缓存保存失败: {e}")
return False
def _load_from_mongodb(self, cache_key: str) -> Optional[Dict]:
"""从MongoDB缓存加载"""
mongodb_client = self.db_manager.get_mongodb_client()
if not mongodb_client:
return None
try:
db = mongodb_client.tradingagents
collection = db.cache
doc = collection.find_one({'_id': cache_key})
if not doc:
return None
# 检查是否过期
if doc.get('expires_at') and doc['expires_at'] < datetime.now():
collection.delete_one({'_id': cache_key})
return None
# 反序列化数据
if doc['data_type'] == 'dataframe':
data = pd.read_json(doc['data'])
else:
data = pickle.loads(bytes.fromhex(doc['data']))
cache_data = {
'data': data,
'metadata': doc['metadata'],
'timestamp': doc['timestamp'],
'backend': 'mongodb'
}
self.logger.debug(f"MongoDB缓存加载成功: {cache_key}")
return cache_data
except Exception as e:
self.logger.error(f"MongoDB缓存加载失败: {e}")
return None
def save_data(self, symbol: str, data: Any, start_date: str = "", end_date: str = "",
data_source: str = "default", data_type: str = "stock_data") -> str:
"""保存数据到缓存"""
# 生成缓存键
cache_key = self._get_cache_key(symbol, start_date, end_date, data_source, data_type)
# 准备元数据
metadata = {
'symbol': symbol,
'start_date': start_date,
'end_date': end_date,
'data_source': data_source,
'data_type': data_type
}
# 获取TTL
ttl_seconds = self._get_ttl_seconds(symbol, data_type)
# 根据主要后端保存
success = False
if self.primary_backend == "redis":
success = self._save_to_redis(cache_key, data, metadata, ttl_seconds)
elif self.primary_backend == "mongodb":
success = self._save_to_mongodb(cache_key, data, metadata, ttl_seconds)
elif self.primary_backend == "file":
success = self._save_to_file(cache_key, data, metadata)
# 如果主要后端失败,使用降级策略
if not success and self.fallback_enabled:
self.logger.warning(f"主要后端({self.primary_backend})保存失败,使用文件缓存降级")
success = self._save_to_file(cache_key, data, metadata)
if success:
self.logger.info(f"数据缓存成功: {symbol} -> {cache_key} (后端: {self.primary_backend})")
else:
self.logger.error(f"数据缓存失败: {symbol}")
return cache_key
def load_data(self, cache_key: str) -> Optional[Any]:
"""从缓存加载数据"""
cache_data = None
# 根据主要后端加载
if self.primary_backend == "redis":
cache_data = self._load_from_redis(cache_key)
elif self.primary_backend == "mongodb":
cache_data = self._load_from_mongodb(cache_key)
elif self.primary_backend == "file":
cache_data = self._load_from_file(cache_key)
# 如果主要后端失败,尝试降级
if not cache_data and self.fallback_enabled:
self.logger.debug(f"主要后端({self.primary_backend})加载失败,尝试文件缓存")
cache_data = self._load_from_file(cache_key)
if not cache_data:
return None
# 检查缓存是否有效仅对文件缓存数据库缓存有自己的TTL机制
if cache_data.get('backend') == 'file':
symbol = cache_data['metadata'].get('symbol', '')
data_type = cache_data['metadata'].get('data_type', 'stock_data')
ttl_seconds = self._get_ttl_seconds(symbol, data_type)
if not self._is_cache_valid(cache_data['timestamp'], ttl_seconds):
self.logger.debug(f"文件缓存已过期: {cache_key}")
return None
return cache_data['data']
def find_cached_data(self, symbol: str, start_date: str = "", end_date: str = "",
data_source: str = "default", data_type: str = "stock_data") -> Optional[str]:
"""查找缓存的数据"""
cache_key = self._get_cache_key(symbol, start_date, end_date, data_source, data_type)
# 检查缓存是否存在且有效
if self.load_data(cache_key) is not None:
return cache_key
return None
def get_cache_stats(self) -> Dict[str, Any]:
"""获取缓存统计信息"""
stats = {
'primary_backend': self.primary_backend,
'fallback_enabled': self.fallback_enabled,
'database_available': self.db_manager.is_database_available(),
'mongodb_available': self.db_manager.is_mongodb_available(),
'redis_available': self.db_manager.is_redis_available(),
'file_cache_directory': str(self.cache_dir),
'file_cache_count': len(list(self.cache_dir.glob("*.pkl"))),
}
# Redis统计
redis_client = self.db_manager.get_redis_client()
if redis_client:
try:
redis_info = redis_client.info()
stats['redis_memory_used'] = redis_info.get('used_memory_human', 'N/A')
stats['redis_keys'] = redis_client.dbsize()
except:
stats['redis_status'] = 'Error'
# MongoDB统计
mongodb_client = self.db_manager.get_mongodb_client()
if mongodb_client:
try:
db = mongodb_client.tradingagents
stats['mongodb_cache_count'] = db.cache.count_documents({})
except:
stats['mongodb_status'] = 'Error'
return stats
def clear_expired_cache(self):
"""清理过期缓存"""
self.logger.info("开始清理过期缓存...")
# 清理文件缓存
cleared_files = 0
for cache_file in self.cache_dir.glob("*.pkl"):
try:
with open(cache_file, 'rb') as f:
cache_data = pickle.load(f)
symbol = cache_data['metadata'].get('symbol', '')
data_type = cache_data['metadata'].get('data_type', 'stock_data')
ttl_seconds = self._get_ttl_seconds(symbol, data_type)
if not self._is_cache_valid(cache_data['timestamp'], ttl_seconds):
cache_file.unlink()
cleared_files += 1
except Exception as e:
self.logger.error(f"清理缓存文件失败 {cache_file}: {e}")
self.logger.info(f"文件缓存清理完成,删除 {cleared_files} 个过期文件")
# MongoDB会自动清理过期文档通过expires_at字段
# Redis会自动清理过期键
# 全局缓存系统实例
_cache_system = None
def get_cache_system() -> AdaptiveCacheSystem:
"""获取全局自适应缓存系统实例"""
global _cache_system
if _cache_system is None:
_cache_system = AdaptiveCacheSystem()
return _cache_system

View File

@ -0,0 +1,515 @@
#!/usr/bin/env python3
"""
Stock Data Cache Manager
Supports local caching of stock data to reduce API calls and improve response speed
"""
import os
import json
import pickle
import pandas as pd
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, Dict, Any, Union
import hashlib
class StockDataCache:
"""Stock Data Cache Manager - Supports optimized caching for US and Chinese stock data"""
def __init__(self, cache_dir: str = None):
"""
Initialize cache manager
Args:
cache_dir: Cache directory path, defaults to tradingagents/dataflows/data_cache
"""
if cache_dir is None:
# Get current file directory
current_dir = Path(__file__).parent
cache_dir = current_dir / "data_cache"
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(exist_ok=True)
# Create subdirectories - categorized by market
self.us_stock_dir = self.cache_dir / "us_stocks"
self.china_stock_dir = self.cache_dir / "china_stocks"
self.us_news_dir = self.cache_dir / "us_news"
self.china_news_dir = self.cache_dir / "china_news"
self.us_fundamentals_dir = self.cache_dir / "us_fundamentals"
self.china_fundamentals_dir = self.cache_dir / "china_fundamentals"
self.metadata_dir = self.cache_dir / "metadata"
# Create all directories
for dir_path in [self.us_stock_dir, self.china_stock_dir, self.us_news_dir,
self.china_news_dir, self.us_fundamentals_dir,
self.china_fundamentals_dir, self.metadata_dir]:
dir_path.mkdir(exist_ok=True)
# Cache configuration - different TTL settings for different markets
self.cache_config = {
'us_stock_data': {
'ttl_hours': 2, # US stock data cached for 2 hours (considering API limits)
'max_files': 1000,
'description': 'US stock historical data'
},
'china_stock_data': {
'ttl_hours': 1, # A-share data cached for 1 hour (high real-time requirement)
'max_files': 1000,
'description': 'A-share historical data'
},
'us_news': {
'ttl_hours': 6, # US stock news cached for 6 hours
'max_files': 500,
'description': 'US stock news data'
},
'china_news': {
'ttl_hours': 4, # A-share news cached for 4 hours
'max_files': 500,
'description': 'A-share news data'
},
'us_fundamentals': {
'ttl_hours': 24, # US stock fundamentals cached for 24 hours
'max_files': 200,
'description': 'US stock fundamentals data'
},
'china_fundamentals': {
'ttl_hours': 12, # A-share fundamentals cached for 12 hours
'max_files': 200,
'description': 'A-share fundamentals data'
}
}
print(f"📁 Cache manager initialized, cache directory: {self.cache_dir}")
print(f"🗄️ Database cache manager initialized")
print(f" US stock data: ✅ Configured")
print(f" A-share data: ✅ Configured")
def _determine_market_type(self, symbol: str) -> str:
"""Determine market type based on stock symbol"""
import re
# Check if it's Chinese A-share (6-digit number)
if re.match(r'^\d{6}$', str(symbol)):
return 'china'
else:
return 'us'
def _generate_cache_key(self, data_type: str, symbol: str, **kwargs) -> str:
"""Generate cache key"""
# Create a string containing all parameters
params_str = f"{data_type}_{symbol}"
for key, value in sorted(kwargs.items()):
params_str += f"_{key}_{value}"
# Use MD5 to generate short unique identifier
cache_key = hashlib.md5(params_str.encode()).hexdigest()[:12]
return f"{symbol}_{data_type}_{cache_key}"
def _get_cache_path(self, data_type: str, cache_key: str, file_format: str = "json", symbol: str = None) -> Path:
"""Get cache file path - supports market classification"""
if symbol:
market_type = self._determine_market_type(symbol)
else:
# Try to extract market type from cache key
market_type = 'us' if not cache_key.startswith(('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) else 'china'
# Select directory based on data type and market type
if data_type == "stock_data":
base_dir = self.china_stock_dir if market_type == 'china' else self.us_stock_dir
elif data_type == "news":
base_dir = self.china_news_dir if market_type == 'china' else self.us_news_dir
elif data_type == "fundamentals":
base_dir = self.china_fundamentals_dir if market_type == 'china' else self.us_fundamentals_dir
else:
base_dir = self.cache_dir
return base_dir / f"{cache_key}.{file_format}"
def _get_metadata_path(self, cache_key: str) -> Path:
"""Get metadata file path"""
return self.metadata_dir / f"{cache_key}_meta.json"
def _save_metadata(self, cache_key: str, metadata: Dict[str, Any]):
"""Save cache metadata"""
metadata_path = self._get_metadata_path(cache_key)
try:
with open(metadata_path, 'w', encoding='utf-8') as f:
json.dump(metadata, f, ensure_ascii=False, indent=2, default=str)
except Exception as e:
print(f"⚠️ Failed to save metadata: {e}")
def _load_metadata(self, cache_key: str) -> Optional[Dict[str, Any]]:
"""Load cache metadata"""
metadata_path = self._get_metadata_path(cache_key)
if not metadata_path.exists():
return None
try:
with open(metadata_path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"⚠️ Failed to load metadata: {e}")
return None
def save_stock_data(self, symbol: str, data: Union[str, pd.DataFrame],
start_date: str, end_date: str, data_source: str = "unknown") -> str:
"""
Save stock data to cache
Args:
symbol: Stock symbol
data: Stock data (string or DataFrame)
start_date: Start date
end_date: End date
data_source: Data source name
Returns:
Cache key
"""
try:
# Generate cache key
cache_key = self._generate_cache_key(
"stock_data", symbol,
start_date=start_date,
end_date=end_date,
data_source=data_source
)
# Determine file format and save data
if isinstance(data, pd.DataFrame):
# Save DataFrame as pickle for better performance
cache_path = self._get_cache_path("stock_data", cache_key, "pkl", symbol)
data.to_pickle(cache_path)
data_type = "dataframe"
else:
# Save string data as JSON
cache_path = self._get_cache_path("stock_data", cache_key, "json", symbol)
with open(cache_path, 'w', encoding='utf-8') as f:
json.dump({"data": data}, f, ensure_ascii=False, indent=2)
data_type = "string"
# Save metadata
market_type = self._determine_market_type(symbol)
metadata = {
"symbol": symbol,
"data_type": data_type,
"start_date": start_date,
"end_date": end_date,
"data_source": data_source,
"market_type": market_type,
"cache_time": datetime.now().isoformat(),
"file_path": str(cache_path),
"cache_key": cache_key
}
self._save_metadata(cache_key, metadata)
print(f"💾 Stock data cached: {symbol} ({market_type.upper()}) -> {cache_key}")
return cache_key
except Exception as e:
print(f"❌ Failed to save stock data cache: {e}")
return None
def load_stock_data(self, cache_key: str) -> Optional[Union[str, pd.DataFrame]]:
"""
Load stock data from cache
Args:
cache_key: Cache key
Returns:
Stock data or None if not found
"""
try:
# Load metadata
metadata = self._load_metadata(cache_key)
if not metadata:
print(f"⚠️ Cache metadata not found: {cache_key}")
return None
# Get file path
cache_path = Path(metadata["file_path"])
if not cache_path.exists():
print(f"⚠️ Cache file not found: {cache_path}")
return None
# Load data based on type
if metadata["data_type"] == "dataframe":
data = pd.read_pickle(cache_path)
else:
with open(cache_path, 'r', encoding='utf-8') as f:
json_data = json.load(f)
data = json_data["data"]
print(f"📖 Stock data loaded from cache: {metadata['symbol']} -> {cache_key}")
return data
except Exception as e:
print(f"❌ Failed to load stock data from cache: {e}")
return None
def find_cached_stock_data(self, symbol: str, start_date: str, end_date: str,
data_source: str = "unknown") -> Optional[str]:
"""
Find cached stock data
Args:
symbol: Stock symbol
start_date: Start date
end_date: End date
data_source: Data source name
Returns:
Cache key if found, None otherwise
"""
# Generate expected cache key
cache_key = self._generate_cache_key(
"stock_data", symbol,
start_date=start_date,
end_date=end_date,
data_source=data_source
)
# Check if metadata exists
metadata = self._load_metadata(cache_key)
if metadata:
cache_path = Path(metadata["file_path"])
if cache_path.exists():
return cache_key
return None
def is_cache_valid(self, cache_key: str, symbol: str = None, data_type: str = "stock_data") -> bool:
"""
Check if cache is still valid
Args:
cache_key: Cache key
symbol: Stock symbol (for market type determination)
data_type: Data type
Returns:
True if cache is valid, False otherwise
"""
try:
# Load metadata
metadata = self._load_metadata(cache_key)
if not metadata:
return False
# Check if file exists
cache_path = Path(metadata["file_path"])
if not cache_path.exists():
return False
# Determine market type and get TTL
if symbol:
market_type = self._determine_market_type(symbol)
else:
market_type = metadata.get("market_type", "us")
cache_type_key = f"{market_type}_{data_type}"
if cache_type_key not in self.cache_config:
cache_type_key = "us_stock_data" # Default fallback
ttl_hours = self.cache_config[cache_type_key]["ttl_hours"]
# Check if cache has expired
cache_time = datetime.fromisoformat(metadata["cache_time"])
expiry_time = cache_time + timedelta(hours=ttl_hours)
is_valid = datetime.now() < expiry_time
if not is_valid:
print(f"⏰ Cache expired: {cache_key} (cached at {cache_time}, TTL: {ttl_hours}h)")
return is_valid
except Exception as e:
print(f"❌ Failed to check cache validity: {e}")
return False
def get_cache_stats(self) -> Dict[str, Any]:
"""
Get cache statistics
Returns:
Dictionary containing cache statistics
"""
try:
stats = {
"cache_dir": str(self.cache_dir),
"total_files": 0,
"total_size_mb": 0,
"stock_data_count": 0,
"news_count": 0,
"fundamentals_count": 0,
"us_data_count": 0,
"china_data_count": 0
}
# Count files in each directory
for dir_path in [self.us_stock_dir, self.china_stock_dir, self.us_news_dir,
self.china_news_dir, self.us_fundamentals_dir,
self.china_fundamentals_dir, self.metadata_dir]:
if dir_path.exists():
files = list(dir_path.glob("*"))
stats["total_files"] += len(files)
# Calculate total size
for file_path in files:
if file_path.is_file():
stats["total_size_mb"] += file_path.stat().st_size / (1024 * 1024)
# Count by data type
if self.us_stock_dir.exists():
stats["stock_data_count"] += len(list(self.us_stock_dir.glob("*")))
stats["us_data_count"] += len(list(self.us_stock_dir.glob("*")))
if self.china_stock_dir.exists():
stats["stock_data_count"] += len(list(self.china_stock_dir.glob("*")))
stats["china_data_count"] += len(list(self.china_stock_dir.glob("*")))
if self.us_news_dir.exists():
stats["news_count"] += len(list(self.us_news_dir.glob("*")))
stats["us_data_count"] += len(list(self.us_news_dir.glob("*")))
if self.china_news_dir.exists():
stats["news_count"] += len(list(self.china_news_dir.glob("*")))
stats["china_data_count"] += len(list(self.china_news_dir.glob("*")))
if self.us_fundamentals_dir.exists():
stats["fundamentals_count"] += len(list(self.us_fundamentals_dir.glob("*")))
stats["us_data_count"] += len(list(self.us_fundamentals_dir.glob("*")))
if self.china_fundamentals_dir.exists():
stats["fundamentals_count"] += len(list(self.china_fundamentals_dir.glob("*")))
stats["china_data_count"] += len(list(self.china_fundamentals_dir.glob("*")))
# Round size to 2 decimal places
stats["total_size_mb"] = round(stats["total_size_mb"], 2)
return stats
except Exception as e:
print(f"❌ Failed to get cache statistics: {e}")
return {"error": str(e)}
def cleanup_expired_cache(self):
"""Clean up expired cache files"""
try:
cleaned_count = 0
# Check all metadata files
if self.metadata_dir.exists():
for metadata_file in self.metadata_dir.glob("*_meta.json"):
try:
cache_key = metadata_file.stem.replace("_meta", "")
# Load metadata
with open(metadata_file, 'r', encoding='utf-8') as f:
metadata = json.load(f)
# Check if cache is expired
if not self.is_cache_valid(cache_key, metadata.get("symbol"), "stock_data"):
# Remove cache file
cache_path = Path(metadata["file_path"])
if cache_path.exists():
cache_path.unlink()
# Remove metadata file
metadata_file.unlink()
cleaned_count += 1
print(f"🗑️ Cleaned expired cache: {cache_key}")
except Exception as e:
print(f"⚠️ Failed to clean cache file {metadata_file}: {e}")
print(f"✅ Cache cleanup completed, removed {cleaned_count} expired files")
except Exception as e:
print(f"❌ Failed to cleanup cache: {e}")
# Global cache instance
_global_cache = None
def get_cache(cache_dir: str = None):
"""
Get global cache instance with intelligent cache selection
This function will automatically choose between:
1. Integrated cache (with database support) if available
2. Traditional file cache as fallback
Args:
cache_dir: Cache directory path
Returns:
Cache instance (IntegratedCacheManager or StockDataCache)
"""
global _global_cache
if _global_cache is None:
# Try to use integrated cache manager first
try:
from .integrated_cache import IntegratedCacheManager
_global_cache = IntegratedCacheManager(cache_dir)
print("🚀 Using integrated cache manager with database support")
except ImportError:
# Fallback to traditional cache
_global_cache = StockDataCache(cache_dir)
print("📁 Using traditional file cache")
except Exception as e:
# If integrated cache fails, fallback to traditional cache
print(f"⚠️ Integrated cache initialization failed: {e}")
print("📁 Falling back to traditional file cache")
_global_cache = StockDataCache(cache_dir)
return _global_cache
# Convenience functions
def save_stock_data(symbol: str, data: Union[str, pd.DataFrame],
start_date: str, end_date: str, data_source: str = "unknown") -> str:
"""Save stock data to cache (convenience function)"""
cache = get_cache()
return cache.save_stock_data(symbol, data, start_date, end_date, data_source)
def load_stock_data(cache_key: str) -> Optional[Union[str, pd.DataFrame]]:
"""Load stock data from cache (convenience function)"""
cache = get_cache()
return cache.load_stock_data(cache_key)
def find_cached_stock_data(symbol: str, start_date: str, end_date: str,
data_source: str = "unknown") -> Optional[str]:
"""Find cached stock data (convenience function)"""
cache = get_cache()
return cache.find_cached_stock_data(symbol, start_date, end_date, data_source)
if __name__ == "__main__":
# Test the cache manager
print("🧪 Testing Stock Data Cache Manager...")
# Initialize cache
cache = StockDataCache()
# Test data
test_data = "Sample stock data for AAPL"
cache_key = cache.save_stock_data("AAPL", test_data, "2024-01-01", "2024-01-31", "test")
# Load data
loaded_data = cache.load_stock_data(cache_key)
print(f"Loaded data: {loaded_data}")
# Check cache validity
is_valid = cache.is_cache_valid(cache_key, "AAPL")
print(f"Cache valid: {is_valid}")
# Get statistics
stats = cache.get_cache_stats()
print(f"Cache stats: {stats}")
print("✅ Cache manager test completed!")

View File

@ -0,0 +1,331 @@
#!/usr/bin/env python3
"""
中国财经数据聚合工具
由于微博API申请困难且功能受限采用多源数据聚合的方式
"""
import requests
import json
import time
import random
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import re
from bs4 import BeautifulSoup
import pandas as pd
class ChineseFinanceDataAggregator:
"""中国财经数据聚合器"""
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
self.session = requests.Session()
self.session.headers.update(self.headers)
def get_stock_sentiment_summary(self, ticker: str, days: int = 7) -> Dict:
"""
获取股票情绪分析汇总
整合多个可获取的中国财经数据源
"""
try:
# 1. 获取财经新闻情绪
news_sentiment = self._get_finance_news_sentiment(ticker, days)
# 2. 获取股吧讨论热度 (如果可以获取)
forum_sentiment = self._get_stock_forum_sentiment(ticker, days)
# 3. 获取财经媒体报道
media_sentiment = self._get_media_coverage_sentiment(ticker, days)
# 4. 综合分析
overall_sentiment = self._calculate_overall_sentiment(
news_sentiment, forum_sentiment, media_sentiment
)
return {
'ticker': ticker,
'analysis_period': f'{days} days',
'overall_sentiment': overall_sentiment,
'news_sentiment': news_sentiment,
'forum_sentiment': forum_sentiment,
'media_sentiment': media_sentiment,
'summary': self._generate_sentiment_summary(overall_sentiment),
'timestamp': datetime.now().isoformat()
}
except Exception as e:
return {
'ticker': ticker,
'error': f'数据获取失败: {str(e)}',
'fallback_message': '由于中国社交媒体API限制建议使用财经新闻和基本面分析作为主要参考',
'timestamp': datetime.now().isoformat()
}
def _get_finance_news_sentiment(self, ticker: str, days: int) -> Dict:
"""获取财经新闻情绪分析"""
try:
# 搜索相关新闻标题和内容
company_name = self._get_company_chinese_name(ticker)
search_terms = [ticker, company_name] if company_name else [ticker]
news_items = []
for term in search_terms:
# 这里可以集成多个新闻源
items = self._search_finance_news(term, days)
news_items.extend(items)
# 简单的情绪分析
positive_count = 0
negative_count = 0
neutral_count = 0
for item in news_items:
sentiment = self._analyze_text_sentiment(item.get('title', '') + ' ' + item.get('content', ''))
if sentiment > 0.1:
positive_count += 1
elif sentiment < -0.1:
negative_count += 1
else:
neutral_count += 1
total = len(news_items)
if total == 0:
return {'sentiment_score': 0, 'confidence': 0, 'news_count': 0}
sentiment_score = (positive_count - negative_count) / total
return {
'sentiment_score': sentiment_score,
'positive_ratio': positive_count / total,
'negative_ratio': negative_count / total,
'neutral_ratio': neutral_count / total,
'news_count': total,
'confidence': min(total / 10, 1.0) # 新闻数量越多,置信度越高
}
except Exception as e:
return {'error': str(e), 'sentiment_score': 0, 'confidence': 0}
def _get_stock_forum_sentiment(self, ticker: str, days: int) -> Dict:
"""获取股票论坛讨论情绪 (模拟数据,实际需要爬虫)"""
# 由于东方财富股吧等平台的反爬虫机制,这里返回模拟数据
# 实际实现需要更复杂的爬虫技术
return {
'sentiment_score': 0,
'discussion_count': 0,
'hot_topics': [],
'note': '股票论坛数据获取受限,建议关注官方财经新闻',
'confidence': 0
}
def _get_media_coverage_sentiment(self, ticker: str, days: int) -> Dict:
"""获取媒体报道情绪"""
try:
# 可以集成RSS源或公开的财经API
coverage_items = self._get_media_coverage(ticker, days)
if not coverage_items:
return {'sentiment_score': 0, 'coverage_count': 0, 'confidence': 0}
# 分析媒体报道的情绪倾向
sentiment_scores = []
for item in coverage_items:
score = self._analyze_text_sentiment(item.get('title', '') + ' ' + item.get('summary', ''))
sentiment_scores.append(score)
avg_sentiment = sum(sentiment_scores) / len(sentiment_scores) if sentiment_scores else 0
return {
'sentiment_score': avg_sentiment,
'coverage_count': len(coverage_items),
'confidence': min(len(coverage_items) / 5, 1.0)
}
except Exception as e:
return {'error': str(e), 'sentiment_score': 0, 'confidence': 0}
def _search_finance_news(self, search_term: str, days: int) -> List[Dict]:
"""搜索财经新闻 (示例实现)"""
# 这里可以集成多个新闻源的API或RSS
# 例如:财联社、新浪财经、东方财富等
# 模拟返回数据结构
return [
{
'title': f'{search_term}相关财经新闻标题',
'content': '新闻内容摘要...',
'source': '财联社',
'publish_time': datetime.now().isoformat(),
'url': 'https://example.com/news/1'
}
]
def _get_media_coverage(self, ticker: str, days: int) -> List[Dict]:
"""获取媒体报道 (示例实现)"""
# 可以集成Google News API或其他新闻聚合服务
return []
def _analyze_text_sentiment(self, text: str) -> float:
"""简单的中文文本情绪分析"""
if not text:
return 0
# 简单的关键词情绪分析
positive_words = ['上涨', '增长', '利好', '看好', '买入', '推荐', '强势', '突破', '创新高']
negative_words = ['下跌', '下降', '利空', '看空', '卖出', '风险', '跌破', '创新低', '亏损']
positive_count = sum(1 for word in positive_words if word in text)
negative_count = sum(1 for word in negative_words if word in text)
if positive_count + negative_count == 0:
return 0
return (positive_count - negative_count) / (positive_count + negative_count)
def _get_company_chinese_name(self, ticker: str) -> Optional[str]:
"""获取公司中文名称"""
# 简单的映射表实际可以从数据库或API获取
name_mapping = {
'AAPL': '苹果',
'TSLA': '特斯拉',
'NVDA': '英伟达',
'MSFT': '微软',
'GOOGL': '谷歌',
'AMZN': '亚马逊'
}
return name_mapping.get(ticker.upper())
def _calculate_overall_sentiment(self, news_sentiment: Dict, forum_sentiment: Dict, media_sentiment: Dict) -> Dict:
"""计算综合情绪分析"""
# 根据各数据源的置信度加权计算
news_weight = news_sentiment.get('confidence', 0)
forum_weight = forum_sentiment.get('confidence', 0)
media_weight = media_sentiment.get('confidence', 0)
total_weight = news_weight + forum_weight + media_weight
if total_weight == 0:
return {'sentiment_score': 0, 'confidence': 0, 'level': 'neutral'}
weighted_sentiment = (
news_sentiment.get('sentiment_score', 0) * news_weight +
forum_sentiment.get('sentiment_score', 0) * forum_weight +
media_sentiment.get('sentiment_score', 0) * media_weight
) / total_weight
# 确定情绪等级
if weighted_sentiment > 0.3:
level = 'very_positive'
elif weighted_sentiment > 0.1:
level = 'positive'
elif weighted_sentiment > -0.1:
level = 'neutral'
elif weighted_sentiment > -0.3:
level = 'negative'
else:
level = 'very_negative'
return {
'sentiment_score': weighted_sentiment,
'confidence': total_weight / 3, # 平均置信度
'level': level
}
def _generate_sentiment_summary(self, overall_sentiment: Dict) -> str:
"""生成情绪分析摘要"""
level = overall_sentiment.get('level', 'neutral')
score = overall_sentiment.get('sentiment_score', 0)
confidence = overall_sentiment.get('confidence', 0)
level_descriptions = {
'very_positive': '非常积极',
'positive': '积极',
'neutral': '中性',
'negative': '消极',
'very_negative': '非常消极'
}
description = level_descriptions.get(level, '中性')
confidence_level = '' if confidence > 0.7 else '' if confidence > 0.3 else ''
return f"市场情绪: {description} (评分: {score:.2f}, 置信度: {confidence_level})"
def get_chinese_social_sentiment(ticker: str, curr_date: str) -> str:
"""
获取中国社交媒体情绪分析的主要接口函数
"""
aggregator = ChineseFinanceDataAggregator()
try:
# 获取情绪分析数据
sentiment_data = aggregator.get_stock_sentiment_summary(ticker, days=7)
# 格式化输出
if 'error' in sentiment_data:
return f"""
中国市场情绪分析报告 - {ticker}
分析日期: {curr_date}
数据获取限制说明:
{sentiment_data.get('fallback_message', '数据获取遇到技术限制')}
建议:
1. 重点关注财经新闻和基本面分析
2. 参考官方财报和业绩指导
3. 关注行业政策和监管动态
4. 考虑国际市场情绪对中概股的影响
: 由于中国社交媒体平台API限制当前主要依赖公开财经数据源进行分析
"""
overall = sentiment_data.get('overall_sentiment', {})
news = sentiment_data.get('news_sentiment', {})
return f"""
中国市场情绪分析报告 - {ticker}
分析日期: {curr_date}
分析周期: {sentiment_data.get('analysis_period', '7天')}
📊 综合情绪评估:
{sentiment_data.get('summary', '数据不足')}
📰 财经新闻情绪:
- 情绪评分: {news.get('sentiment_score', 0):.2f}
- 正面新闻比例: {news.get('positive_ratio', 0):.1%}
- 负面新闻比例: {news.get('negative_ratio', 0):.1%}
- 新闻数量: {news.get('news_count', 0)}
💡 投资建议:
基于当前可获取的中国市场数据建议投资者:
1. 密切关注官方财经媒体报道
2. 重视基本面分析和财务数据
3. 考虑政策环境对股价的影响
4. 关注国际市场动态
数据说明:
由于中国社交媒体平台API获取限制本分析主要基于公开财经新闻数据
建议结合其他分析维度进行综合判断
生成时间: {sentiment_data.get('timestamp', datetime.now().isoformat())}
"""
except Exception as e:
return f"""
中国市场情绪分析 - {ticker}
分析日期: {curr_date}
分析失败: {str(e)}
💡 替代建议:
1. 查看财经新闻网站的相关报道
2. 关注雪球东方财富等投资社区讨论
3. 参考专业机构的研究报告
4. 重点分析基本面和技术面数据
: 中国社交媒体数据获取存在技术限制建议以基本面分析为主
"""

View File

@ -0,0 +1,528 @@
#!/usr/bin/env python3
"""
MongoDB + Redis 数据库缓存管理器
提供高性能的股票数据缓存和持久化存储
"""
import os
import json
import pickle
import hashlib
from datetime import datetime, timedelta
from typing import Optional, Dict, Any, List, Union
import pandas as pd
# MongoDB
try:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
MONGODB_AVAILABLE = True
except ImportError:
MONGODB_AVAILABLE = False
print("⚠️ pymongo 未安装MongoDB功能不可用")
# Redis
try:
import redis
from redis.exceptions import ConnectionError as RedisConnectionError
REDIS_AVAILABLE = True
except ImportError:
REDIS_AVAILABLE = False
print("⚠️ redis 未安装Redis功能不可用")
class DatabaseCacheManager:
"""MongoDB + Redis 数据库缓存管理器"""
def __init__(self,
mongodb_url: Optional[str] = None,
redis_url: Optional[str] = None,
mongodb_db: str = "tradingagents",
redis_db: int = 0):
"""
初始化数据库缓存管理器
Args:
mongodb_url: MongoDB连接URL默认使用配置文件端口
redis_url: Redis连接URL默认使用配置文件端口
mongodb_db: MongoDB数据库名
redis_db: Redis数据库编号
"""
# 从配置文件获取正确的端口
mongodb_port = os.getenv("MONGODB_PORT", "27018")
redis_port = os.getenv("REDIS_PORT", "6380")
mongodb_password = os.getenv("MONGODB_PASSWORD", "tradingagents123")
redis_password = os.getenv("REDIS_PASSWORD", "tradingagents123")
self.mongodb_url = mongodb_url or os.getenv("MONGODB_URL", f"mongodb://admin:{mongodb_password}@localhost:{mongodb_port}")
self.redis_url = redis_url or os.getenv("REDIS_URL", f"redis://:{redis_password}@localhost:{redis_port}")
self.mongodb_db_name = mongodb_db
self.redis_db = redis_db
# 初始化连接
self.mongodb_client = None
self.mongodb_db = None
self.redis_client = None
self._init_mongodb()
self._init_redis()
print(f"🗄️ 数据库缓存管理器初始化完成")
print(f" MongoDB: {'✅ 已连接' if self.mongodb_client else '❌ 未连接'}")
print(f" Redis: {'✅ 已连接' if self.redis_client else '❌ 未连接'}")
def _init_mongodb(self):
"""初始化MongoDB连接"""
if not MONGODB_AVAILABLE:
return
try:
self.mongodb_client = MongoClient(
self.mongodb_url,
serverSelectionTimeoutMS=5000, # 5秒超时
connectTimeoutMS=5000
)
# 测试连接
self.mongodb_client.admin.command('ping')
self.mongodb_db = self.mongodb_client[self.mongodb_db_name]
# 创建索引
self._create_mongodb_indexes()
print(f"✅ MongoDB连接成功: {self.mongodb_url}")
except Exception as e:
print(f"❌ MongoDB连接失败: {e}")
self.mongodb_client = None
self.mongodb_db = None
def _init_redis(self):
"""初始化Redis连接"""
if not REDIS_AVAILABLE:
return
try:
self.redis_client = redis.from_url(
self.redis_url,
db=self.redis_db,
socket_timeout=5,
socket_connect_timeout=5,
decode_responses=True
)
# 测试连接
self.redis_client.ping()
print(f"✅ Redis连接成功: {self.redis_url}")
except Exception as e:
print(f"❌ Redis连接失败: {e}")
self.redis_client = None
def _create_mongodb_indexes(self):
"""创建MongoDB索引"""
if self.mongodb_db is None:
return
try:
# 股票数据集合索引
stock_collection = self.mongodb_db.stock_data
stock_collection.create_index([
("symbol", 1),
("data_source", 1),
("start_date", 1),
("end_date", 1)
])
stock_collection.create_index([("created_at", 1)])
# 新闻数据集合索引
news_collection = self.mongodb_db.news_data
news_collection.create_index([
("symbol", 1),
("data_source", 1),
("date_range", 1)
])
news_collection.create_index([("created_at", 1)])
# 基本面数据集合索引
fundamentals_collection = self.mongodb_db.fundamentals_data
fundamentals_collection.create_index([
("symbol", 1),
("data_source", 1),
("analysis_date", 1)
])
fundamentals_collection.create_index([("created_at", 1)])
print("✅ MongoDB索引创建完成")
except Exception as e:
print(f"⚠️ MongoDB索引创建失败: {e}")
def _generate_cache_key(self, data_type: str, symbol: str, **kwargs) -> str:
"""生成缓存键"""
params_str = f"{data_type}_{symbol}"
for key, value in sorted(kwargs.items()):
params_str += f"_{key}_{value}"
cache_key = hashlib.md5(params_str.encode()).hexdigest()[:16]
return f"{data_type}:{symbol}:{cache_key}"
def save_stock_data(self, symbol: str, data: Union[pd.DataFrame, str],
start_date: str = None, end_date: str = None,
data_source: str = "unknown", market_type: str = None) -> str:
"""
保存股票数据到MongoDB和Redis
Args:
symbol: 股票代码
data: 股票数据
start_date: 开始日期
end_date: 结束日期
data_source: 数据源
market_type: 市场类型 (us/china)
Returns:
cache_key: 缓存键
"""
cache_key = self._generate_cache_key("stock", symbol,
start_date=start_date,
end_date=end_date,
source=data_source)
# 自动推断市场类型
if market_type is None:
# 根据股票代码格式推断市场类型
import re
if re.match(r'^\d{6}$', symbol): # 6位数字为A股
market_type = "china"
else: # 其他格式为美股
market_type = "us"
# 准备文档数据
doc = {
"_id": cache_key,
"symbol": symbol,
"market_type": market_type,
"data_type": "stock_data",
"start_date": start_date,
"end_date": end_date,
"data_source": data_source,
"created_at": datetime.utcnow(),
"updated_at": datetime.utcnow()
}
# 处理数据格式
if isinstance(data, pd.DataFrame):
doc["data"] = data.to_json(orient='records', date_format='iso')
doc["data_format"] = "dataframe_json"
else:
doc["data"] = str(data)
doc["data_format"] = "text"
# 保存到MongoDB持久化
if self.mongodb_db is not None:
try:
collection = self.mongodb_db.stock_data
collection.replace_one({"_id": cache_key}, doc, upsert=True)
print(f"💾 股票数据已保存到MongoDB: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ MongoDB保存失败: {e}")
# 保存到Redis快速缓存6小时过期
if self.redis_client:
try:
redis_data = {
"data": doc["data"],
"data_format": doc["data_format"],
"symbol": symbol,
"data_source": data_source,
"created_at": doc["created_at"].isoformat()
}
self.redis_client.setex(
cache_key,
6 * 3600, # 6小时过期
json.dumps(redis_data, ensure_ascii=False)
)
print(f"⚡ 股票数据已缓存到Redis: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ Redis缓存失败: {e}")
return cache_key
def load_stock_data(self, cache_key: str) -> Optional[Union[pd.DataFrame, str]]:
"""从Redis或MongoDB加载股票数据"""
# 首先尝试从Redis加载更快
if self.redis_client:
try:
redis_data = self.redis_client.get(cache_key)
if redis_data:
data_dict = json.loads(redis_data)
print(f"⚡ 从Redis加载数据: {cache_key}")
if data_dict["data_format"] == "dataframe_json":
return pd.read_json(data_dict["data"], orient='records')
else:
return data_dict["data"]
except Exception as e:
print(f"⚠️ Redis加载失败: {e}")
# 如果Redis没有从MongoDB加载
if self.mongodb_db is not None:
try:
collection = self.mongodb_db.stock_data
doc = collection.find_one({"_id": cache_key})
if doc:
print(f"💾 从MongoDB加载数据: {cache_key}")
# 同时更新到Redis缓存
if self.redis_client:
try:
redis_data = {
"data": doc["data"],
"data_format": doc["data_format"],
"symbol": doc["symbol"],
"data_source": doc["data_source"],
"created_at": doc["created_at"].isoformat()
}
self.redis_client.setex(
cache_key,
6 * 3600,
json.dumps(redis_data, ensure_ascii=False)
)
print(f"⚡ 数据已同步到Redis缓存")
except Exception as e:
print(f"⚠️ Redis同步失败: {e}")
if doc["data_format"] == "dataframe_json":
return pd.read_json(doc["data"], orient='records')
else:
return doc["data"]
except Exception as e:
print(f"⚠️ MongoDB加载失败: {e}")
return None
def find_cached_stock_data(self, symbol: str, start_date: str = None,
end_date: str = None, data_source: str = None,
max_age_hours: int = 6) -> Optional[str]:
"""查找匹配的缓存数据"""
# 生成精确匹配的缓存键
exact_key = self._generate_cache_key("stock", symbol,
start_date=start_date,
end_date=end_date,
source=data_source)
# 检查Redis中是否有精确匹配
if self.redis_client and self.redis_client.exists(exact_key):
print(f"⚡ Redis中找到精确匹配: {symbol} -> {exact_key}")
return exact_key
# 检查MongoDB中的匹配项
if self.mongodb_db is not None:
try:
collection = self.mongodb_db.stock_data
cutoff_time = datetime.utcnow() - timedelta(hours=max_age_hours)
query = {
"symbol": symbol,
"created_at": {"$gte": cutoff_time}
}
if data_source:
query["data_source"] = data_source
if start_date:
query["start_date"] = start_date
if end_date:
query["end_date"] = end_date
doc = collection.find_one(query, sort=[("created_at", -1)])
if doc:
cache_key = doc["_id"]
print(f"💾 MongoDB中找到匹配: {symbol} -> {cache_key}")
return cache_key
except Exception as e:
print(f"⚠️ MongoDB查询失败: {e}")
print(f"❌ 未找到有效缓存: {symbol}")
return None
def save_news_data(self, symbol: str, news_data: str,
start_date: str = None, end_date: str = None,
data_source: str = "unknown") -> str:
"""保存新闻数据到MongoDB和Redis"""
cache_key = self._generate_cache_key("news", symbol,
start_date=start_date,
end_date=end_date,
source=data_source)
doc = {
"_id": cache_key,
"symbol": symbol,
"data_type": "news_data",
"date_range": f"{start_date}_{end_date}",
"start_date": start_date,
"end_date": end_date,
"data_source": data_source,
"data": news_data,
"created_at": datetime.utcnow(),
"updated_at": datetime.utcnow()
}
# 保存到MongoDB
if self.mongodb_db is not None:
try:
collection = self.mongodb_db.news_data
collection.replace_one({"_id": cache_key}, doc, upsert=True)
print(f"📰 新闻数据已保存到MongoDB: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ MongoDB保存失败: {e}")
# 保存到Redis24小时过期
if self.redis_client:
try:
redis_data = {
"data": news_data,
"symbol": symbol,
"data_source": data_source,
"created_at": doc["created_at"].isoformat()
}
self.redis_client.setex(
cache_key,
24 * 3600, # 24小时过期
json.dumps(redis_data, ensure_ascii=False)
)
print(f"⚡ 新闻数据已缓存到Redis: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ Redis缓存失败: {e}")
return cache_key
def save_fundamentals_data(self, symbol: str, fundamentals_data: str,
analysis_date: str = None,
data_source: str = "unknown") -> str:
"""保存基本面数据到MongoDB和Redis"""
if not analysis_date:
analysis_date = datetime.now().strftime("%Y-%m-%d")
cache_key = self._generate_cache_key("fundamentals", symbol,
date=analysis_date,
source=data_source)
doc = {
"_id": cache_key,
"symbol": symbol,
"data_type": "fundamentals_data",
"analysis_date": analysis_date,
"data_source": data_source,
"data": fundamentals_data,
"created_at": datetime.utcnow(),
"updated_at": datetime.utcnow()
}
# 保存到MongoDB
if self.mongodb_db is not None:
try:
collection = self.mongodb_db.fundamentals_data
collection.replace_one({"_id": cache_key}, doc, upsert=True)
print(f"💼 基本面数据已保存到MongoDB: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ MongoDB保存失败: {e}")
# 保存到Redis24小时过期
if self.redis_client:
try:
redis_data = {
"data": fundamentals_data,
"symbol": symbol,
"data_source": data_source,
"analysis_date": analysis_date,
"created_at": doc["created_at"].isoformat()
}
self.redis_client.setex(
cache_key,
24 * 3600, # 24小时过期
json.dumps(redis_data, ensure_ascii=False)
)
print(f"⚡ 基本面数据已缓存到Redis: {symbol} -> {cache_key}")
except Exception as e:
print(f"⚠️ Redis缓存失败: {e}")
return cache_key
def get_cache_stats(self) -> Dict[str, Any]:
"""获取缓存统计信息"""
stats = {
"mongodb": {"available": self.mongodb_db is not None, "collections": {}},
"redis": {"available": self.redis_client is not None, "keys": 0, "memory_usage": "N/A"}
}
# MongoDB统计
if self.mongodb_db is not None:
try:
for collection_name in ["stock_data", "news_data", "fundamentals_data"]:
collection = self.mongodb_db[collection_name]
count = collection.count_documents({})
size = self.mongodb_db.command("collStats", collection_name).get("size", 0)
stats["mongodb"]["collections"][collection_name] = {
"count": count,
"size_mb": round(size / (1024 * 1024), 2)
}
except Exception as e:
print(f"⚠️ MongoDB统计获取失败: {e}")
# Redis统计
if self.redis_client:
try:
info = self.redis_client.info()
stats["redis"]["keys"] = info.get("db0", {}).get("keys", 0)
stats["redis"]["memory_usage"] = f"{info.get('used_memory_human', 'N/A')}"
except Exception as e:
print(f"⚠️ Redis统计获取失败: {e}")
return stats
def clear_old_cache(self, max_age_days: int = 7):
"""清理过期缓存"""
cutoff_time = datetime.utcnow() - timedelta(days=max_age_days)
cleared_count = 0
# 清理MongoDB
if self.mongodb_db is not None:
try:
for collection_name in ["stock_data", "news_data", "fundamentals_data"]:
collection = self.mongodb_db[collection_name]
result = collection.delete_many({"created_at": {"$lt": cutoff_time}})
cleared_count += result.deleted_count
print(f"🧹 MongoDB {collection_name} 清理了 {result.deleted_count} 条记录")
except Exception as e:
print(f"⚠️ MongoDB清理失败: {e}")
# Redis会自动过期不需要手动清理
print(f"🧹 总共清理了 {cleared_count} 条过期记录")
return cleared_count
def close(self):
"""关闭数据库连接"""
if self.mongodb_client:
self.mongodb_client.close()
print("🔒 MongoDB连接已关闭")
if self.redis_client:
self.redis_client.close()
print("🔒 Redis连接已关闭")
# 全局数据库缓存实例
_db_cache_instance = None
def get_db_cache() -> DatabaseCacheManager:
"""获取全局数据库缓存实例"""
global _db_cache_instance
if _db_cache_instance is None:
_db_cache_instance = DatabaseCacheManager()
return _db_cache_instance

View File

@ -0,0 +1,286 @@
#!/usr/bin/env python3
"""
集成缓存管理器
结合原有缓存系统和新的自适应数据库支持
提供向后兼容的接口
"""
import os
import logging
from pathlib import Path
from typing import Any, Dict, Optional, Union
import pandas as pd
# 导入原有缓存系统
from .cache_manager import StockDataCache
# 导入自适应缓存系统
try:
from .adaptive_cache import get_cache_system
from ..config.database_manager import get_database_manager
ADAPTIVE_CACHE_AVAILABLE = True
except ImportError:
ADAPTIVE_CACHE_AVAILABLE = False
class IntegratedCacheManager:
"""集成缓存管理器 - 智能选择缓存策略"""
def __init__(self, cache_dir: str = None):
self.logger = logging.getLogger(__name__)
# 初始化原有缓存系统(作为备用)
self.legacy_cache = StockDataCache(cache_dir)
# 尝试初始化自适应缓存系统
self.adaptive_cache = None
self.use_adaptive = False
if ADAPTIVE_CACHE_AVAILABLE:
try:
self.adaptive_cache = get_cache_system()
self.db_manager = get_database_manager()
self.use_adaptive = True
self.logger.info("✅ 自适应缓存系统已启用")
except Exception as e:
self.logger.warning(f"自适应缓存系统初始化失败,使用传统缓存: {e}")
self.use_adaptive = False
else:
self.logger.info("自适应缓存系统不可用,使用传统文件缓存")
# 显示当前配置
self._log_cache_status()
def _log_cache_status(self):
"""记录缓存状态"""
if self.use_adaptive:
backend = self.adaptive_cache.primary_backend
mongodb_available = self.db_manager.is_mongodb_available()
redis_available = self.db_manager.is_redis_available()
self.logger.info(f"📊 缓存配置:")
self.logger.info(f" 主要后端: {backend}")
self.logger.info(f" MongoDB: {'✅ 可用' if mongodb_available else '❌ 不可用'}")
self.logger.info(f" Redis: {'✅ 可用' if redis_available else '❌ 不可用'}")
self.logger.info(f" 降级支持: {'✅ 启用' if self.adaptive_cache.fallback_enabled else '❌ 禁用'}")
else:
self.logger.info("📁 使用传统文件缓存系统")
def save_stock_data(self, symbol: str, data: Any, start_date: str = None,
end_date: str = None, data_source: str = "default") -> str:
"""
保存股票数据到缓存
Args:
symbol: 股票代码
data: 股票数据
start_date: 开始日期
end_date: 结束日期
data_source: 数据源
Returns:
缓存键
"""
if self.use_adaptive:
# 使用自适应缓存系统
return self.adaptive_cache.save_data(
symbol=symbol,
data=data,
start_date=start_date or "",
end_date=end_date or "",
data_source=data_source,
data_type="stock_data"
)
else:
# 使用传统缓存系统
return self.legacy_cache.save_stock_data(
symbol=symbol,
data=data,
start_date=start_date,
end_date=end_date,
data_source=data_source
)
def load_stock_data(self, cache_key: str) -> Optional[Any]:
"""
从缓存加载股票数据
Args:
cache_key: 缓存键
Returns:
股票数据或None
"""
if self.use_adaptive:
# 使用自适应缓存系统
return self.adaptive_cache.load_data(cache_key)
else:
# 使用传统缓存系统
return self.legacy_cache.load_stock_data(cache_key)
def find_cached_stock_data(self, symbol: str, start_date: str = None,
end_date: str = None, data_source: str = "default") -> Optional[str]:
"""
查找缓存的股票数据
Args:
symbol: 股票代码
start_date: 开始日期
end_date: 结束日期
data_source: 数据源
Returns:
缓存键或None
"""
if self.use_adaptive:
# 使用自适应缓存系统
return self.adaptive_cache.find_cached_data(
symbol=symbol,
start_date=start_date or "",
end_date=end_date or "",
data_source=data_source,
data_type="stock_data"
)
else:
# 使用传统缓存系统
return self.legacy_cache.find_cached_stock_data(
symbol=symbol,
start_date=start_date,
end_date=end_date,
data_source=data_source
)
def save_news_data(self, symbol: str, data: Any, data_source: str = "default") -> str:
"""保存新闻数据"""
if self.use_adaptive:
return self.adaptive_cache.save_data(
symbol=symbol,
data=data,
data_source=data_source,
data_type="news_data"
)
else:
return self.legacy_cache.save_news_data(symbol, data, data_source)
def load_news_data(self, cache_key: str) -> Optional[Any]:
"""加载新闻数据"""
if self.use_adaptive:
return self.adaptive_cache.load_data(cache_key)
else:
return self.legacy_cache.load_news_data(cache_key)
def save_fundamentals_data(self, symbol: str, data: Any, data_source: str = "default") -> str:
"""保存基本面数据"""
if self.use_adaptive:
return self.adaptive_cache.save_data(
symbol=symbol,
data=data,
data_source=data_source,
data_type="fundamentals_data"
)
else:
return self.legacy_cache.save_fundamentals_data(symbol, data, data_source)
def load_fundamentals_data(self, cache_key: str) -> Optional[Any]:
"""加载基本面数据"""
if self.use_adaptive:
return self.adaptive_cache.load_data(cache_key)
else:
return self.legacy_cache.load_fundamentals_data(cache_key)
def get_cache_stats(self) -> Dict[str, Any]:
"""获取缓存统计信息"""
if self.use_adaptive:
# 获取自适应缓存统计
adaptive_stats = self.adaptive_cache.get_cache_stats()
# 添加传统缓存统计
legacy_stats = self.legacy_cache.get_cache_stats()
return {
"cache_system": "adaptive",
"adaptive_cache": adaptive_stats,
"legacy_cache": legacy_stats,
"database_available": self.db_manager.is_database_available(),
"mongodb_available": self.db_manager.is_mongodb_available(),
"redis_available": self.db_manager.is_redis_available()
}
else:
# 只返回传统缓存统计
legacy_stats = self.legacy_cache.get_cache_stats()
return {
"cache_system": "legacy",
"legacy_cache": legacy_stats,
"database_available": False,
"mongodb_available": False,
"redis_available": False
}
def clear_expired_cache(self):
"""清理过期缓存"""
if self.use_adaptive:
self.adaptive_cache.clear_expired_cache()
# 总是清理传统缓存
self.legacy_cache.clear_expired_cache()
def get_cache_backend_info(self) -> Dict[str, Any]:
"""获取缓存后端信息"""
if self.use_adaptive:
return {
"system": "adaptive",
"primary_backend": self.adaptive_cache.primary_backend,
"fallback_enabled": self.adaptive_cache.fallback_enabled,
"mongodb_available": self.db_manager.is_mongodb_available(),
"redis_available": self.db_manager.is_redis_available()
}
else:
return {
"system": "legacy",
"primary_backend": "file",
"fallback_enabled": False,
"mongodb_available": False,
"redis_available": False
}
def is_database_available(self) -> bool:
"""检查数据库是否可用"""
if self.use_adaptive:
return self.db_manager.is_database_available()
return False
def get_performance_mode(self) -> str:
"""获取性能模式"""
if not self.use_adaptive:
return "基础模式 (文件缓存)"
mongodb_available = self.db_manager.is_mongodb_available()
redis_available = self.db_manager.is_redis_available()
if redis_available and mongodb_available:
return "高性能模式 (Redis + MongoDB + 文件)"
elif redis_available:
return "快速模式 (Redis + 文件)"
elif mongodb_available:
return "持久化模式 (MongoDB + 文件)"
else:
return "标准模式 (智能文件缓存)"
# 全局集成缓存管理器实例
_integrated_cache = None
def get_cache() -> IntegratedCacheManager:
"""获取全局集成缓存管理器实例"""
global _integrated_cache
if _integrated_cache is None:
_integrated_cache = IntegratedCacheManager()
return _integrated_cache
# 向后兼容的函数
def get_stock_cache():
"""向后兼容:获取股票缓存"""
return get_cache()
def create_cache_manager(cache_dir: str = None):
"""向后兼容:创建缓存管理器"""
return IntegratedCacheManager(cache_dir)

View File

@ -3,6 +3,13 @@ from .reddit_utils import fetch_top_from_category
from .yfin_utils import *
from .stockstats_utils import *
from .googlenews_utils import *
# Import Chinese finance utilities if available
try:
from .chinese_finance_utils import get_chinese_social_sentiment
except ImportError:
def get_chinese_social_sentiment(*args, **kwargs):
return "Chinese finance utilities not available"
from .finnhub_utils import get_data_in_range
from dateutil.relativedelta import relativedelta
from concurrent.futures import ThreadPoolExecutor
@ -43,7 +50,14 @@ def get_finnhub_news(
result = get_data_in_range(ticker, before, curr_date, "news_data", DATA_DIR)
if len(result) == 0:
return ""
error_msg = f"⚠️ Unable to retrieve news data for {ticker} ({before} to {curr_date})\n"
error_msg += f"Possible reasons:\n"
error_msg += f"1. Data files do not exist or path configuration is incorrect\n"
error_msg += f"2. No news data available for the specified date range\n"
error_msg += f"3. Need to download or update Finnhub news data first\n"
error_msg += f"Suggestion: Check data directory configuration or re-fetch news data"
print(f"📰 [DEBUG] {error_msg}")
return error_msg
combined_result = ""
for day, data in result.items():
@ -805,3 +819,133 @@ def get_fundamentals_openai(ticker, curr_date):
)
return response.output[1].content[0].text
def get_fundamentals_finnhub(ticker, curr_date):
"""
Use Finnhub API to get stock fundamental data as an alternative to OpenAI
Args:
ticker (str): Stock symbol
curr_date (str): Current date in yyyy-mm-dd format
Returns:
str: Formatted fundamental data report
"""
try:
import finnhub
import os
# Try to import cache manager
try:
from .cache_manager import get_cache
cache = get_cache()
# Check cache first
cached_key = cache.find_cached_stock_data(ticker, curr_date, curr_date, "finnhub_fundamentals")
if cached_key and cache.is_cache_valid(cached_key, ticker):
cached_data = cache.load_stock_data(cached_key)
if cached_data:
print(f"💾 [DEBUG] Loading Finnhub fundamental data from cache: {ticker}")
return cached_data
except ImportError:
cache = None
print("⚠️ Cache manager not available, proceeding without cache")
# Get Finnhub API key
api_key = os.getenv('FINNHUB_API_KEY')
if not api_key:
return "Error: FINNHUB_API_KEY environment variable not configured"
# Initialize Finnhub client
finnhub_client = finnhub.Client(api_key=api_key)
print(f"📊 [DEBUG] Using Finnhub API to get fundamental data for {ticker}...")
# Get basic financial data
try:
basic_financials = finnhub_client.company_basic_financials(ticker, 'all')
except Exception as e:
print(f"❌ [DEBUG] Failed to get Finnhub basic financials: {str(e)}")
basic_financials = None
# Get company profile
try:
company_profile = finnhub_client.company_profile2(symbol=ticker)
except Exception as e:
print(f"❌ [DEBUG] Failed to get Finnhub company profile: {str(e)}")
company_profile = None
# Get earnings data
try:
earnings = finnhub_client.company_earnings(ticker, limit=4)
except Exception as e:
print(f"❌ [DEBUG] Failed to get Finnhub earnings data: {str(e)}")
earnings = None
# Format report
report = f"# {ticker} Fundamental Analysis Report (Finnhub Data Source)\n\n"
report += f"**Data Retrieved**: {curr_date}\n"
report += f"**Data Source**: Finnhub API\n\n"
# Company profile section
if company_profile:
report += "## Company Profile\n"
report += f"- **Company Name**: {company_profile.get('name', 'N/A')}\n"
report += f"- **Industry**: {company_profile.get('finnhubIndustry', 'N/A')}\n"
report += f"- **Country**: {company_profile.get('country', 'N/A')}\n"
report += f"- **Currency**: {company_profile.get('currency', 'N/A')}\n"
report += f"- **Market Cap**: {company_profile.get('marketCapitalization', 'N/A')} million USD\n"
report += f"- **Shares Outstanding**: {company_profile.get('shareOutstanding', 'N/A')} million shares\n\n"
# Basic financial metrics
if basic_financials and 'metric' in basic_financials:
metrics = basic_financials['metric']
report += "## Key Financial Metrics\n"
# Valuation metrics
report += "### Valuation Metrics\n"
report += f"- **P/E Ratio (TTM)**: {metrics.get('peBasicExclExtraTTM', 'N/A')}\n"
report += f"- **P/B Ratio**: {metrics.get('pbAnnual', 'N/A')}\n"
report += f"- **P/S Ratio (TTM)**: {metrics.get('psAnnual', 'N/A')}\n"
report += f"- **EV/EBITDA (TTM)**: {metrics.get('evEbitdaTTM', 'N/A')}\n\n"
# Profitability metrics
report += "### Profitability Metrics\n"
report += f"- **ROE (TTM)**: {metrics.get('roeTTM', 'N/A')}%\n"
report += f"- **ROA (TTM)**: {metrics.get('roaTTM', 'N/A')}%\n"
report += f"- **Gross Margin (TTM)**: {metrics.get('grossMarginTTM', 'N/A')}%\n"
report += f"- **Net Margin (TTM)**: {metrics.get('netProfitMarginTTM', 'N/A')}%\n\n"
# Growth metrics
report += "### Growth Metrics\n"
report += f"- **Revenue Growth (5Y)**: {metrics.get('revenueGrowthTTMYoy', 'N/A')}%\n"
report += f"- **EPS Growth (5Y)**: {metrics.get('epsGrowthTTMYoy', 'N/A')}%\n\n"
# Earnings data
if earnings and len(earnings) > 0:
report += "## Recent Earnings\n"
for i, earning in enumerate(earnings[:4]): # Show last 4 quarters
report += f"### Q{i+1} (Period: {earning.get('period', 'N/A')})\n"
report += f"- **Actual EPS**: ${earning.get('actual', 'N/A')}\n"
report += f"- **Estimated EPS**: ${earning.get('estimate', 'N/A')}\n"
if earning.get('actual') and earning.get('estimate'):
surprise = earning['actual'] - earning['estimate']
report += f"- **Surprise**: ${surprise:.2f}\n"
report += "\n"
# Cache the result if cache is available
if cache:
try:
cache.save_stock_data(ticker, report, curr_date, curr_date, "finnhub_fundamentals")
print(f"💾 [DEBUG] Cached Finnhub fundamental data for {ticker}")
except Exception as e:
print(f"⚠️ [DEBUG] Failed to cache data: {e}")
print(f"✅ [DEBUG] Successfully retrieved Finnhub fundamental data for {ticker}")
return report
except ImportError:
return "Error: finnhub-python package not installed. Please install with: pip install finnhub-python"
except Exception as e:
error_msg = f"Error retrieving Finnhub fundamental data for {ticker}: {str(e)}"
print(f"❌ [DEBUG] {error_msg}")
return error_msg

View File

@ -0,0 +1,398 @@
#!/usr/bin/env python3
"""
优化的A股数据获取工具
集成缓存策略和通达信API提高数据获取效率
"""
import os
import time
import random
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from .cache_manager import get_cache
from .config import get_config
class OptimizedChinaDataProvider:
"""优化的A股数据提供器 - 集成缓存和通达信API"""
def __init__(self):
self.cache = get_cache()
self.config = get_config()
self.last_api_call = 0
self.min_api_interval = 0.5 # 通达信API调用间隔较短
print("📊 优化A股数据提供器初始化完成")
def _wait_for_rate_limit(self):
"""等待API限制"""
current_time = time.time()
time_since_last_call = current_time - self.last_api_call
if time_since_last_call < self.min_api_interval:
wait_time = self.min_api_interval - time_since_last_call
time.sleep(wait_time)
self.last_api_call = time.time()
def get_stock_data(self, symbol: str, start_date: str, end_date: str,
force_refresh: bool = False) -> str:
"""
获取A股数据 - 优先使用缓存
Args:
symbol: 股票代码6位数字
start_date: 开始日期 (YYYY-MM-DD)
end_date: 结束日期 (YYYY-MM-DD)
force_refresh: 是否强制刷新缓存
Returns:
格式化的股票数据字符串
"""
print(f"📈 获取A股数据: {symbol} ({start_date}{end_date})")
# 检查缓存(除非强制刷新)
if not force_refresh:
cache_key = self.cache.find_cached_stock_data(
symbol=symbol,
start_date=start_date,
end_date=end_date,
data_source="tdx"
)
if cache_key:
cached_data = self.cache.load_stock_data(cache_key)
if cached_data:
print(f"⚡ 从缓存加载A股数据: {symbol}")
return cached_data
# 缓存未命中从通达信API获取
print(f"🌐 从通达信API获取数据: {symbol}")
try:
# API限制处理
self._wait_for_rate_limit()
# 调用通达信API
from .tdx_utils import get_china_stock_data
formatted_data = get_china_stock_data(
stock_code=symbol,
start_date=start_date,
end_date=end_date
)
# 检查是否获取成功
if "" in formatted_data or "错误" in formatted_data:
print(f"❌ 通达信API调用失败: {symbol}")
# 尝试从旧缓存获取数据
old_cache = self._try_get_old_cache(symbol, start_date, end_date)
if old_cache:
print(f"📁 使用过期缓存数据: {symbol}")
return old_cache
# 生成备用数据
return self._generate_fallback_data(symbol, start_date, end_date, "通达信API调用失败")
# 保存到缓存
self.cache.save_stock_data(
symbol=symbol,
data=formatted_data,
start_date=start_date,
end_date=end_date,
data_source="tdx"
)
print(f"✅ A股数据获取成功: {symbol}")
return formatted_data
except Exception as e:
error_msg = f"通达信API调用异常: {str(e)}"
print(f"{error_msg}")
# 尝试从旧缓存获取数据
old_cache = self._try_get_old_cache(symbol, start_date, end_date)
if old_cache:
print(f"📁 使用过期缓存数据: {symbol}")
return old_cache
# 生成备用数据
return self._generate_fallback_data(symbol, start_date, end_date, error_msg)
def get_fundamentals_data(self, symbol: str, force_refresh: bool = False) -> str:
"""
获取A股基本面数据 - 优先使用缓存
Args:
symbol: 股票代码
force_refresh: 是否强制刷新缓存
Returns:
格式化的基本面数据字符串
"""
print(f"📊 获取A股基本面数据: {symbol}")
# 检查缓存(除非强制刷新)
if not force_refresh:
# 查找基本面数据缓存
for metadata_file in self.cache.metadata_dir.glob(f"*_meta.json"):
try:
import json
with open(metadata_file, 'r', encoding='utf-8') as f:
metadata = json.load(f)
if (metadata.get('symbol') == symbol and
metadata.get('data_type') == 'fundamentals' and
metadata.get('market_type') == 'china'):
cache_key = metadata_file.stem.replace('_meta', '')
if self.cache.is_cache_valid(cache_key, symbol=symbol, data_type='fundamentals'):
cached_data = self.cache.load_stock_data(cache_key)
if cached_data:
print(f"⚡ 从缓存加载A股基本面数据: {symbol}")
return cached_data
except Exception:
continue
# 缓存未命中,生成基本面分析
print(f"🔍 生成A股基本面分析: {symbol}")
try:
# 先获取股票数据
current_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
stock_data = self.get_stock_data(symbol, start_date, current_date)
# 生成基本面分析报告
fundamentals_data = self._generate_fundamentals_report(symbol, stock_data)
# 保存到缓存
self.cache.save_fundamentals_data(
symbol=symbol,
fundamentals_data=fundamentals_data,
data_source="tdx_analysis"
)
print(f"✅ A股基本面数据生成成功: {symbol}")
return fundamentals_data
except Exception as e:
error_msg = f"基本面数据生成失败: {str(e)}"
print(f"{error_msg}")
return self._generate_fallback_fundamentals(symbol, error_msg)
def _generate_fundamentals_report(self, symbol: str, stock_data: str) -> str:
"""基于股票数据生成基本面分析报告"""
# 从股票数据中提取信息
company_name = "未知公司"
current_price = "N/A"
if "股票名称:" in stock_data:
lines = stock_data.split('\n')
for line in lines:
if "股票名称:" in line:
company_name = line.split(':')[1].strip()
elif "当前价格:" in line:
current_price = line.split(':')[1].strip()
report = f"""# 中国A股基本面分析报告 - {symbol}{company_name}
## 公司基本信息
- 股票代码{symbol}
- 股票名称{company_name}
- 行业分类根据股票代码判断所属行业
- 所属市场深圳证券交易所/上海证券交易所
- 最新股价{current_price}
- 分析日期{datetime.now().strftime('%Y年%m月%d')}
## 财务状况分析
基于最新的市场数据和技术指标分析
### 资产负债表分析
- **总资产规模**作为A股上市公司具备一定的资产规模
- **负债结构**需要关注资产负债率和流动比率
- **股东权益**关注净资产收益率和每股净资产
### 现金流分析
- **经营现金流**关注主营业务现金流入情况
- **投资现金流**分析公司投资扩张策略
- **筹资现金流**关注融资结构和偿债能力
## 盈利能力分析
### 收入分析
- **营业收入增长率**关注收入增长趋势
- **主营业务收入占比**分析业务集中度
- **收入季节性**识别业务周期性特征
### 利润分析
- **毛利率水平**反映产品竞争力
- **净利润率**体现整体盈利能力
- **ROE净资产收益率**衡量股东回报水平
## 成长性分析
### 历史成长性
- **营收复合增长率**过去3-5年的收入增长情况
- **净利润增长率**盈利增长的可持续性
- **市场份额变化**在行业中的竞争地位
### 未来成长潜力
- **行业发展前景**所处行业的成长空间
- **公司战略规划**未来发展方向和投资计划
- **创新能力**研发投入和技术优势
## 估值分析
### 相对估值
- **市盈率PE**与同行业公司对比
- **市净率PB**相对于净资产的估值水平
- **市销率PS**相对于营业收入的估值
### 绝对估值
- **DCF估值**基于现金流贴现的内在价值
- **资产价值**净资产重估价值
- **分红收益率**股息回报分析
## 风险分析
### 系统性风险
- **宏观经济风险**经济周期对公司的影响
- **政策风险**行业政策变化的影响
- **市场风险**股市波动对估值的影响
### 非系统性风险
- **经营风险**公司特有的经营风险
- **财务风险**债务结构和偿债能力风险
- **管理风险**管理层变动和决策风险
## 投资建议
### 综合评价
基于以上分析该股票的投资价值评估
**优势**
- A股市场上市公司监管相对完善
- 具备一定的市场地位和品牌价值
- 财务信息透明度较高
**风险**
- 需要关注宏观经济环境变化
- 行业竞争加剧的影响
- 政策调整对业务的潜在影响
### 操作建议
- **投资策略**建议采用价值投资策略关注长期基本面
- **仓位建议**根据风险承受能力合理配置仓位
- **关注指标**重点关注ROEPE现金流等核心指标
---
*本报告基于公开信息和技术分析生成仅供参考不构成投资建议投资有风险入市需谨慎*
数据来源通达信API + 基本面分析
生成时间{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
return report
def _try_get_old_cache(self, symbol: str, start_date: str, end_date: str) -> Optional[str]:
"""尝试获取过期的缓存数据作为备用"""
try:
# 查找任何相关的缓存不考虑TTL
for metadata_file in self.cache.metadata_dir.glob(f"*_meta.json"):
try:
import json
with open(metadata_file, 'r', encoding='utf-8') as f:
metadata = json.load(f)
if (metadata.get('symbol') == symbol and
metadata.get('data_type') == 'stock_data' and
metadata.get('market_type') == 'china'):
cache_key = metadata_file.stem.replace('_meta', '')
cached_data = self.cache.load_stock_data(cache_key)
if cached_data:
return cached_data + "\n\n⚠️ 注意: 使用的是过期缓存数据"
except Exception:
continue
except Exception:
pass
return None
def _generate_fallback_data(self, symbol: str, start_date: str, end_date: str, error_msg: str) -> str:
"""生成备用数据"""
return f"""# {symbol} A股数据获取失败
## ❌ 错误信息
{error_msg}
## 📊 模拟数据(仅供演示)
- 股票代码: {symbol}
- 股票名称: 模拟公司
- 数据期间: {start_date} {end_date}
- 模拟价格: ¥{random.uniform(10, 50):.2f}
- 模拟涨跌: {random.uniform(-5, 5):+.2f}%
## ⚠️ 重要提示
由于通达信API限制或网络问题无法获取实时数据
建议稍后重试或检查网络连接
生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
def _generate_fallback_fundamentals(self, symbol: str, error_msg: str) -> str:
"""生成备用基本面数据"""
return f"""# {symbol} A股基本面分析失败
## ❌ 错误信息
{error_msg}
## 📊 基本信息
- 股票代码: {symbol}
- 分析状态: 数据获取失败
- 建议: 稍后重试或检查网络连接
生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
# 全局实例
_china_data_provider = None
def get_optimized_china_data_provider() -> OptimizedChinaDataProvider:
"""获取全局A股数据提供器实例"""
global _china_data_provider
if _china_data_provider is None:
_china_data_provider = OptimizedChinaDataProvider()
return _china_data_provider
def get_china_stock_data_cached(symbol: str, start_date: str, end_date: str,
force_refresh: bool = False) -> str:
"""
获取A股数据的便捷函数
Args:
symbol: 股票代码6位数字
start_date: 开始日期 (YYYY-MM-DD)
end_date: 结束日期 (YYYY-MM-DD)
force_refresh: 是否强制刷新缓存
Returns:
格式化的股票数据字符串
"""
provider = get_optimized_china_data_provider()
return provider.get_stock_data(symbol, start_date, end_date, force_refresh)
def get_china_fundamentals_cached(symbol: str, force_refresh: bool = False) -> str:
"""
获取A股基本面数据的便捷函数
Args:
symbol: 股票代码6位数字
force_refresh: 是否强制刷新缓存
Returns:
格式化的基本面数据字符串
"""
provider = get_optimized_china_data_provider()
return provider.get_fundamentals_data(symbol, force_refresh)

View File

@ -0,0 +1,404 @@
#!/usr/bin/env python3
"""
Optimized US Stock Data Fetcher
Integrates caching strategy to reduce API calls and improve response speed
"""
import os
import time
import random
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
import yfinance as yf
import pandas as pd
from .cache_manager import get_cache
from .config import get_config
class OptimizedUSDataProvider:
"""Optimized US Stock Data Provider - Integrates caching and API rate limiting"""
def __init__(self):
self.cache = get_cache()
self.config = get_config()
self.last_api_call = 0
self.min_api_interval = 1.0 # Minimum API call interval (seconds)
print("📊 Optimized US stock data provider initialized")
def _wait_for_rate_limit(self):
"""Wait for API rate limit"""
current_time = time.time()
time_since_last_call = current_time - self.last_api_call
if time_since_last_call < self.min_api_interval:
wait_time = self.min_api_interval - time_since_last_call
print(f"⏳ API rate limit wait {wait_time:.1f}s...")
time.sleep(wait_time)
self.last_api_call = time.time()
def get_stock_data(self, symbol: str, start_date: str, end_date: str,
force_refresh: bool = False) -> str:
"""
Get US stock data - prioritize cache usage
Args:
symbol: Stock symbol
start_date: Start date (YYYY-MM-DD)
end_date: End date (YYYY-MM-DD)
force_refresh: Whether to force refresh cache
Returns:
Formatted stock data string
"""
try:
# Check cache first (unless force refresh)
if not force_refresh:
cache_key = self.cache.find_cached_stock_data(
symbol, start_date, end_date, "optimized_yfinance"
)
if cache_key and self.cache.is_cache_valid(cache_key, symbol):
cached_data = self.cache.load_stock_data(cache_key)
if cached_data:
print(f"📖 Using cached data for {symbol}")
if isinstance(cached_data, pd.DataFrame):
return self._format_stock_data(cached_data, symbol)
else:
return cached_data
# Fetch new data from API
print(f"🌐 Fetching new data for {symbol} from {start_date} to {end_date}")
# Wait for rate limit
self._wait_for_rate_limit()
# Try Yahoo Finance first
try:
data = self._fetch_from_yfinance(symbol, start_date, end_date)
if data is not None and not data.empty:
# Cache the DataFrame
cache_key = self.cache.save_stock_data(
symbol, data, start_date, end_date, "optimized_yfinance"
)
# Format and return
formatted_data = self._format_stock_data(data, symbol)
print(f"✅ Successfully fetched and cached data for {symbol}")
return formatted_data
else:
print(f"⚠️ No data returned from Yahoo Finance for {symbol}")
except Exception as e:
print(f"❌ Yahoo Finance error for {symbol}: {e}")
# Fallback: Try FINNHUB (if API key available)
try:
finnhub_data = self._fetch_from_finnhub(symbol, start_date, end_date)
if finnhub_data:
# Cache the string data
cache_key = self.cache.save_stock_data(
symbol, finnhub_data, start_date, end_date, "optimized_finnhub"
)
print(f"✅ Successfully fetched data from FINNHUB for {symbol}")
return finnhub_data
except Exception as e:
print(f"❌ FINNHUB error for {symbol}: {e}")
# If all fails, return error message
error_msg = f"❌ Failed to fetch data for {symbol} from {start_date} to {end_date}"
print(error_msg)
return error_msg
except Exception as e:
error_msg = f"❌ Unexpected error fetching data for {symbol}: {e}"
print(error_msg)
return error_msg
def _fetch_from_yfinance(self, symbol: str, start_date: str, end_date: str) -> Optional[pd.DataFrame]:
"""Fetch data from Yahoo Finance"""
try:
ticker = yf.Ticker(symbol)
data = ticker.history(start=start_date, end=end_date)
if data.empty:
print(f"⚠️ No data available for {symbol} in the specified date range")
return None
# Reset index to make Date a column
data = data.reset_index()
# Ensure we have the required columns
required_columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']
missing_columns = [col for col in required_columns if col not in data.columns]
if missing_columns:
print(f"⚠️ Missing columns for {symbol}: {missing_columns}")
return None
return data[required_columns]
except Exception as e:
print(f"❌ Yahoo Finance fetch error for {symbol}: {e}")
return None
def _fetch_from_finnhub(self, symbol: str, start_date: str, end_date: str) -> Optional[str]:
"""Fetch data from FINNHUB API"""
try:
# Check if FINNHUB API key is available
finnhub_api_key = os.getenv('FINNHUB_API_KEY')
if not finnhub_api_key:
print("⚠️ FINNHUB API key not found, skipping FINNHUB data fetch")
return None
import finnhub
# Initialize FINNHUB client
finnhub_client = finnhub.Client(api_key=finnhub_api_key)
# Convert dates to timestamps
start_timestamp = int(datetime.strptime(start_date, '%Y-%m-%d').timestamp())
end_timestamp = int(datetime.strptime(end_date, '%Y-%m-%d').timestamp())
# Fetch candle data
candle_data = finnhub_client.stock_candles(symbol, 'D', start_timestamp, end_timestamp)
if candle_data['s'] != 'ok':
print(f"⚠️ FINNHUB returned status: {candle_data['s']} for {symbol}")
return None
# Format data
formatted_data = self._format_finnhub_data(candle_data, symbol)
return formatted_data
except ImportError:
print("⚠️ finnhub-python package not installed, skipping FINNHUB data fetch")
return None
except Exception as e:
print(f"❌ FINNHUB fetch error for {symbol}: {e}")
return None
def _format_stock_data(self, data: pd.DataFrame, symbol: str) -> str:
"""Format DataFrame stock data into string"""
try:
# Ensure Date column is properly formatted
if 'Date' in data.columns:
data['Date'] = pd.to_datetime(data['Date']).dt.strftime('%Y-%m-%d')
# Round numerical columns to 2 decimal places
numeric_columns = ['Open', 'High', 'Low', 'Close']
for col in numeric_columns:
if col in data.columns:
data[col] = data[col].round(2)
# Format volume as integer
if 'Volume' in data.columns:
data['Volume'] = data['Volume'].astype(int)
# Create formatted string
formatted_lines = [f"Stock Data for {symbol}:"]
formatted_lines.append("Date,Open,High,Low,Close,Volume")
for _, row in data.iterrows():
line = f"{row['Date']},{row['Open']},{row['High']},{row['Low']},{row['Close']},{row['Volume']}"
formatted_lines.append(line)
# Add summary statistics
if len(data) > 0:
formatted_lines.append(f"\nSummary for {symbol}:")
formatted_lines.append(f"Period: {data['Date'].iloc[0]} to {data['Date'].iloc[-1]}")
formatted_lines.append(f"Total trading days: {len(data)}")
formatted_lines.append(f"Average volume: {data['Volume'].mean():,.0f}")
formatted_lines.append(f"Price range: ${data['Low'].min():.2f} - ${data['High'].max():.2f}")
# Calculate basic statistics
start_price = data['Open'].iloc[0]
end_price = data['Close'].iloc[-1]
price_change = end_price - start_price
price_change_pct = (price_change / start_price) * 100
formatted_lines.append(f"Period return: {price_change_pct:+.2f}% (${price_change:+.2f})")
return "\n".join(formatted_lines)
except Exception as e:
print(f"❌ Error formatting stock data for {symbol}: {e}")
return f"Error formatting data for {symbol}: {str(e)}"
def _format_finnhub_data(self, candle_data: Dict, symbol: str) -> str:
"""Format FINNHUB candle data into string"""
try:
# Extract data arrays
timestamps = candle_data['t']
opens = candle_data['o']
highs = candle_data['h']
lows = candle_data['l']
closes = candle_data['c']
volumes = candle_data['v']
# Create formatted string
formatted_lines = [f"Stock Data for {symbol} (FINNHUB):"]
formatted_lines.append("Date,Open,High,Low,Close,Volume")
for i in range(len(timestamps)):
date = datetime.fromtimestamp(timestamps[i]).strftime('%Y-%m-%d')
line = f"{date},{opens[i]:.2f},{highs[i]:.2f},{lows[i]:.2f},{closes[i]:.2f},{int(volumes[i])}"
formatted_lines.append(line)
# Add summary
if len(timestamps) > 0:
start_date = datetime.fromtimestamp(timestamps[0]).strftime('%Y-%m-%d')
end_date = datetime.fromtimestamp(timestamps[-1]).strftime('%Y-%m-%d')
formatted_lines.append(f"\nSummary for {symbol}:")
formatted_lines.append(f"Period: {start_date} to {end_date}")
formatted_lines.append(f"Total trading days: {len(timestamps)}")
formatted_lines.append(f"Average volume: {sum(volumes)/len(volumes):,.0f}")
formatted_lines.append(f"Price range: ${min(lows):.2f} - ${max(highs):.2f}")
# Calculate return
price_change = closes[-1] - opens[0]
price_change_pct = (price_change / opens[0]) * 100
formatted_lines.append(f"Period return: {price_change_pct:+.2f}% (${price_change:+.2f})")
return "\n".join(formatted_lines)
except Exception as e:
print(f"❌ Error formatting FINNHUB data for {symbol}: {e}")
return f"Error formatting FINNHUB data for {symbol}: {str(e)}"
def get_stock_with_indicators(self, symbol: str, start_date: str, end_date: str,
indicators: list = None) -> str:
"""
Get stock data with technical indicators
Args:
symbol: Stock symbol
start_date: Start date (YYYY-MM-DD)
end_date: End date (YYYY-MM-DD)
indicators: List of indicators to calculate ['sma_20', 'rsi', 'macd']
Returns:
Formatted stock data with indicators
"""
try:
# Get basic stock data
basic_data = self.get_stock_data(symbol, start_date, end_date)
if basic_data.startswith(""):
return basic_data
# If no indicators requested, return basic data
if not indicators:
return basic_data
# Fetch DataFrame for indicator calculation
data_df = self._fetch_from_yfinance(symbol, start_date, end_date)
if data_df is None or data_df.empty:
return basic_data
# Calculate indicators
indicator_data = self._calculate_indicators(data_df, indicators)
# Combine basic data with indicators
combined_data = basic_data + "\n\nTechnical Indicators:\n" + indicator_data
return combined_data
except Exception as e:
error_msg = f"❌ Error getting stock data with indicators for {symbol}: {e}"
print(error_msg)
return error_msg
def _calculate_indicators(self, data: pd.DataFrame, indicators: list) -> str:
"""Calculate technical indicators"""
try:
indicator_lines = []
for indicator in indicators:
if indicator == 'sma_20':
data['SMA_20'] = data['Close'].rolling(window=20).mean()
latest_sma = data['SMA_20'].iloc[-1]
indicator_lines.append(f"SMA(20): ${latest_sma:.2f}")
elif indicator == 'rsi':
# Simple RSI calculation
delta = data['Close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
latest_rsi = rsi.iloc[-1]
indicator_lines.append(f"RSI(14): {latest_rsi:.2f}")
elif indicator == 'macd':
# Simple MACD calculation
ema_12 = data['Close'].ewm(span=12).mean()
ema_26 = data['Close'].ewm(span=26).mean()
macd_line = ema_12 - ema_26
signal_line = macd_line.ewm(span=9).mean()
latest_macd = macd_line.iloc[-1]
latest_signal = signal_line.iloc[-1]
indicator_lines.append(f"MACD: {latest_macd:.4f}, Signal: {latest_signal:.4f}")
return "\n".join(indicator_lines)
except Exception as e:
print(f"❌ Error calculating indicators: {e}")
return f"Error calculating indicators: {str(e)}"
# Global provider instance
_global_provider = None
def get_optimized_us_data_provider() -> OptimizedUSDataProvider:
"""
Get global optimized US data provider instance
Returns:
OptimizedUSDataProvider instance
"""
global _global_provider
if _global_provider is None:
_global_provider = OptimizedUSDataProvider()
return _global_provider
# Convenience functions
def get_optimized_stock_data(symbol: str, start_date: str, end_date: str,
force_refresh: bool = False) -> str:
"""Get optimized stock data (convenience function)"""
provider = get_optimized_us_data_provider()
return provider.get_stock_data(symbol, start_date, end_date, force_refresh)
def get_stock_with_indicators(symbol: str, start_date: str, end_date: str,
indicators: list = None) -> str:
"""Get stock data with technical indicators (convenience function)"""
provider = get_optimized_us_data_provider()
return provider.get_stock_with_indicators(symbol, start_date, end_date, indicators)
if __name__ == "__main__":
# Test the optimized data provider
print("🧪 Testing Optimized US Data Provider...")
# Initialize provider
provider = OptimizedUSDataProvider()
# Test data fetch
data = provider.get_stock_data("AAPL", "2024-01-01", "2024-01-31")
print("Sample data:")
print(data[:500] + "..." if len(data) > 500 else data)
# Test with indicators
data_with_indicators = provider.get_stock_with_indicators(
"AAPL", "2024-01-01", "2024-01-31",
indicators=['sma_20', 'rsi', 'macd']
)
print("\nData with indicators:")
print(data_with_indicators[-500:] if len(data_with_indicators) > 500 else data_with_indicators)
print("✅ Optimized data provider test completed!")

View File

@ -0,0 +1,395 @@
#!/usr/bin/env python3
"""
实时新闻数据获取工具
解决新闻滞后性问题
"""
import requests
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import time
import os
from dataclasses import dataclass
@dataclass
class NewsItem:
"""新闻项目数据结构"""
title: str
content: str
source: str
publish_time: datetime
url: str
urgency: str # high, medium, low
relevance_score: float
class RealtimeNewsAggregator:
"""实时新闻聚合器"""
def __init__(self):
self.headers = {
'User-Agent': 'TradingAgents-CN/1.0'
}
# API密钥配置
self.finnhub_key = os.getenv('FINNHUB_API_KEY')
self.alpha_vantage_key = os.getenv('ALPHA_VANTAGE_API_KEY')
self.newsapi_key = os.getenv('NEWSAPI_KEY')
def get_realtime_stock_news(self, ticker: str, hours_back: int = 6) -> List[NewsItem]:
"""
获取实时股票新闻
优先级专业API > 新闻API > 搜索引擎
"""
all_news = []
# 1. FinnHub实时新闻 (最高优先级)
finnhub_news = self._get_finnhub_realtime_news(ticker, hours_back)
all_news.extend(finnhub_news)
# 2. Alpha Vantage新闻
av_news = self._get_alpha_vantage_news(ticker, hours_back)
all_news.extend(av_news)
# 3. NewsAPI (如果配置了)
if self.newsapi_key:
newsapi_news = self._get_newsapi_news(ticker, hours_back)
all_news.extend(newsapi_news)
# 4. 中文财经新闻源
chinese_news = self._get_chinese_finance_news(ticker, hours_back)
all_news.extend(chinese_news)
# 去重和排序
unique_news = self._deduplicate_news(all_news)
return sorted(unique_news, key=lambda x: x.publish_time, reverse=True)
def _get_finnhub_realtime_news(self, ticker: str, hours_back: int) -> List[NewsItem]:
"""获取FinnHub实时新闻"""
if not self.finnhub_key:
return []
try:
# 计算时间范围
end_time = datetime.now()
start_time = end_time - timedelta(hours=hours_back)
# FinnHub API调用
url = "https://finnhub.io/api/v1/company-news"
params = {
'symbol': ticker,
'from': start_time.strftime('%Y-%m-%d'),
'to': end_time.strftime('%Y-%m-%d'),
'token': self.finnhub_key
}
response = requests.get(url, params=params, headers=self.headers)
response.raise_for_status()
news_data = response.json()
news_items = []
for item in news_data:
# 检查新闻时效性
publish_time = datetime.fromtimestamp(item.get('datetime', 0))
if publish_time < start_time:
continue
# 评估紧急程度
urgency = self._assess_news_urgency(item.get('headline', ''), item.get('summary', ''))
news_items.append(NewsItem(
title=item.get('headline', ''),
content=item.get('summary', ''),
source=item.get('source', 'FinnHub'),
publish_time=publish_time,
url=item.get('url', ''),
urgency=urgency,
relevance_score=self._calculate_relevance(item.get('headline', ''), ticker)
))
return news_items
except Exception as e:
print(f"FinnHub新闻获取失败: {e}")
return []
def _get_alpha_vantage_news(self, ticker: str, hours_back: int) -> List[NewsItem]:
"""获取Alpha Vantage新闻"""
if not self.alpha_vantage_key:
return []
try:
url = "https://www.alphavantage.co/query"
params = {
'function': 'NEWS_SENTIMENT',
'tickers': ticker,
'apikey': self.alpha_vantage_key,
'limit': 50
}
response = requests.get(url, params=params, headers=self.headers)
response.raise_for_status()
data = response.json()
news_items = []
if 'feed' in data:
for item in data['feed']:
# 解析时间
time_str = item.get('time_published', '')
try:
publish_time = datetime.strptime(time_str, '%Y%m%dT%H%M%S')
except:
continue
# 检查时效性
if publish_time < datetime.now() - timedelta(hours=hours_back):
continue
urgency = self._assess_news_urgency(item.get('title', ''), item.get('summary', ''))
news_items.append(NewsItem(
title=item.get('title', ''),
content=item.get('summary', ''),
source=item.get('source', 'Alpha Vantage'),
publish_time=publish_time,
url=item.get('url', ''),
urgency=urgency,
relevance_score=self._calculate_relevance(item.get('title', ''), ticker)
))
return news_items
except Exception as e:
print(f"Alpha Vantage新闻获取失败: {e}")
return []
def _get_newsapi_news(self, ticker: str, hours_back: int) -> List[NewsItem]:
"""获取NewsAPI新闻"""
try:
# 构建搜索查询
company_names = {
'AAPL': 'Apple',
'TSLA': 'Tesla',
'NVDA': 'NVIDIA',
'MSFT': 'Microsoft',
'GOOGL': 'Google'
}
query = f"{ticker} OR {company_names.get(ticker, ticker)}"
url = "https://newsapi.org/v2/everything"
params = {
'q': query,
'language': 'en',
'sortBy': 'publishedAt',
'from': (datetime.now() - timedelta(hours=hours_back)).isoformat(),
'apiKey': self.newsapi_key
}
response = requests.get(url, params=params, headers=self.headers)
response.raise_for_status()
data = response.json()
news_items = []
for item in data.get('articles', []):
# 解析时间
time_str = item.get('publishedAt', '')
try:
publish_time = datetime.fromisoformat(time_str.replace('Z', '+00:00'))
except:
continue
urgency = self._assess_news_urgency(item.get('title', ''), item.get('description', ''))
news_items.append(NewsItem(
title=item.get('title', ''),
content=item.get('description', ''),
source=item.get('source', {}).get('name', 'NewsAPI'),
publish_time=publish_time,
url=item.get('url', ''),
urgency=urgency,
relevance_score=self._calculate_relevance(item.get('title', ''), ticker)
))
return news_items
except Exception as e:
print(f"NewsAPI新闻获取失败: {e}")
return []
def _get_chinese_finance_news(self, ticker: str, hours_back: int) -> List[NewsItem]:
"""获取中文财经新闻"""
# 这里可以集成中文财经新闻API
# 例如:财联社、新浪财经、东方财富等
try:
# 示例集成财联社API (需要申请)
# 或者使用RSS源
news_items = []
# 财联社RSS (如果可用)
rss_sources = [
"https://www.cls.cn/api/sw?app=CailianpressWeb&os=web&sv=7.7.5",
# 可以添加更多RSS源
]
for rss_url in rss_sources:
try:
items = self._parse_rss_feed(rss_url, ticker, hours_back)
news_items.extend(items)
except:
continue
return news_items
except Exception as e:
print(f"中文财经新闻获取失败: {e}")
return []
def _parse_rss_feed(self, rss_url: str, ticker: str, hours_back: int) -> List[NewsItem]:
"""解析RSS源"""
# 简化实现实际需要使用feedparser库
return []
def _assess_news_urgency(self, title: str, content: str) -> str:
"""评估新闻紧急程度"""
text = (title + ' ' + content).lower()
# 高紧急度关键词
high_urgency_keywords = [
'breaking', 'urgent', 'alert', 'emergency', 'halt', 'suspend',
'突发', '紧急', '暂停', '停牌', '重大'
]
# 中等紧急度关键词
medium_urgency_keywords = [
'earnings', 'report', 'announce', 'launch', 'merger', 'acquisition',
'财报', '发布', '宣布', '并购', '收购'
]
if any(keyword in text for keyword in high_urgency_keywords):
return 'high'
elif any(keyword in text for keyword in medium_urgency_keywords):
return 'medium'
else:
return 'low'
def _calculate_relevance(self, title: str, ticker: str) -> float:
"""计算新闻相关性分数"""
text = title.lower()
ticker_lower = ticker.lower()
# 基础相关性
if ticker_lower in text:
return 1.0
# 公司名称匹配
company_names = {
'aapl': ['apple', 'iphone', 'ipad', 'mac'],
'tsla': ['tesla', 'elon musk', 'electric vehicle'],
'nvda': ['nvidia', 'gpu', 'ai chip'],
'msft': ['microsoft', 'windows', 'azure'],
'googl': ['google', 'alphabet', 'search']
}
if ticker_lower in company_names:
for name in company_names[ticker_lower]:
if name in text:
return 0.8
return 0.3 # 默认相关性
def _deduplicate_news(self, news_items: List[NewsItem]) -> List[NewsItem]:
"""去重新闻"""
seen_titles = set()
unique_news = []
for item in news_items:
# 简单的标题去重
title_key = item.title.lower().strip()
if title_key not in seen_titles and len(title_key) > 10:
seen_titles.add(title_key)
unique_news.append(item)
return unique_news
def format_news_report(self, news_items: List[NewsItem], ticker: str) -> str:
"""格式化新闻报告"""
if not news_items:
return f"未获取到{ticker}的实时新闻数据。"
# 按紧急程度分组
high_urgency = [n for n in news_items if n.urgency == 'high']
medium_urgency = [n for n in news_items if n.urgency == 'medium']
low_urgency = [n for n in news_items if n.urgency == 'low']
report = f"# {ticker} 实时新闻分析报告\n\n"
report += f"📅 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
report += f"📊 新闻总数: {len(news_items)}\n\n"
if high_urgency:
report += "## 🚨 紧急新闻\n\n"
for news in high_urgency[:3]: # 最多显示3条
report += f"### {news.title}\n"
report += f"**来源**: {news.source} | **时间**: {news.publish_time.strftime('%H:%M')}\n"
report += f"{news.content}\n\n"
if medium_urgency:
report += "## 📢 重要新闻\n\n"
for news in medium_urgency[:5]: # 最多显示5条
report += f"### {news.title}\n"
report += f"**来源**: {news.source} | **时间**: {news.publish_time.strftime('%H:%M')}\n"
report += f"{news.content}\n\n"
# 添加时效性说明
latest_news = max(news_items, key=lambda x: x.publish_time)
time_diff = datetime.now() - latest_news.publish_time
report += f"\n## ⏰ 数据时效性\n"
report += f"最新新闻发布于: {time_diff.total_seconds() / 60:.0f}分钟前\n"
if time_diff.total_seconds() < 1800: # 30分钟内
report += "🟢 数据时效性: 优秀 (30分钟内)\n"
elif time_diff.total_seconds() < 3600: # 1小时内
report += "🟡 数据时效性: 良好 (1小时内)\n"
else:
report += "🔴 数据时效性: 一般 (超过1小时)\n"
return report
def get_realtime_stock_news(ticker: str, curr_date: str, hours_back: int = 6) -> str:
"""
获取实时股票新闻的主要接口函数
"""
aggregator = RealtimeNewsAggregator()
try:
# 获取实时新闻
news_items = aggregator.get_realtime_stock_news(ticker, hours_back)
# 格式化报告
report = aggregator.format_news_report(news_items, ticker)
return report
except Exception as e:
return f"""
实时新闻获取失败 - {ticker}
分析日期: {curr_date}
错误信息: {str(e)}
💡 备用建议:
1. 检查API密钥配置 (FINNHUB_API_KEY, NEWSAPI_KEY)
2. 使用基础新闻分析作为备选
3. 关注官方财经媒体的最新报道
4. 考虑使用专业金融终端获取实时新闻
: 实时新闻获取依赖外部API服务的可用性
"""

View File

@ -0,0 +1,116 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
股票数据API接口
提供简单易用的股票数据获取接口内置完整的降级机制
"""
from typing import Dict, List, Optional, Any
from .stock_data_service import get_stock_data_service
def get_stock_info(stock_code: str) -> Optional[Dict[str, Any]]:
"""
获取单个股票的基础信息
Args:
stock_code: 股票代码 '000001'
Returns:
Dict: 股票信息包含code, name, market, category等字段
如果获取失败返回包含error字段的字典
Example:
>>> info = get_stock_info('000001')
>>> print(info['name']) # 输出: 平安银行
"""
service = get_stock_data_service()
return service.get_stock_basic_info(stock_code)
def get_all_stocks() -> List[Dict[str, Any]]:
"""
获取所有股票列表
Returns:
List[Dict]: 股票列表每个元素包含股票基础信息
如果获取失败返回包含error字段的字典
Example:
>>> stocks = get_all_stocks()
>>> print(f"共有{len(stocks)}只股票")
"""
service = get_stock_data_service()
result = service.get_stock_basic_info()
if isinstance(result, list):
return result
elif isinstance(result, dict) and 'error' in result:
return [result] # 返回错误信息
else:
return []
def get_stock_data(stock_code: str, start_date: str, end_date: str) -> str:
"""
获取股票历史数据带降级机制
Args:
stock_code: 股票代码
start_date: 开始日期 'YYYY-MM-DD'
end_date: 结束日期 'YYYY-MM-DD'
Returns:
str: 格式化的股票数据报告
Example:
>>> data = get_stock_data('000001', '2024-01-01', '2024-01-31')
>>> print(data)
"""
service = get_stock_data_service()
return service.get_stock_data_with_fallback(stock_code, start_date, end_date)
def search_stocks_by_name(name: str) -> List[Dict[str, Any]]:
"""
根据股票名称搜索股票需要MongoDB支持
Args:
name: 股票名称关键词
Returns:
List[Dict]: 匹配的股票列表
Example:
>>> results = search_stocks_by_name('银行')
>>> for stock in results:
... print(f"{stock['code']}: {stock['name']}")
"""
# 这个功能需要MongoDB支持暂时通过原有方式实现
try:
from ..examples.stock_query_examples import EnhancedStockQueryService
service = EnhancedStockQueryService()
return service.query_stocks_by_name(name)
except Exception as e:
return [{'error': f'名称搜索功能不可用: {str(e)}'}]
def check_data_sources() -> Dict[str, Any]:
"""
检查数据源状态
Returns:
Dict: 各数据源的可用状态
Example:
>>> status = check_data_sources()
>>> print(f"MongoDB可用: {status['mongodb_available']}")
>>> print(f"通达信API可用: {status['tdx_api_available']}")
"""
service = get_stock_data_service()
return {
'mongodb_available': service.db_manager is not None and service.db_manager.mongodb_db is not None,
'tdx_api_available': service.tdx_provider is not None,
'enhanced_fetcher_available': True, # 这个通常都可用
'fallback_mode': service.db_manager is None or service.db_manager.mongodb_db is None,
'recommendation': (
"所有数据源正常" if service.db_manager and service.db_manager.mongodb_db
else "建议配置MongoDB以获得最佳性能当前使用通达信API降级模式"
)
}

View File

@ -0,0 +1,279 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
统一的股票数据获取服务
实现MongoDB -> 通达信API的完整降级机制
"""
import pandas as pd
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
import logging
try:
from tradingagents.config.database_manager import get_database_manager
DATABASE_MANAGER_AVAILABLE = True
except ImportError:
DATABASE_MANAGER_AVAILABLE = False
try:
from .tdx_utils import get_tdx_provider, TongDaXinDataProvider
TDX_AVAILABLE = True
except ImportError:
TDX_AVAILABLE = False
try:
import sys
import os
# 添加utils目录到路径
utils_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'utils')
if utils_path not in sys.path:
sys.path.append(utils_path)
from enhanced_stock_list_fetcher import enhanced_fetch_stock_list
ENHANCED_FETCHER_AVAILABLE = True
except ImportError:
ENHANCED_FETCHER_AVAILABLE = False
logger = logging.getLogger(__name__)
class StockDataService:
"""
统一的股票数据获取服务
实现完整的降级机制MongoDB -> 通达信API -> 缓存 -> 错误处理
"""
def __init__(self):
self.db_manager = None
self.tdx_provider = None
self._init_services()
def _init_services(self):
"""初始化服务"""
# 尝试初始化数据库管理器
if DATABASE_MANAGER_AVAILABLE:
try:
self.db_manager = get_database_manager()
if self.db_manager.is_mongodb_available():
print("✅ MongoDB连接成功")
else:
print("⚠️ MongoDB连接失败将使用通达信API")
except Exception as e:
print(f"⚠️ 数据库管理器初始化失败: {e}")
self.db_manager = None
# 尝试初始化通达信提供器
if TDX_AVAILABLE:
try:
self.tdx_provider = get_tdx_provider()
print("✅ 通达信API初始化成功")
except Exception as e:
print(f"⚠️ 通达信API初始化失败: {e}")
self.tdx_provider = None
def get_stock_basic_info(self, stock_code: str = None) -> Optional[Dict[str, Any]]:
"""
获取股票基础信息单个股票或全部股票
Args:
stock_code: 股票代码如果为None则返回所有股票
Returns:
Dict: 股票基础信息
"""
print(f"📊 获取股票基础信息: {stock_code or '全部股票'}")
# 1. 优先从MongoDB获取
if self.db_manager and self.db_manager.is_mongodb_available():
try:
result = self._get_from_mongodb(stock_code)
if result:
print(f"✅ 从MongoDB获取成功: {len(result) if isinstance(result, list) else 1}条记录")
return result
except Exception as e:
print(f"⚠️ MongoDB查询失败: {e}")
# 2. 降级到通达信API
print("🔄 MongoDB不可用降级到通达信API")
if ENHANCED_FETCHER_AVAILABLE:
try:
result = self._get_from_tdx_api(stock_code)
if result:
print(f"✅ 从通达信API获取成功: {len(result) if isinstance(result, list) else 1}条记录")
# 尝试缓存到MongoDB如果可用
self._cache_to_mongodb(result)
return result
except Exception as e:
print(f"⚠️ 通达信API查询失败: {e}")
# 3. 最后的降级方案
print("❌ 所有数据源都不可用")
return self._get_fallback_data(stock_code)
def _get_from_mongodb(self, stock_code: str = None) -> Optional[Dict[str, Any]]:
"""从MongoDB获取数据"""
try:
mongodb_client = self.db_manager.get_mongodb_client()
if not mongodb_client:
return None
db = mongodb_client[self.db_manager.mongodb_config["database"]]
collection = db['stock_basic_info']
if stock_code:
# 获取单个股票
result = collection.find_one({'code': stock_code})
return result if result else None
else:
# 获取所有股票
cursor = collection.find({})
results = list(cursor)
return results if results else None
except Exception as e:
logger.error(f"MongoDB查询失败: {e}")
return None
def _get_from_tdx_api(self, stock_code: str = None) -> Optional[Dict[str, Any]]:
"""从通达信API获取数据"""
try:
if stock_code:
# 获取单个股票信息
if self.tdx_provider:
# 使用现有的股票名称获取方法
stock_name = self.tdx_provider._get_stock_name(stock_code)
return {
'code': stock_code,
'name': stock_name,
'market': self._get_market_name(stock_code),
'category': self._get_stock_category(stock_code),
'source': 'tdx_api',
'updated_at': datetime.now().isoformat()
}
else:
# 获取所有股票列表
stock_df = enhanced_fetch_stock_list(
type_='stock',
enable_server_failover=True,
max_retries=3
)
if stock_df is not None and not stock_df.empty:
# 转换为字典列表
results = []
for _, row in stock_df.iterrows():
results.append({
'code': row.get('code', ''),
'name': row.get('name', ''),
'market': row.get('market', ''),
'category': row.get('category', ''),
'source': 'tdx_api',
'updated_at': datetime.now().isoformat()
})
return results
except Exception as e:
logger.error(f"通达信API查询失败: {e}")
return None
def _cache_to_mongodb(self, data: Any) -> bool:
"""将数据缓存到MongoDB"""
if not self.db_manager or not self.db_manager.mongodb_db:
return False
try:
collection = self.db_manager.mongodb_db['stock_basic_info']
if isinstance(data, list):
# 批量插入
for item in data:
collection.update_one(
{'code': item['code']},
{'$set': item},
upsert=True
)
print(f"💾 已缓存{len(data)}条记录到MongoDB")
elif isinstance(data, dict):
# 单条插入
collection.update_one(
{'code': data['code']},
{'$set': data},
upsert=True
)
print(f"💾 已缓存股票{data['code']}到MongoDB")
return True
except Exception as e:
logger.error(f"缓存到MongoDB失败: {e}")
return False
def _get_fallback_data(self, stock_code: str = None) -> Dict[str, Any]:
"""最后的降级数据"""
if stock_code:
return {
'code': stock_code,
'name': f'股票{stock_code}',
'market': self._get_market_name(stock_code),
'category': '未知',
'source': 'fallback',
'updated_at': datetime.now().isoformat(),
'error': '所有数据源都不可用'
}
else:
return {
'error': '无法获取股票列表,请检查网络连接和数据库配置',
'suggestion': '请确保MongoDB已配置或网络连接正常以访问通达信API'
}
def _get_market_name(self, stock_code: str) -> str:
"""根据股票代码判断市场"""
if stock_code.startswith(('60', '68', '90')):
return '上海'
elif stock_code.startswith(('00', '30', '20')):
return '深圳'
else:
return '未知'
def _get_stock_category(self, stock_code: str) -> str:
"""根据股票代码判断类别"""
if stock_code.startswith('60'):
return '沪市主板'
elif stock_code.startswith('68'):
return '科创板'
elif stock_code.startswith('00'):
return '深市主板'
elif stock_code.startswith('30'):
return '创业板'
elif stock_code.startswith('20'):
return '深市B股'
else:
return '其他'
def get_stock_data_with_fallback(self, stock_code: str, start_date: str, end_date: str) -> str:
"""
获取股票数据带降级机制
这是对现有get_china_stock_data函数的增强
"""
print(f"📊 获取股票数据: {stock_code} ({start_date}{end_date})")
# 首先确保股票基础信息可用
stock_info = self.get_stock_basic_info(stock_code)
if stock_info and 'error' in stock_info:
return f"❌ 无法获取股票{stock_code}的基础信息: {stock_info.get('error', '未知错误')}"
# 调用现有的get_china_stock_data函数
try:
from .tdx_utils import get_china_stock_data
return get_china_stock_data(stock_code, start_date, end_date)
except Exception as e:
return f"❌ 获取股票数据失败: {str(e)}\n\n💡 建议:\n1. 检查网络连接\n2. 确认股票代码格式正确\n3. 检查MongoDB配置"
# 全局服务实例
_stock_data_service = None
def get_stock_data_service() -> StockDataService:
"""获取股票数据服务实例(单例模式)"""
global _stock_data_service
if _stock_data_service is None:
_stock_data_service = StockDataService()
return _stock_data_service

View File

@ -0,0 +1,856 @@
#!/usr/bin/env python3
"""
通达信API数据获取工具
支持A股港股实时数据和历史数据
"""
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Tuple
import warnings
warnings.filterwarnings('ignore')
# 导入数据库管理器
try:
from tradingagents.config.database_manager import get_database_manager
DB_MANAGER_AVAILABLE = True
except ImportError:
DB_MANAGER_AVAILABLE = False
print("⚠️ 数据库缓存管理器不可用,尝试文件缓存")
# 导入MongoDB股票信息查询
try:
import os
from pymongo import MongoClient
MONGODB_AVAILABLE = True
except ImportError:
MONGODB_AVAILABLE = False
print("⚠️ pymongo未安装无法从MongoDB获取股票名称")
try:
from .cache_manager import get_cache
FILE_CACHE_AVAILABLE = True
except ImportError:
FILE_CACHE_AVAILABLE = False
print("⚠️ 文件缓存管理器不可用将直接从API获取数据")
try:
# 通达信Python接口
import pytdx
from pytdx.hq import TdxHq_API
from pytdx.exhq import TdxExHq_API
TDX_AVAILABLE = True
except ImportError:
TDX_AVAILABLE = False
print("⚠️ pytdx库未安装无法使用通达信API")
print("💡 安装命令: pip install pytdx")
class TongDaXinDataProvider:
"""通达信数据提供器"""
def __init__(self):
print(f"🔍 [DEBUG] 初始化通达信数据提供器...")
self.api = None
self.exapi = None # 扩展行情API
self.connected = False
print(f"🔍 [DEBUG] 检查pytdx库可用性: {TDX_AVAILABLE}")
if not TDX_AVAILABLE:
error_msg = "pytdx库未安装请运行: pip install pytdx"
print(f"❌ [DEBUG] {error_msg}")
raise ImportError(error_msg)
print(f"✅ [DEBUG] pytdx库检查通过")
def connect(self):
"""连接通达信服务器"""
print(f"🔍 [DEBUG] 开始连接通达信服务器...")
try:
# 尝试从配置文件加载可用服务器
print(f"🔍 [DEBUG] 加载服务器配置...")
working_servers = self._load_working_servers()
# 如果没有配置文件,使用默认服务器列表
if not working_servers:
print(f"🔍 [DEBUG] 未找到配置文件,使用默认服务器列表")
working_servers = [
{'ip': '115.238.56.198', 'port': 7709},
{'ip': '115.238.90.165', 'port': 7709},
{'ip': '180.153.18.170', 'port': 7709},
{'ip': '119.147.212.81', 'port': 7709}, # 备用
]
else:
print(f"🔍 [DEBUG] 从配置文件加载了 {len(working_servers)} 个服务器")
# 尝试连接可用服务器
print(f"🔍 [DEBUG] 创建通达信API实例...")
self.api = TdxHq_API()
print(f"🔍 [DEBUG] 开始尝试连接服务器...")
for i, server in enumerate(working_servers):
try:
print(f"🔍 [DEBUG] 尝试连接服务器 {i+1}/{len(working_servers)}: {server['ip']}:{server['port']}")
result = self.api.connect(server['ip'], server['port'])
print(f"🔍 [DEBUG] 连接结果: {result}")
if result:
print(f"✅ 通达信API连接成功: {server['ip']}:{server['port']}")
self.connected = True
return True
except Exception as e:
print(f"⚠️ 服务器 {server['ip']}:{server['port']} 连接失败: {e}")
continue
print("❌ 所有通达信服务器连接失败")
self.connected = False
return False
except Exception as e:
print(f"❌ 通达信API连接失败: {e}")
self.connected = False
return False
def _load_working_servers(self):
"""加载可用服务器配置"""
try:
import json
import os
config_file = 'tdx_servers_config.json'
if os.path.exists(config_file):
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
return config.get('working_servers', [])
except Exception:
pass
return []
def disconnect(self):
"""断开连接"""
try:
if self.api:
self.api.disconnect()
if self.exapi:
self.exapi.disconnect()
self.connected = False
print("✅ 通达信API连接已断开")
except:
pass
def is_connected(self):
"""检查连接状态"""
if not self.connected or not self.api:
return False
# 尝试简单的API调用来验证连接是否有效
try:
# 获取市场信息作为连接测试
result = self.api.get_security_count(0) # 获取深圳市场股票数量
return result is not None and result > 0
except Exception as e:
print(f"🔍 [DEBUG] 连接测试失败: {e}")
self.connected = False
return False
def _get_stock_name(self, stock_code: str) -> str:
"""
获取股票名称
优先级缓存 -> MongoDB -> 常用股票映射 -> API获取仅深圳市场 -> 默认格式
Args:
stock_code: 股票代码
Returns:
str: 股票名称
"""
global _stock_name_cache
# 首先检查缓存
if stock_code in _stock_name_cache:
return _stock_name_cache[stock_code]
# 优先从MongoDB获取
mongodb_name = _get_stock_name_from_mongodb(stock_code)
if mongodb_name:
_stock_name_cache[stock_code] = mongodb_name
return mongodb_name
# 检查常用股票映射表
if stock_code in _common_stock_names:
name = _common_stock_names[stock_code]
_stock_name_cache[stock_code] = name
return name
# 如果API不可用直接返回默认格式
if not self.connected:
if not self.connect():
default_name = f'股票{stock_code}'
_stock_name_cache[stock_code] = default_name
return default_name
try:
# 仅对深圳市场尝试从API获取上海市场的get_security_list不可用
market = self._get_market_code(stock_code)
if market == 0: # 深圳市场
try:
for start_pos in range(0, 2000, 1000): # 分批获取
stock_list = self.api.get_security_list(market, start_pos)
if stock_list:
for stock_info in stock_list:
if stock_info.get('code') == stock_code:
stock_name = stock_info.get('name', '').strip()
if stock_name:
_stock_name_cache[stock_code] = stock_name
return stock_name
except Exception as e:
print(f"⚠️ 获取深圳股票列表失败: {e}")
# 如果都失败了,返回默认格式并缓存
default_name = f'股票{stock_code}'
_stock_name_cache[stock_code] = default_name
return default_name
except Exception as e:
print(f"⚠️ 获取股票名称失败: {e}")
default_name = f'股票{stock_code}'
_stock_name_cache[stock_code] = default_name
return default_name
def get_real_time_data(self, stock_code: str) -> Dict:
"""
获取股票实时数据
Args:
stock_code: 股票代码
Returns:
Dict: 实时数据
"""
if not self.connected:
if not self.connect():
return {}
try:
market = self._get_market_code(stock_code)
# 获取实时数据
data = self.api.get_security_quotes([(market, stock_code)])
if not data:
return {}
quote = data[0]
# 安全获取字段避免KeyError
def safe_get(key, default=0):
return quote.get(key, default)
return {
'code': stock_code,
'name': self._get_stock_name(stock_code), # 使用独立的股票名称获取方法
'price': safe_get('price'),
'last_close': safe_get('last_close'),
'open': safe_get('open'),
'high': safe_get('high'),
'low': safe_get('low'),
'volume': safe_get('vol'),
'amount': safe_get('amount'),
'change': safe_get('price') - safe_get('last_close'),
'change_percent': ((safe_get('price') - safe_get('last_close')) / safe_get('last_close') * 100) if safe_get('last_close') > 0 else 0,
'bid_prices': [safe_get(f'bid{i}') for i in range(1, 6)],
'bid_volumes': [safe_get(f'bid_vol{i}') for i in range(1, 6)],
'ask_prices': [safe_get(f'ask{i}') for i in range(1, 6)],
'ask_volumes': [safe_get(f'ask_vol{i}') for i in range(1, 6)],
'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
except Exception as e:
print(f"获取实时数据失败: {e}")
return {}
def get_stock_history_data(self, stock_code: str, start_date: str, end_date: str, period: str = 'D') -> pd.DataFrame:
"""
获取股票历史数据
Args:
stock_code: 股票代码
start_date: 开始日期 'YYYY-MM-DD'
end_date: 结束日期 'YYYY-MM-DD'
period: 周期 'D'=日线, 'W'=周线, 'M'=月线
Returns:
DataFrame: 历史数据
"""
if not self.connected:
if not self.connect():
return pd.DataFrame()
try:
market = self._get_market_code(stock_code)
# 计算需要获取的数据量
start_dt = datetime.strptime(start_date, '%Y-%m-%d')
end_dt = datetime.strptime(end_date, '%Y-%m-%d')
days_diff = (end_dt - start_dt).days
# 根据周期调整数据量
if period == 'D':
count = min(days_diff + 10, 800) # 日线最多800条
elif period == 'W':
count = min(days_diff // 7 + 10, 800)
elif period == 'M':
count = min(days_diff // 30 + 10, 800)
else:
count = 800
# 获取K线数据
category_map = {'D': 9, 'W': 5, 'M': 6}
category = category_map.get(period, 9)
data = self.api.get_security_bars(category, market, stock_code, 0, count)
if not data:
return pd.DataFrame()
# 转换为DataFrame
df = pd.DataFrame(data)
# 处理数据格式
df['datetime'] = pd.to_datetime(df['datetime'])
df = df.set_index('datetime')
df = df.sort_index()
# 筛选日期范围
df = df[start_date:end_date]
# 重命名列以匹配Yahoo Finance格式
df = df.rename(columns={
'open': 'Open',
'high': 'High',
'low': 'Low',
'close': 'Close',
'vol': 'Volume',
'amount': 'Amount'
})
# 添加股票代码信息
df['Symbol'] = stock_code
return df
except Exception as e:
print(f"获取历史数据失败: {e}")
return pd.DataFrame()
def get_stock_technical_indicators(self, stock_code: str, period: int = 20) -> Dict:
"""
计算技术指标
Args:
stock_code: 股票代码
period: 计算周期
Returns:
Dict: 技术指标数据
"""
try:
# 获取最近的历史数据
end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=period*2)).strftime('%Y-%m-%d')
df = self.get_stock_history_data(stock_code, start_date, end_date)
if df.empty:
return {}
# 计算技术指标
indicators = {}
# 移动平均线
indicators['MA5'] = df['Close'].rolling(5).mean().iloc[-1] if len(df) >= 5 else None
indicators['MA10'] = df['Close'].rolling(10).mean().iloc[-1] if len(df) >= 10 else None
indicators['MA20'] = df['Close'].rolling(20).mean().iloc[-1] if len(df) >= 20 else None
# RSI
if len(df) >= 14:
delta = df['Close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs = gain / loss
indicators['RSI'] = (100 - (100 / (1 + rs))).iloc[-1]
# MACD
if len(df) >= 26:
exp1 = df['Close'].ewm(span=12).mean()
exp2 = df['Close'].ewm(span=26).mean()
macd = exp1 - exp2
signal = macd.ewm(span=9).mean()
indicators['MACD'] = macd.iloc[-1]
indicators['MACD_Signal'] = signal.iloc[-1]
indicators['MACD_Histogram'] = (macd - signal).iloc[-1]
# 布林带
if len(df) >= 20:
sma = df['Close'].rolling(20).mean()
std = df['Close'].rolling(20).std()
indicators['BB_Upper'] = (sma + 2 * std).iloc[-1]
indicators['BB_Middle'] = sma.iloc[-1]
indicators['BB_Lower'] = (sma - 2 * std).iloc[-1]
return indicators
except Exception as e:
print(f"计算技术指标失败: {e}")
return {}
def search_stocks(self, keyword: str) -> List[Dict]:
"""
搜索股票
Args:
keyword: 搜索关键词股票代码或名称
Returns:
List[Dict]: 搜索结果
"""
if not self.connected:
if not self.connect():
return []
try:
# 通达信没有直接的搜索API这里提供一个简化的实现
# 实际使用中可以维护一个股票代码表
# 常见股票代码映射
stock_mapping = {
'平安银行': '000001',
'万科A': '000002',
'中国平安': '601318',
'贵州茅台': '600519',
'招商银行': '600036',
'五粮液': '000858',
'格力电器': '000651',
'美的集团': '000333',
'中国石化': '600028',
'工商银行': '601398'
}
results = []
# 按关键词搜索
for name, code in stock_mapping.items():
if keyword.lower() in name.lower() or keyword in code:
# 获取实时数据
realtime_data = self.get_real_time_data(code)
if realtime_data:
results.append({
'code': code,
'name': name,
'price': realtime_data.get('price', 0),
'change_percent': realtime_data.get('change_percent', 0)
})
return results
except Exception as e:
print(f"搜索股票失败: {e}")
return []
def _get_market_code(self, stock_code: str) -> int:
"""
根据股票代码判断市场
Args:
stock_code: 股票代码
Returns:
int: 市场代码 (0=深圳, 1=上海)
"""
if stock_code.startswith(('000', '002', '003', '300')):
return 0 # 深圳
elif stock_code.startswith(('600', '601', '603', '605', '688')):
return 1 # 上海
else:
return 0 # 默认深圳
def get_market_overview(self) -> Dict:
"""获取市场概览"""
if not self.connected:
if not self.connect():
return {}
try:
# 获取主要指数数据
indices = {
'上证指数': ('1', '000001'),
'深证成指': ('0', '399001'),
'创业板指': ('0', '399006'),
'科创50': ('1', '000688')
}
market_data = {}
for name, (market, code) in indices.items():
try:
data = self.api.get_security_quotes([(int(market), code)])
if data:
quote = data[0]
market_data[name] = {
'price': quote['price'],
'change': quote['price'] - quote['last_close'],
'change_percent': ((quote['price'] - quote['last_close']) / quote['last_close'] * 100) if quote['last_close'] > 0 else 0,
'volume': quote['vol']
}
except:
continue
return market_data
except Exception as e:
print(f"获取市场概览失败: {e}")
return {}
# 全局实例和缓存
_tdx_provider = None
_stock_name_cache = {} # 股票名称缓存避免重复API调用
_mongodb_client = None
_mongodb_db = None
def _get_mongodb_connection():
"""获取MongoDB连接"""
global _mongodb_client, _mongodb_db
if not MONGODB_AVAILABLE:
return None, None
if _mongodb_client is None or _mongodb_db is None:
try:
# 从环境变量获取MongoDB配置
config = {
'host': os.getenv('MONGODB_HOST', 'localhost'),
'port': int(os.getenv('MONGODB_PORT', 27018)),
'username': os.getenv('MONGODB_USERNAME'),
'password': os.getenv('MONGODB_PASSWORD'),
'database': os.getenv('MONGODB_DATABASE', 'tradingagents'),
'auth_source': os.getenv('MONGODB_AUTH_SOURCE', 'admin')
}
# 构建连接字符串
if config.get('username') and config.get('password'):
connection_string = f"mongodb://{config['username']}:{config['password']}@{config['host']}:{config['port']}/{config['auth_source']}"
else:
connection_string = f"mongodb://{config['host']}:{config['port']}/"
# 创建客户端
_mongodb_client = MongoClient(
connection_string,
serverSelectionTimeoutMS=3000 # 3秒超时
)
# 测试连接
_mongodb_client.admin.command('ping')
# 选择数据库
_mongodb_db = _mongodb_client[config['database']]
except Exception as e:
print(f"⚠️ MongoDB连接失败: {e}")
_mongodb_client = None
_mongodb_db = None
return _mongodb_client, _mongodb_db
def _get_stock_name_from_mongodb(stock_code: str) -> Optional[str]:
"""从MongoDB获取股票名称"""
try:
client, db = _get_mongodb_connection()
if db is None:
return None
collection = db['stock_basic_info']
stock_info = collection.find_one({'code': stock_code})
if stock_info and 'name' in stock_info:
return stock_info['name'].strip()
return None
except Exception as e:
print(f"⚠️ 从MongoDB获取股票名称失败: {e}")
return None
# 精简的常用股票名称映射(仅包含最常见的股票)
_common_stock_names = {
# 深圳主板
'000001': '平安银行',
'000002': '万科A',
'000858': '五粮液',
'000895': '双汇发展',
# 深圳中小板
'002594': '比亚迪',
'002415': '海康威视',
'002304': '洋河股份',
# 深圳创业板
'300059': '东方财富',
'300750': '宁德时代',
'300015': '爱尔眼科',
# 上海主板
'600519': '贵州茅台',
'600036': '招商银行',
'601398': '工商银行',
'601127': '小康股份',
'600000': '浦发银行',
'601318': '中国平安',
'600276': '恒瑞医药',
'600887': '伊利股份',
# 科创板
'688981': '中芯国际',
'688599': '天合光能',
}
def get_tdx_provider() -> TongDaXinDataProvider:
"""获取通达信数据提供器实例"""
global _tdx_provider
if _tdx_provider is None:
print(f"🔍 [DEBUG] 创建新的通达信数据提供器实例...")
_tdx_provider = TongDaXinDataProvider()
print(f"🔍 [DEBUG] 通达信数据提供器实例创建完成")
else:
print(f"🔍 [DEBUG] 使用现有的通达信数据提供器实例")
# 检查连接状态,如果连接断开则重新创建
if not _tdx_provider.is_connected():
print(f"🔍 [DEBUG] 检测到连接断开,重新创建通达信数据提供器...")
_tdx_provider = TongDaXinDataProvider()
print(f"🔍 [DEBUG] 通达信数据提供器重新创建完成")
return _tdx_provider
def get_china_stock_data(stock_code: str, start_date: str, end_date: str) -> str:
"""
获取中国股票数据的主要接口函数支持缓存
Args:
stock_code: 股票代码 ( '000001')
start_date: 开始日期 'YYYY-MM-DD'
end_date: 结束日期 'YYYY-MM-DD'
Returns:
str: 格式化的股票数据
"""
print(f"📊 正在获取中国股票数据: {stock_code} ({start_date}{end_date})")
# 优先尝试从数据库缓存加载数据使用统一的database_manager
try:
from tradingagents.config.database_manager import get_database_manager
db_manager = get_database_manager()
if db_manager.is_mongodb_available():
# 直接使用MongoDB客户端查询缓存数据
mongodb_client = db_manager.get_mongodb_client()
if mongodb_client:
db = mongodb_client[db_manager.mongodb_config["database"]]
collection = db.stock_data
# 查询最近的缓存数据
from datetime import datetime, timedelta
cutoff_time = datetime.utcnow() - timedelta(hours=6)
cached_doc = collection.find_one({
"symbol": stock_code,
"market_type": "china",
"created_at": {"$gte": cutoff_time}
}, sort=[("created_at", -1)])
if cached_doc and 'data' in cached_doc:
print(f"🗄️ 从MongoDB缓存加载数据: {stock_code}")
return cached_doc['data']
except Exception as e:
print(f"⚠️ 从MongoDB加载缓存失败: {e}")
# 如果数据库缓存不可用,尝试文件缓存
if FILE_CACHE_AVAILABLE:
cache = get_cache()
cache_key = cache.find_cached_stock_data(
symbol=stock_code,
start_date=start_date,
end_date=end_date,
data_source="tdx",
max_age_hours=6 # 6小时内的缓存有效
)
if cache_key:
cached_data = cache.load_stock_data(cache_key)
if cached_data:
print(f"💾 从文件缓存加载数据: {stock_code} -> {cache_key}")
return cached_data
print(f"🌐 从通达信API获取数据: {stock_code}")
try:
provider = get_tdx_provider()
# 获取历史数据
df = provider.get_stock_history_data(stock_code, start_date, end_date)
if df.empty:
error_msg = f"❌ 未能获取股票 {stock_code} 的历史数据"
print(error_msg)
return error_msg
# 获取实时数据
realtime_data = provider.get_real_time_data(stock_code)
# 获取技术指标
indicators = provider.get_stock_technical_indicators(stock_code)
# 格式化输出
result = f"""
# {stock_code} 股票数据分析
## 📊 实时行情
- 股票名称: {realtime_data.get('name', 'N/A')}
- 当前价格: ¥{realtime_data.get('price', 0):.2f}
- 涨跌幅: {realtime_data.get('change_percent', 0):.2f}%
- 成交量: {realtime_data.get('volume', 0):,}
- 更新时间: {realtime_data.get('update_time', 'N/A')}
## 📈 历史数据概览
- 数据期间: {start_date} {end_date}
- 数据条数: {len(df)}
- 期间最高: ¥{df['High'].max():.2f}
- 期间最低: ¥{df['Low'].min():.2f}
- 期间涨幅: {((df['Close'].iloc[-1] - df['Close'].iloc[0]) / df['Close'].iloc[0] * 100):.2f}%
## 🔍 技术指标
- MA5: ¥{indicators.get('MA5', 0):.2f}
- MA10: ¥{indicators.get('MA10', 0):.2f}
- MA20: ¥{indicators.get('MA20', 0):.2f}
- RSI: {indicators.get('RSI', 0):.2f}
- MACD: {indicators.get('MACD', 0):.4f}
## 📋 最近5日数据
{df.tail().to_string()}
数据来源: 通达信API (实时数据)
"""
# 优先保存到数据库缓存使用统一的database_manager
try:
from tradingagents.config.database_manager import get_database_manager
db_manager = get_database_manager()
if db_manager.is_mongodb_available():
# 直接使用MongoDB客户端保存数据
mongodb_client = db_manager.get_mongodb_client()
if mongodb_client:
db = mongodb_client[db_manager.mongodb_config["database"]]
collection = db.stock_data
doc = {
"symbol": stock_code,
"market_type": "china",
"data": result,
"metadata": {
'start_date': start_date,
'end_date': end_date,
'data_source': 'tdx',
'realtime_data': realtime_data,
'indicators': indicators,
'history_count': len(df)
},
"created_at": datetime.utcnow(),
"updated_at": datetime.utcnow()
}
collection.replace_one(
{"symbol": stock_code, "market_type": "china"},
doc,
upsert=True
)
print(f"💾 数据已保存到MongoDB: {stock_code}")
except Exception as e:
print(f"⚠️ 保存到MongoDB失败: {e}")
# 同时保存到文件缓存作为备份
if FILE_CACHE_AVAILABLE:
cache = get_cache()
cache.save_stock_data(
symbol=stock_code,
data=result,
start_date=start_date,
end_date=end_date,
data_source="tdx"
)
return result
except Exception as e:
import traceback
error_details = traceback.format_exc()
print(f"❌ [DEBUG] 通达信API调用失败:")
print(f"❌ [DEBUG] 错误类型: {type(e).__name__}")
print(f"❌ [DEBUG] 错误信息: {str(e)}")
print(f"❌ [DEBUG] 详细堆栈:")
print(error_details)
return f"""
中国股票数据获取失败 - {stock_code}
错误类型: {type(e).__name__}
错误信息: {str(e)}
🔍 调试信息:
{error_details}
💡 解决建议:
1. 检查pytdx库是否已安装: pip install pytdx
2. 确认股票代码格式正确 (: 000001, 600519)
3. 检查网络连接是否正常
4. 尝试重新连接通达信服务器
: 通达信API需要网络连接到通达信服务器
"""
def get_china_market_overview() -> str:
"""获取中国股市概览"""
try:
provider = get_tdx_provider()
market_data = provider.get_market_overview()
if not market_data:
return "无法获取市场概览数据"
result = "# 中国股市概览\n\n"
for name, data in market_data.items():
change_symbol = "📈" if data['change'] >= 0 else "📉"
result += f"## {change_symbol} {name}\n"
result += f"- 当前点位: {data['price']:.2f}\n"
result += f"- 涨跌点数: {data['change']:+.2f}\n"
result += f"- 涨跌幅: {data['change_percent']:+.2f}%\n"
result += f"- 成交量: {data['volume']:,}\n\n"
result += f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
result += "数据来源: 通达信API\n"
return result
except Exception as e:
return f"获取市场概览失败: {str(e)}"
# 在文件末尾添加以下函数
def get_china_stock_data_enhanced(stock_code: str, start_date: str, end_date: str) -> str:
"""
增强版中国股票数据获取函数完整降级机制
这是get_china_stock_data的增强版本
Args:
stock_code: 股票代码 ( '000001')
start_date: 开始日期 'YYYY-MM-DD'
end_date: 结束日期 'YYYY-MM-DD'
Returns:
str: 格式化的股票数据
"""
try:
from .stock_data_service import get_stock_data_service
service = get_stock_data_service()
return service.get_stock_data_with_fallback(stock_code, start_date, end_date)
except ImportError:
# 如果新服务不可用,降级到原有函数
print("⚠️ 增强服务不可用,使用原有函数")
return get_china_stock_data(stock_code, start_date, end_date)
except Exception as e:
print(f"⚠️ 增强服务出错,降级到原有函数: {e}")
return get_china_stock_data(stock_code, start_date, end_date)
# ... existing code ...

View File

@ -3,12 +3,14 @@ import os
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_dir": os.path.join(os.path.expanduser("~"), "Documents", "TradingAgents", "data"),
"data_cache_dir": os.path.join(
os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"dataflows/data_cache",
),
# LLM settings
# Supported providers: "openai", "anthropic", "google", "dashscope", "ollama", "openrouter"
# For DashScope: set llm_provider="dashscope", deep_think_llm="qwen-plus", quick_think_llm="qwen-turbo"
"llm_provider": "openai",
"deep_think_llm": "o4-mini",
"quick_think_llm": "gpt-4o-mini",
@ -19,4 +21,7 @@ DEFAULT_CONFIG = {
"max_recur_limit": 100,
# Tool settings
"online_tools": True,
# Note: Database and cache configuration is now managed by .env file and config.database_manager
# No database/cache settings in default config to avoid configuration conflicts
}

View File

@ -10,6 +10,14 @@ from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_google_genai import ChatGoogleGenerativeAI
# Import DashScope adapter if available
try:
from tradingagents.llm_adapters.dashscope_adapter import ChatDashScope
DASHSCOPE_AVAILABLE = True
except ImportError:
DASHSCOPE_AVAILABLE = False
ChatDashScope = None
from langgraph.prebuilt import ToolNode
from tradingagents.agents import *
@ -65,8 +73,35 @@ class TradingAgentsGraph:
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":
self.deep_thinking_llm = ChatGoogleGenerativeAI(model=self.config["deep_think_llm"])
self.quick_thinking_llm = ChatGoogleGenerativeAI(model=self.config["quick_think_llm"])
google_api_key = os.getenv('GOOGLE_API_KEY')
self.deep_thinking_llm = ChatGoogleGenerativeAI(
model=self.config["deep_think_llm"],
google_api_key=google_api_key,
temperature=0.1,
max_tokens=2000
)
self.quick_thinking_llm = ChatGoogleGenerativeAI(
model=self.config["quick_think_llm"],
google_api_key=google_api_key,
temperature=0.1,
max_tokens=2000
)
elif (self.config["llm_provider"].lower() == "dashscope" or
"dashscope" in self.config["llm_provider"].lower() or
"alibaba" in self.config["llm_provider"].lower()):
if not DASHSCOPE_AVAILABLE:
raise ValueError("DashScope adapter not available. Please install dashscope package: pip install dashscope")
self.deep_thinking_llm = ChatDashScope(
model=self.config["deep_think_llm"],
temperature=0.1,
max_tokens=2000
)
self.quick_thinking_llm = ChatDashScope(
model=self.config["quick_think_llm"],
temperature=0.1,
max_tokens=2000
)
else:
raise ValueError(f"Unsupported LLM provider: {self.config['llm_provider']}")

View File

@ -0,0 +1,4 @@
# LLM Adapters for TradingAgents
from .dashscope_adapter import ChatDashScope
__all__ = ["ChatDashScope"]

View File

@ -0,0 +1,288 @@
"""
阿里百炼大模型 (DashScope) 适配器
TradingAgents 提供阿里百炼大模型的 LangChain 兼容接口
"""
import os
import json
from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator, Sequence
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage, SystemMessage
from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.callbacks.manager import CallbackManagerForLLMRun, AsyncCallbackManagerForLLMRun
from langchain_core.tools import BaseTool
from langchain_core.utils.function_calling import convert_to_openai_tool
from pydantic import Field, SecretStr
import dashscope
from dashscope import Generation
from ..config.config_manager import token_tracker
class ChatDashScope(BaseChatModel):
"""阿里百炼大模型的 LangChain 适配器"""
# 模型配置
model: str = Field(default="qwen-turbo", description="DashScope 模型名称")
api_key: Optional[SecretStr] = Field(default=None, description="DashScope API 密钥")
temperature: float = Field(default=0.1, description="生成温度")
max_tokens: int = Field(default=2000, description="最大生成token数")
top_p: float = Field(default=0.9, description="核采样参数")
# 内部属性
_client: Any = None
def __init__(self, **kwargs):
"""初始化 DashScope 客户端"""
super().__init__(**kwargs)
# 设置API密钥
api_key = self.api_key
if api_key is None:
api_key = os.getenv("DASHSCOPE_API_KEY")
if api_key is None:
raise ValueError(
"DashScope API key not found. Please set DASHSCOPE_API_KEY environment variable "
"or pass api_key parameter."
)
# 配置 DashScope
if isinstance(api_key, SecretStr):
dashscope.api_key = api_key.get_secret_value()
else:
dashscope.api_key = api_key
@property
def _llm_type(self) -> str:
"""返回LLM类型"""
return "dashscope"
def _convert_messages_to_dashscope_format(self, messages: List[BaseMessage]) -> List[Dict[str, str]]:
"""将 LangChain 消息格式转换为 DashScope 格式"""
dashscope_messages = []
for message in messages:
if isinstance(message, SystemMessage):
role = "system"
elif isinstance(message, HumanMessage):
role = "user"
elif isinstance(message, AIMessage):
role = "assistant"
else:
# 默认作为用户消息处理
role = "user"
content = message.content
if isinstance(content, list):
# 处理多模态内容,目前只提取文本
text_content = ""
for item in content:
if isinstance(item, dict) and item.get("type") == "text":
text_content += item.get("text", "")
content = text_content
dashscope_messages.append({
"role": role,
"content": str(content)
})
return dashscope_messages
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
"""生成聊天回复"""
# 转换消息格式
dashscope_messages = self._convert_messages_to_dashscope_format(messages)
# 准备请求参数
request_params = {
"model": self.model,
"messages": dashscope_messages,
"result_format": "message",
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"top_p": self.top_p,
}
# 添加停止词
if stop:
request_params["stop"] = stop
# 合并额外参数
request_params.update(kwargs)
try:
# 调用 DashScope API
response = Generation.call(**request_params)
if response.status_code == 200:
# 解析响应
output = response.output
message_content = output.choices[0].message.content
# 提取token使用量信息
input_tokens = 0
output_tokens = 0
# DashScope API响应中包含usage信息
if hasattr(response, 'usage') and response.usage:
usage = response.usage
# 根据API文档usage可能包含input_tokens和output_tokens
if hasattr(usage, 'input_tokens'):
input_tokens = usage.input_tokens
if hasattr(usage, 'output_tokens'):
output_tokens = usage.output_tokens
# 有些情况下可能是total_tokens
elif hasattr(usage, 'total_tokens'):
# 估算输入和输出token如果没有分别提供
total_tokens = usage.total_tokens
# 简单估算假设输入占30%输出占70%
input_tokens = int(total_tokens * 0.3)
output_tokens = int(total_tokens * 0.7)
# 记录token使用量
if input_tokens > 0 or output_tokens > 0:
try:
# 生成会话ID如果没有提供
session_id = kwargs.get('session_id', f"dashscope_{hash(str(messages))%10000}")
analysis_type = kwargs.get('analysis_type', 'stock_analysis')
# 使用TokenTracker记录使用量
token_tracker.track_usage(
provider="dashscope",
model_name=self.model,
input_tokens=input_tokens,
output_tokens=output_tokens,
session_id=session_id,
analysis_type=analysis_type
)
except Exception as track_error:
# 记录失败不应该影响主要功能
print(f"Token tracking failed: {track_error}")
# 创建 AI 消息
ai_message = AIMessage(content=message_content)
# 创建生成结果
generation = ChatGeneration(message=ai_message)
return ChatResult(generations=[generation])
else:
raise Exception(f"DashScope API error: {response.code} - {response.message}")
except Exception as e:
raise Exception(f"Error calling DashScope API: {str(e)}")
async def _agenerate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
"""异步生成聊天回复"""
# 目前使用同步方法,后续可以实现真正的异步
return self._generate(messages, stop, run_manager, **kwargs)
def bind_tools(
self,
tools: Sequence[Union[Dict[str, Any], type, BaseTool]],
**kwargs: Any,
) -> "ChatDashScope":
"""绑定工具到模型"""
# 注意DashScope 目前不直接支持工具调用
# 这里我们返回一个新的实例,但实际上工具调用需要在应用层处理
formatted_tools = []
for tool in tools:
if hasattr(tool, "name") and hasattr(tool, "description"):
# 这是一个 BaseTool 实例
formatted_tools.append({
"name": tool.name,
"description": tool.description,
"parameters": getattr(tool, "args_schema", {})
})
elif isinstance(tool, dict):
formatted_tools.append(tool)
else:
# 尝试转换为 OpenAI 工具格式
try:
formatted_tools.append(convert_to_openai_tool(tool))
except Exception:
pass
# 创建新实例,保存工具信息
new_instance = self.__class__(
model=self.model,
api_key=self.api_key,
temperature=self.temperature,
max_tokens=self.max_tokens,
top_p=self.top_p,
**kwargs
)
new_instance._tools = formatted_tools
return new_instance
@property
def _identifying_params(self) -> Dict[str, Any]:
"""返回标识参数"""
return {
"model": self.model,
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"top_p": self.top_p,
}
# 支持的模型列表
DASHSCOPE_MODELS = {
# 通义千问系列
"qwen-turbo": {
"description": "通义千问 Turbo - 快速响应,适合日常对话",
"context_length": 8192,
"recommended_for": ["快速任务", "日常对话", "简单分析"]
},
"qwen-plus": {
"description": "通义千问 Plus - 平衡性能和成本",
"context_length": 32768,
"recommended_for": ["复杂分析", "专业任务", "深度思考"]
},
"qwen-max": {
"description": "通义千问 Max - 最强性能",
"context_length": 32768,
"recommended_for": ["最复杂任务", "专业分析", "高质量输出"]
},
"qwen-max-longcontext": {
"description": "通义千问 Max 长文本版 - 支持超长上下文",
"context_length": 1000000,
"recommended_for": ["长文档分析", "大量数据处理", "复杂推理"]
},
}
def get_available_models() -> Dict[str, Dict[str, Any]]:
"""获取可用的 DashScope 模型列表"""
return DASHSCOPE_MODELS
def create_dashscope_llm(
model: str = "qwen-plus",
api_key: Optional[str] = None,
temperature: float = 0.1,
max_tokens: int = 2000,
**kwargs
) -> ChatDashScope:
"""创建 DashScope LLM 实例的便捷函数"""
return ChatDashScope(
model=model,
api_key=api_key,
temperature=temperature,
max_tokens=max_tokens,
**kwargs
)