feat: add certificate verification functionality

This commit is contained in:
Kevin Bruton 2025-09-29 17:51:09 +02:00
parent eb0121b787
commit cea3411911
13 changed files with 1092 additions and 15 deletions

View File

@ -1,17 +1,100 @@
# Trading Agents Environment Variables
# TradingAgents Environment Configuration
# Copy this file to .env and fill in your values
# Finnhub API Key (required for market data)
# =============================================================================
# API Keys (Required)
# =============================================================================
OPENAI_API_KEY=your_openai_api_key_here
FINNHUB_API_KEY=your_finnhub_api_key_here
# OpenAI API Key (required for AI processing)
OPENAI_API_KEY=your_openai_api_key_here
# Optional API Keys
OPENROUTER_API_KEY=your_openrouter_api_key_here
GOOGLE_API_KEY=your_google_api_key_here
ANTHROPIC_API_KEY=your_anthropic_api_key_here
# Reddit API Credentials (required for social media analysis)
REDDIT_CLIENT_ID=your_reddit_client_id_here
REDDIT_CLIENT_SECRET=your_reddit_client_secret_here
# Reddit API (Optional - for social media analysis)
REDDIT_CLIENT_ID=your_reddit_client_id
REDDIT_CLIENT_SECRET=your_reddit_client_secret
REDDIT_USER_AGENT=TradingAgents/1.0
# =============================================================================
# SSL/TLS Certificate Configuration (OPTIONAL)
# =============================================================================
# Certificate Bundle Path (ONLY set if you need a custom certificate bundle)
# If not set, system default SSL behavior is used
# Common locations:
# - macOS: /etc/ssl/cert.pem
# - Ubuntu/Debian: /etc/ssl/certs/ca-certificates.crt
# - CentOS/RHEL: /etc/pki/tls/certs/ca-bundle.crt
# - Custom: /path/to/your/custom-ca-bundle.crt
# REQUESTS_CA_BUNDLE=/etc/ssl/cert.pem
# CURL_CA_BUNDLE=/etc/ssl/cert.pem
# SSL Verification (ONLY set to false if needed for development/testing)
# If not set, SSL verification is enabled by default (recommended)
# SSL_VERIFY=false
# HTTP Timeout (ONLY set if default timeout is insufficient)
# If not set, uses reasonable defaults
# HTTP_TIMEOUT=60
# =============================================================================
# Proxy Configuration (ONLY if behind corporate firewall)
# =============================================================================
# HTTP/HTTPS Proxy Settings (ONLY set if required by your network)
# If not set, direct connections are used
# HTTP_PROXY=http://proxy.company.com:8080
# HTTPS_PROXY=https://proxy.company.com:8080
# Proxy with authentication
# HTTP_PROXY=http://username:password@proxy.company.com:8080
# HTTPS_PROXY=https://username:password@proxy.company.com:8080
# =============================================================================
# Application Settings
# =============================================================================
# Results Directory
TRADINGAGENTS_RESULTS_DIR=./results
# Ollama Configuration (if using local Ollama)
OLLAMA_HOST=localhost
# Optional Configuration
# DEBUG=True
# LOG_LEVEL=INFO
# LOG_LEVEL=INFO
# =============================================================================
# SSL Certificate Examples for Common Enterprise Environments
# =============================================================================
# Example 1: Using system certificate store (macOS)
# REQUESTS_CA_BUNDLE=/System/Library/OpenSSL/certs/cert.pem
# Example 2: Using system certificate store (Ubuntu/Debian)
# REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
# Example 3: Using custom corporate certificate bundle
# REQUESTS_CA_BUNDLE=/usr/local/share/ca-certificates/corporate-ca-bundle.crt
# Example 4: Disabling SSL verification (development only)
# SSL_VERIFY=false
# =============================================================================
# Troubleshooting SSL Issues
# =============================================================================
# If you encounter SSL certificate errors:
# 1. Run the diagnostic tool: python diagnose_ssl.py
# 2. Check if your organization uses a custom CA
# 3. Ask your IT department for the corporate certificate bundle
# 4. Try using certifi's bundle: pip install certifi
# 5. Set REQUESTS_CA_BUNDLE to certifi's location (usually shown by diagnose_ssl.py)
# Common SSL Error Solutions:
# - "certificate verify failed": Set REQUESTS_CA_BUNDLE to correct cert bundle
# - "SSL: WRONG_VERSION_NUMBER": Check if you're behind a proxy
# - "Connection timeout": Increase HTTP_TIMEOUT or check proxy settings
# - "Name or service not known": Check DNS settings and proxy configuration

117
SSL_CONFIGURATION.md Normal file
View File

