# 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.