818 lines
20 KiB
Markdown
818 lines
20 KiB
Markdown
# TradingAgents: Technical Debt & Architectural Improvements
|
|
|
|
**Modernization & Code Quality Enhancements**
|
|
|
|
---
|
|
|
|
## 🔧 TECHNICAL DEBT
|
|
|
|
### 1. Type Safety & Static Analysis
|
|
**Priority:** High
|
|
**Effort:** 2-3 weeks
|
|
**Impact:** Reduces bugs, improves maintainability
|
|
|
|
**Current Issues:**
|
|
- Limited type hints throughout codebase
|
|
- No mypy or pyright validation
|
|
- Dynamic typing makes refactoring risky
|
|
|
|
**Solution:**
|
|
|
|
```python
|
|
# tradingagents/types.py
|
|
"""Comprehensive type definitions for TradingAgents."""
|
|
|
|
from typing import TypedDict, Literal, Protocol
|
|
from decimal import Decimal
|
|
from datetime import datetime
|
|
|
|
# Type aliases
|
|
Ticker = str
|
|
Signal = Literal["BUY", "SELL", "HOLD"]
|
|
Timestamp = str # ISO format
|
|
|
|
# Structured types
|
|
class StockData(TypedDict):
|
|
"""Stock price data structure."""
|
|
open: Decimal
|
|
high: Decimal
|
|
low: Decimal
|
|
close: Decimal
|
|
volume: int
|
|
timestamp: datetime
|
|
|
|
class AnalystReport(TypedDict):
|
|
"""Analyst report structure."""
|
|
analyst_type: Literal["market", "fundamentals", "news", "social"]
|
|
ticker: Ticker
|
|
date: str
|
|
analysis: str
|
|
confidence: float
|
|
recommendation: Signal
|
|
reasoning: str
|
|
|
|
class TradingDecision(TypedDict):
|
|
"""Final trading decision structure."""
|
|
ticker: Ticker
|
|
signal: Signal
|
|
confidence: float
|
|
timestamp: Timestamp
|
|
analyst_reports: dict[str, AnalystReport]
|
|
risk_assessment: str
|
|
position_size: Decimal
|
|
|
|
# Protocol for data vendors
|
|
class DataVendor(Protocol):
|
|
"""Interface for data vendors."""
|
|
|
|
def get_stock_data(
|
|
self,
|
|
ticker: Ticker,
|
|
start_date: str,
|
|
end_date: str
|
|
) -> list[StockData]:
|
|
"""Fetch historical stock data."""
|
|
...
|
|
|
|
def get_fundamentals(
|
|
self,
|
|
ticker: Ticker
|
|
) -> dict[str, any]:
|
|
"""Fetch fundamental data."""
|
|
...
|
|
|
|
# Refactor with types
|
|
def propagate(
|
|
self,
|
|
ticker: Ticker,
|
|
date: str
|
|
) -> tuple[dict[str, any], Signal]:
|
|
"""
|
|
Run TradingAgents analysis with full type safety.
|
|
|
|
Args:
|
|
ticker: Stock symbol (e.g., "NVDA")
|
|
date: Analysis date in YYYY-MM-DD format
|
|
|
|
Returns:
|
|
Tuple of (full_state, signal)
|
|
|
|
Raises:
|
|
ValueError: If ticker or date format is invalid
|
|
APIError: If data fetching fails
|
|
"""
|
|
# Implementation with full type checking
|
|
```
|
|
|
|
**Validation Setup:**
|
|
|
|
```yaml
|
|
# .github/workflows/type-check.yml
|
|
name: Type Check
|
|
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
mypy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-python@v4
|
|
with:
|
|
python-version: '3.11'
|
|
- name: Install dependencies
|
|
run: |
|
|
pip install mypy
|
|
pip install -r requirements.txt
|
|
- name: Run mypy
|
|
run: mypy tradingagents/ --strict
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Dependency Management
|
|
**Priority:** High
|
|
**Effort:** 1 week
|
|
**Impact:** Reproducible builds, security
|
|
|
|
**Current Issues:**
|
|
- `requirements.txt` lacks version pinning
|
|
- No dependency vulnerability scanning
|
|
- Missing dependency groups (dev, test, prod)
|
|
|
|
**Solution:**
|
|
|
|
```toml
|
|
# pyproject.toml
|
|
[project]
|
|
name = "tradingagents"
|
|
version = "1.0.0"
|
|
description = "Multi-Agent LLM Financial Trading Framework"
|
|
requires-python = ">=3.9"
|
|
|
|
dependencies = [
|
|
"langchain-openai>=0.1.0,<0.2.0",
|
|
"langchain-anthropic>=0.1.0,<0.2.0",
|
|
"langchain-google-genai>=1.0.0,<2.0.0",
|
|
"langgraph>=0.1.0,<0.2.0",
|
|
"pandas>=2.0.0,<3.0.0",
|
|
"yfinance>=0.2.0,<0.3.0",
|
|
"alpaca-py>=0.7.0,<0.8.0",
|
|
"chainlit>=1.0.0,<2.0.0",
|
|
"plotly>=5.0.0,<6.0.0",
|
|
"fastapi>=0.100.0,<0.101.0",
|
|
"uvicorn>=0.23.0,<0.24.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=7.0.0",
|
|
"pytest-cov>=4.0.0",
|
|
"pytest-asyncio>=0.21.0",
|
|
"black>=23.0.0",
|
|
"isort>=5.12.0",
|
|
"mypy>=1.0.0",
|
|
"ruff>=0.1.0",
|
|
]
|
|
|
|
test = [
|
|
"pytest>=7.0.0",
|
|
"pytest-mock>=3.11.0",
|
|
"pytest-timeout>=2.1.0",
|
|
"freezegun>=1.2.0",
|
|
]
|
|
|
|
docs = [
|
|
"mkdocs>=1.5.0",
|
|
"mkdocs-material>=9.0.0",
|
|
"mkdocstrings[python]>=0.22.0",
|
|
]
|
|
|
|
[tool.black]
|
|
line-length = 100
|
|
target-version = ['py39', 'py310', 'py311']
|
|
|
|
[tool.isort]
|
|
profile = "black"
|
|
line_length = 100
|
|
|
|
[tool.mypy]
|
|
python_version = "3.9"
|
|
warn_return_any = true
|
|
warn_unused_configs = true
|
|
disallow_untyped_defs = true
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
python_files = ["test_*.py"]
|
|
python_classes = ["Test*"]
|
|
python_functions = ["test_*"]
|
|
addopts = "-v --cov=tradingagents --cov-report=html --cov-report=term"
|
|
```
|
|
|
|
**Security Scanning:**
|
|
|
|
```yaml
|
|
# .github/workflows/security.yml
|
|
name: Security Scan
|
|
|
|
on:
|
|
push:
|
|
schedule:
|
|
- cron: '0 0 * * 0' # Weekly
|
|
|
|
jobs:
|
|
scan:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- name: Run Snyk
|
|
uses: snyk/actions/python-3.9@master
|
|
env:
|
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
|
|
|
- name: Run Safety
|
|
run: |
|
|
pip install safety
|
|
safety check --json
|
|
|
|
- name: Run Bandit
|
|
run: |
|
|
pip install bandit
|
|
bandit -r tradingagents/ -ll
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Configuration Management
|
|
**Priority:** Medium
|
|
**Effort:** 1 week
|
|
**Impact:** Flexibility, maintainability
|
|
|
|
**Current Issues:**
|
|
- Configuration scattered across files
|
|
- Hard to override for different environments
|
|
- No validation of config values
|
|
|
|
**Solution:**
|
|
|
|
```python
|
|
# tradingagents/config/config.py
|
|
from pydantic import BaseSettings, Field, validator
|
|
from typing import Literal, Optional
|
|
from pathlib import Path
|
|
|
|
class DatabaseConfig(BaseSettings):
|
|
"""Database configuration."""
|
|
host: str = "localhost"
|
|
port: int = 5432
|
|
name: str = "tradingagents"
|
|
user: str = "postgres"
|
|
password: str = Field(..., env="DB_PASSWORD")
|
|
|
|
class Config:
|
|
env_prefix = "DB_"
|
|
|
|
class LLMConfig(BaseSettings):
|
|
"""LLM configuration."""
|
|
provider: Literal["openai", "anthropic", "google"] = "openai"
|
|
deep_think_model: str = "gpt-4o"
|
|
quick_think_model: str = "gpt-4o-mini"
|
|
temperature: float = Field(1.0, ge=0.0, le=2.0)
|
|
max_tokens: Optional[int] = Field(None, ge=1, le=100000)
|
|
|
|
@validator("provider")
|
|
def validate_provider(cls, v):
|
|
"""Ensure API key exists for provider."""
|
|
key_env = f"{v.upper()}_API_KEY"
|
|
if not os.getenv(key_env):
|
|
raise ValueError(f"{key_env} not set")
|
|
return v
|
|
|
|
class Config:
|
|
env_prefix = "LLM_"
|
|
|
|
class BrokerConfig(BaseSettings):
|
|
"""Broker configuration."""
|
|
type: Literal["alpaca", "ib", "mock"] = "alpaca"
|
|
paper_trading: bool = True
|
|
api_key: Optional[str] = Field(None, env="BROKER_API_KEY")
|
|
secret_key: Optional[str] = Field(None, env="BROKER_SECRET_KEY")
|
|
|
|
class Config:
|
|
env_prefix = "BROKER_"
|
|
|
|
class TradingConfig(BaseSettings):
|
|
"""Trading configuration."""
|
|
max_debate_rounds: int = Field(1, ge=0, le=5)
|
|
max_risk_discuss_rounds: int = Field(1, ge=0, le=5)
|
|
default_position_size: float = Field(0.1, ge=0.01, le=1.0)
|
|
risk_tolerance: Literal["conservative", "moderate", "aggressive"] = "moderate"
|
|
|
|
class Config:
|
|
env_prefix = "TRADING_"
|
|
|
|
class TradingAgentsConfig(BaseSettings):
|
|
"""Main configuration."""
|
|
# Paths
|
|
project_dir: Path = Path(__file__).parent.parent
|
|
data_dir: Path = Field(Path("./data"), env="TRADINGAGENTS_DATA_DIR")
|
|
results_dir: Path = Field(Path("./results"), env="TRADINGAGENTS_RESULTS_DIR")
|
|
|
|
# Sub-configs
|
|
llm: LLMConfig = Field(default_factory=LLMConfig)
|
|
broker: BrokerConfig = Field(default_factory=BrokerConfig)
|
|
trading: TradingConfig = Field(default_factory=TradingConfig)
|
|
database: Optional[DatabaseConfig] = None
|
|
|
|
# Environment
|
|
environment: Literal["development", "staging", "production"] = "development"
|
|
debug: bool = Field(False, env="DEBUG")
|
|
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
env_file_encoding = "utf-8"
|
|
|
|
@validator("data_dir", "results_dir")
|
|
def create_directories(cls, v):
|
|
"""Ensure directories exist."""
|
|
v.mkdir(parents=True, exist_ok=True)
|
|
return v
|
|
|
|
# Usage
|
|
config = TradingAgentsConfig()
|
|
|
|
# Access nested config
|
|
print(f"Using {config.llm.provider} with {config.llm.deep_think_model}")
|
|
|
|
# Environment-specific configs
|
|
# development.env, staging.env, production.env
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Error Handling & Resilience
|
|
**Priority:** High
|
|
**Effort:** 2 weeks
|
|
**Impact:** Reliability, user experience
|
|
|
|
**Current Issues:**
|
|
- Inconsistent error handling
|
|
- No retry logic for transient failures
|
|
- Poor error messages
|
|
|
|
**Solution:**
|
|
|
|
```python
|
|
# tradingagents/resilience/retry.py
|
|
from functools import wraps
|
|
import time
|
|
from typing import Type, Tuple
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def retry_with_backoff(
|
|
max_attempts: int = 3,
|
|
backoff_factor: float = 2.0,
|
|
exceptions: Tuple[Type[Exception], ...] = (Exception,),
|
|
on_retry: callable = None
|
|
):
|
|
"""
|
|
Retry decorator with exponential backoff.
|
|
|
|
Args:
|
|
max_attempts: Maximum number of retry attempts
|
|
backoff_factor: Multiplier for backoff delay
|
|
exceptions: Tuple of exceptions to catch and retry
|
|
on_retry: Callback function called on each retry
|
|
"""
|
|
def decorator(func):
|
|
@wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
attempt = 1
|
|
delay = 1.0
|
|
|
|
while attempt <= max_attempts:
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except exceptions as e:
|
|
if attempt == max_attempts:
|
|
logger.error(
|
|
f"{func.__name__} failed after {max_attempts} attempts: {e}"
|
|
)
|
|
raise
|
|
|
|
logger.warning(
|
|
f"{func.__name__} failed (attempt {attempt}/{max_attempts}): {e}. "
|
|
f"Retrying in {delay}s..."
|
|
)
|
|
|
|
if on_retry:
|
|
on_retry(attempt, e)
|
|
|
|
time.sleep(delay)
|
|
delay *= backoff_factor
|
|
attempt += 1
|
|
|
|
return wrapper
|
|
return decorator
|
|
|
|
# Usage
|
|
@retry_with_backoff(
|
|
max_attempts=3,
|
|
backoff_factor=2.0,
|
|
exceptions=(APIError, ConnectionError, TimeoutError)
|
|
)
|
|
def get_stock_data(ticker: str, date: str) -> dict:
|
|
"""Fetch stock data with automatic retry."""
|
|
return api.fetch_data(ticker, date)
|
|
|
|
# Circuit breaker pattern
|
|
class CircuitBreaker:
|
|
"""Circuit breaker for external services."""
|
|
|
|
def __init__(
|
|
self,
|
|
failure_threshold: int = 5,
|
|
timeout: int = 60,
|
|
name: str = "service"
|
|
):
|
|
self.failure_threshold = failure_threshold
|
|
self.timeout = timeout
|
|
self.name = name
|
|
self.failure_count = 0
|
|
self.last_failure_time = None
|
|
self.state = "closed" # closed, open, half-open
|
|
|
|
def call(self, func, *args, **kwargs):
|
|
"""Execute function with circuit breaker."""
|
|
|
|
if self.state == "open":
|
|
if self._should_attempt_reset():
|
|
self.state = "half-open"
|
|
else:
|
|
raise CircuitBreakerOpenError(
|
|
f"Circuit breaker is OPEN for {self.name}"
|
|
)
|
|
|
|
try:
|
|
result = func(*args, **kwargs)
|
|
|
|
# Success - reset if half-open
|
|
if self.state == "half-open":
|
|
self.state = "closed"
|
|
self.failure_count = 0
|
|
logger.info(f"Circuit breaker CLOSED for {self.name}")
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
self.failure_count += 1
|
|
self.last_failure_time = time.time()
|
|
|
|
if self.failure_count >= self.failure_threshold:
|
|
self.state = "open"
|
|
logger.error(
|
|
f"Circuit breaker OPENED for {self.name} "
|
|
f"after {self.failure_count} failures"
|
|
)
|
|
|
|
raise
|
|
|
|
def _should_attempt_reset(self) -> bool:
|
|
"""Check if enough time has passed to attempt reset."""
|
|
return (
|
|
self.last_failure_time and
|
|
time.time() - self.last_failure_time >= self.timeout
|
|
)
|
|
|
|
# Usage
|
|
alpaca_breaker = CircuitBreaker(name="alpaca_api", failure_threshold=5)
|
|
|
|
def get_account_info():
|
|
return alpaca_breaker.call(broker.get_account)
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Testing Infrastructure
|
|
**Priority:** High
|
|
**Effort:** 2-3 weeks
|
|
**Impact:** Quality, confidence
|
|
|
|
**Current Issues:**
|
|
- Test coverage gaps
|
|
- No integration tests
|
|
- Slow test suite
|
|
- No test fixtures for LLM responses
|
|
|
|
**Solution:**
|
|
|
|
```python
|
|
# tests/conftest.py
|
|
import pytest
|
|
from unittest.mock import Mock, patch
|
|
from decimal import Decimal
|
|
from tradingagents.graph.trading_graph import TradingAgentsGraph
|
|
|
|
@pytest.fixture
|
|
def mock_llm():
|
|
"""Mock LLM for testing."""
|
|
llm = Mock()
|
|
llm.invoke.return_value = Mock(
|
|
content="BUY signal with 85% confidence. Strong fundamentals..."
|
|
)
|
|
return llm
|
|
|
|
@pytest.fixture
|
|
def mock_broker():
|
|
"""Mock broker for testing."""
|
|
broker = Mock()
|
|
broker.get_account.return_value = BrokerAccount(
|
|
account_number="TEST123",
|
|
cash=Decimal("100000.00"),
|
|
buying_power=Decimal("200000.00"),
|
|
portfolio_value=Decimal("100000.00"),
|
|
equity=Decimal("100000.00"),
|
|
last_equity=Decimal("100000.00"),
|
|
multiplier=Decimal("2"),
|
|
)
|
|
return broker
|
|
|
|
@pytest.fixture
|
|
def sample_stock_data():
|
|
"""Sample stock data for testing."""
|
|
return {
|
|
"AAPL": pd.DataFrame({
|
|
"open": [150.0, 151.0, 152.0],
|
|
"high": [152.0, 153.0, 154.0],
|
|
"low": [149.0, 150.0, 151.0],
|
|
"close": [151.0, 152.0, 153.0],
|
|
"volume": [1000000, 1100000, 1200000]
|
|
})
|
|
}
|
|
|
|
@pytest.fixture
|
|
def trading_graph(mock_llm):
|
|
"""TradingAgents graph with mocked LLM."""
|
|
with patch('tradingagents.llm_factory.LLMFactory.create_llm', return_value=mock_llm):
|
|
ta = TradingAgentsGraph(
|
|
selected_analysts=["market"],
|
|
debug=True
|
|
)
|
|
yield ta
|
|
|
|
# Integration tests
|
|
# tests/integration/test_full_workflow.py
|
|
@pytest.mark.integration
|
|
@pytest.mark.slow
|
|
def test_full_trading_workflow(trading_graph, mock_broker):
|
|
"""Test complete trading workflow."""
|
|
|
|
# 1. Analyze
|
|
_, signal = trading_graph.propagate("AAPL", "2024-05-10")
|
|
assert signal in ["BUY", "SELL", "HOLD"]
|
|
|
|
# 2. Execute
|
|
if signal == "BUY":
|
|
order = mock_broker.buy_market("AAPL", Decimal("10"))
|
|
assert order.status == OrderStatus.SUBMITTED
|
|
|
|
# 3. Track
|
|
positions = mock_broker.get_positions()
|
|
assert any(p.symbol == "AAPL" for p in positions)
|
|
|
|
# Performance tests
|
|
@pytest.mark.benchmark
|
|
def test_propagate_performance(benchmark, trading_graph):
|
|
"""Benchmark propagate performance."""
|
|
|
|
result = benchmark(
|
|
trading_graph.propagate,
|
|
"AAPL",
|
|
"2024-05-10"
|
|
)
|
|
|
|
# Should complete in < 30 seconds
|
|
assert benchmark.stats["mean"] < 30.0
|
|
|
|
# Property-based testing
|
|
from hypothesis import given, strategies as st
|
|
|
|
@given(
|
|
ticker=st.text(min_size=1, max_size=5, alphabet=st.characters(whitelist_categories=('Lu',))),
|
|
quantity=st.decimals(min_value=1, max_value=1000)
|
|
)
|
|
def test_order_creation_properties(ticker, quantity):
|
|
"""Property-based test for order creation."""
|
|
order = MarketOrder(ticker, quantity)
|
|
|
|
assert order.symbol == ticker
|
|
assert order.quantity == quantity
|
|
assert order.order_type == OrderType.MARKET
|
|
```
|
|
|
|
**CI/CD Integration:**
|
|
|
|
```yaml
|
|
# .github/workflows/test.yml
|
|
name: Test Suite
|
|
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
python-version: ['3.9', '3.10', '3.11']
|
|
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v4
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: Cache dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: ~/.cache/pip
|
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
pip install -e ".[dev,test]"
|
|
|
|
- name: Run unit tests
|
|
run: pytest tests/unit -v --cov --cov-report=xml
|
|
|
|
- name: Run integration tests
|
|
run: pytest tests/integration -v --slow
|
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
|
|
- name: Upload coverage
|
|
uses: codecov/codecov-action@v3
|
|
with:
|
|
file: ./coverage.xml
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Documentation
|
|
**Priority:** Medium
|
|
**Effort:** 2 weeks
|
|
**Impact:** Onboarding, maintenance
|
|
|
|
**Solution:**
|
|
|
|
```python
|
|
# Set up MkDocs
|
|
# mkdocs.yml
|
|
site_name: TradingAgents Documentation
|
|
theme:
|
|
name: material
|
|
features:
|
|
- navigation.tabs
|
|
- navigation.sections
|
|
- search.suggest
|
|
- search.highlight
|
|
- content.code.copy
|
|
|
|
nav:
|
|
- Home: index.md
|
|
- Getting Started:
|
|
- Installation: getting-started/installation.md
|
|
- Quick Start: getting-started/quickstart.md
|
|
- Configuration: getting-started/configuration.md
|
|
- User Guide:
|
|
- Analysis: guide/analysis.md
|
|
- Trading: guide/trading.md
|
|
- Portfolio: guide/portfolio.md
|
|
- Backtesting: guide/backtesting.md
|
|
- API Reference:
|
|
- TradingAgentsGraph: api/trading-graph.md
|
|
- Portfolio: api/portfolio.md
|
|
- Brokers: api/brokers.md
|
|
- Advanced:
|
|
- Custom Strategies: advanced/strategies.md
|
|
- LLM Configuration: advanced/llm.md
|
|
- Production Deployment: advanced/production.md
|
|
- Contributing:
|
|
- Development Guide: contributing/development.md
|
|
- Architecture: contributing/architecture.md
|
|
- Testing: contributing/testing.md
|
|
|
|
plugins:
|
|
- search
|
|
- mkdocstrings:
|
|
handlers:
|
|
python:
|
|
options:
|
|
show_source: true
|
|
show_root_heading: true
|
|
|
|
# Auto-generate API docs from docstrings
|
|
# docs/api/trading-graph.md
|
|
::: tradingagents.graph.trading_graph.TradingAgentsGraph
|
|
options:
|
|
show_root_heading: true
|
|
show_source: true
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ ARCHITECTURAL IMPROVEMENTS
|
|
|
|
### 1. Event-Driven Architecture
|
|
**Current:** Synchronous, blocking operations
|
|
**Proposed:** Async, event-driven
|
|
|
|
```python
|
|
# tradingagents/events/bus.py
|
|
from typing import Callable, List
|
|
import asyncio
|
|
|
|
class EventBus:
|
|
"""Central event bus for loosely coupled components."""
|
|
|
|
def __init__(self):
|
|
self.subscribers: dict[str, List[Callable]] = {}
|
|
|
|
def subscribe(self, event_type: str, handler: Callable):
|
|
"""Subscribe to event type."""
|
|
if event_type not in self.subscribers:
|
|
self.subscribers[event_type] = []
|
|
self.subscribers[event_type].append(handler)
|
|
|
|
async def publish(self, event_type: str, data: dict):
|
|
"""Publish event to all subscribers."""
|
|
if event_type in self.subscribers:
|
|
tasks = [
|
|
handler(data)
|
|
for handler in self.subscribers[event_type]
|
|
]
|
|
await asyncio.gather(*tasks)
|
|
|
|
# Usage
|
|
event_bus = EventBus()
|
|
|
|
# Subscribe
|
|
async def on_signal_generated(data):
|
|
"""Handle signal generation."""
|
|
logger.info(f"Signal generated: {data['signal']} for {data['ticker']}")
|
|
await alert_manager.notify(data)
|
|
|
|
event_bus.subscribe("signal_generated", on_signal_generated)
|
|
|
|
# Publish
|
|
await event_bus.publish("signal_generated", {
|
|
"ticker": "NVDA",
|
|
"signal": "BUY",
|
|
"confidence": 0.85
|
|
})
|
|
```
|
|
|
|
### 2. Microservices Architecture
|
|
**Current:** Monolithic
|
|
**Proposed:** Decomposed services
|
|
|
|
```
|
|
Services:
|
|
- Analysis Service (TradingAgents core)
|
|
- Data Service (market data)
|
|
- Execution Service (order management)
|
|
- Portfolio Service (position tracking)
|
|
- Notification Service (alerts)
|
|
- API Gateway (unified interface)
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Technical Debt Summary
|
|
|
|
| Area | Priority | Effort | Impact | ROI |
|
|
|------|----------|--------|--------|-----|
|
|
| Type Safety | High | 2-3 weeks | High | ⭐⭐⭐⭐⭐ |
|
|
| Dependencies | High | 1 week | High | ⭐⭐⭐⭐⭐ |
|
|
| Configuration | Medium | 1 week | Medium | ⭐⭐⭐⭐ |
|
|
| Error Handling | High | 2 weeks | High | ⭐⭐⭐⭐⭐ |
|
|
| Testing | High | 2-3 weeks | Very High | ⭐⭐⭐⭐⭐ |
|
|
| Documentation | Medium | 2 weeks | High | ⭐⭐⭐⭐ |
|
|
|
|
**Total Effort:** 10-13 weeks (2.5-3 months)
|
|
|
|
**Expected Benefits:**
|
|
- 50% fewer production bugs
|
|
- 80% faster onboarding
|
|
- 3x easier refactoring
|
|
- 90% test coverage
|
|
- Professional codebase quality
|
|
|
|
---
|
|
|
|
*See also: STRATEGIC_IMPROVEMENTS.md, MEDIUM_TERM_ENHANCEMENTS.md, STRATEGIC_INITIATIVES.md*
|