@ -0,0 +1,117 @@
# SSL Certificate Bundle Configuration for TradingAgents
## Overview
This implementation provides flexible SSL/TLS certificate configuration for TradingAgents while maintaining backward compatibility. The system only applies custom SSL settings when explicitly configured via environment variables.
## Key Features
### 1. Environment Variable Based Configuration
- `REQUESTS_CA_BUNDLE` or `CURL_CA_BUNDLE`: Path to custom certificate bundle
- `SSL_VERIFY`: Enable/disable SSL verification (true/false)
- `HTTP_TIMEOUT`: Custom timeout for HTTP requests (seconds)
- `HTTP_PROXY`: HTTP proxy server
- `HTTPS_PROXY`: HTTPS proxy server
### 2. Default Behavior Preservation
- **If no environment variables are set**: Uses system default SSL behavior
- **Only applies custom settings when explicitly configured**
- **Empty or undefined variables are ignored**
### 3. Comprehensive Coverage
- **LangChain LLM clients**: Custom SSL configuration for OpenAI, OpenRouter, etc.
- **HTTP requests**: Custom configuration for Google News, Reddit APIs
- **Global SSL setup**: Sets environment variables for libraries that respect them
## Usage Examples
### Basic Usage (No Custom SSL)
```bash
# No SSL environment variables set
# Uses system default SSL behavior
python webapp/main.py
```
### Custom Certificate Bundle
```bash
# Use custom corporate certificate bundle
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca-bundle.crt
python webapp/main.py
```
### Development/Testing (Disable SSL Verification)
```bash
# Disable SSL verification (NOT recommended for production)
export SSL_VERIFY=false
python webapp/main.py
```
### Behind Corporate Proxy
```bash
# Configure proxy settings
export HTTP_PROXY=http://proxy.company.com:8080
export HTTPS_PROXY=https://proxy.company.com:8080
export REQUESTS_CA_BUNDLE=/etc/ssl/corporate-ca-bundle.crt
python webapp/main.py
```
## Files Modified
### Core Configuration
- `tradingagents/default_config.py`: Added SSL configuration parameters
- `tradingagents/dataflows/ssl_utils.py`: SSL utility functions (NEW)
### Integration Points
- `tradingagents/graph/trading_graph.py`: LLM client SSL configuration
- `tradingagents/dataflows/googlenews_utils.py`: HTTP requests SSL configuration
- `tradingagents/dataflows/interface.py`: Integration with SSL configuration
### Documentation and Tools
- `.env.example`: Updated with SSL configuration examples
- `diagnose_ssl.py`: SSL diagnostic tool (NEW)
- `test_ssl_config.py`: SSL configuration test suite (NEW)
## Testing
Run the diagnostic tool to check your SSL configuration:
```bash
python diagnose_ssl.py
```
Run the test suite to verify SSL configuration behavior:
```bash
python test_ssl_config.py
```
## Troubleshooting
### Common SSL Errors and Solutions
1. **Certificate verification failed**
- Set `REQUESTS_CA_BUNDLE` to correct certificate bundle path
- Check if your organization uses custom CA certificates
2. **SSL: WRONG_VERSION_NUMBER**
- Usually indicates proxy configuration issues
- Set appropriate `HTTP_PROXY` and `HTTPS_PROXY` variables
3. **Connection timeout**
- Increase `HTTP_TIMEOUT` value
- Check network connectivity and proxy settings
4. **Name or service not known**
- Check DNS settings
- Verify proxy configuration
### Getting Help
1. Run `python diagnose_ssl.py` for comprehensive SSL diagnostics
2. Check your organization's IT documentation for certificate bundles
3. Contact your IT department for corporate proxy and certificate information
## Security Considerations
- **Never disable SSL verification in production**
- **Use custom certificate bundles for corporate environments**
- **Keep certificate bundles updated**
- **Secure proxy credentials if using authenticated proxies**

106
combine_certificates.py Normal file
View File

