feat: Add autonomous trading system with IBKR integration

- Implement IBKR connector for live portfolio monitoring
- Add multi-source data aggregator (congressional trades, news, insider trading)
- Create AI-powered signal processor with TradingAgents integration
- Build multi-channel alert system (Discord, Telegram, Email)
- Set up automated scheduler for 24/7 monitoring
- Add comprehensive configuration and safety controls
- Include portfolio analysis tools for IBKR positions

This system monitors markets continuously, tracks congressional trades,
and provides actionable trading signals with specific entry/exit prices.
This commit is contained in:
Zygmunt Dyras 2025-10-08 00:49:35 +02:00
parent 32be17c606
commit 22ff8d8a4f
16 changed files with 3524 additions and 2 deletions

282
AUTONOMOUS_README.md Normal file
View File

@ -0,0 +1,282 @@
# 🤖 Autonomous Trading System
A 24/7 intelligent trading system that monitors your IBKR portfolio, analyzes multiple data sources, and provides actionable trading signals.
## 🚀 Features
### Real-Time Monitoring
- **IBKR Portfolio Sync** - Live connection to your Interactive Brokers account
- **Position Tracking** - Monitor P&L, cost basis, and performance
- **Risk Management** - Automatic alerts for position limits and losses
### Multi-Source Intelligence
- **Congressional Trades** - Track politician stock trades via QuiverQuant
- **Insider Trading** - Monitor SEC filings and insider activity
- **Market Sentiment** - News analysis and social media sentiment
- **Technical Analysis** - Support/resistance, RSI, moving averages
- **AI Analysis** - TradingAgents multi-agent evaluation
### Smart Alerts
- **Trading Signals** - Specific entry/exit prices with confidence scores
- **Risk Warnings** - Position concentration and loss alerts
- **Opportunity Detection** - Congressional trades matching your portfolio
- **Multi-Channel** - Discord, Telegram, Email notifications
## 📋 Prerequisites
1. **Interactive Brokers Account** with TWS or IB Gateway
2. **API Keys**:
- OpenAI API key (required)
- Alpha Vantage API key (required)
- QuiverQuant API key (optional, for congressional trades)
- Discord Webhook URL (optional, for alerts)
## 🛠️ Installation
### 1. Install Dependencies
```bash
# Install autonomous system requirements
pip install -r requirements_autonomous.txt
# Install base TradingAgents requirements
pip install -r requirements.txt
```
### 2. Configure IBKR
1. Open TWS or IB Gateway
2. Enable API connections:
- File → Global Configuration → API → Settings
- Enable "Enable ActiveX and Socket Clients"
- Add "127.0.0.1" to trusted IPs
3. Note the port:
- TWS Paper: 7497
- TWS Live: 7496
- IB Gateway Paper: 4002
- IB Gateway Live: 4001
### 3. Set Environment Variables
Create or update `.env` file:
```bash
# === REQUIRED ===
OPENAI_API_KEY=your-openai-key
ALPHA_VANTAGE_API_KEY=your-alpha-vantage-key
# === IBKR Settings ===
IBKR_HOST=127.0.0.1
IBKR_PORT=7497 # Paper trading port
IBKR_CLIENT_ID=1
# === Optional Data Sources ===
QUIVER_API_KEY=your-quiver-key # For congressional trades
POLYGON_API_KEY=your-polygon-key # For real-time data
NEWS_API_KEY=your-news-api-key # For news aggregation
# === Notifications (at least one recommended) ===
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
TELEGRAM_CHAT_ID=your-chat-id
# === Trading Settings ===
TRADING_ENABLED=false # Set to true to enable trading
PAPER_TRADING=true # Use paper account
MAX_POSITION_SIZE=0.20 # Max 20% per position
CONFIDENCE_THRESHOLD=70 # Min confidence for trades
```
## 🎯 Quick Start
### 1. Test Connection
```bash
# Test IBKR connection
python -c "from autonomous.ibkr_connector import IBKRConnector; import asyncio; asyncio.run(IBKRConnector().connect())"
```
### 2. Start Monitoring (Safe Mode)
```bash
# Start with monitoring only (no trading)
TRADING_ENABLED=false python autonomous_trader.py
```
### 3. Paper Trading
```bash
# Test with paper account
PAPER_TRADING=true TRADING_ENABLED=true python autonomous_trader.py
```
### 4. Live Trading (⚠️ Use with caution!)
```bash
# Live trading - BE VERY CAREFUL
PAPER_TRADING=false TRADING_ENABLED=true python autonomous_trader.py
```
## 📊 System Architecture
```
┌─────────────────────────────────────────────────┐
│ AUTONOMOUS TRADER │
├─────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ IBKR │ │ Data │ │ AI │ │
│ │ Connector│ │Aggregator│ │ Analysis │ │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
│ └─────────────┼─────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │Signal Processor│ │
│ └───────┬────────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │
│ │ Alerts │ │ Risk │ │ Orders │ │
│ │ Engine │ │ Manager│ │(Future)│ │
│ └────────┘ └────────┘ └────────┘ │
│ │
└─────────────────────────────────────────────────┘
```
## 📱 Alert Examples
### Trading Signal Alert
```
🎯 ACTION: BUY
📈 TICKER: NVDA
💰 ENTRY: $132.50 - $133.00
🎯 TARGET 1: $138.00 (+4.1%)
🛑 STOP LOSS: $129.00 (-2.6%)
📊 CONFIDENCE: 85%
📝 REASONING:
• Congressional buying detected
• Strong technical breakout
• Positive earnings momentum
```
### Risk Warning Alert
```
⚠️ Risk Alert: AVGO
AVGO is down 5.2%
Current: $912.45
P&L: -$2,145.00
Consider stop loss or hedging
```
## 🕐 Schedule
The system runs these tasks automatically:
| Task | Frequency | Description |
|------|-----------|-------------|
| Portfolio Sync | 5 min | Update positions and P&L |
| Market Scan | 15 min | Look for opportunities |
| Congressional Check | 1 hour | New politician trades |
| News Monitor | 30 min | Sentiment analysis |
| Risk Check | 30 min | Portfolio risk assessment |
| Daily Summary | 4:30 PM | End of day report |
## 🛡️ Safety Features
1. **Position Limits** - Max 20% in any single position
2. **Daily Loss Limit** - Stops at 5% daily loss
3. **Paper Trading Mode** - Test without real money
4. **Manual Confirmation** - Required for large trades
5. **Stop Loss** - Automatic stop loss recommendations
6. **Circuit Breakers** - Halts during extreme volatility
## 🔧 Customization
### Add Custom Indicators
Edit `autonomous/signal_processor.py`:
```python
async def calculate_custom_indicator(self, ticker: str):
# Your custom logic here
pass
```
### Modify Alert Channels
Edit `autonomous/alert_engine.py`:
```python
async def _send_custom_channel(self, title, message):
# Your custom notification method
pass
```
### Change Trading Rules
Edit `autonomous/config/settings.py`:
```python
MAX_POSITION_SIZE = 0.15 # 15% instead of 20%
CONFIDENCE_THRESHOLD = 80 # Require 80% confidence
```
## 📈 Performance Monitoring
View logs and metrics:
```bash
# View real-time logs
tail -f autonomous_trader.log
# Check alert history
python -c "from autonomous.alert_engine import AlertEngine; print(AlertEngine().alert_history)"
```
## 🐛 Troubleshooting
### IBKR Connection Issues
- Ensure TWS/Gateway is running
- Check API settings are enabled
- Verify port number matches config
- Check firewall isn't blocking connection
### No Alerts Received
- Verify at least one notification channel is configured
- Check webhook URLs are correct
- Test with console output first
### High API Usage
- Reduce `MARKET_SCAN_INTERVAL` in config
- Use fewer models or smaller LLMs
- Implement caching for repeated queries
## ⚠️ Disclaimer
**This system is for educational and research purposes only.**
- Trading involves substantial risk of loss
- Past performance doesn't guarantee future results
- Always do your own research
- Start with paper trading
- Never risk more than you can afford to lose
## 📚 Resources
- [IBKR API Documentation](https://interactivebrokers.github.io/)
- [QuiverQuant API](https://www.quiverquant.com/api)
- [TradingAgents Documentation](../README.md)
- [Discord Webhooks Guide](https://discord.com/developers/docs/resources/webhook)
## 🤝 Support
For issues or questions:
1. Check the logs: `autonomous_trader.log`
2. Review configuration in `.env`
3. Test components individually
4. Open an issue with error details
---
**Remember**: Start small, test thoroughly, and never trade with money you can't afford to lose! 🎯

View File

@ -0,0 +1,272 @@
# 🤖 Autonomous Trading Intelligence System
## System Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ AUTONOMOUS TRADING BRAIN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ IBKR Live │ │ Market Data │ │ Alternative │ │
│ │ Integration │ │ Aggregator │ │ Data Sources │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ AI BRAIN │ │
│ │ (TradingAgents│ │
│ │ + Custom) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │Position │ │ Risk Mgmt │ │ Alert │ │
│ │Manager │ │ Engine │ │ System │ │
│ └─────────┘ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## 1. Core Components
### A. IBKR Live Integration Module
```python
# Key Features:
- Real-time portfolio sync via IB Gateway/TWS API
- Position tracking (shares, P&L, cost basis)
- Order execution capability (with safety controls)
- Account balance and margin monitoring
- Historical trade analysis
```
### B. Data Aggregation Pipeline
```python
DATA_SOURCES = {
"market_data": {
"real_time": ["IEX Cloud", "Polygon.io", "AlphaVantage Premium"],
"historical": ["yfinance", "IBKR API"]
},
"alternative_data": {
"congressional_trades": ["CapitolTrades API", "QuiverQuant"],
"insider_trading": ["SEC EDGAR", "OpenInsider API"],
"social_sentiment": ["Reddit API", "Twitter/X API", "StockTwits"],
"news": ["NewsAPI", "Benzinga", "Bloomberg Terminal"],
"earnings": ["AlphaVantage", "Yahoo Finance", "Earnings Whispers"],
"options_flow": ["FlowAlgo", "Unusual Whales API"],
"institutional": ["13F filings", "WhaleWisdom API"]
},
"economic_data": {
"fed": ["FRED API"],
"macro": ["TradingEconomics", "World Bank API"]
}
}
```
### C. Autonomous Monitoring System
```python
MONITORING_INTERVALS = {
"portfolio_health": "5 minutes",
"market_movers": "15 minutes",
"news_scan": "30 minutes",
"congressional_trades": "1 hour",
"earnings_calendar": "daily",
"technical_analysis": "1 hour",
"risk_assessment": "30 minutes"
}
```
## 2. Implementation Plan
### Phase 1: Foundation (Week 1-2)
- [ ] Set up IBKR API connection using ib_insync
- [ ] Create database (PostgreSQL/TimescaleDB) for historical data
- [ ] Build basic portfolio monitoring dashboard
- [ ] Implement core data fetching modules
### Phase 2: Intelligence Layer (Week 3-4)
- [ ] Integrate TradingAgents with continuous monitoring
- [ ] Add custom AI agents for specific strategies
- [ ] Implement pattern recognition system
- [ ] Create backtesting framework
### Phase 3: Alerting & Automation (Week 5-6)
- [ ] Build multi-channel alert system (Discord/Telegram/Email)
- [ ] Create trading signal generator
- [ ] Implement paper trading mode
- [ ] Add risk management rules
### Phase 4: Advanced Features (Week 7-8)
- [ ] Congressional trade mirroring alerts
- [ ] Earnings play recommendations
- [ ] Options strategy suggestions
- [ ] Portfolio rebalancing recommendations
## 3. Key Modules to Build
### A. Portfolio Monitor (`portfolio_monitor.py`)
```python
class PortfolioMonitor:
def __init__(self):
self.ibkr_client = IBKRClient()
self.positions = {}
self.alerts = []
async def sync_portfolio(self):
"""Sync with IBKR every 5 minutes"""
async def calculate_metrics(self):
"""Calculate P&L, exposure, risk metrics"""
async def generate_recommendations(self):
"""AI-powered buy/sell recommendations"""
```
### B. Market Scanner (`market_scanner.py`)
```python
class MarketScanner:
def __init__(self):
self.scanners = {
"momentum": MomentumScanner(),
"value": ValueScanner(),
"breakout": BreakoutScanner(),
"insider": InsiderScanner(),
"congressional": CongressionalScanner()
}
async def scan_opportunities(self):
"""Continuous market scanning"""
async def rank_opportunities(self):
"""AI-powered opportunity ranking"""
```
### C. Alert Engine (`alert_engine.py`)
```python
class AlertEngine:
def __init__(self):
self.channels = {
"discord": DiscordBot(),
"telegram": TelegramBot(),
"email": EmailNotifier(),
"sms": TwilioSMS()
}
async def send_alert(self, alert_type, message, priority):
"""Multi-channel alert distribution"""
```
## 4. Alert Types & Actions
### 🚨 CRITICAL ALERTS (Immediate Action)
- Stop loss triggers
- Margin calls
- Extreme volatility in holdings
- Major news affecting positions
### 📊 TRADING SIGNALS
```
FORMAT:
━━━━━━━━━━━━━━━━━━━━━━━
🎯 ACTION: BUY/SELL
📈 TICKER: NVDA
💰 PRICE: $450.25
🎯 TARGET: $465.00
🛑 STOP: $445.00
📊 CONFIDENCE: 85%
📝 REASON: Congressional buying + Earnings beat
━━━━━━━━━━━━━━━━━━━━━━━
```
### 🔍 OPPORTUNITY ALERTS
- Congressional trades matching your watchlist
- Unusual options activity
- Insider buying in your sectors
- Earnings surprises
- Technical breakouts
## 5. Database Schema
```sql
-- Portfolio tracking
CREATE TABLE positions (
id SERIAL PRIMARY KEY,
ticker VARCHAR(10),
shares INTEGER,
avg_cost DECIMAL,
current_price DECIMAL,
last_updated TIMESTAMP
);
-- Trade recommendations
CREATE TABLE recommendations (
id SERIAL PRIMARY KEY,
ticker VARCHAR(10),
action VARCHAR(10),
price_target DECIMAL,
stop_loss DECIMAL,
confidence DECIMAL,
reasoning TEXT,
created_at TIMESTAMP
);
-- Congressional trades
CREATE TABLE congressional_trades (
id SERIAL PRIMARY KEY,
politician VARCHAR(100),
ticker VARCHAR(10),
action VARCHAR(10),
amount_range VARCHAR(50),
filed_date DATE
);
```
## 6. Deployment Strategy
### Local Server Setup
```bash
# Docker Compose for all services
docker-compose up -d postgres redis rabbitmq
# Main application
python autonomous_trader.py --mode=production
# Background workers
celery -A tasks worker --loglevel=info
celery -A tasks beat --loglevel=info
```
### Cloud Deployment (AWS/GCP)
```yaml
services:
- trading_brain: EC2/Compute Engine
- database: RDS/Cloud SQL
- message_queue: SQS/Pub-Sub
- monitoring: CloudWatch/Stackdriver
- alerts: Lambda/Cloud Functions
```
## 7. Safety Features
### Risk Controls
```python
RISK_LIMITS = {
"max_position_size": 0.20, # 20% of portfolio
"max_daily_loss": 0.05, # 5% daily loss limit
"max_trades_per_day": 10,
"require_confirmation": True, # For trades > $10k
"paper_trade_first": True # Test mode
}
```
### Fail-Safes
- Circuit breakers for extreme market conditions
- Automatic position hedging
- Emergency liquidation protocols
- Manual override capabilities
## 8. Quick Start Implementation
Let me create the initial autonomous monitoring script:

105
analyze_portfolio.py Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Analyze your IBKR portfolio positions"""
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
from dotenv import load_dotenv
from datetime import datetime, timedelta
import time
# Load environment variables
load_dotenv()
# Your IBKR positions
PORTFOLIO = [
{"ticker": "AVGO", "name": "Broadcom Inc", "shares": 43},
{"ticker": "MSFT", "name": "Microsoft Corp", "shares": 12},
{"ticker": "MU", "name": "Micron Technology Inc", "shares": 13},
{"ticker": "NVDA", "name": "Nvidia Corp", "shares": 30},
{"ticker": "SXRV", "name": "iShares NASDAQ 100 USD ACC", "shares": 9},
{"ticker": "TSM", "name": "Taiwan Semiconductor SP ADR", "shares": 15},
]
print("=" * 70)
print("🏦 IBKR Portfolio Analysis - TradingAgents")
print("=" * 70)
print("\nYour positions:")
for pos in PORTFOLIO:
print(f"{pos['ticker']:6s} - {pos['shares']:3d} shares - {pos['name']}")
# Configure for efficient analysis
config = DEFAULT_CONFIG.copy()
config["deep_think_llm"] = "gpt-4o-mini" # Use faster model for bulk analysis
config["quick_think_llm"] = "gpt-4o-mini"
config["max_debate_rounds"] = 1 # Keep it fast
# Configure data sources
config["data_vendors"] = {
"core_stock_apis": "yfinance",
"technical_indicators": "yfinance",
"fundamental_data": "alpha_vantage",
"news_data": "alpha_vantage",
}
# Use recent date for analysis
analysis_date = (datetime.now() - timedelta(days=5)).strftime("%Y-%m-%d")
print(f"\n📅 Analysis date: {analysis_date}")
print("🤖 Using: gpt-4o-mini (fast mode)")
print("📊 Data sources: yfinance + Alpha Vantage")
print("\n" + "=" * 70)
# Initialize the trading graph
ta = TradingAgentsGraph(debug=False, config=config)
# Store decisions
decisions = {}
# Analyze each position
for i, position in enumerate(PORTFOLIO, 1):
ticker = position["ticker"]
# Skip ETF for now (SXRV might not have all data available)
if ticker == "SXRV":
print(f"\n[{i}/6] Skipping {ticker} (ETF - limited data)")
decisions[ticker] = "ETF - Manual review recommended"
continue
print(f"\n[{i}/6] Analyzing {ticker} ({position['name']})...")
print(" 🔄 Agents working...")
try:
start_time = time.time()
_, decision = ta.propagate(ticker, analysis_date)
elapsed = time.time() - start_time
decisions[ticker] = decision
print(f" ✅ Complete ({elapsed:.1f}s)")
# Brief pause to avoid rate limits
if i < len(PORTFOLIO):
time.sleep(2)
except Exception as e:
print(f" ❌ Error: {str(e)[:100]}")
decisions[ticker] = f"Error during analysis: {str(e)[:100]}"
# Summary Report
print("\n" + "=" * 70)
print("📈 PORTFOLIO ANALYSIS SUMMARY")
print("=" * 70)
for position in PORTFOLIO:
ticker = position["ticker"]
print(f"\n{'='*70}")
print(f"📊 {ticker} - {position['name']} ({position['shares']} shares)")
print(f"{'='*70}")
if ticker in decisions:
print(decisions[ticker])
print("\n" + "=" * 70)
print("✅ Portfolio analysis complete!")
print("\nNote: This is AI analysis for research purposes only.")
print("Always do your own due diligence before making trading decisions.")
print("=" * 70)

21
autonomous/__init__.py Normal file
View File

@ -0,0 +1,21 @@
"""
Autonomous Trading System
========================
A 24/7 monitoring system that integrates with IBKR and multiple data sources
to provide real-time trading recommendations.
"""
__version__ = "0.1.0"
from .ibkr_connector import IBKRConnector
from .data_aggregator import DataAggregator
from .signal_processor import SignalProcessor
from .alert_engine import AlertEngine
__all__ = [
"IBKRConnector",
"DataAggregator",
"SignalProcessor",
"AlertEngine",
]

468
autonomous/alert_engine.py Normal file
View File

@ -0,0 +1,468 @@
"""
Alert Engine
===========
Multi-channel notification system for trading alerts.
Supports Discord, Telegram, Email, and console output.
"""
import asyncio
import logging
import json
from typing import Dict, List, Optional, Any
from datetime import datetime
from enum import Enum
import os
# Optional imports for different notification channels
try:
import discord
from discord import Webhook
import aiohttp
DISCORD_AVAILABLE = True
except ImportError:
DISCORD_AVAILABLE = False
print("Discord not installed. Install with: pip install discord.py")
try:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
EMAIL_AVAILABLE = True
except ImportError:
EMAIL_AVAILABLE = False
logger = logging.getLogger(__name__)
class AlertPriority(Enum):
"""Alert priority levels"""
CRITICAL = "critical" # Immediate action required
HIGH = "high" # Important, time-sensitive
MEDIUM = "medium" # Standard alerts
LOW = "low" # Informational
INFO = "info" # Non-actionable info
class AlertType(Enum):
"""Types of alerts"""
TRADING_SIGNAL = "trading_signal"
PORTFOLIO_UPDATE = "portfolio_update"
RISK_WARNING = "risk_warning"
CONGRESSIONAL_TRADE = "congressional_trade"
INSIDER_TRADE = "insider_trade"
EARNINGS = "earnings"
MARKET_MOVE = "market_move"
SYSTEM = "system"
class AlertEngine:
"""
Manages multi-channel alert distribution
"""
def __init__(self, config: Optional[Dict] = None):
"""
Initialize alert engine
Args:
config: Configuration with API keys and settings
"""
self.config = config or {}
self.discord_webhook_url = self.config.get('discord_webhook_url')
self.telegram_bot_token = self.config.get('telegram_bot_token')
self.telegram_chat_id = self.config.get('telegram_chat_id')
self.email_config = self.config.get('email', {})
# Track sent alerts to avoid duplicates
self.sent_alerts: List[Dict] = []
self.alert_history: List[Dict] = []
async def send_alert(self,
title: str,
message: str,
alert_type: AlertType,
priority: AlertPriority,
data: Optional[Dict] = None,
channels: Optional[List[str]] = None) -> bool:
"""
Send alert through specified channels
Args:
title: Alert title
message: Alert message
alert_type: Type of alert
priority: Alert priority
data: Additional data
channels: List of channels to use (discord, telegram, email, console)
Returns:
True if alert sent successfully
"""
# Default channels based on priority
if channels is None:
if priority == AlertPriority.CRITICAL:
channels = ['discord', 'telegram', 'email', 'console']
elif priority == AlertPriority.HIGH:
channels = ['discord', 'telegram', 'console']
elif priority == AlertPriority.MEDIUM:
channels = ['discord', 'console']
else:
channels = ['console']
# Check for duplicate alerts
alert_hash = f"{title}_{message}_{datetime.now().strftime('%Y%m%d%H')}"
if alert_hash in [a.get('hash') for a in self.sent_alerts[-100:]]:
logger.info("Skipping duplicate alert")
return False
# Create alert record
alert_record = {
'hash': alert_hash,
'title': title,
'message': message,
'type': alert_type.value,
'priority': priority.value,
'timestamp': datetime.now().isoformat(),
'data': data
}
success = False
# Send to each channel
tasks = []
if 'discord' in channels and self.discord_webhook_url:
tasks.append(self._send_discord(title, message, alert_type, priority, data))
if 'telegram' in channels and self.telegram_bot_token:
tasks.append(self._send_telegram(title, message, alert_type, priority, data))
if 'email' in channels and self.email_config:
tasks.append(self._send_email(title, message, alert_type, priority, data))
if 'console' in channels:
self._send_console(title, message, alert_type, priority, data)
success = True
# Execute all sends in parallel
if tasks:
results = await asyncio.gather(*tasks, return_exceptions=True)
success = any(r is True for r in results if not isinstance(r, Exception))
# Record alert
if success:
self.sent_alerts.append(alert_record)
self.alert_history.append(alert_record)
return success
async def _send_discord(self,
title: str,
message: str,
alert_type: AlertType,
priority: AlertPriority,
data: Optional[Dict]) -> bool:
"""Send alert to Discord via webhook"""
if not DISCORD_AVAILABLE or not self.discord_webhook_url:
return False
try:
# Color based on priority
colors = {
AlertPriority.CRITICAL: 0xFF0000, # Red
AlertPriority.HIGH: 0FFA500, # Orange
AlertPriority.MEDIUM: 0x00FF00, # Green
AlertPriority.LOW: 0x0000FF, # Blue
AlertPriority.INFO: 0x808080 # Gray
}
# Icons for different alert types
icons = {
AlertType.TRADING_SIGNAL: "🎯",
AlertType.PORTFOLIO_UPDATE: "📊",
AlertType.RISK_WARNING: "⚠️",
AlertType.CONGRESSIONAL_TRADE: "🏛️",
AlertType.INSIDER_TRADE: "👔",
AlertType.EARNINGS: "📈",
AlertType.MARKET_MOVE: "📉",
AlertType.SYSTEM: "🔧"
}
icon = icons.get(alert_type, "📢")
async with aiohttp.ClientSession() as session:
webhook = Webhook.from_url(self.discord_webhook_url, session=session)
# Create embed
embed = discord.Embed(
title=f"{icon} {title}",
description=message,
color=colors.get(priority, 0x000000),
timestamp=datetime.now()
)
# Add fields from data
if data:
for key, value in list(data.items())[:5]: # Limit to 5 fields
if isinstance(value, (str, int, float)):
embed.add_field(name=key.replace('_', ' ').title(),
value=str(value),
inline=True)
embed.set_footer(text=f"Alert Type: {alert_type.value} | Priority: {priority.value}")
await webhook.send(embed=embed)
logger.info(f"Discord alert sent: {title}")
return True
except Exception as e:
logger.error(f"Failed to send Discord alert: {e}")
return False
async def _send_telegram(self,
title: str,
message: str,
alert_type: AlertType,
priority: AlertPriority,
data: Optional[Dict]) -> bool:
"""Send alert to Telegram"""
if not self.telegram_bot_token or not self.telegram_chat_id:
return False
try:
import aiohttp
# Format message for Telegram
icons = {
AlertPriority.CRITICAL: "🚨",
AlertPriority.HIGH: "⚠️",
AlertPriority.MEDIUM: "📊",
AlertPriority.LOW: "",
AlertPriority.INFO: "💡"
}
icon = icons.get(priority, "📢")
telegram_message = f"{icon} *{title}*\n\n{message}"
if data:
telegram_message += "\n\n*Details:*\n"
for key, value in list(data.items())[:5]:
if isinstance(value, (str, int, float)):
telegram_message += f"{key}: {value}\n"
# Send via Telegram Bot API
url = f"https://api.telegram.org/bot{self.telegram_bot_token}/sendMessage"
payload = {
'chat_id': self.telegram_chat_id,
'text': telegram_message,
'parse_mode': 'Markdown'
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload) as response:
if response.status == 200:
logger.info(f"Telegram alert sent: {title}")
return True
else:
logger.error(f"Telegram API error: {response.status}")
return False
except Exception as e:
logger.error(f"Failed to send Telegram alert: {e}")
return False
async def _send_email(self,
title: str,
message: str,
alert_type: AlertType,
priority: AlertPriority,
data: Optional[Dict]) -> bool:
"""Send alert via email"""
if not EMAIL_AVAILABLE or not self.email_config:
return False
try:
# Email configuration
smtp_server = self.email_config.get('smtp_server', 'smtp.gmail.com')
smtp_port = self.email_config.get('smtp_port', 587)
sender_email = self.email_config.get('sender_email')
sender_password = self.email_config.get('sender_password')
recipient_email = self.email_config.get('recipient_email')
if not all([sender_email, sender_password, recipient_email]):
return False
# Create message
msg = MIMEMultipart('alternative')
msg['Subject'] = f"[{priority.value.upper()}] {title}"
msg['From'] = sender_email
msg['To'] = recipient_email
# Create HTML content
html_content = f"""
<html>
<body>
<h2>{title}</h2>
<p>{message.replace(chr(10), '<br>')}</p>
<hr>
<p><small>Alert Type: {alert_type.value} | Priority: {priority.value}</small></p>
</body>
</html>
"""
# Attach HTML content
html_part = MIMEText(html_content, 'html')
msg.attach(html_part)
# Send email
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(sender_email, sender_password)
server.send_message(msg)
logger.info(f"Email alert sent: {title}")
return True
except Exception as e:
logger.error(f"Failed to send email alert: {e}")
return False
def _send_console(self,
title: str,
message: str,
alert_type: AlertType,
priority: AlertPriority,
data: Optional[Dict]):
"""Display alert in console"""
# Color codes for terminal
colors = {
AlertPriority.CRITICAL: "\033[91m", # Red
AlertPriority.HIGH: "\033[93m", # Yellow
AlertPriority.MEDIUM: "\033[92m", # Green
AlertPriority.LOW: "\033[94m", # Blue
AlertPriority.INFO: "\033[95m" # Magenta
}
reset_color = "\033[0m"
color = colors.get(priority, "")
print(f"\n{color}{'='*60}")
print(f"🔔 ALERT: {title}")
print(f"{'='*60}{reset_color}")
print(f"Type: {alert_type.value} | Priority: {priority.value}")
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"\n{message}")
if data:
print("\nAdditional Data:")
for key, value in data.items():
print(f" {key}: {value}")
print(f"{color}{'='*60}{reset_color}\n")
async def send_trading_signal(self, recommendation):
"""
Send a trading signal alert
Args:
recommendation: TradingRecommendation object
"""
from .signal_processor import TradingRecommendation
if isinstance(recommendation, TradingRecommendation):
# Format title based on action
emoji = "🟢" if recommendation.action == "BUY" else "🔴"
title = f"{emoji} {recommendation.action} Signal: {recommendation.ticker}"
# Calculate potential gains
gain_1 = ((recommendation.target_price_1 - recommendation.current_price) /
recommendation.current_price) * 100
stop_loss_pct = ((recommendation.stop_loss - recommendation.current_price) /
recommendation.current_price) * 100
message = f"""
Action: {recommendation.action}
Current Price: ${recommendation.current_price:.2f}
Entry Range: ${recommendation.entry_price_min:.2f} - ${recommendation.entry_price_max:.2f}
Target 1: ${recommendation.target_price_1:.2f} ({gain_1:+.1f}%)
Stop Loss: ${recommendation.stop_loss:.2f} ({stop_loss_pct:.1f}%)
Confidence: {recommendation.confidence:.0f}%
Position Size: {recommendation.position_size:.1%}
Risk Level: {recommendation.risk_level}
Reasoning: {recommendation.reasoning[:200]}
"""
# Determine priority based on confidence
if recommendation.confidence >= 85:
priority = AlertPriority.HIGH
elif recommendation.confidence >= 70:
priority = AlertPriority.MEDIUM
else:
priority = AlertPriority.LOW
await self.send_alert(
title=title,
message=message,
alert_type=AlertType.TRADING_SIGNAL,
priority=priority,
data={
'ticker': recommendation.ticker,
'action': recommendation.action,
'confidence': recommendation.confidence
}
)
async def send_portfolio_summary(self, portfolio_data: Dict):
"""Send daily portfolio summary"""
title = "📊 Daily Portfolio Summary"
total_value = portfolio_data.get('total_value', 0)
total_pnl = portfolio_data.get('total_unrealized_pnl', 0)
pnl_percent = portfolio_data.get('total_percent_change', 0)
message = f"""
Total Value: ${total_value:,.2f}
Unrealized P&L: ${total_pnl:+,.2f} ({pnl_percent:+.2f}%)
Positions: {portfolio_data.get('position_count', 0)}
Top Performers:
"""
# Add top positions
positions = portfolio_data.get('positions', [])
sorted_positions = sorted(positions, key=lambda x: x['percent'], reverse=True)
for pos in sorted_positions[:3]:
message += f"{pos['ticker']}: ${pos['value']:,.2f} ({pos['percent']:+.2f}%)\n"
await self.send_alert(
title=title,
message=message,
alert_type=AlertType.PORTFOLIO_UPDATE,
priority=AlertPriority.INFO
)
# Example usage
async def main():
"""Example of using the alert engine"""
config = {
'discord_webhook_url': os.getenv('DISCORD_WEBHOOK_URL'),
'telegram_bot_token': os.getenv('TELEGRAM_BOT_TOKEN'),
'telegram_chat_id': os.getenv('TELEGRAM_CHAT_ID'),
}
alert_engine = AlertEngine(config)
# Send test alert
await alert_engine.send_alert(
title="Test Trading Alert",
message="NVDA showing strong buy signals based on congressional activity",
alert_type=AlertType.TRADING_SIGNAL,
priority=AlertPriority.HIGH,
data={'ticker': 'NVDA', 'confidence': 85}
)
if __name__ == "__main__":
asyncio.run(main())

View File

@ -0,0 +1,180 @@
"""
Configuration Settings
=====================
Central configuration for the autonomous trading system.
"""
import os
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
class Config:
"""Configuration class for autonomous trading system"""
# === IBKR Settings ===
IBKR_HOST = os.getenv('IBKR_HOST', '127.0.0.1')
IBKR_PORT = int(os.getenv('IBKR_PORT', 7497)) # 7497=paper, 7496=live
IBKR_CLIENT_ID = int(os.getenv('IBKR_CLIENT_ID', 1))
# === API Keys ===
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
ALPHA_VANTAGE_API_KEY = os.getenv('ALPHA_VANTAGE_API_KEY')
QUIVER_API_KEY = os.getenv('QUIVER_API_KEY')
POLYGON_API_KEY = os.getenv('POLYGON_API_KEY')
NEWS_API_KEY = os.getenv('NEWS_API_KEY')
# === Notification Settings ===
# Discord
DISCORD_WEBHOOK_URL = os.getenv('DISCORD_WEBHOOK_URL')
# Telegram
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
TELEGRAM_CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
# Email
EMAIL_SMTP_SERVER = os.getenv('EMAIL_SMTP_SERVER', 'smtp.gmail.com')
EMAIL_SMTP_PORT = int(os.getenv('EMAIL_SMTP_PORT', 587))
EMAIL_SENDER = os.getenv('EMAIL_SENDER')
EMAIL_PASSWORD = os.getenv('EMAIL_PASSWORD')
EMAIL_RECIPIENT = os.getenv('EMAIL_RECIPIENT')
# === Portfolio Settings ===
# Your IBKR portfolio tickers
PORTFOLIO_TICKERS = [
"AVGO", # Broadcom - 43 shares
"MSFT", # Microsoft - 12 shares
"MU", # Micron - 13 shares
"NVDA", # Nvidia - 30 shares
"TSM", # Taiwan Semi - 15 shares
]
# Additional tickers to monitor
WATCHLIST = os.getenv('WATCHLIST', 'AAPL,TSLA,META,GOOGL,AMZN').split(',')
# === Trading Settings ===
TRADING_ENABLED = os.getenv('TRADING_ENABLED', 'false').lower() == 'true'
PAPER_TRADING = os.getenv('PAPER_TRADING', 'true').lower() == 'true'
# Risk Management
MAX_POSITION_SIZE = float(os.getenv('MAX_POSITION_SIZE', 0.20)) # 20% max per position
MAX_DAILY_LOSS = float(os.getenv('MAX_DAILY_LOSS', 0.05)) # 5% daily loss limit
MAX_TRADES_PER_DAY = int(os.getenv('MAX_TRADES_PER_DAY', 10))
STOP_LOSS_PERCENT = float(os.getenv('STOP_LOSS_PERCENT', 0.03)) # 3% stop loss
# Position Sizing
BASE_POSITION_SIZE = float(os.getenv('BASE_POSITION_SIZE', 0.10)) # 10% base
MIN_POSITION_SIZE = float(os.getenv('MIN_POSITION_SIZE', 0.05)) # 5% minimum
CONFIDENCE_THRESHOLD = float(os.getenv('CONFIDENCE_THRESHOLD', 70)) # Min 70% confidence
# === AI Model Settings ===
DEEP_THINK_MODEL = os.getenv('DEEP_THINK_MODEL', 'gpt-4o-mini')
QUICK_THINK_MODEL = os.getenv('QUICK_THINK_MODEL', 'gpt-4o-mini')
MAX_DEBATE_ROUNDS = int(os.getenv('MAX_DEBATE_ROUNDS', 1))
# === Schedule Settings (in minutes) ===
PORTFOLIO_CHECK_INTERVAL = int(os.getenv('PORTFOLIO_CHECK_INTERVAL', 5))
MARKET_SCAN_INTERVAL = int(os.getenv('MARKET_SCAN_INTERVAL', 15))
NEWS_CHECK_INTERVAL = int(os.getenv('NEWS_CHECK_INTERVAL', 30))
CONGRESS_CHECK_INTERVAL = int(os.getenv('CONGRESS_CHECK_INTERVAL', 60))
RISK_CHECK_INTERVAL = int(os.getenv('RISK_CHECK_INTERVAL', 30))
# === Database Settings ===
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_PORT = int(os.getenv('DB_PORT', 5432))
DB_NAME = os.getenv('DB_NAME', 'trading_autonomous')
DB_USER = os.getenv('DB_USER', 'trader')
DB_PASSWORD = os.getenv('DB_PASSWORD', 'password')
@classmethod
def get_db_url(cls):
"""Get database connection URL"""
return f"postgresql://{cls.DB_USER}:{cls.DB_PASSWORD}@{cls.DB_HOST}:{cls.DB_PORT}/{cls.DB_NAME}"
@classmethod
def to_dict(cls):
"""Convert config to dictionary"""
return {
# IBKR
'ibkr_host': cls.IBKR_HOST,
'ibkr_port': cls.IBKR_PORT,
'ibkr_client_id': cls.IBKR_CLIENT_ID,
# API Keys
'openai_api_key': cls.OPENAI_API_KEY,
'alpha_vantage_api_key': cls.ALPHA_VANTAGE_API_KEY,
'quiver_api_key': cls.QUIVER_API_KEY,
# Notifications
'discord_webhook_url': cls.DISCORD_WEBHOOK_URL,
'telegram_bot_token': cls.TELEGRAM_BOT_TOKEN,
'telegram_chat_id': cls.TELEGRAM_CHAT_ID,
'email': {
'smtp_server': cls.EMAIL_SMTP_SERVER,
'smtp_port': cls.EMAIL_SMTP_PORT,
'sender_email': cls.EMAIL_SENDER,
'sender_password': cls.EMAIL_PASSWORD,
'recipient_email': cls.EMAIL_RECIPIENT
},
# Portfolio
'portfolio_tickers': cls.PORTFOLIO_TICKERS,
'watchlist': cls.WATCHLIST,
# Trading
'trading_enabled': cls.TRADING_ENABLED,
'paper_trading': cls.PAPER_TRADING,
'max_position_size': cls.MAX_POSITION_SIZE,
'confidence_threshold': cls.CONFIDENCE_THRESHOLD,
# AI Models
'deep_think_llm': cls.DEEP_THINK_MODEL,
'quick_think_llm': cls.QUICK_THINK_MODEL,
'max_debate_rounds': cls.MAX_DEBATE_ROUNDS
}
@classmethod
def validate(cls):
"""Validate configuration"""
errors = []
# Check required API keys
if not cls.OPENAI_API_KEY:
errors.append("OPENAI_API_KEY is required")
if not cls.ALPHA_VANTAGE_API_KEY:
errors.append("ALPHA_VANTAGE_API_KEY is required")
# Check notification settings
if not any([cls.DISCORD_WEBHOOK_URL, cls.TELEGRAM_BOT_TOKEN, cls.EMAIL_SENDER]):
errors.append("At least one notification method must be configured")
# Validate risk settings
if cls.MAX_POSITION_SIZE > 0.5:
errors.append("MAX_POSITION_SIZE should not exceed 50%")
if cls.MAX_DAILY_LOSS > 0.1:
errors.append("MAX_DAILY_LOSS should not exceed 10%")
if errors:
print("Configuration errors:")
for error in errors:
print(f" - {error}")
return False
return True
# Example config validation
if __name__ == "__main__":
if Config.validate():
print("✅ Configuration valid")
print(f"Portfolio: {Config.PORTFOLIO_TICKERS}")
print(f"Trading: {'ENABLED' if Config.TRADING_ENABLED else 'DISABLED'}")
print(f"Mode: {'PAPER' if Config.PAPER_TRADING else 'LIVE'}")
else:
print("❌ Configuration invalid")

View File

@ -0,0 +1,495 @@
"""
Data Aggregator
===============
Aggregates data from multiple sources including:
- Congressional trades (QuiverQuant)
- Market data (yfinance, Alpha Vantage)
- News sentiment
- Insider trading
- Earnings calendars
"""
import asyncio
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
from dataclasses import dataclass
import os
import json
import aiohttp
# Import existing TradingAgents data tools
import sys
sys.path.append('..')
from tradingagents.dataflows.y_finance import YfinanceInterface
from tradingagents.dataflows.alpha_vantage_news import AlphaVantageNewsInterface
# Optional imports
try:
import quiverquant
QUIVER_AVAILABLE = True
except ImportError:
QUIVER_AVAILABLE = False
print("QuiverQuant not installed. Install with: pip install quiverquant")
try:
import yfinance as yf
YFINANCE_AVAILABLE = True
except ImportError:
YFINANCE_AVAILABLE = False
logger = logging.getLogger(__name__)
@dataclass
class CongressionalTrade:
"""Represents a congressional trade"""
politician: str
ticker: str
action: str # 'purchase' or 'sale'
amount_range: str
transaction_date: datetime
filing_date: datetime
party: str
state: str
chamber: str # 'house' or 'senate'
@dataclass
class InsiderTrade:
"""Represents an insider trade"""
insider_name: str
ticker: str
action: str # 'Buy' or 'Sell'
shares: int
value: float
transaction_date: datetime
position: str # CEO, CFO, Director, etc.
@dataclass
class EarningsEvent:
"""Represents an earnings event"""
ticker: str
earnings_date: datetime
eps_estimate: float
eps_actual: Optional[float]
revenue_estimate: float
revenue_actual: Optional[float]
surprise_percent: Optional[float]
@dataclass
class MarketSignal:
"""Aggregated market signal"""
ticker: str
signal_type: str # 'congressional', 'insider', 'earnings', 'technical'
action: str # 'BUY', 'SELL', 'HOLD'
confidence: float # 0-100
data: Dict[str, Any]
timestamp: datetime
class DataAggregator:
"""
Aggregates data from multiple sources for trading decisions
"""
def __init__(self, config: Optional[Dict] = None):
"""
Initialize data aggregator
Args:
config: Configuration dictionary with API keys
"""
self.config = config or {}
self.quiver_client = None
self.congressional_trades: List[CongressionalTrade] = []
self.insider_trades: List[InsiderTrade] = []
self.earnings_calendar: List[EarningsEvent] = []
self.market_signals: List[MarketSignal] = []
# Initialize QuiverQuant if available
if QUIVER_AVAILABLE and self.config.get('quiver_api_key'):
self.quiver_client = quiverquant.quiver(self.config['quiver_api_key'])
async def fetch_congressional_trades(self,
tickers: Optional[List[str]] = None,
days_back: int = 30) -> List[CongressionalTrade]:
"""
Fetch recent congressional trades
Args:
tickers: List of tickers to filter (None for all)
days_back: Number of days to look back
Returns:
List of congressional trades
"""
trades = []
if not self.quiver_client:
logger.warning("QuiverQuant not configured, using mock data")
# Return mock data for demonstration
return self._get_mock_congressional_trades(tickers)
try:
# Fetch congressional trading data
df = self.quiver_client.congress_trading()
if df is not None and not df.empty:
# Filter by date
cutoff_date = datetime.now() - timedelta(days=days_back)
df['TransactionDate'] = pd.to_datetime(df['TransactionDate'])
df = df[df['TransactionDate'] >= cutoff_date]
# Filter by tickers if provided
if tickers:
df = df[df['Ticker'].isin(tickers)]
# Convert to CongressionalTrade objects
for _, row in df.iterrows():
trade = CongressionalTrade(
politician=row.get('Representative', 'Unknown'),
ticker=row.get('Ticker', ''),
action=row.get('Transaction', 'Unknown').lower(),
amount_range=row.get('Range', 'Unknown'),
transaction_date=row.get('TransactionDate'),
filing_date=row.get('FilingDate', row.get('TransactionDate')),
party=row.get('Party', 'Unknown'),
state=row.get('State', 'Unknown'),
chamber=row.get('Chamber', 'house')
)
trades.append(trade)
self.congressional_trades = trades
logger.info(f"Fetched {len(trades)} congressional trades")
except Exception as e:
logger.error(f"Error fetching congressional trades: {e}")
trades = self._get_mock_congressional_trades(tickers)
return trades
def _get_mock_congressional_trades(self, tickers: Optional[List[str]] = None) -> List[CongressionalTrade]:
"""Get mock congressional trades for testing"""
mock_trades = [
CongressionalTrade(
politician="Nancy Pelosi",
ticker="NVDA",
action="purchase",
amount_range="$1,000,001 - $5,000,000",
transaction_date=datetime.now() - timedelta(days=5),
filing_date=datetime.now() - timedelta(days=2),
party="D",
state="CA",
chamber="house"
),
CongressionalTrade(
politician="Dan Crenshaw",
ticker="MSFT",
action="purchase",
amount_range="$15,001 - $50,000",
transaction_date=datetime.now() - timedelta(days=10),
filing_date=datetime.now() - timedelta(days=7),
party="R",
state="TX",
chamber="house"
),
CongressionalTrade(
politician="Josh Gottheimer",
ticker="AVGO",
action="purchase",
amount_range="$50,001 - $100,000",
transaction_date=datetime.now() - timedelta(days=3),
filing_date=datetime.now() - timedelta(days=1),
party="D",
state="NJ",
chamber="house"
)
]
if tickers:
mock_trades = [t for t in mock_trades if t.ticker in tickers]
return mock_trades
async def fetch_insider_trades(self,
ticker: str,
days_back: int = 90) -> List[InsiderTrade]:
"""
Fetch insider trading data
Args:
ticker: Stock ticker
days_back: Number of days to look back
Returns:
List of insider trades
"""
trades = []
if not YFINANCE_AVAILABLE:
logger.warning("yfinance not available for insider data")
return trades
try:
stock = yf.Ticker(ticker)
insider_df = stock.insider_transactions
if insider_df is not None and not insider_df.empty:
cutoff_date = datetime.now() - timedelta(days=days_back)
for _, row in insider_df.iterrows():
# Parse date
date_str = row.get('Date', '')
try:
trade_date = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
except:
continue
if trade_date >= cutoff_date:
trade = InsiderTrade(
insider_name=row.get('Insider', 'Unknown'),
ticker=ticker,
action='Buy' if row.get('Transaction', '').lower() in ['buy', 'purchase'] else 'Sell',
shares=int(row.get('Shares', 0)),
value=float(row.get('Value', 0)),
transaction_date=trade_date,
position=row.get('Position', 'Unknown')
)
trades.append(trade)
logger.info(f"Fetched {len(trades)} insider trades for {ticker}")
except Exception as e:
logger.error(f"Error fetching insider trades: {e}")
return trades
async def fetch_earnings_calendar(self,
tickers: List[str],
days_ahead: int = 30) -> List[EarningsEvent]:
"""
Fetch upcoming earnings events
Args:
tickers: List of tickers to check
days_ahead: Number of days to look ahead
Returns:
List of earnings events
"""
events = []
if not YFINANCE_AVAILABLE:
return events
try:
for ticker in tickers:
stock = yf.Ticker(ticker)
calendar = stock.calendar
if calendar is not None and not calendar.empty:
# Get earnings date
earnings_date = calendar.get('Earnings Date')
if earnings_date and len(earnings_date) > 0:
event = EarningsEvent(
ticker=ticker,
earnings_date=earnings_date[0] if isinstance(earnings_date, list) else earnings_date,
eps_estimate=calendar.get('EPS Estimate', 0),
eps_actual=None, # Will be filled after earnings
revenue_estimate=calendar.get('Revenue Estimate', 0),
revenue_actual=None,
surprise_percent=None
)
events.append(event)
self.earnings_calendar = events
logger.info(f"Fetched {len(events)} upcoming earnings events")
except Exception as e:
logger.error(f"Error fetching earnings calendar: {e}")
return events
async def fetch_market_sentiment(self, ticker: str) -> Dict[str, Any]:
"""
Fetch market sentiment from various sources
Returns:
Dictionary with sentiment data
"""
sentiment = {
'ticker': ticker,
'overall_sentiment': 'neutral',
'sentiment_score': 0.0,
'sources': {}
}
try:
# Fetch news sentiment using existing TradingAgents tools
news_interface = AlphaVantageNewsInterface()
news_data = news_interface.get_news(ticker, datetime.now().strftime('%Y-%m-%d'))
if news_data:
# Simple sentiment analysis based on news
positive_keywords = ['beat', 'surge', 'jump', 'gain', 'profit', 'upgrade']
negative_keywords = ['miss', 'fall', 'drop', 'loss', 'downgrade', 'concern']
positive_count = 0
negative_count = 0
for article in news_data[:10]: # Check first 10 articles
title = article.get('title', '').lower()
for keyword in positive_keywords:
if keyword in title:
positive_count += 1
for keyword in negative_keywords:
if keyword in title:
negative_count += 1
# Calculate sentiment score
total = positive_count + negative_count
if total > 0:
sentiment['sentiment_score'] = (positive_count - negative_count) / total
if sentiment['sentiment_score'] > 0.2:
sentiment['overall_sentiment'] = 'positive'
elif sentiment['sentiment_score'] < -0.2:
sentiment['overall_sentiment'] = 'negative'
sentiment['sources']['news'] = {
'positive_articles': positive_count,
'negative_articles': negative_count,
'total_articles': len(news_data)
}
except Exception as e:
logger.error(f"Error fetching sentiment: {e}")
return sentiment
async def aggregate_signals(self, tickers: List[str]) -> List[MarketSignal]:
"""
Aggregate all data sources into trading signals
Args:
tickers: List of tickers to analyze
Returns:
List of market signals
"""
signals = []
# Fetch all data
congress_trades = await self.fetch_congressional_trades(tickers, days_back=7)
for ticker in tickers:
# Congressional signal
ticker_congress = [t for t in congress_trades if t.ticker == ticker]
if ticker_congress:
recent_purchases = [t for t in ticker_congress if 'purchase' in t.action.lower()]
recent_sales = [t for t in ticker_congress if 'sale' in t.action.lower()]
if len(recent_purchases) > len(recent_sales):
signal = MarketSignal(
ticker=ticker,
signal_type='congressional',
action='BUY',
confidence=min(80 + len(recent_purchases) * 5, 95),
data={
'trades': [
{
'politician': t.politician,
'amount': t.amount_range,
'date': t.transaction_date.isoformat()
}
for t in recent_purchases[:3]
]
},
timestamp=datetime.now()
)
signals.append(signal)
# Insider signal
insider_trades = await self.fetch_insider_trades(ticker, days_back=30)
if insider_trades:
recent_buys = [t for t in insider_trades if t.action == 'Buy']
recent_sells = [t for t in insider_trades if t.action == 'Sell']
if len(recent_buys) > len(recent_sells) * 2: # Strong buy signal
signal = MarketSignal(
ticker=ticker,
signal_type='insider',
action='BUY',
confidence=min(70 + len(recent_buys) * 3, 90),
data={
'insider_buys': len(recent_buys),
'insider_sells': len(recent_sells),
'net_buying': sum(t.value for t in recent_buys)
},
timestamp=datetime.now()
)
signals.append(signal)
# Sentiment signal
sentiment = await self.fetch_market_sentiment(ticker)
if sentiment['overall_sentiment'] == 'positive' and sentiment['sentiment_score'] > 0.3:
signal = MarketSignal(
ticker=ticker,
signal_type='sentiment',
action='BUY',
confidence=min(60 + sentiment['sentiment_score'] * 100, 85),
data=sentiment,
timestamp=datetime.now()
)
signals.append(signal)
self.market_signals = signals
logger.info(f"Generated {len(signals)} market signals")
return signals
def get_top_opportunities(self, n: int = 5) -> List[MarketSignal]:
"""
Get top trading opportunities based on confidence
Args:
n: Number of opportunities to return
Returns:
Top n signals by confidence
"""
sorted_signals = sorted(self.market_signals, key=lambda x: x.confidence, reverse=True)
return sorted_signals[:n]
# Example usage
async def main():
"""Example of using the data aggregator"""
config = {
'quiver_api_key': os.getenv('QUIVER_API_KEY'),
'alpha_vantage_api_key': os.getenv('ALPHA_VANTAGE_API_KEY')
}
aggregator = DataAggregator(config)
# Your portfolio tickers
portfolio_tickers = ["AVGO", "MSFT", "MU", "NVDA", "TSM"]
# Fetch congressional trades
congress_trades = await aggregator.fetch_congressional_trades(portfolio_tickers)
print(f"\nCongressional Trades:")
for trade in congress_trades:
print(f" {trade.politician} - {trade.action} {trade.ticker} ({trade.amount_range})")
# Aggregate all signals
signals = await aggregator.aggregate_signals(portfolio_tickers)
print(f"\nTop Trading Signals:")
for signal in aggregator.get_top_opportunities(3):
print(f" {signal.ticker}: {signal.action} (Confidence: {signal.confidence}%) - {signal.signal_type}")
if __name__ == "__main__":
asyncio.run(main())

View File

@ -0,0 +1,336 @@
"""
IBKR (Interactive Brokers) Connector
====================================
Manages live connection to IBKR TWS/Gateway for portfolio monitoring and trading.
"""
import asyncio
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta
from dataclasses import dataclass
import os
# Note: ib_insync needs to be installed
try:
from ib_insync import IB, Stock, Contract, MarketOrder, LimitOrder, util
IBKR_AVAILABLE = True
except ImportError:
IBKR_AVAILABLE = False
print("Warning: ib_insync not installed. Install with: pip install ib_insync")
logger = logging.getLogger(__name__)
@dataclass
class Position:
"""Represents a portfolio position"""
ticker: str
shares: int
avg_cost: float
current_price: float
market_value: float
unrealized_pnl: float
realized_pnl: float
percent_change: float
@dataclass
class AccountInfo:
"""Account information from IBKR"""
net_liquidation: float
buying_power: float
cash_balance: float
total_positions: int
day_pnl: float
total_pnl: float
class IBKRConnector:
"""
Handles all interactions with Interactive Brokers API
"""
def __init__(self,
host: str = "127.0.0.1",
port: int = 7497, # 7497 for TWS paper, 7496 for TWS live
client_id: int = 1):
"""
Initialize IBKR connector
Args:
host: IP address of TWS/Gateway
port: Port number (7497 for paper, 7496 for live)
client_id: Unique client identifier
"""
self.host = host
self.port = port
self.client_id = client_id
self.ib = None
self.positions: Dict[str, Position] = {}
self.account_info: Optional[AccountInfo] = None
self.is_connected = False
async def connect(self) -> bool:
"""Connect to IBKR TWS/Gateway"""
if not IBKR_AVAILABLE:
logger.error("ib_insync not installed")
return False
try:
self.ib = IB()
await self.ib.connectAsync(self.host, self.port, self.client_id)
self.is_connected = True
logger.info(f"Connected to IBKR at {self.host}:{self.port}")
# Request account updates
self.ib.reqAccountUpdates()
return True
except Exception as e:
logger.error(f"Failed to connect to IBKR: {e}")
self.is_connected = False
return False
async def disconnect(self):
"""Disconnect from IBKR"""
if self.ib and self.is_connected:
self.ib.disconnect()
self.is_connected = False
logger.info("Disconnected from IBKR")
async def sync_portfolio(self) -> Dict[str, Position]:
"""
Sync portfolio positions from IBKR
Returns:
Dictionary of positions keyed by ticker
"""
if not self.is_connected:
logger.error("Not connected to IBKR")
return {}
try:
# Get all positions
ib_positions = self.ib.positions()
self.positions.clear()
for pos in ib_positions:
if pos.position != 0: # Skip closed positions
ticker = pos.contract.symbol
# Get current market price
contract = Stock(ticker, 'SMART', 'USD')
self.ib.qualifyContracts(contract)
ticker_data = self.ib.reqMktData(contract, '', False, False)
await asyncio.sleep(1) # Wait for price data
current_price = ticker_data.marketPrice()
if current_price is None or current_price <= 0:
current_price = ticker_data.last or pos.avgCost
market_value = pos.position * current_price
unrealized_pnl = market_value - (pos.position * pos.avgCost)
percent_change = ((current_price - pos.avgCost) / pos.avgCost) * 100
position = Position(
ticker=ticker,
shares=int(pos.position),
avg_cost=pos.avgCost,
current_price=current_price,
market_value=market_value,
unrealized_pnl=unrealized_pnl,
realized_pnl=0, # Would need to track trades
percent_change=percent_change
)
self.positions[ticker] = position
logger.info(f"Synced position: {ticker} - {position.shares} shares @ ${current_price:.2f}")
logger.info(f"Portfolio sync complete: {len(self.positions)} positions")
return self.positions
except Exception as e:
logger.error(f"Error syncing portfolio: {e}")
return self.positions
async def get_account_info(self) -> Optional[AccountInfo]:
"""Get account information"""
if not self.is_connected:
return None
try:
account_values = self.ib.accountValues()
# Extract key values
values_dict = {av.tag: av.value for av in account_values}
self.account_info = AccountInfo(
net_liquidation=float(values_dict.get('NetLiquidation', 0)),
buying_power=float(values_dict.get('BuyingPower', 0)),
cash_balance=float(values_dict.get('TotalCashBalance', 0)),
total_positions=len(self.positions),
day_pnl=float(values_dict.get('DailyPnL', 0)),
total_pnl=float(values_dict.get('UnrealizedPnL', 0))
)
return self.account_info
except Exception as e:
logger.error(f"Error getting account info: {e}")
return None
async def get_position(self, ticker: str) -> Optional[Position]:
"""Get specific position by ticker"""
return self.positions.get(ticker)
async def place_order(self,
ticker: str,
action: str, # 'BUY' or 'SELL'
quantity: int,
order_type: str = 'LIMIT',
limit_price: Optional[float] = None,
stop_price: Optional[float] = None) -> Optional[str]:
"""
Place an order (with safety checks)
Args:
ticker: Stock symbol
action: 'BUY' or 'SELL'
quantity: Number of shares
order_type: 'MARKET', 'LIMIT', 'STOP', 'STOP_LIMIT'
limit_price: Limit price for limit orders
stop_price: Stop price for stop orders
Returns:
Order ID if successful, None otherwise
"""
if not self.is_connected:
logger.error("Not connected to IBKR")
return None
# Safety check - require confirmation for large orders
if quantity > 100 or (limit_price and limit_price * quantity > 10000):
logger.warning(f"Large order detected: {action} {quantity} {ticker}")
# In production, you'd want manual confirmation here
try:
# Create contract
contract = Stock(ticker, 'SMART', 'USD')
self.ib.qualifyContracts(contract)
# Create order based on type
if order_type == 'MARKET':
order = MarketOrder(action, quantity)
elif order_type == 'LIMIT' and limit_price:
order = LimitOrder(action, quantity, limit_price)
else:
logger.error(f"Unsupported order type: {order_type}")
return None
# Place the order
trade = self.ib.placeOrder(contract, order)
# Wait for order to be acknowledged
await asyncio.sleep(1)
if trade.orderStatus.status in ['Submitted', 'PreSubmitted', 'Filled']:
logger.info(f"Order placed: {action} {quantity} {ticker} - ID: {trade.order.orderId}")
return str(trade.order.orderId)
else:
logger.error(f"Order failed: {trade.orderStatus.status}")
return None
except Exception as e:
logger.error(f"Error placing order: {e}")
return None
async def get_market_data(self, ticker: str) -> Optional[Dict[str, float]]:
"""
Get real-time market data for a ticker
Returns:
Dictionary with price data
"""
if not self.is_connected:
return None
try:
contract = Stock(ticker, 'SMART', 'USD')
self.ib.qualifyContracts(contract)
ticker_data = self.ib.reqMktData(contract, '', False, False)
await asyncio.sleep(2) # Wait for data
return {
'last': ticker_data.last or 0,
'bid': ticker_data.bid or 0,
'ask': ticker_data.ask or 0,
'volume': ticker_data.volume or 0,
'high': ticker_data.high or 0,
'low': ticker_data.low or 0,
'close': ticker_data.close or 0
}
except Exception as e:
logger.error(f"Error getting market data for {ticker}: {e}")
return None
def get_portfolio_summary(self) -> Dict[str, Any]:
"""Get portfolio summary"""
if not self.positions:
return {}
total_value = sum(p.market_value for p in self.positions.values())
total_cost = sum(p.shares * p.avg_cost for p in self.positions.values())
total_pnl = sum(p.unrealized_pnl for p in self.positions.values())
return {
'total_value': total_value,
'total_cost': total_cost,
'total_unrealized_pnl': total_pnl,
'total_percent_change': ((total_value - total_cost) / total_cost * 100) if total_cost > 0 else 0,
'position_count': len(self.positions),
'positions': [
{
'ticker': p.ticker,
'shares': p.shares,
'value': p.market_value,
'pnl': p.unrealized_pnl,
'percent': p.percent_change
}
for p in self.positions.values()
]
}
# Example usage
async def main():
"""Example of using the IBKR connector"""
connector = IBKRConnector(port=7497) # Paper trading port
# Connect
if await connector.connect():
# Sync portfolio
positions = await connector.sync_portfolio()
print(f"Found {len(positions)} positions")
# Get account info
account = await connector.get_account_info()
if account:
print(f"Net Liquidation: ${account.net_liquidation:,.2f}")
print(f"Buying Power: ${account.buying_power:,.2f}")
# Get market data
data = await connector.get_market_data("AAPL")
if data:
print(f"AAPL Last Price: ${data['last']}")
# Disconnect
await connector.disconnect()
if __name__ == "__main__":
# For testing
asyncio.run(main())

522
autonomous/scheduler.py Normal file
View File

@ -0,0 +1,522 @@
"""
Scheduler
=========
Orchestrates periodic tasks for the autonomous trading system.
Uses APScheduler for task scheduling and asyncio for async execution.
"""
import asyncio
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, time, timedelta
import os
import sys
# Add parent directory to path for imports
sys.path.append('..')
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
from .ibkr_connector import IBKRConnector
from .data_aggregator import DataAggregator
from .signal_processor import SignalProcessor
from .alert_engine import AlertEngine, AlertType, AlertPriority
logger = logging.getLogger(__name__)
class AutonomousScheduler:
"""
Main scheduler for the autonomous trading system
"""
def __init__(self, config: Optional[Dict] = None):
"""
Initialize the scheduler
Args:
config: Configuration dictionary
"""
self.config = config or {}
self.scheduler = AsyncIOScheduler()
# Initialize components
self.ibkr = IBKRConnector(
host=self.config.get('ibkr_host', '127.0.0.1'),
port=self.config.get('ibkr_port', 7497),
client_id=self.config.get('ibkr_client_id', 1)
)
self.data_agg = DataAggregator(config)
self.signal_processor = SignalProcessor(self.ibkr, self.data_agg, config)
self.alert_engine = AlertEngine(config)
# Track system state
self.is_running = False
self.last_portfolio_check = None
self.last_market_scan = None
self.trading_enabled = self.config.get('trading_enabled', False)
# Your portfolio tickers
self.portfolio_tickers = ["AVGO", "MSFT", "MU", "NVDA", "TSM"]
self.watchlist = self.config.get('watchlist', ["AAPL", "TSLA", "META", "GOOGL"])
async def initialize(self) -> bool:
"""
Initialize connections and verify system readiness
Returns:
True if initialization successful
"""
try:
# Connect to IBKR
logger.info("Connecting to IBKR...")
connected = await self.ibkr.connect()
if not connected:
logger.error("Failed to connect to IBKR. Make sure TWS/Gateway is running.")
await self.alert_engine.send_alert(
title="System Initialization Failed",
message="Could not connect to IBKR. Check TWS/Gateway.",
alert_type=AlertType.SYSTEM,
priority=AlertPriority.CRITICAL
)
return False
# Initial portfolio sync
logger.info("Syncing portfolio...")
await self.ibkr.sync_portfolio()
# Send startup notification
await self.alert_engine.send_alert(
title="🚀 Autonomous Trading System Started",
message=f"System initialized successfully.\nMonitoring {len(self.portfolio_tickers)} positions.",
alert_type=AlertType.SYSTEM,
priority=AlertPriority.INFO
)
self.is_running = True
return True
except Exception as e:
logger.error(f"Initialization error: {e}")
return False
def setup_schedules(self):
"""Configure all scheduled tasks"""
# Portfolio monitoring - every 5 minutes
self.scheduler.add_job(
self.monitor_portfolio,
IntervalTrigger(minutes=5),
id='portfolio_monitor',
name='Portfolio Monitor',
misfire_grace_time=60
)
# Market scanning - every 15 minutes during market hours
self.scheduler.add_job(
self.scan_markets,
IntervalTrigger(minutes=15),
id='market_scanner',
name='Market Scanner',
misfire_grace_time=120
)
# Congressional trades check - every hour
self.scheduler.add_job(
self.check_congressional_trades,
IntervalTrigger(hours=1),
id='congressional_monitor',
name='Congressional Trade Monitor',
misfire_grace_time=300
)
# News monitoring - every 30 minutes
self.scheduler.add_job(
self.monitor_news,
IntervalTrigger(minutes=30),
id='news_monitor',
name='News Monitor',
misfire_grace_time=120
)
# Daily portfolio summary - at 4:30 PM ET (after market close)
self.scheduler.add_job(
self.daily_summary,
CronTrigger(hour=16, minute=30),
id='daily_summary',
name='Daily Summary',
misfire_grace_time=300
)
# Risk check - every 30 minutes during market hours
self.scheduler.add_job(
self.check_risk,
IntervalTrigger(minutes=30),
id='risk_monitor',
name='Risk Monitor',
misfire_grace_time=120
)
# Pre-market check - at 8:30 AM ET
self.scheduler.add_job(
self.premarket_check,
CronTrigger(hour=8, minute=30),
id='premarket_check',
name='Pre-Market Check',
misfire_grace_time=300
)
logger.info(f"Scheduled {len(self.scheduler.get_jobs())} jobs")
async def monitor_portfolio(self):
"""Monitor portfolio positions and P&L"""
if not self.is_running:
return
try:
logger.info("Running portfolio monitor...")
# Sync positions
positions = await self.ibkr.sync_portfolio()
if not positions:
logger.warning("No positions found")
return
# Check for significant changes
for ticker, position in positions.items():
# Alert on large losses
if position.percent_change < -5:
await self.alert_engine.send_alert(
title=f"⚠️ Risk Alert: {ticker}",
message=f"{ticker} is down {abs(position.percent_change):.1f}%\n"
f"Current: ${position.current_price:.2f}\n"
f"P&L: ${position.unrealized_pnl:,.2f}",
alert_type=AlertType.RISK_WARNING,
priority=AlertPriority.HIGH,
data={'ticker': ticker, 'loss_percent': position.percent_change}
)
# Alert on large gains
elif position.percent_change > 10:
await self.alert_engine.send_alert(
title=f"📈 Profit Alert: {ticker}",
message=f"{ticker} is up {position.percent_change:.1f}%\n"
f"Consider taking profits.\n"
f"P&L: ${position.unrealized_pnl:,.2f}",
alert_type=AlertType.PORTFOLIO_UPDATE,
priority=AlertPriority.MEDIUM,
data={'ticker': ticker, 'gain_percent': position.percent_change}
)
self.last_portfolio_check = datetime.now()
except Exception as e:
logger.error(f"Portfolio monitor error: {e}")
async def scan_markets(self):
"""Scan markets for trading opportunities"""
if not self.is_running:
return
try:
logger.info("Running market scanner...")
# Get all tickers to scan
all_tickers = list(set(self.portfolio_tickers + self.watchlist))
# Aggregate signals
await self.data_agg.aggregate_signals(all_tickers)
# Process signals
recommendations = []
for ticker in all_tickers:
rec = await self.signal_processor.process_signals(ticker)
if rec and rec.action != 'HOLD' and rec.confidence > 70:
recommendations.append(rec)
# Send top recommendations
recommendations.sort(key=lambda x: x.confidence, reverse=True)
for rec in recommendations[:3]: # Top 3 opportunities
await self.alert_engine.send_trading_signal(rec)
await asyncio.sleep(1) # Avoid flooding
self.last_market_scan = datetime.now()
logger.info(f"Found {len(recommendations)} opportunities")
except Exception as e:
logger.error(f"Market scanner error: {e}")
async def check_congressional_trades(self):
"""Check for new congressional trades"""
if not self.is_running:
return
try:
logger.info("Checking congressional trades...")
# Get recent trades
trades = await self.data_agg.fetch_congressional_trades(
self.portfolio_tickers,
days_back=2
)
# Alert on trades matching portfolio
for trade in trades:
if trade.ticker in self.portfolio_tickers:
emoji = "🟢" if 'purchase' in trade.action else "🔴"
await self.alert_engine.send_alert(
title=f"🏛️ Congressional Trade: {trade.ticker}",
message=f"{emoji} {trade.politician} ({trade.party}-{trade.state}) "
f"{trade.action} {trade.ticker}\n"
f"Amount: {trade.amount_range}\n"
f"Filed: {trade.filing_date.strftime('%Y-%m-%d')}",
alert_type=AlertType.CONGRESSIONAL_TRADE,
priority=AlertPriority.HIGH,
data={
'ticker': trade.ticker,
'politician': trade.politician,
'action': trade.action
}
)
except Exception as e:
logger.error(f"Congressional trade check error: {e}")
async def monitor_news(self):
"""Monitor news for portfolio companies"""
if not self.is_running:
return
try:
logger.info("Monitoring news...")
for ticker in self.portfolio_tickers[:3]: # Limit to avoid rate limits
sentiment = await self.data_agg.fetch_market_sentiment(ticker)
# Alert on strong sentiment
if sentiment['sentiment_score'] > 0.5:
await self.alert_engine.send_alert(
title=f"📰 Positive News: {ticker}",
message=f"Strong positive sentiment detected for {ticker}\n"
f"Sentiment Score: {sentiment['sentiment_score']:.2f}",
alert_type=AlertType.MARKET_MOVE,
priority=AlertPriority.MEDIUM,
data=sentiment
)
await asyncio.sleep(2) # Rate limiting
except Exception as e:
logger.error(f"News monitor error: {e}")
async def check_risk(self):
"""Monitor portfolio risk levels"""
if not self.is_running:
return
try:
logger.info("Checking portfolio risk...")
# Get account info
account_info = await self.ibkr.get_account_info()
if not account_info:
return
portfolio_summary = self.ibkr.get_portfolio_summary()
# Check daily P&L
if account_info.day_pnl < -1000: # Lost more than $1000 today
await self.alert_engine.send_alert(
title="⚠️ Daily Loss Alert",
message=f"Today's P&L: ${account_info.day_pnl:,.2f}\n"
f"Consider reducing risk or stopping for the day.",
alert_type=AlertType.RISK_WARNING,
priority=AlertPriority.HIGH
)
# Check concentration risk
if portfolio_summary['positions']:
max_position = max(portfolio_summary['positions'],
key=lambda x: x['value'])
concentration = max_position['value'] / portfolio_summary['total_value']
if concentration > 0.30: # More than 30% in one position
await self.alert_engine.send_alert(
title=f"⚠️ Concentration Risk: {max_position['ticker']}",
message=f"{max_position['ticker']} is {concentration:.1%} of portfolio\n"
f"Consider rebalancing to reduce risk.",
alert_type=AlertType.RISK_WARNING,
priority=AlertPriority.MEDIUM
)
except Exception as e:
logger.error(f"Risk check error: {e}")
async def premarket_check(self):
"""Pre-market preparation and alerts"""
if not self.is_running:
return
try:
logger.info("Running pre-market check...")
# Check earnings today
earnings = await self.data_agg.fetch_earnings_calendar(
self.portfolio_tickers,
days_ahead=1
)
for event in earnings:
await self.alert_engine.send_alert(
title=f"📊 Earnings Today: {event.ticker}",
message=f"{event.ticker} reports earnings today\n"
f"EPS Estimate: ${event.eps_estimate:.2f}",
alert_type=AlertType.EARNINGS,
priority=AlertPriority.HIGH
)
# Run full market scan
await self.scan_markets()
except Exception as e:
logger.error(f"Pre-market check error: {e}")
async def daily_summary(self):
"""Generate and send daily portfolio summary"""
if not self.is_running:
return
try:
logger.info("Generating daily summary...")
# Get portfolio summary
portfolio_summary = self.ibkr.get_portfolio_summary()
account_info = await self.ibkr.get_account_info()
if portfolio_summary and account_info:
await self.alert_engine.send_portfolio_summary(portfolio_summary)
# Additional daily stats
message = f"""
📊 **Daily Trading Summary**
Account Value: ${account_info.net_liquidation:,.2f}
Today's P&L: ${account_info.day_pnl:+,.2f}
Buying Power: ${account_info.buying_power:,.2f}
Top Opportunities Found: {len(self.signal_processor.recommendations)}
Alerts Sent Today: {len(self.alert_engine.sent_alerts)}
System Status: All systems operational
"""
await self.alert_engine.send_alert(
title="📈 End of Day Report",
message=message,
alert_type=AlertType.PORTFOLIO_UPDATE,
priority=AlertPriority.INFO
)
except Exception as e:
logger.error(f"Daily summary error: {e}")
async def start(self):
"""Start the autonomous trading system"""
logger.info("Starting Autonomous Trading System...")
# Initialize
if not await self.initialize():
logger.error("Failed to initialize system")
return
# Setup schedules
self.setup_schedules()
# Start scheduler
self.scheduler.start()
logger.info("Scheduler started")
# Run initial scans
await self.monitor_portfolio()
await self.scan_markets()
logger.info("Autonomous Trading System is running")
# Keep running
try:
while self.is_running:
await asyncio.sleep(60) # Check every minute
except KeyboardInterrupt:
logger.info("Shutting down...")
await self.stop()
async def stop(self):
"""Stop the autonomous trading system"""
logger.info("Stopping Autonomous Trading System...")
self.is_running = False
# Shutdown scheduler
if self.scheduler.running:
self.scheduler.shutdown(wait=False)
# Disconnect from IBKR
await self.ibkr.disconnect()
# Send shutdown notification
await self.alert_engine.send_alert(
title="🛑 System Shutdown",
message="Autonomous Trading System stopped",
alert_type=AlertType.SYSTEM,
priority=AlertPriority.INFO
)
logger.info("System stopped")
# Main entry point
async def main():
"""Main entry point for the autonomous system"""
# Load configuration
config = {
# IBKR settings
'ibkr_host': os.getenv('IBKR_HOST', '127.0.0.1'),
'ibkr_port': int(os.getenv('IBKR_PORT', 7497)), # 7497 for paper, 7496 for live
'ibkr_client_id': int(os.getenv('IBKR_CLIENT_ID', 1)),
# API keys
'quiver_api_key': os.getenv('QUIVER_API_KEY'),
'alpha_vantage_api_key': os.getenv('ALPHA_VANTAGE_API_KEY'),
'openai_api_key': os.getenv('OPENAI_API_KEY'),
# Notification settings
'discord_webhook_url': os.getenv('DISCORD_WEBHOOK_URL'),
'telegram_bot_token': os.getenv('TELEGRAM_BOT_TOKEN'),
'telegram_chat_id': os.getenv('TELEGRAM_CHAT_ID'),
# Trading settings
'trading_enabled': os.getenv('TRADING_ENABLED', 'false').lower() == 'true',
'watchlist': ['AAPL', 'TSLA', 'META', 'GOOGL', 'AMZN']
}
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Create and start scheduler
scheduler = AutonomousScheduler(config)
await scheduler.start()
if __name__ == "__main__":
asyncio.run(main())

View File

@ -0,0 +1,414 @@
"""
Signal Processor
===============
Processes signals from multiple sources and generates actionable trading recommendations
with specific entry/exit prices using TradingAgents AI.
"""
import asyncio
import logging
from typing import Dict, List, Optional, Any, Tuple
from datetime import datetime, timedelta
from dataclasses import dataclass
import statistics
# Import TradingAgents
import sys
sys.path.append('..')
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
# Import our modules
from .ibkr_connector import IBKRConnector, Position
from .data_aggregator import DataAggregator, MarketSignal
logger = logging.getLogger(__name__)
@dataclass
class TradingRecommendation:
"""Complete trading recommendation with entry/exit points"""
ticker: str
action: str # 'BUY', 'SELL', 'HOLD'
current_price: float
entry_price_min: float
entry_price_max: float
target_price_1: float
target_price_2: float
stop_loss: float
confidence: float # 0-100
position_size: float # Percentage of portfolio
reasoning: str
data_sources: List[str]
risk_level: str # 'LOW', 'MEDIUM', 'HIGH'
timestamp: datetime
class SignalProcessor:
"""
Processes signals and generates trading recommendations
"""
def __init__(self,
ibkr_connector: IBKRConnector,
data_aggregator: DataAggregator,
config: Optional[Dict] = None):
"""
Initialize signal processor
Args:
ibkr_connector: IBKR connection instance
data_aggregator: Data aggregator instance
config: Configuration dictionary
"""
self.ibkr = ibkr_connector
self.data_agg = data_aggregator
self.config = config or DEFAULT_CONFIG.copy()
# Configure TradingAgents for fast processing
self.config['deep_think_llm'] = 'gpt-4o-mini'
self.config['quick_think_llm'] = 'gpt-4o-mini'
self.config['max_debate_rounds'] = 1
self.trading_agents = TradingAgentsGraph(debug=False, config=self.config)
self.recommendations: List[TradingRecommendation] = []
async def calculate_technical_levels(self, ticker: str) -> Dict[str, float]:
"""
Calculate technical support/resistance levels
Args:
ticker: Stock ticker
Returns:
Dictionary with technical levels
"""
try:
import yfinance as yf
stock = yf.Ticker(ticker)
# Get recent price data
hist = stock.history(period="3mo")
if hist.empty:
return {}
current_price = hist['Close'].iloc[-1]
high_3m = hist['High'].max()
low_3m = hist['Low'].min()
# Calculate moving averages
ma_20 = hist['Close'].tail(20).mean()
ma_50 = hist['Close'].tail(50).mean() if len(hist) >= 50 else ma_20
# Calculate support/resistance levels
# Using pivot points
last_high = hist['High'].iloc[-1]
last_low = hist['Low'].iloc[-1]
last_close = hist['Close'].iloc[-1]
pivot = (last_high + last_low + last_close) / 3
resistance_1 = 2 * pivot - last_low
resistance_2 = pivot + (last_high - last_low)
support_1 = 2 * pivot - last_high
support_2 = pivot - (last_high - last_low)
# Calculate RSI
delta = hist['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))
current_rsi = rsi.iloc[-1]
return {
'current_price': current_price,
'resistance_1': resistance_1,
'resistance_2': resistance_2,
'support_1': support_1,
'support_2': support_2,
'ma_20': ma_20,
'ma_50': ma_50,
'high_3m': high_3m,
'low_3m': low_3m,
'rsi': current_rsi,
'pivot': pivot
}
except Exception as e:
logger.error(f"Error calculating technical levels for {ticker}: {e}")
return {}
async def analyze_position_sizing(self,
ticker: str,
confidence: float,
risk_level: str) -> float:
"""
Calculate appropriate position size based on portfolio and risk
Args:
ticker: Stock ticker
confidence: Signal confidence (0-100)
risk_level: Risk level of the trade
Returns:
Position size as percentage of portfolio
"""
# Get current portfolio info
await self.ibkr.sync_portfolio()
portfolio_summary = self.ibkr.get_portfolio_summary()
# Check if we already have a position
existing_position = await self.ibkr.get_position(ticker)
# Base position sizing rules
max_position = 0.20 # Max 20% in any single position
base_size = 0.10 # Base 10% position
# Adjust based on confidence
if confidence > 90:
size_multiplier = 1.5
elif confidence > 80:
size_multiplier = 1.2
elif confidence > 70:
size_multiplier = 1.0
else:
size_multiplier = 0.7
# Adjust based on risk
risk_multipliers = {
'LOW': 1.2,
'MEDIUM': 1.0,
'HIGH': 0.6
}
risk_multiplier = risk_multipliers.get(risk_level, 1.0)
# Calculate position size
position_size = base_size * size_multiplier * risk_multiplier
# If we already have a position, consider scaling
if existing_position:
current_weight = existing_position.market_value / portfolio_summary['total_value']
remaining_capacity = max_position - current_weight
position_size = min(position_size, remaining_capacity)
# Ensure within limits
position_size = max(0.05, min(position_size, max_position)) # Between 5% and 20%
return round(position_size, 3)
async def process_signals(self, ticker: str) -> Optional[TradingRecommendation]:
"""
Process all signals for a ticker and generate recommendation
Args:
ticker: Stock ticker
Returns:
Trading recommendation if signals are strong enough
"""
try:
# Get all signals for this ticker
signals = [s for s in self.data_agg.market_signals if s.ticker == ticker]
if not signals:
logger.info(f"No signals for {ticker}")
return None
# Calculate technical levels
tech_levels = await self.calculate_technical_levels(ticker)
if not tech_levels:
logger.warning(f"Could not calculate technical levels for {ticker}")
return None
current_price = tech_levels['current_price']
# Run TradingAgents analysis
logger.info(f"Running TradingAgents analysis for {ticker}")
_, ai_decision = self.trading_agents.propagate(ticker, datetime.now().strftime('%Y-%m-%d'))
# Combine AI decision with our signals
avg_confidence = statistics.mean([s.confidence for s in signals])
# Determine action based on signals and AI
buy_signals = [s for s in signals if s.action == 'BUY']
sell_signals = [s for s in signals if s.action == 'SELL']
if len(buy_signals) > len(sell_signals) and 'buy' in ai_decision.lower():
action = 'BUY'
# Entry points near support or current price
entry_min = min(current_price * 0.995, tech_levels['support_1'])
entry_max = current_price * 1.005
# Targets based on resistance levels
target_1 = tech_levels['resistance_1']
target_2 = tech_levels['resistance_2']
# Stop loss below support
stop_loss = tech_levels['support_1'] * 0.98
elif len(sell_signals) > len(buy_signals) or 'sell' in ai_decision.lower():
action = 'SELL'
# For selling, entry is current price range
entry_min = current_price * 0.995
entry_max = current_price * 1.01
# Targets for selling (lower prices)
target_1 = tech_levels['support_1']
target_2 = tech_levels['support_2']
# Stop loss above resistance for shorts
stop_loss = tech_levels['resistance_1'] * 1.02
else:
action = 'HOLD'
entry_min = entry_max = current_price
target_1 = target_2 = current_price
stop_loss = current_price * 0.95
# Determine risk level
rsi = tech_levels.get('rsi', 50)
if rsi > 70 or rsi < 30:
risk_level = 'HIGH'
elif 40 <= rsi <= 60:
risk_level = 'LOW'
else:
risk_level = 'MEDIUM'
# Calculate position size
position_size = await self.analyze_position_sizing(ticker, avg_confidence, risk_level)
# Build reasoning
reasoning_parts = [
f"AI Analysis: {ai_decision[:200]}",
f"Signals: {len(buy_signals)} buy, {len(sell_signals)} sell"
]
for signal in signals[:2]: # Include top 2 signals
if signal.signal_type == 'congressional':
reasoning_parts.append(f"Congressional activity detected")
elif signal.signal_type == 'insider':
reasoning_parts.append(f"Insider buying activity")
elif signal.signal_type == 'sentiment':
reasoning_parts.append(f"Positive market sentiment")
reasoning_parts.append(f"RSI: {rsi:.1f}")
# Create recommendation
recommendation = TradingRecommendation(
ticker=ticker,
action=action,
current_price=current_price,
entry_price_min=round(entry_min, 2),
entry_price_max=round(entry_max, 2),
target_price_1=round(target_1, 2),
target_price_2=round(target_2, 2),
stop_loss=round(stop_loss, 2),
confidence=avg_confidence,
position_size=position_size,
reasoning=' | '.join(reasoning_parts),
data_sources=[s.signal_type for s in signals],
risk_level=risk_level,
timestamp=datetime.now()
)
self.recommendations.append(recommendation)
return recommendation
except Exception as e:
logger.error(f"Error processing signals for {ticker}: {e}")
return None
async def process_portfolio(self) -> List[TradingRecommendation]:
"""
Process entire portfolio and generate recommendations
Returns:
List of trading recommendations
"""
# Get current positions
await self.ibkr.sync_portfolio()
positions = self.ibkr.positions
# Get tickers from portfolio
portfolio_tickers = list(positions.keys())
# Also analyze some high-opportunity tickers even if not in portfolio
watchlist = ["NVDA", "TSLA", "AAPL", "MSFT", "AVGO"]
all_tickers = list(set(portfolio_tickers + watchlist))
# Get signals for all tickers
await self.data_agg.aggregate_signals(all_tickers)
# Process each ticker
recommendations = []
for ticker in all_tickers:
logger.info(f"Processing {ticker}")
rec = await self.process_signals(ticker)
if rec and rec.action != 'HOLD':
recommendations.append(rec)
# Sort by confidence
recommendations.sort(key=lambda x: x.confidence, reverse=True)
logger.info(f"Generated {len(recommendations)} recommendations")
return recommendations
def format_recommendation(self, rec: TradingRecommendation) -> str:
"""
Format recommendation for display
Args:
rec: Trading recommendation
Returns:
Formatted string
"""
# Calculate percentage gains
gain_1 = ((rec.target_price_1 - rec.current_price) / rec.current_price) * 100
gain_2 = ((rec.target_price_2 - rec.current_price) / rec.current_price) * 100
loss = ((rec.stop_loss - rec.current_price) / rec.current_price) * 100
formatted = f"""
🎯 ACTION: {rec.action}
📈 TICKER: {rec.ticker}
💰 CURRENT: ${rec.current_price:.2f}
📍 ENTRY: ${rec.entry_price_min:.2f} - ${rec.entry_price_max:.2f}
🎯 TARGET 1: ${rec.target_price_1:.2f} ({gain_1:+.1f}%)
🎯 TARGET 2: ${rec.target_price_2:.2f} ({gain_2:+.1f}%)
🛑 STOP LOSS: ${rec.stop_loss:.2f} ({loss:.1f}%)
📊 CONFIDENCE: {rec.confidence:.0f}%
💼 POSITION SIZE: {rec.position_size:.1%} of portfolio
RISK: {rec.risk_level}
📝 REASONING:
{rec.reasoning}
📅 Generated: {rec.timestamp.strftime('%Y-%m-%d %H:%M')}
"""
return formatted
# Example usage
async def main():
"""Example of using the signal processor"""
# Initialize components
ibkr = IBKRConnector()
data_agg = DataAggregator()
processor = SignalProcessor(ibkr, data_agg)
# Connect to IBKR (would need TWS running)
# await ibkr.connect()
# Process a single ticker
rec = await processor.process_signals("NVDA")
if rec:
print(processor.format_recommendation(rec))
if __name__ == "__main__":
asyncio.run(main())

102
autonomous_trader.py Normal file
View File

@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""
Autonomous Trading System Main Entry Point
==========================================
Run this to start the 24/7 autonomous trading system.
"""
import asyncio
import logging
import sys
import signal
from pathlib import Path
# Add current directory to path
sys.path.insert(0, str(Path(__file__).parent))
from autonomous.scheduler import AutonomousScheduler
from autonomous.config.settings import Config
# Global scheduler instance for signal handling
scheduler = None
def signal_handler(sig, frame):
"""Handle shutdown signals gracefully"""
print("\n🛑 Shutdown signal received...")
if scheduler:
asyncio.create_task(scheduler.stop())
sys.exit(0)
async def main():
"""Main entry point"""
global scheduler
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('autonomous_trader.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
print("""
🤖 AUTONOMOUS TRADING SYSTEM 🤖
24/7 Market Monitoring | AI-Powered | Multi-Source
""")
# Validate configuration
if not Config.validate():
logger.error("Configuration validation failed. Please check your settings.")
return
# Display configuration
print(f"📊 Portfolio: {', '.join(Config.PORTFOLIO_TICKERS)}")
print(f"👀 Watchlist: {', '.join(Config.WATCHLIST)}")
print(f"🎯 Mode: {'PAPER TRADING' if Config.PAPER_TRADING else '⚠️ LIVE TRADING'}")
print(f"💼 Trading: {'ENABLED' if Config.TRADING_ENABLED else 'DISABLED (Monitoring Only)'}")
print()
# Confirm before starting
if not Config.PAPER_TRADING and Config.TRADING_ENABLED:
response = input("⚠️ WARNING: Live trading is enabled! Continue? (yes/no): ")
if response.lower() != 'yes':
print("Aborted.")
return
# Create scheduler
scheduler = AutonomousScheduler(Config.to_dict())
# Setup signal handlers
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
try:
# Start the system
await scheduler.start()
except Exception as e:
logger.error(f"Fatal error: {e}", exc_info=True)
if scheduler:
await scheduler.stop()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n👋 Goodbye!")
except Exception as e:
print(f"❌ Error: {e}")
sys.exit(1)

17
main.py
View File

@ -23,9 +23,22 @@ config["data_vendors"] = {
# Initialize with custom config
ta = TradingAgentsGraph(debug=True, config=config)
# forward propagate
_, decision = ta.propagate("NVDA", "2024-05-10")
# Your IBKR portfolio tickers
PORTFOLIO_TICKERS = ["AVGO", "MSFT", "MU", "NVDA", "TSM"] # Excluding SXRV (ETF)
# Analyze your largest position (AVGO - 43 shares)
print("Analyzing AVGO (Broadcom) - Your largest position...")
_, decision = ta.propagate("AVGO", "2024-10-01")
print("\n" + "="*60)
print("AVGO Analysis Result:")
print("="*60)
print(decision)
# Uncomment below to analyze all positions:
# for ticker in PORTFOLIO_TICKERS:
# print(f"\nAnalyzing {ticker}...")
# _, decision = ta.propagate(ticker, "2024-10-01")
# print(f"{ticker} Decision: {decision[:200]}...") # First 200 chars
# Memorize mistakes and reflect
# ta.reflect_and_remember(1000) # parameter is the position returns

54
quick_demo.py Normal file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Quick demo of TradingAgents analyzing a stock"""
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
from dotenv import load_dotenv
import datetime
# Load environment variables
load_dotenv()
print("=" * 60)
print("🚀 TradingAgents Quick Demo")
print("=" * 60)
# Configure for fast testing
config = DEFAULT_CONFIG.copy()
config["deep_think_llm"] = "gpt-4o-mini" # Use faster model
config["quick_think_llm"] = "gpt-4o-mini" # Use faster model
config["max_debate_rounds"] = 1 # Reduce debate rounds for speed
# Configure data sources
config["data_vendors"] = {
"core_stock_apis": "yfinance",
"technical_indicators": "yfinance",
"fundamental_data": "alpha_vantage",
"news_data": "alpha_vantage",
}
print("\n📊 Analyzing NVDA stock for 2024-05-10...")
print(" Using: gpt-4o-mini (fast mode)")
print(" Data: yfinance + Alpha Vantage")
print("\n🔄 This will take 1-2 minutes as agents analyze the data...\n")
# Initialize the trading graph
ta = TradingAgentsGraph(debug=True, config=config)
# Run analysis
try:
_, decision = ta.propagate("NVDA", "2024-05-10")
print("\n" + "=" * 60)
print("📈 TRADING DECISION")
print("=" * 60)
print(decision)
except Exception as e:
print(f"\n❌ Error during analysis: {e}")
print("\nThis might happen if:")
print("- API rate limits are reached")
print("- Network connectivity issues")
print("- Invalid API keys")
print("\n✅ Demo complete!")

View File

@ -0,0 +1,63 @@
# Autonomous Trading System Requirements
# Install with: pip install -r requirements_autonomous.txt
# === Core Trading Requirements ===
# Already in main requirements.txt but listed for clarity
yfinance>=0.2.63
pandas>=2.3.0
numpy>=2.1.0
# === IBKR Connection ===
ib_insync==0.9.86
# === Database ===
psycopg2-binary==2.9.9
sqlalchemy==2.0.23
alembic==1.13.1
# === Scheduling & Async ===
APScheduler==3.10.4
asyncio-throttle==1.0.2
aiohttp>=3.8.5
nest-asyncio>=1.5.8
# === Alternative Data Sources ===
quiverquant==0.1.8 # Congressional trades
polygon-api-client==1.13.2 # Real-time market data
newsapi-python==0.2.7 # News aggregation
alpaca-py==0.28.0 # Alternative market data
# === Notifications ===
discord.py==2.3.2 # Discord alerts
python-telegram-bot==20.7 # Telegram alerts
# === Web Dashboard (Optional) ===
fastapi==0.109.0
uvicorn[standard]==0.25.0
jinja2==3.1.3
websockets>=12.0
plotly==5.18.0 # Interactive charts
# === Redis (for caching and task queue) ===
redis==5.0.1
celery==5.3.4
# === Additional Analysis Tools ===
ta==0.11.0 # Technical analysis
scipy>=1.11.4 # Statistical analysis
scikit-learn>=1.3.2 # ML for pattern recognition
# === Monitoring & Logging ===
prometheus-client==0.19.0 # Metrics
python-json-logger==2.0.7 # Structured logging
sentry-sdk==1.39.1 # Error tracking (optional)
# === Testing ===
pytest==7.4.3
pytest-asyncio==0.21.1
pytest-mock==3.12.0
# === Development Tools ===
python-dotenv==1.0.0 # Environment variables
black==23.12.1 # Code formatting
pylint==3.0.3 # Code linting

41
test_api.py Normal file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Quick test to verify OpenAI API is working"""
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def test_openai():
"""Test OpenAI API connectivity"""
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
print("❌ No OpenAI API key found")
return False
print(f"✅ OpenAI API Key found: {api_key[:20]}...")
try:
from openai import OpenAI
client = OpenAI(api_key=api_key)
# Test with a simple completion
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Say 'API working!'"}],
max_tokens=10
)
result = response.choices[0].message.content
print(f"✅ OpenAI API Response: {result}")
return True
except Exception as e:
print(f"❌ OpenAI API Error: {e}")
return False
if __name__ == "__main__":
print("Testing OpenAI API...")
test_openai()

154
test_setup.py Normal file
View File

@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""Test script to verify TradingAgents setup and API connectivity"""
import os
import sys
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
def check_api_keys():
"""Check if required API keys are set"""
print("🔍 Checking API Keys...")
alpha_vantage_key = os.getenv("ALPHA_VANTAGE_API_KEY")
openai_key = os.getenv("OPENAI_API_KEY")
status = {}
if alpha_vantage_key and alpha_vantage_key != "alpha_vantage_api_key_placeholder":
print("✅ Alpha Vantage API Key: SET")
status["alpha_vantage"] = True
else:
print("❌ Alpha Vantage API Key: NOT SET or using placeholder")
status["alpha_vantage"] = False
if openai_key and openai_key != "openai_api_key_placeholder":
print("✅ OpenAI API Key: SET")
status["openai"] = True
else:
print("❌ OpenAI API Key: NOT SET or using placeholder")
status["openai"] = False
return status
def test_imports():
"""Test if all required packages can be imported"""
print("\n🔍 Testing Package Imports...")
packages = [
"langchain_openai",
"langchain_experimental",
"pandas",
"yfinance",
"langgraph",
"tradingagents"
]
failed = []
for package in packages:
try:
__import__(package)
print(f"{package}: OK")
except ImportError as e:
print(f"{package}: FAILED - {e}")
failed.append(package)
return len(failed) == 0
def test_alpha_vantage_connection():
"""Test Alpha Vantage API connectivity"""
print("\n🔍 Testing Alpha Vantage API...")
api_key = os.getenv("ALPHA_VANTAGE_API_KEY")
if not api_key or api_key == "alpha_vantage_api_key_placeholder":
print("⏭️ Skipping Alpha Vantage test - API key not set")
return False
try:
import requests
# Test with a simple quote endpoint
url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=AAPL&apikey={api_key}"
response = requests.get(url, timeout=10)
data = response.json()
if "Global Quote" in data:
print("✅ Alpha Vantage API: Connected successfully")
return True
elif "Note" in data:
print("⚠️ Alpha Vantage API: Rate limit reached")
return True
elif "Error Message" in data:
print(f"❌ Alpha Vantage API: {data['Error Message']}")
return False
else:
print("❌ Alpha Vantage API: Unexpected response")
return False
except Exception as e:
print(f"❌ Alpha Vantage API: Connection failed - {e}")
return False
def test_yfinance():
"""Test yfinance data fetching"""
print("\n🔍 Testing yfinance...")
try:
import yfinance as yf
ticker = yf.Ticker("AAPL")
info = ticker.info
if info and "symbol" in info:
print("✅ yfinance: Working correctly")
return True
else:
print("❌ yfinance: Failed to fetch data")
return False
except Exception as e:
print(f"❌ yfinance: Error - {e}")
return False
def main():
print("=" * 60)
print("🚀 TradingAgents Setup Test")
print("=" * 60)
# Check API keys
api_status = check_api_keys()
# Test imports
imports_ok = test_imports()
# Test connections if keys are available
if api_status["alpha_vantage"]:
test_alpha_vantage_connection()
test_yfinance()
# Summary
print("\n" + "=" * 60)
print("📊 SUMMARY")
print("=" * 60)
if not api_status["openai"]:
print("\n⚠️ IMPORTANT: You need to set your OpenAI API key!")
print(" Either:")
print(" 1. Add it to the .env file")
print(" 2. Export it: export OPENAI_API_KEY='your-key-here'")
if imports_ok and api_status["alpha_vantage"]:
print("\n✅ Basic setup is complete!")
print("\n📝 Next steps:")
if not api_status["openai"]:
print("1. Add your OpenAI API key")
print("2. Run: python main.py")
print(" OR")
print(" python -m cli.main")
else:
print("1. Run: python main.py")
print(" OR")
print(" python -m cli.main")
else:
print("\n❌ Setup incomplete. Please fix the issues above.")
if __name__ == "__main__":
main()