Updated the Roadmap

This commit is contained in:
Manav Chaudhary 2026-02-23 17:14:24 -05:00
parent 18ff684dfc
commit 867dc56dda
3 changed files with 868 additions and 548 deletions

BIN
docs/LangGraphFlow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because it is too large Load Diff

498
docs/design_reference.md Normal file
View File

@ -0,0 +1,498 @@
# TradeDog — Design Reference
*All architecture patterns, code snippets, schemas, and design decisions for TradeDog.
Referenced by [TradeDog_Roadmap.md](TradeDog_Roadmap.md) — use the roadmap for task tracking, use this doc for implementation details.*
---
## Table of Contents
- [Architecture Target](#architecture-target)
- [Data Source Strategy](#data-source-strategy)
- [Watchlist Design](#watchlist-design)
- [Market Hours and Scheduling](#market-hours-and-scheduling)
- [Execution Layer Architecture](#execution-layer-architecture)
- [Broker Interface](#broker-interface)
- [Position Sizing Formula](#position-sizing-formula)
- [Conviction Scoring Design](#conviction-scoring-design)
- [Auto-Buy Rules](#auto-buy-rules)
- [Agent Prompt Additions](#agent-prompt-additions)
- [Exit Conditions and Rules](#exit-conditions-and-rules)
- [Monitor Loop](#monitor-loop)
- [Trailing Stop Implementation](#trailing-stop-implementation)
- [Reversal Detection](#reversal-detection)
- [Portfolio Guard Design](#portfolio-guard-design)
- [Database Schema](#database-schema)
- [Dashboard Specs](#dashboard-specs)
- [Target File Structure](#target-file-structure)
- [Broker Setup Commands](#broker-setup-commands)
- [Cost Estimate](#cost-estimate)
- [US Regulatory Note](#us-regulatory-note)
---
## Architecture Target
```
[Watchlist Scanner]
|
[Research Pipeline] <-- Fundamentals / Sentiment / News / Technical
|
[Bull vs Bear Debate]
|
[Trader -> Risk Manager -> Fund Manager]
|
Conviction Score
|
[Auto-Buy Engine] <--> [Broker API: Alpaca / IBKR]
|
[Position Monitor -- runs every N minutes]
|-- Profit target hit -> SELL
|-- Trailing stop triggered -> SELL
|-- Reversal signal detected -> SELL
|-- Time-based exit (optional)
|
[Trade Logger + Dashboard]
```
---
## Data Source Strategy
| Source | Use Case | Cost | Notes |
|---|---|---|---|
| FinnHub | Real-time quotes, news, insider trades | Free tier | Already wired in |
| `yfinance` | OHLCV history, fundamentals fallback | Free | Add as secondary/fallback |
| Polygon.io | Higher-quality tick data if needed later | Paid | Skip for now |
| Alpha Vantage | Alternative fundamentals | Free tier | Keep as backup |
Both FinnHub and yfinance have excellent US coverage. yfinance is the primary fallback when FinnHub returns empty or errors.
---
## Watchlist Design
Starter watchlist (~36 quality tickers across sectors):
```json
{
"large_cap": ["AAPL", "MSFT", "NVDA", "GOOGL", "AMZN", "META", "JPM", "UNH", "V", "MA"],
"growth": ["CRWD", "SNOW", "NET", "DDOG", "SMCI", "ARM", "PLTR"],
"value": ["BRK-B", "JNJ", "PG", "KO", "WMT", "HD", "MCD"],
"financials": ["GS", "MS", "BAC", "C", "WFC"],
"energy": ["XOM", "CVX", "COP", "SLB"]
}
```
**Liquidity Filter** — only analyze stocks with sufficient volume to avoid slippage:
```python
MIN_AVG_DAILY_VOLUME = 1_000_000 # 1M shares/day minimum
MIN_MARKET_CAP = 2_000_000_000 # $2B market cap minimum
```
---
## Market Hours and Scheduling
- NYSE/NASDAQ: 9:30 AM - 4:00 PM ET
- Pre-market analysis run: 8:00-9:15 AM ET (agents analyze, build signals)
- Market open execution window: 9:30-10:30 AM ET (buy signals fire here)
- Monitoring loop: runs every 5 min during market hours
- After-hours: position review, log summary, prep next day's watchlist
---
## Execution Layer Architecture
New module: `execution/`
```
execution/
├── broker_interface.py <-- Abstract base class
├── alpaca_broker.py <-- Alpaca implementation
├── ibkr_broker.py <-- IBKR implementation (Phase 7)
├── paper_broker.py <-- Local simulation (no API needed)
└── order_manager.py <-- Order lifecycle tracking
```
Start with `PaperBroker` — a pure Python simulation that tracks positions in a local SQLite database. This lets you test the full loop without any API dependency.
---
## Broker Interface
Define this first. All broker implementations (Paper, Alpaca, IBKR) implement this:
```python
class BrokerInterface:
def place_market_buy(self, ticker: str, qty: int) -> Order: ...
def place_market_sell(self, ticker: str, qty: int) -> Order: ...
def get_positions(self) -> list[Position]: ...
def get_account(self) -> AccountInfo: ...
def cancel_order(self, order_id: str) -> bool: ...
```
---
## Position Sizing Formula
Start simple. Scale by conviction, cap at a percentage of account value:
```python
def calculate_position_size(account_value, conviction_score, price, max_position_pct=0.05):
max_dollars = account_value * max_position_pct
dollars_to_invest = max_dollars * conviction_score
shares = int(dollars_to_invest / price)
return max(1, shares)
```
---
## Conviction Scoring Design
The agents currently produce a BUY/SELL/HOLD decision with rationale. Extend this to produce a **conviction score (0-100)**.
**Weighted scoring approach:**
```python
CONVICTION_WEIGHTS = {
"technical": 0.25, # RSI, MACD, moving average signals
"fundamental": 0.25, # P/E, growth, financial health
"sentiment": 0.20, # Social/news mood
"bull_bear": 0.30, # Debate outcome (most decisive)
}
def calculate_conviction(agent_signals: dict) -> float:
score = 0
for agent, weight in CONVICTION_WEIGHTS.items():
# Each agent returns -1.0 (strong sell) to +1.0 (strong buy)
score += agent_signals[agent] * weight
return score # -1.0 to +1.0
```
---
## Auto-Buy Rules
```python
BUY_THRESHOLD = 0.65 # Must have >65% conviction to buy
MIN_AGENTS_AGREE = 3 # At least 3 of 4 analyst agents must agree direction
MAX_POSITIONS = 10 # Never hold more than 10 stocks at once
COOLDOWN_HOURS = 24 # Don't re-analyze same stock within 24h of last trade
```
---
## Agent Prompt Additions
Add to each analyst agent's system prompt for structured output parsing:
```
At the end of your analysis, always output a JSON block:
{"signal": "BUY|SELL|HOLD", "conviction": 0.85, "key_reason": "..."}
```
Then parse this structured output in the graph state.
---
## Exit Conditions and Rules
| Condition | Rule | Notes |
|---|---|---|
| Profit target | Exit when gain >= 15% | Hard target |
| Trailing stop | Exit when price drops 7% from highest point reached | Locks in gains |
| Stop loss | Exit when loss >= 8% from entry | Hard floor |
| Reversal signal | Exit when Technical Agent says strong SELL | Agent-driven exit |
| Time-based | Exit after 30 days if none of above triggered | Prevents zombie positions |
---
## Monitor Loop
New module: `monitoring/`
```
monitoring/
├── position_monitor.py <-- Main loop
├── exit_rules.py <-- All exit condition logic
├── price_feed.py <-- Real-time price fetching
└── alert_manager.py <-- Notifications
```
**Core loop pseudocode:**
```python
async def monitor_loop(interval_seconds=300): # Check every 5 min
while True:
positions = broker.get_positions()
for position in positions:
current_price = price_feed.get_price(position.ticker)
exit_rule = exit_rules.check(position, current_price)
if exit_rule.should_exit:
broker.place_market_sell(position.ticker, position.qty)
log_exit(position, exit_rule.reason)
await asyncio.sleep(interval_seconds)
```
---
## Trailing Stop Implementation
```python
class Position:
ticker: str
entry_price: float
qty: int
highest_price: float # Track this, update every check
entry_time: datetime
def check_trailing_stop(position, current_price, trail_pct=0.07):
if current_price > position.highest_price:
position.highest_price = current_price
# Save updated high to DB
trail_level = position.highest_price * (1 - trail_pct)
if current_price <= trail_level:
return ExitSignal(should_exit=True, reason="TRAILING_STOP")
return ExitSignal(should_exit=False)
```
---
## Reversal Detection
Cost-effective approach: don't run the full 7-agent pipeline for monitoring. Only run the Technical Analyst:
```python
async def check_reversal(position):
tech_signal = technical_agent.quick_check(position.ticker)
if tech_signal.rsi > 75 and tech_signal.macd_cross == "BEARISH":
return ExitSignal(should_exit=True, reason="REVERSAL_SIGNAL")
```
---
## Portfolio Guard Design
**Hard limits:**
```python
PORTFOLIO_RULES = {
"max_positions": 10, # Never hold more than 10 stocks
"max_sector_exposure": 0.30, # No single sector > 30% of portfolio
"max_single_position": 0.08, # No single stock > 8% of portfolio
"max_single_exchange": 0.60, # No more than 60% in NYSE or NASDAQ alone
"daily_loss_limit": -0.03, # Stop all buys if down 3% on the day
"weekly_loss_limit": -0.07, # Stop all activity if down 7% in a week
"cash_reserve": 0.10, # Always keep 10% cash
}
```
**Guard class:**
```python
class PortfolioGuard:
def can_open_position(self, ticker, proposed_size) -> tuple[bool, str]:
checks = [
self._check_max_positions(),
self._check_sector_exposure(ticker),
self._check_daily_loss(),
self._check_cash_reserve(proposed_size),
]
failures = [r for r in checks if not r.passed]
if failures:
return False, failures[0].reason
return True, "OK"
```
Insert `PortfolioGuard.can_open_position()` between Fund Manager approval and order execution.
---
## Database Schema
Use **SQLite** to start (zero infrastructure). Migrate to Postgres later if needed.
```sql
-- All positions (open and closed)
CREATE TABLE positions (
id INTEGER PRIMARY KEY,
ticker TEXT NOT NULL,
exchange TEXT NOT NULL, -- 'NYSE' or 'NASDAQ'
entry_price REAL NOT NULL,
entry_time DATETIME NOT NULL,
qty INTEGER NOT NULL,
highest_price REAL, -- For trailing stop
exit_price REAL,
exit_time DATETIME,
exit_reason TEXT, -- 'PROFIT_TARGET', 'TRAILING_STOP', etc.
status TEXT DEFAULT 'OPEN' -- 'OPEN' or 'CLOSED'
);
-- All agent signals (for analysis and debugging)
CREATE TABLE signals (
id INTEGER PRIMARY KEY,
ticker TEXT NOT NULL,
timestamp DATETIME NOT NULL,
conviction_score REAL,
agent_decision TEXT, -- JSON of each agent's output
action_taken TEXT, -- 'BOUGHT', 'SKIPPED', 'REJECTED_BY_GUARD'
skip_reason TEXT
);
-- Daily portfolio snapshots
CREATE TABLE portfolio_snapshots (
id INTEGER PRIMARY KEY,
snapshot_date DATE NOT NULL,
total_value REAL,
cash REAL,
num_positions INTEGER,
daily_pnl REAL,
daily_pnl_pct REAL
);
-- System events and errors
CREATE TABLE system_log (
id INTEGER PRIMARY KEY,
timestamp DATETIME NOT NULL,
level TEXT, -- 'INFO', 'WARNING', 'ERROR'
component TEXT,
message TEXT
);
```
---
## Dashboard Specs
Stack: **Streamlit** + Plotly + Pandas. Run locally.
```bash
pip install streamlit plotly pandas
```
**Page 1: Portfolio Overview**
- Current positions table (ticker, entry price, current price, P&L%, trailing stop level)
- Total portfolio value + daily P&L
- Cash available
- Sector exposure chart
**Page 2: Signal Feed**
- Live log of agent decisions (last 50)
- Conviction scores with color coding (green = strong buy, yellow = weak, gray = hold)
- Pending signals not yet executed
**Page 3: Trade History**
- All closed trades with entry/exit/reason/profit
- Win rate, average return, best/worst trade
- Monthly return chart
**Page 4: Agent Monitor**
- Which tickers were analyzed today
- Agent breakdown per analysis (which agents said BUY vs SELL)
- API costs tracker (LLM call count x estimated cost)
---
## Target File Structure
```
TradeDog/
├── tradingagents/ <-- Upstream framework (minimal changes)
│ ├── agents/
│ ├── dataflows/
│ │ ├── yfinance_fallback.py <-- NEW: Fallback when FinnHub fails
│ │ └── data_validator.py <-- NEW: Validates data quality
│ ├── graph/trading_graph.py
│ └── default_config.py
├── execution/ <-- NEW: Order execution
│ ├── broker_interface.py
│ ├── paper_broker.py
│ ├── alpaca_broker.py
│ ├── ibkr_broker.py
│ └── order_manager.py
├── monitoring/ <-- NEW: Position monitoring
│ ├── position_monitor.py
│ ├── exit_rules.py
│ ├── price_feed.py
│ └── alert_manager.py
├── portfolio/ <-- NEW: Risk management
│ ├── portfolio_guard.py
│ ├── conviction_gate.py
│ └── position_sizer.py
├── database/ <-- NEW: Data persistence
│ ├── schema.sql
│ ├── db.py
│ └── models.py
├── dashboard/ <-- NEW: Streamlit UI
│ └── app.py
├── watchlist/ <-- NEW: Curated tickers
│ ├── watchlist.json <-- NYSE/NASDAQ curated tickers
│ └── sector_map.json <-- Ticker -> sector classification
├── scheduler/ <-- NEW: Orchestrates daily run
│ └── main_loop.py
├── tests/
│ └── ...
├── docs/
│ ├── architecture.md
│ ├── agent_contracts.md
│ ├── design_reference.md
│ └── TradeDog_Roadmap.md
├── .env
├── main.py
└── requirements.txt
```
---
## Broker Setup Commands
**Alpaca (recommended for paper + live):**
```bash
pip install alpaca-py
```
Free paper trading API, full NYSE/NASDAQ coverage, no account minimums, clean REST API + Python SDK. Same SDK for paper and live — just swap credentials.
**IBKR (alternative for live trading):**
```bash
pip install ib_insync
# Requires IBKR TWS or Gateway running locally
```
Build `IBKRBroker` implementing the same `BrokerInterface`. Switching brokers is a config change — the rest of the system doesn't care.
---
## Cost Estimate
| Item | Cost |
|---|---|
| LLM API (Claude Sonnet for analysts, Opus for Trader/Risk) | ~$30-80/mo |
| FinnHub free tier | $0 |
| yfinance | $0 |
| Alpaca paper trading | $0 |
| IBKR live account | $0 (no monthly fee) |
| Hosting (run on your laptop) | $0 |
Keep costs low by: analyzing each ticker once per day (not per minute), using Claude Haiku for the analyst agents, and only using a more powerful model for the final Trader and Risk Manager decisions.
---
## US Regulatory Note
For personal automated trading in a US brokerage account, you operate under standard retail trading rules. If you make more than 3 day trades in a 5-day rolling window with under $25,000 in the account, you'll trigger **Pattern Day Trader** rules. Since TradeDog is a swing trading system (holding for days to weeks), this typically isn't an issue — but keep it in mind when sizing up.