@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Certificate Bundle Combiner for TradingAgents
This script combines your corporate certificate bundle (Netskope) with
the certifi certificate bundle to ensure all certificates are available.
"""
import os
import shutil
from pathlib import Path
def combine_certificate_bundles():
"""Combine corporate and certifi certificate bundles"""
print("🔗 Certificate Bundle Combiner")
print("=" * 40)
# Paths
corporate_bundle = "/Users/kevin.bruton/netskope-certificates/netskope-cert-bundle.pem"
try:
import certifi
certifi_bundle = certifi.where()
except ImportError:
print("❌ certifi package not found. Please install it: pip install certifi")
return False
combined_bundle = "/Users/kevin.bruton/netskope-certificates/combined-cert-bundle.pem"
print(f"📋 Corporate bundle: {corporate_bundle}")
print(f"📋 Certifi bundle: {certifi_bundle}")
print(f"📋 Combined bundle: {combined_bundle}")
# Check if corporate bundle exists
if not os.path.exists(corporate_bundle):
print(f"❌ Corporate certificate bundle not found: {corporate_bundle}")
return False
# Create combined bundle
try:
with open(combined_bundle, 'w') as combined_file:
# Write corporate certificates first
print("📝 Adding corporate certificates...")
with open(corporate_bundle, 'r') as corp_file:
combined_file.write(corp_file.read())
# Add separator
combined_file.write("\n# Certifi certificates below\n")
# Write certifi certificates
print("📝 Adding certifi certificates...")
with open(certifi_bundle, 'r') as certifi_file:
certifi_content = certifi_file.read()
combined_file.write(certifi_content)
print(f"✅ Combined certificate bundle created: {combined_bundle}")
# Set permissions
os.chmod(combined_bundle, 0o644)
# Show usage instructions
print("\n💡 Usage Instructions:")
print(f" Add this to your .env file:")
print(f" REQUESTS_CA_BUNDLE={combined_bundle}")
print(f" CURL_CA_BUNDLE={combined_bundle}")
print("\n Or export in your shell:")
print(f" export REQUESTS_CA_BUNDLE={combined_bundle}")
print(f" export CURL_CA_BUNDLE={combined_bundle}")
return True
except Exception as e:
print(f"❌ Error creating combined bundle: {e}")
return False
def test_combined_bundle():
"""Test the combined certificate bundle"""
combined_bundle = "/Users/kevin.bruton/netskope-certificates/combined-cert-bundle.pem"
if not os.path.exists(combined_bundle):
print("❌ Combined bundle not found. Run combine_certificate_bundles() first.")
return False
print(f"\n🧪 Testing combined certificate bundle: {combined_bundle}")
import requests
test_urls = [
"https://www.google.com",
"https://api.openai.com/v1/models",
"https://openrouter.ai/api/v1/models"
]
for url in test_urls:
try:
response = requests.get(url, verify=combined_bundle, timeout=10)
print(f"{url} - Status: {response.status_code}")
except Exception as e:
print(f"{url} - Error: {e}")
return True
if __name__ == "__main__":
if combine_certificate_bundles():
test_combined_bundle()

60
debug_streaming.py Normal file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Debug script to test the TradingAgentsGraph streaming behavior
"""
import os
from dotenv import load_dotenv
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
# Load environment variables
load_dotenv()
def debug_callback(state):
"""Debug callback to see what state is being passed"""
print(f"\n🔍 CALLBACK RECEIVED:")
print(f" State type: {type(state)}")
print(f" State keys: {list(state.keys()) if isinstance(state, dict) else 'Not a dict'}")
if isinstance(state, dict):
for key, value in state.items():
if key in ["__end__", "messages"]:
continue
print(f" {key}: {type(value)} - {str(value)[:100]}...")
print("-" * 50)
def test_streaming():
"""Test the streaming functionality"""
print("🚀 Testing TradingAgentsGraph streaming...")
# Create a minimal config for testing
config = DEFAULT_CONFIG.copy()
config["llm_provider"] = "openai"
config["quick_think_llm"] = "gpt-3.5-turbo"
config["deep_think_llm"] = "gpt-4"
try:
# Initialize the graph
print("📊 Initializing TradingAgentsGraph...")
graph = TradingAgentsGraph(config=config)
# Test propagation with callback
print("🔄 Starting propagation with callback...")
final_state, signal = graph.propagate(
company_name="AAPL",
trade_date="2024-01-01",
on_step_callback=debug_callback
)
print(f"✅ Propagation completed!")
print(f"📈 Final signal: {signal}")
print(f"🎯 Final state keys: {list(final_state.keys())}")
except Exception as e:
print(f"❌ Error during streaming test: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
test_streaming()

159
diagnose_ssl.py Normal file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""
SSL Certificate Diagnostic Tool for TradingAgents
This script helps diagnose SSL/TLS certificate issues and provides guidance
on how to configure certificate bundles properly.
"""
import os
import sys
import ssl
import socket
import requests
from urllib.parse import urlparse
from dotenv import load_dotenv
load_dotenv()
from tradingagents.dataflows.ssl_utils import get_certificate_info, get_ssl_config
from tradingagents.default_config import DEFAULT_CONFIG
def test_ssl_connection(hostname, port=443):
"""Test SSL connection to a specific hostname."""
print(f"\n🔒 Testing SSL connection to {hostname}:{port}")
try:
# Create SSL context
context = ssl.create_default_context()
# Connect and get certificate info
with socket.create_connection((hostname, port), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
print(f"✅ SSL connection successful")
print(f" Subject: {cert.get('subject', 'Unknown')}")
print(f" Issuer: {cert.get('issuer', 'Unknown')}")
print(f" Version: {cert.get('version', 'Unknown')}")
return True
except Exception as e:
print(f"❌ SSL connection failed: {e}")
return False
def test_requests_connection(url):
"""Test HTTP request with requests library."""
print(f"\n🌐 Testing HTTP request to {url}")
try:
response = requests.get(url, timeout=10)
print(f"✅ HTTP request successful")
print(f" Status: {response.status_code}")
print(f" SSL Cert: {response.raw.connection.sock.getpeercert().get('subject', 'Unknown') if hasattr(response.raw.connection, 'sock') else 'Unknown'}")
return True
except requests.exceptions.SSLError as e:
print(f"❌ SSL Error: {e}")
return False
except Exception as e:
print(f"❌ Request failed: {e}")
return False
def test_with_custom_cert_bundle(url, cert_bundle_path):
"""Test HTTP request with custom certificate bundle."""
print(f"\n🔐 Testing with custom cert bundle: {cert_bundle_path}")
if not os.path.exists(cert_bundle_path):
print(f"❌ Certificate bundle not found: {cert_bundle_path}")
return False
try:
response = requests.get(url, verify=cert_bundle_path, timeout=10)
print(f"✅ Request with custom cert bundle successful")
print(f" Status: {response.status_code}")
return True
except Exception as e:
print(f"❌ Request with custom cert bundle failed: {e}")
return False
def main():
"""Main diagnostic function."""
print("🔍 TradingAgents SSL Certificate Diagnostic Tool")
print("=" * 50)
# Get certificate information
print("\n📋 Certificate Bundle Information:")
cert_info = get_certificate_info()
for key, value in cert_info.items():
if isinstance(value, list):
print(f" {key}: {', '.join(value) if value else 'None found'}")
else:
print(f" {key}: {value}")
# Test SSL configuration
print(f"\n⚙️ Current SSL Configuration:")
ssl_config = get_ssl_config(DEFAULT_CONFIG)
for key, value in ssl_config.items():
print(f" {key}: {value}")
# Test common endpoints
test_endpoints = [
("api.openai.com", 443),
("openrouter.ai", 443),
("generativelanguage.googleapis.com", 443),
("www.google.com", 443)
]
print(f"\n🎯 Testing SSL connections:")
for hostname, port in test_endpoints:
test_ssl_connection(hostname, port)
# Test HTTP requests
test_urls = [
"https://api.openai.com/v1/models",
"https://www.google.com/search?q=test",
"https://openrouter.ai/api/v1/models"
]
print(f"\n🌍 Testing HTTP requests:")
for url in test_urls:
test_requests_connection(url)
# Test with different certificate bundles
if cert_info.get("certifi_bundle") and cert_info["certifi_bundle"] != "Not available (certifi not installed)":
print(f"\n🧪 Testing with certifi bundle:")
test_with_custom_cert_bundle("https://www.google.com", cert_info["certifi_bundle"])
# Provide recommendations
print(f"\n💡 Recommendations:")
print(" 📋 Certificate Bundle Configuration:")
print(" • Only set if you need a custom certificate bundle")
print(" • If not set, system default SSL behavior is used")
print(" export REQUESTS_CA_BUNDLE=/path/to/your/ca-bundle.crt")
print(" export CURL_CA_BUNDLE=/path/to/your/ca-bundle.crt")
print("\n ⚠️ SSL Verification (use with caution):")
print(" • Only disable for development/testing")
print(" • If not set, SSL verification is enabled by default")
print(" export SSL_VERIFY=false")
print("\n ⏱️ Timeout Configuration:")
print(" • Only set if default timeout is insufficient")
print(" export HTTP_TIMEOUT=60")
print("\n 🌐 Proxy Configuration:")
print(" • Only required if behind corporate firewall")
print(" export HTTP_PROXY=http://proxy.company.com:8080")
print(" export HTTPS_PROXY=https://proxy.company.com:8080")
print("\n 📝 Configuration:")
print(" • Add these to your .env file or export in shell")
print(" • Leave unset to use system defaults")
print(" • Only configure what you actually need")
if __name__ == "__main__":
main()

97
test_ssl_config.py Normal file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""
Test SSL configuration behavior
"""
import os
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config
def test_ssl_config():
"""Test SSL configuration with different environment variable settings"""
print("🧪 Testing SSL Configuration Behavior")
print("=" * 50)
# Test 1: No environment variables set
print("\n1⃣ Test: No SSL environment variables set")
os.environ.pop("REQUESTS_CA_BUNDLE", None)
os.environ.pop("CURL_CA_BUNDLE", None)
os.environ.pop("SSL_VERIFY", None)
os.environ.pop("HTTP_TIMEOUT", None)
from tradingagents.default_config import DEFAULT_CONFIG
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" SSL Config: {ssl_config}")
print(f" Expected: Empty or minimal config (default behavior)")
# Test 2: Custom certificate bundle set
print("\n2⃣ Test: Custom certificate bundle set")
os.environ["REQUESTS_CA_BUNDLE"] = "/custom/path/ca-bundle.crt"
# Re-import to get updated config
from importlib import reload
import tradingagents.default_config
reload(tradingagents.default_config)
from tradingagents.default_config import DEFAULT_CONFIG
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" Config ssl_cert_bundle: {config.get('ssl_cert_bundle')}")
print(f" SSL Config: {ssl_config}")
print(f" Expected: cert_bundle and verify set to custom path")
# Test 3: SSL verification disabled
print("\n3⃣ Test: SSL verification disabled")
os.environ.pop("REQUESTS_CA_BUNDLE", None)
os.environ["SSL_VERIFY"] = "false"
reload(tradingagents.default_config)
from tradingagents.default_config import DEFAULT_CONFIG
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" Config ssl_verify: {config.get('ssl_verify')}")
print(f" SSL Config: {ssl_config}")
print(f" Expected: verify set to False")
# Test 4: Timeout and proxy settings
print("\n4⃣ Test: Timeout and proxy settings")
os.environ["HTTP_TIMEOUT"] = "60"
os.environ["HTTP_PROXY"] = "http://proxy.example.com:8080"
os.environ["HTTPS_PROXY"] = "https://proxy.example.com:8080"
reload(tradingagents.default_config)
from tradingagents.default_config import DEFAULT_CONFIG
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" Config timeout: {config.get('http_timeout')}")
print(f" Config proxies: HTTP={config.get('http_proxy')}, HTTPS={config.get('https_proxy')}")
print(f" SSL Config: {ssl_config}")
print(f" Expected: timeout and proxies in ssl_config")
# Test 5: Empty environment variables (should not be used)
print("\n5⃣ Test: Empty environment variables")
os.environ["REQUESTS_CA_BUNDLE"] = ""
os.environ["HTTP_TIMEOUT"] = ""
reload(tradingagents.default_config)
from tradingagents.default_config import DEFAULT_CONFIG
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" Config ssl_cert_bundle: '{config.get('ssl_cert_bundle')}'")
print(f" Config http_timeout: {config.get('http_timeout')}")
print(f" SSL Config: {ssl_config}")
print(f" Expected: Empty values should not be used")
# Clean up
for var in ["REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_VERIFY", "HTTP_TIMEOUT", "HTTP_PROXY", "HTTPS_PROXY"]:
os.environ.pop(var, None)
print("\n✅ SSL configuration tests completed")
if __name__ == "__main__":
test_ssl_config()

101
test_ssl_connectivity.py Normal file
View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""
Test SSL connectivity for TradingAgents components
"""
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def test_api_connections():
"""Test API connections that TradingAgents will use"""
print("🔍 Testing TradingAgents API Connections")
print("=" * 50)
# Test 1: Basic HTTP requests with proper SSL
print("\n1⃣ Testing HTTP requests with SSL configuration:")
import requests
test_endpoints = [
("OpenAI API", "https://api.openai.com/v1/models"),
("Google Search", "https://www.google.com/search?q=AAPL"),
("OpenRouter API", "https://openrouter.ai/api/v1/models"),
]
for name, url in test_endpoints:
try:
response = requests.get(url, timeout=10)
print(f"{name}: Status {response.status_code}")
except Exception as e:
print(f"{name}: {e}")
# Test 2: LangChain LLM initialization
print("\n2⃣ Testing LangChain LLM initialization:")
try:
from langchain_openai import ChatOpenAI
# Test with the SSL configuration
llm = ChatOpenAI(
model="gpt-3.5-turbo",
api_key=os.getenv("OPENAI_API_KEY", "test-key")
)
print(" ✅ ChatOpenAI initialization successful")
# Test a simple API call (this might fail due to API key, but SSL should work)
try:
# This will test SSL connectivity
response = llm.invoke("Hello")
print(" ✅ ChatOpenAI API call successful")
except Exception as e:
if "401" in str(e) or "Unauthorized" in str(e):
print(" ✅ ChatOpenAI SSL working (401 = API key issue, not SSL)")
else:
print(f" ⚠️ ChatOpenAI API call error: {e}")
except Exception as e:
print(f" ❌ ChatOpenAI initialization failed: {e}")
# Test 3: TradingAgents configuration
print("\n3⃣ Testing TradingAgents SSL configuration:")
try:
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config
print(f" 📋 SSL cert bundle: {DEFAULT_CONFIG.get('ssl_cert_bundle')}")
print(f" 📋 SSL verify: {DEFAULT_CONFIG.get('ssl_verify')}")
ssl_config = get_ssl_config(DEFAULT_CONFIG)
print(f" 📋 SSL config: {ssl_config}")
# Set up global SSL configuration
setup_global_ssl_config(DEFAULT_CONFIG)
print(" ✅ Global SSL configuration applied")
except Exception as e:
print(f" ❌ TradingAgents SSL configuration failed: {e}")
# Test 4: Google News functionality
print("\n4⃣ Testing Google News data retrieval:")
try:
from tradingagents.dataflows.googlenews_utils import getNewsData
from tradingagents.dataflows.ssl_utils import get_ssl_config
from tradingagents.default_config import DEFAULT_CONFIG
ssl_config = get_ssl_config(DEFAULT_CONFIG)
# Test news retrieval with SSL config
news_results = getNewsData("AAPL", "2024-01-01", "2024-01-02", ssl_config)
print(f" ✅ Google News retrieval successful, got {len(news_results)} results")
except Exception as e:
print(f" ❌ Google News retrieval failed: {e}")
if __name__ == "__main__":
test_api_connections()

138
test_ssl_issues.py Normal file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
Test TradingAgents SSL connections specifically
"""
import os
import sys
from pathlib import Path
# Add project root to path
project_root = Path(__file__).parent.absolute()
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from dotenv import load_dotenv
load_dotenv()
from tradingagents.default_config import DEFAULT_CONFIG
from tradingagents.dataflows.ssl_utils import get_ssl_config, setup_global_ssl_config, get_certificate_info
from tradingagents.graph.trading_graph import TradingAgentsGraph
import requests
def test_certificate_issues():
"""Test SSL certificate issues that might occur in TradingAgents"""
print("🔍 Testing TradingAgents SSL Certificate Issues")
print("=" * 55)
# Show environment variables
print("\n📋 Environment Variables:")
ssl_vars = ["REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_VERIFY", "HTTP_TIMEOUT",
"HTTP_PROXY", "HTTPS_PROXY", "OPENAI_API_KEY", "FINNHUB_API_KEY"]
for var in ssl_vars:
value = os.getenv(var)
if value:
if "API_KEY" in var:
print(f" {var}: {'*' * min(8, len(value))}...")
else:
print(f" {var}: {value}")
else:
print(f" {var}: Not set")
# Show certificate info
print("\n📋 Certificate Bundle Information:")
cert_info = get_certificate_info()
for key, value in cert_info.items():
if isinstance(value, list):
print(f" {key}: {', '.join(value) if value else 'None found'}")
else:
print(f" {key}: {value}")
# Test SSL config
print("\n⚙️ Current SSL Configuration:")
config = DEFAULT_CONFIG.copy()
ssl_config = get_ssl_config(config)
print(f" SSL Config: {ssl_config}")
# Set up global SSL config
print("\n🔧 Setting up global SSL configuration...")
setup_global_ssl_config(config)
# Test basic HTTPS connections
test_urls = [
"https://api.openai.com/",
"https://www.google.com/",
"https://openrouter.ai/",
"https://finnhub.io/"
]
print(f"\n🌐 Testing HTTPS connections:")
for url in test_urls:
try:
print(f" Testing {url}...")
response = requests.get(url, timeout=10)
print(f"{url}: Status {response.status_code}")
except requests.exceptions.SSLError as e:
print(f" ❌ SSL Error for {url}: {e}")
except Exception as e:
print(f" ❌ Connection error for {url}: {e}")
# Test TradingAgentsGraph initialization
print(f"\n🤖 Testing TradingAgentsGraph initialization:")
try:
# Create minimal config
test_config = DEFAULT_CONFIG.copy()
test_config["llm_provider"] = "openai"
test_config["quick_think_llm"] = "gpt-3.5-turbo"
test_config["deep_think_llm"] = "gpt-4"
print(" Creating TradingAgentsGraph...")
graph = TradingAgentsGraph(config=test_config)
print(" ✅ TradingAgentsGraph created successfully")
# Test if we can make a simple LLM call
print(" Testing LLM connection...")
try:
# This won't actually make an API call but will test the LLM initialization
llm = graph.quick_thinking_llm
print(f" ✅ LLM initialized: {llm}")
except Exception as e:
print(f" ❌ LLM initialization error: {e}")
except Exception as e:
print(f" ❌ TradingAgentsGraph initialization error: {e}")
import traceback
traceback.print_exc()
# Recommendations based on findings
print(f"\n💡 Troubleshooting Recommendations:")
# Check if we're on macOS and suggest system certificates
if sys.platform == "darwin":
macos_cert = "/etc/ssl/cert.pem"
if os.path.exists(macos_cert):
print(f" 📱 macOS detected - try: export REQUESTS_CA_BUNDLE={macos_cert}")
else:
print(f" 📱 macOS detected but {macos_cert} not found")
# Check for certifi
try:
import certifi
print(f" 🔐 Certifi available - try: export REQUESTS_CA_BUNDLE={certifi.where()}")
except ImportError:
print(f" ❌ Certifi not installed - try: pip install certifi")
# Corporate environment suggestions
print(f" 🏢 If behind corporate firewall:")
print(f" • Contact IT for corporate certificate bundle")
print(f" • Check if HTTP_PROXY/HTTPS_PROXY needed")
print(f" • Ask about custom CA certificates")
# Temporary workaround (not recommended for production)
print(f" 🚨 Temporary workaround (development only):")
print(f" export SSL_VERIFY=false")
print(f" ⚠️ This disables SSL verification - use with caution!")
if __name__ == "__main__":
test_certificate_issues()

View File

@ -23,20 +23,36 @@ def is_rate_limited(response):
wait=wait_exponential(multiplier=1, min=4, max=60),
stop=stop_after_attempt(5),
)
def make_request(url, headers):
def make_request(url, headers, ssl_config=None):
"""Make a request with retry logic for rate limiting"""
# Random delay before each request to avoid detection
time.sleep(random.uniform(2, 6))
response = requests.get(url, headers=headers)
# Prepare SSL configuration - only use if explicitly configured
kwargs = {}
if ssl_config:
if ssl_config.get("cert_bundle"):
kwargs["verify"] = ssl_config["cert_bundle"]
elif "verify" in ssl_config:
kwargs["verify"] = ssl_config["verify"]
if ssl_config.get("timeout"):
kwargs["timeout"] = ssl_config["timeout"]
if ssl_config.get("proxies"):
kwargs["proxies"] = ssl_config["proxies"]
response = requests.get(url, headers=headers, **kwargs)
return response
def getNewsData(query, start_date, end_date):
def getNewsData(query, start_date, end_date, ssl_config=None):
"""
Scrape Google News search results for a given query and date range.
query: str - search query
start_date: str - start date in the format yyyy-mm-dd or mm/dd/yyyy
end_date: str - end date in the format yyyy-mm-dd or mm/dd/yyyy
ssl_config: dict - SSL configuration including cert_bundle, verify, timeout, proxies
"""
if "-" in start_date:
start_date = datetime.strptime(start_date, "%Y-%m-%d")
@ -64,7 +80,7 @@ def getNewsData(query, start_date, end_date):
)
try:
response = make_request(url, headers)
response = make_request(url, headers, ssl_config)
soup = BeautifulSoup(response.content, "html.parser")
results_on_page = soup.select("div.SoaBEf")

View File

@ -4,6 +4,7 @@ from .yfin_utils import *
from .stockstats_utils import *
from .googlenews_utils import *
from .finnhub_utils import get_data_in_range
from .ssl_utils import get_ssl_config, setup_global_ssl_config
from dateutil.relativedelta import relativedelta
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
@ -294,7 +295,13 @@ def get_google_news(
before = start_date - relativedelta(days=look_back_days)
before = before.strftime("%Y-%m-%d")
news_results = getNewsData(query, before, curr_date)
config = get_config()
ssl_config = get_ssl_config(config)
# Only pass ssl_config if it has actual configuration
if ssl_config:
news_results = getNewsData(query, before, curr_date, ssl_config)
else:
news_results = getNewsData(query, before, curr_date)
news_str = ""

View File

@ -0,0 +1,149 @@
"""
SSL/TLS configuration utilities for TradingAgents
"""
import os
import ssl
import certifi
from typing import Dict, Any, Optional
def get_ssl_config(config: Dict[str, Any]) -> Dict[str, Any]:
"""
Create SSL configuration dictionary from the main config.
Args:
config: Main configuration dictionary
Returns:
SSL configuration dictionary with cert_bundle, verify, timeout, proxies
"""
ssl_config = {}
# Certificate bundle configuration - only use if explicitly specified
cert_bundle = config.get("ssl_cert_bundle")
if cert_bundle and cert_bundle.strip():
# Use explicitly specified certificate bundle
ssl_config["cert_bundle"] = cert_bundle
ssl_config["verify"] = cert_bundle
elif not config.get("ssl_verify", True):
# Only disable SSL verification if explicitly set to false
ssl_config["verify"] = False
# If no explicit cert bundle and ssl_verify is true (default),
# don't set anything - use default behavior
# Timeout configuration
if config.get("http_timeout"):
ssl_config["timeout"] = config["http_timeout"]
# Proxy configuration
proxies = {}
if config.get("http_proxy"):
proxies["http"] = config["http_proxy"]
if config.get("https_proxy"):
proxies["https"] = config["https_proxy"]
if proxies:
ssl_config["proxies"] = proxies
return ssl_config
def setup_global_ssl_config(config: Dict[str, Any]) -> None:
"""
Set up global SSL configuration for the application.
This affects all SSL connections made by requests and other libraries.
Only sets configuration if explicitly specified in environment variables.
Args:
config: Main configuration dictionary
"""
# Set environment variables for requests library only if explicitly configured
cert_bundle = config.get("ssl_cert_bundle")
if cert_bundle and cert_bundle.strip():
os.environ["REQUESTS_CA_BUNDLE"] = cert_bundle
os.environ["CURL_CA_BUNDLE"] = cert_bundle
print(f"🔒 Using custom SSL certificate bundle: {cert_bundle}")
# Set SSL verification for requests only if explicitly disabled
if not config.get("ssl_verify", True):
# Disable SSL warnings when verification is disabled
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
print("⚠️ SSL certificate verification disabled")
# Set proxy environment variables if specified
if config.get("http_proxy"):
os.environ["HTTP_PROXY"] = config["http_proxy"]
print(f"🌐 Using HTTP proxy: {config['http_proxy']}")
if config.get("https_proxy"):
os.environ["HTTPS_PROXY"] = config["https_proxy"]
print(f"🌐 Using HTTPS proxy: {config['https_proxy']}")
# Set timeout if specified
if config.get("http_timeout"):
print(f"⏱️ HTTP timeout set to: {config['http_timeout']} seconds")
def create_ssl_context(cert_bundle: Optional[str] = None, verify_ssl: bool = True) -> ssl.SSLContext:
"""
Create a custom SSL context with specified certificate bundle.
Args:
cert_bundle: Path to certificate bundle file
verify_ssl: Whether to verify SSL certificates
Returns:
Configured SSL context
"""
if not verify_ssl:
# Create unverified context (not recommended for production)
context = ssl._create_unverified_context()
else:
# Create default context
context = ssl.create_default_context()
if cert_bundle:
# Load custom certificate bundle
context.load_verify_locations(cafile=cert_bundle)
return context
def get_certificate_info() -> Dict[str, str]:
"""
Get information about available certificate bundles.
Returns:
Dictionary with certificate bundle information
"""
info = {}
# Check certifi bundle
try:
import certifi
info["certifi_bundle"] = certifi.where()
except ImportError:
info["certifi_bundle"] = "Not available (certifi not installed)"
# Check environment variables
info["env_ca_bundle"] = os.getenv("REQUESTS_CA_BUNDLE", "Not set")
info["env_curl_bundle"] = os.getenv("CURL_CA_BUNDLE", "Not set")
# Check system certificate stores
common_cert_paths = [
"/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu
"/etc/pki/tls/certs/ca-bundle.crt", # RedHat/CentOS
"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD
"/etc/ssl/cert.pem", # OpenBSD
"/System/Library/OpenSSL/certs/cert.pem", # macOS
]
available_system_certs = []
for path in common_cert_paths:
if os.path.exists(path):
available_system_certs.append(path)
info["system_cert_bundles"] = available_system_certs
return info

View File

@ -26,4 +26,11 @@ DEFAULT_CONFIG = {
"online_tools": True,
"user_position": "none",
"cost_per_trade": 0.0,
# SSL/TLS Certificate settings - only use if explicitly set
"ssl_cert_bundle": os.getenv("REQUESTS_CA_BUNDLE") or os.getenv("CURL_CA_BUNDLE"),
"ssl_verify": os.getenv("SSL_VERIFY", "true").lower() in ("true", "1", "yes"),
"http_timeout": int(os.getenv("HTTP_TIMEOUT")) if os.getenv("HTTP_TIMEOUT") else None,
# Proxy settings (if needed)
"http_proxy": os.getenv("HTTP_PROXY"),
"https_proxy": os.getenv("HTTPS_PROXY"),
}

View File

@ -21,6 +21,7 @@ from tradingagents.agents.utils.agent_states import (
RiskDebateState,
)
from tradingagents.dataflows.interface import set_config
from tradingagents.dataflows.ssl_utils import setup_global_ssl_config
from .conditional_logic import ConditionalLogic
from .setup import GraphSetup
@ -50,6 +51,9 @@ class TradingAgentsGraph:
# Update the interface's config
set_config(self.config)
# Set up global SSL configuration
setup_global_ssl_config(self.config)
# Create necessary directories
os.makedirs(
@ -79,15 +83,48 @@ class TradingAgentsGraph:
"export OPENAI_API_KEY=your_openai_key_here"
)
# Prepare SSL configuration for HTTP client - only if explicitly configured
http_client_kwargs = {}
cert_bundle = self.config.get("ssl_cert_bundle")
if cert_bundle and cert_bundle.strip():
import httpx
http_client_kwargs["verify"] = cert_bundle
elif not self.config.get("ssl_verify", True):
import httpx
http_client_kwargs["verify"] = False
if self.config.get("http_timeout"):
import httpx
http_client_kwargs["timeout"] = self.config["http_timeout"]
# Add proxy configuration if specified
if self.config.get("http_proxy") or self.config.get("https_proxy"):
import httpx
proxies = {}
if self.config.get("http_proxy"):
proxies["http://"] = self.config["http_proxy"]
if self.config.get("https_proxy"):
proxies["https://"] = self.config["https_proxy"]
http_client_kwargs["proxies"] = proxies
# Create HTTP client only if we have custom settings
http_client = None
if http_client_kwargs:
import httpx
http_client = httpx.Client(**http_client_kwargs)
self.deep_thinking_llm = ChatOpenAI(
model=self.config["deep_think_llm"],
base_url=self.config["backend_url"],
api_key=api_key
api_key=api_key,
http_client=http_client
)
self.quick_thinking_llm = ChatOpenAI(
model=self.config["quick_think_llm"],
base_url=self.config["backend_url"],
api_key=api_key
api_key=api_key,
http_client=http_client
)
elif self.config["llm_provider"].lower() == "anthropic":
self.deep_thinking_llm = ChatAnthropic(model=self.config["deep_think_llm"], base_url=self.config["backend_url"])