29 KiB
PHASES.md
Complete roadmap for market-screener evolution from Phase 9 through Phase 16+.
Phase 9 — Subdomain Restructure: Server Layer Organization
Goal: Reorganize server/ from flat layer-based structure to domain-driven structure.
Timeline: 3 weeks.
9a — Create shared infrastructure layer
Create server/domains/shared/ hierarchy:
server/domains/shared/
├── entities/ (models + their types together)
│ ├── Asset.ts
│ ├── Stock.ts
│ ├── Etf.ts
│ ├── Bond.ts
│ └── index.ts
├── adapters/ (external API wrappers, renamed from "clients")
│ ├── YahooFinanceAdapter.ts
│ ├── AnthropicAdapter.ts
│ ├── SimpleFINAdapter.ts
│ └── index.ts
├── services/ (cross-domain services)
│ ├── BenchmarkProvider.ts
│ ├── CatalystAnalyst.ts
│ ├── LLMAnalyst.ts
│ └── index.ts
├── scoring/ (rules + regime management)
│ ├── ScoringConfig.ts
│ ├── GateValidator.ts
│ ├── MarketRegime.ts
│ └── index.ts
├── persistence/ (SQLite stores)
│ ├── MarketCallStore.ts
│ ├── PortfolioStore.ts
│ └── index.ts
├── types/ (all domain types)
│ ├── asset.model.ts
│ ├── finance.model.ts
│ ├── market.model.ts
│ ├── [...other models]
│ └── index.ts
├── config/
│ └── constants.ts
├── utils/
│ ├── logger.ts
│ ├── Chunker.ts
│ └── index.ts
├── db/
│ └── index.ts
├── schemas.ts
└── index.ts
9b — Extract screener domain
server/domains/screener/
├── ScreenerController.ts
├── ScreenerEngine.ts
├── PersonalFinanceAnalyzer.ts
├── scorers/
│ ├── StockScorer.ts
│ ├── EtfScorer.ts
│ ├── BondScorer.ts
│ └── index.ts
├── transform/
│ ├── DataMapper.ts
│ ├── RuleMerger.ts
│ └── index.ts
└── index.ts
9c — Extract portfolio domain
server/domains/portfolio/
├── PortfolioController.ts
├── PortfolioAdvisor.ts
├── persistence/
│ └── PortfolioStore.ts
└── index.ts
9d — Extract calls domain
server/domains/calls/
├── CallsController.ts
├── CalendarService.ts
├── persistence/
│ └── MarketCallStore.ts
└── index.ts
9e — Extract finance domain
server/domains/finance/
├── FinanceController.ts
└── index.ts
9f — Clean up old directories
Remove: server/controllers/, server/services/, server/repositories/, server/clients/, server/models/, server/scorers/, server/config/, server/types/, server/utils/
9g — Update documentation in CLAUDE.md
Update "Server layer map" section with new domain structure.
9h — Smoke test all routes
Create integration smoke test verifying all major routes work after restructure.
Phase 10 — UI Component Restructure & Clarity
Goal: Mirror Phase 9 server restructure at UI layer. Organize components by domain.
Timeline: 1 week.
10a — Create component hierarchy
ui/src/lib/components/
├── shared/
│ ├── Spinner.svelte
│ ├── VerdictPill.svelte
│ ├── SignalBadge.svelte
│ └── index.ts
├── screener/
│ ├── AssetTable.svelte
│ ├── AnalysisSidebar.svelte
│ └── index.ts
├── portfolio/
│ ├── AddHoldingForm.svelte
│ ├── AdviceTable.svelte
│ └── index.ts
└── calls/
├── CallForm.svelte
├── CallCard.svelte
└── index.ts
10b — Split utils and types
lib/utils/
├── formatting.ts
├── sorting.ts
├── verdicts.ts
└── index.ts
lib/types/
├── ui.types.ts
├── portfolio.types.ts
└── index.ts
10c — Update all imports in routes + stores
10d — Extract reusable layout components
10e — UI Phase 10 complete
Phase 10.5 — Professional-Grade Screener UI (Institutional Research Tool)
Goal: Build professional screener interface showing complete investment research capabilities.
Timeline: 4-6 weeks (after Phase 10).
10.5a — Three-Layer Layout
Sidebar (280px) | Main Table (flex) | Tearsheet Panel (420px)
────────────────┼──────────────────┼──────────────────────
Advanced │ Compact table │ Forensic detail
filters │ 10 columns only │ Full metrics
(left) │ │ Peer comparison
│ Scannable │ Decision framework
Quick presets │ minimal │ Risk breakdown
│ │ (right side-panel)
10.5b — Sidebar: Advanced Filtering
- Preset buttons: All, Strong Buy, Buy, Hold, Avoid
- Custom filters: P/E Range, ROE Min, Dip %, D/E Max
- Quick presets: "Value Trap Screen", "Growth at Fair Price", "Dip Opportunity"
10.5c — Main Table: Minimal, Scannable
10 columns: Ticker | Price | Verdict | Score | P/E | ROE | 52W | DCF | Flags | Menu
- Sortable, sticky header
- Monospace numbers (professional)
- Color-coded metrics
- Click row → opens tearsheet
10.5d — Tearsheet Panel: Professional Research
Right-side slide-in (420px) with sections:
- Core Metrics (4-grid, color-coded cards)
- Valuation Context (comparison table)
- Decision Framework (gate-by-gate breakdown)
- Risk Breakdown (ranked, quantified)
- Threshold Sensitivity (what-if scenarios)
- Peer Comparison
- CTA Row (Add to Watchlist, Decision Log)
10.5e — Decision Logging & Backtest
- Save thesis + entry date/price
- Track 30/60/90 day outcomes
- Simple review modal ("did thesis play out?")
- Backtest dashboard (win rate by signal type)
10.5f — Implementation (Phased)
- Week 1-2: Core UI (sidebar, table, tearsheet basic)
- Week 2-3: Tearsheet sections (all 7 sections)
- Week 3-4: Interactivity (sorting, filters, animation)
- Week 4-5: Decision logging
- Week 5-6: Backtest dashboard (optional)
Phase 10.6 — Portfolio Integration: Market Analysis → Action
Goal: Connect screener signals + market context to portfolio decisions.
10.6a — Market-Aware Position Sizing
Auto-calculate recommended position size based on:
- Stock verdict
- Market regime
- Sector momentum
- Portfolio allocation
Display: "Recommended: 2-4% of portfolio" or "$2,000-$4,000"
10.6b — Portfolio Dashboard: Integrated View
Single screen showing:
- Holdings + P&L
- Allocation vs Target
- Market Context
- Screener Signals
- Recommended Action
10.6c — Screener-Portfolio Bridge
Add "Your Holdings" column in screener showing:
- "You own 2% | +$1,000 gain"
- Verdict changes
- Thesis change alerts
10.6d — Thesis Journal (Simplified)
When adding position:
- Why I'm buying (pick ONE reason)
- What I'll watch (pick 1-2 metrics)
- Review date (auto 30 days)
10.6e — Rebalancing Advisor
Monitor allocation vs target. When screener verdict changes on existing holding, suggest action.
Phase 10.7 — Newbie UX: Progressive Disclosure
Goal: Professional tool with newbie-friendly interface. Same power, different experience.
10.7a — Screener Entry: Strategy-Based
Instead of filters, ask: "What are you looking for?"
Options:
- ○ Solid companies at good prices (Balanced)
- ○ Hot stocks with momentum (Momentum)
- ○ Beaten-down bargains (Value)
- ○ Let me customize filters (Advanced)
10.7b — Table View: Plain Language Explanations
Minimal table: Ticker | Price | Verdict | Why? ℹ️
Clicking "ℹ️" shows plain-language explanation with reasons, scores, and what it means.
10.7c — Buy Decision Helper
Calculate recommended position size automatically. Show:
- Star rating (intuitive)
- Concrete dollars (not abstract %)
- Clear "safe" path highlighted
10.7d — Portfolio Status View (Not Analysis)
Show status + guidance, not complex metrics:
- Visual breakdown (bars)
- What it means
- Concrete actions (sell, buy, do nothing)
10.7e — Market Context: Status Light + Impact
Use traffic light system:
- 🟢 Good / ⚠️ Mixed / 🔴 Bad
- Plain explanation of why
- Impact on YOUR portfolio
10.7f — Thesis Logging: Simple Checklist
Pick ONE reason + 1-2 metrics to watch. Built-in review schedule.
10.7g — After Buying: 30-Day Check-In
Auto-reminder after 30 days showing:
- How metrics moved vs prediction
- Thesis status (working / shaken / broken)
- Next action
10.7h — Newbie Mode vs Pro Mode (Toggle)
Newbie Mode: Simplified screener, plain language, auto position sizing, status lights, guided workflows
Pro Mode: Full filter control, all metrics, raw data, advanced analysis, complete transparency
Phase 10.8 — Earnings Calendar: Context, Not Destination
Goal: Integrate earnings data contextually, NOT as standalone tab.
10.8a — Earnings in Screener Tearsheet (Primary)
UPCOMING EVENTS:
├── Earnings: July 30, 2026 (18 days away)
│ ├── EPS estimate: $6.50
│ ├── Historical beat rate: 65%
│ ├── Avg price move on earnings: +3% (beat), -2% (miss)
│ └── Timing decision: "Buy now before earnings?" or "Wait?"
│
├── Ex-dividend: June 15 (6 days away)
│ └── Dividend: $0.24/share
│
└── Analyst call: Post-earnings July 30
10.8b — Earnings in Portfolio (Secondary)
Portfolio holdings view shows upcoming events for YOUR positions with thesis-specific tracking.
10.8c — Earnings Discovery Widget (Optional, Tertiary)
Light calendar feature in screener header (NOT main nav):
📅 25 earnings this week in your screened results
└── [View by day] [View by verdict]
10.8d — What NOT to Build
❌ Standalone "Calendar" nav tab — creates bloat, out-of-context data, redundant.
10.8e — Earnings in Thesis Journal
Earnings become key tracking metric when user logs thesis.
10.8f — Design Note: Revisit Earnings Display Format
⚠️ DESIGN REVIEW NEEDED:
Consider consistency across three locations, visual hierarchy differences, and mobile responsiveness before finalizing visual design.
Phase 10.9 — Strong Buys: Professional Dip Opportunity Monitor
Goal: Flag quality stocks when they drop 5%+ from 52W high, with market analysis of why.
10.9a — Data Structure
| Field | Source | Purpose |
|---|---|---|
| Ticker | Yahoo Finance | Stock identifier |
| Current Price | Yahoo Finance daily fetch | Entry price today |
| 52W High | Yahoo Finance | Reference for dip % |
| Dip % | Calculated | Triggers display if ≥5% |
| Screener Verdict | ScreenerEngine | Quality ranking |
| Dip Reason | Market Analysis | Macro vs company issue |
| Market Context | Daily fetched | Why dropped? Temporary? |
| Your Play | LLM analysis | Buy dip or wait? |
| Recommended Action | Position sizing | "Add 2-4% to portfolio" |
10.9b — Fetching Mechanism (Daily)
- Get "Too Big to Fail" universe (~150 stocks: mega-cap + large-cap + watchlist)
- Fetch prices + 52W high (one Yahoo batch call)
- Filter dips ≥5% from 52W high
- Run screener on dipped stocks
- Analyze why dipped (macro vs company)
- Combine + cache (TTL 24 hours)
- API serves from cache
10.9c — UI: Tabular Display of Dip Opportunities
| Ticker | Price | Dip % | Verdict | Why It Dipped | Your Play | Action |
|---|---|---|---|---|---|---|
| AAPL | $189.50 | -9.76% | Strong Buy (8.2) | Fed rates high (macro, not company) | Buy dip. iPhone intact. | [+2-4%] |
| JPM | $215.30 | -7.2% | Strong Buy (7.8) | Sector rotation (capital away) | Defensive play. Undervalued. | [+3%] |
- Sortable by: Dip %, Verdict, Your Play
- Click row → full tearsheet
- Daily refresh
- Threshold configurable: 5% (default) → 10% → 15%
10.9d — Configuration (User Control)
Settings > Strong Buys Monitor:
Stock Universe:
☑ Mega-cap (10)
☑ Large-cap (50)
☑ My Watchlist (custom)
Dip Threshold:
○ 5% (Aggressive)
○ 10% (Balanced)
○ 15% (Conservative)
Update Frequency:
○ Daily morning (9:30 AM)
● Daily EOD (4:00 PM)
10.9e — Design Note: Revisit Tabular Format
⚠️ DESIGN REVIEW NEEDED:
Consider:
- Card-based alternative (cleaner, easier scan) vs current compact table
- Hybrid approach (desktop table + mobile cards)
Recommendation: Implement Phase 10.9a, gather user feedback, adjust design.
Phase 10.5j — Comprehensive Free Data Stack (Zero Cost, Zero Redundancy)
Philosophy: Professional-grade screener using only FREE sources. No $99-$200/mo subscriptions. Each source has ONE clear job (no duplication).
Data Sources
| Source | Cost | Job | Why |
|---|---|---|---|
| Yahoo Finance (YahooFinanceClient) | $0 | Core metrics (P/E, ROE, FCF, D/E, analyst ratings) | Already integrated. No alternatives needed. |
| yfinance | $0 | Per-ticker enrichment (news, earnings dates, dividends) | Wraps Yahoo, optimized for news extraction. |
| Finnhub FREE | $0 | Earnings calendar + estimates only | Reliable future events (3-month lookahead). |
| Alpha Vantage FREE | $0 | Market context + sentiment (macro-focused) | Sector trends, Fed decisions, keyword search. |
| API Ninjas FREE | $0 | Earnings backup only (redundancy layer) | Fallback if Finnhub hits rate limits. |
| Your LLM (Claude) | ~$50/mo | Intelligence layer (turns data into insights) | Sentiment analysis, decision framework, thesis validation. |
Total: ~$50/mo (just LLM), vs $300-400/mo for Bloomberg/FactSet.
Data Flow in Tearsheet
- User screens stocks → ScreenerEngine uses YahooFinanceClient
- Metrics cached in memory/state (no extra calls)
- User clicks row → Tearsheet opens
- Fetch per-ticker enrichment on-demand (yfinance, Finnhub, Alpha Vantage — parallel)
- Process with LLM (if enabled) for sentiment + decision framework
- Display complete tearsheet
Integration Timeline
- Week 1: Add yfinance news enrichment
- Week 2: Add Finnhub earnings calendar
- Week 3: Add Alpha Vantage market context
- Week 4: Add API Ninjas as backup
- Week 5: Wire everything into tearsheet
- Week 6: Add LLM enrichment (optional)
Why This Approach
✅ Zero Cost: $0/month (all sources FREE) ✅ Zero Redundancy: Each source has ONE job, no overlap ✅ Professional Grade: Layered sources like institutional traders use ✅ Reliability: Redundancy where it matters (earnings calendar via Finnhub + API Ninjas backup) ✅ Intelligent: Your LLM adds 10x value without additional data cost
Rate Limits & Sustainability
- Yahoo Finance: No official limits (proven in production)
- yfinance: No limits (wraps Yahoo)
- Finnhub FREE: 60 calls/minute (sufficient for 250 stocks)
- Alpha Vantage FREE: 5 calls/minute (one daily call, easily manageable)
- API Ninjas: 100 calls/month (backup only, minimal usage)
Phase 11 — Day Trading: Authentication & Authorization
Goal: Add multi-user support with JWT auth, role-based access control, and portfolio isolation.
Timeline: 2-3 weeks.
Why Auth is First
Can't test multi-user portfolios, public + private access, Discord notifications with user context, or trade journal attribution without auth.
11a — Create auth domain
server/domains/auth/
├── AuthController.ts
├── AuthService.ts
├── JWTStrategy.ts
├── RBACGuard.ts
├── persistence/
│ └── UserStore.ts
└── types/
└── auth.model.ts
11b — Database schema changes
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT DEFAULT 'viewer' CHECK (role IN ('trader', 'viewer', 'admin')),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login DATETIME
);
ALTER TABLE holdings ADD COLUMN user_id TEXT NOT NULL REFERENCES users(id);
ALTER TABLE market_calls ADD COLUMN created_by TEXT REFERENCES users(id);
11c — Middleware + route protection
Apply RBACGuard to protected routes. JWT secret from env var.
11d — UI auth layer
Add routes/auth/login/ and routes/auth/register/.
Create lib/stores/auth.store.svelte.ts for currentUser, JWT, login/logout.
Phase 12 — Day Trading: News Webhooks
Goal: Ingest real-time market news via Polygon.io webhooks.
Timeline: 2-3 weeks.
Why Webhooks Come Second
News feeds everything downstream: Safe Buys monitor, LLM analysis, price dips.
12a — Create news domain
server/domains/news/
├── NewsController.ts
├── WebhookHandler.ts
├── NewsStore.ts
├── NewsQueue.ts (BullMQ worker)
├── persistence/
│ └── NewsArticleStore.ts
└── types/
└── news.model.ts
12b — Database schema
CREATE TABLE news_articles (
id TEXT PRIMARY KEY,
ticker TEXT NOT NULL,
headline TEXT NOT NULL,
body TEXT,
source TEXT,
url TEXT,
sentiment TEXT,
published_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_news_ticker_date ON news_articles(ticker, published_at DESC);
12c — Set up Polygon.io webhook
- Subscribe to Polygon news API (~$200/mo)
- Register webhook:
https://yourapp.com/webhooks/news - Validate signature (Polygon sends HMAC)
- Queue article for async processing
12d — Async processing with BullMQ
Queue processes articles:
- Store in DB
- Trigger LLM analysis if key tickers mentioned
- Notify subscribers (Discord, etc)
Phase 13 — Day Trading: Prompt Caching & LLM Optimization
Goal: Reduce LLM costs by 90% using Anthropic prompt caching.
Timeline: 2-3 weeks.
13a — Create llm domain
server/domains/llm/
├── LLMRouter.ts
├── PromptCache.ts
├── LLMAnalyst.ts (refactored)
├── persistence/
│ ├── AnalysisStore.ts
│ └── CacheStore.ts
└── types/
└── llm.model.ts
13b — Database schema
CREATE TABLE llm_analysis (
id TEXT PRIMARY KEY,
ticker TEXT NOT NULL,
analysis_result TEXT NOT NULL,
model_used TEXT DEFAULT 'claude-opus',
tokens_used INTEGER,
cache_hit BOOLEAN DEFAULT false,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
13c — Implement Anthropic prompt caching
Add cache_control: { type: 'ephemeral' } to system prompt message block.
Use anthropic-beta: prompt-caching-2024-07-31 header.
13d — LLM Router for cost optimization
Route to cheaper models (Sonnet) when cost-sensitive. Fallback to OpenAI if rate-limited.
Phase 14 — Day Trading: Safe Buys Monitor with Discord Alerts
Goal: Monitor safe-buy stocks in real-time, detect 5%+ dips, notify via Discord.
Timeline: 3-4 weeks.
14a — Create trading domain
server/domains/trading/
├── TradingController.ts
├── DipDetector.ts
├── PriceMonitor.ts
├── DiscordNotifier.ts
├── persistence/
│ ├── PriceSnapshotStore.ts
│ └── TradeSignalStore.ts
└── types/
└── trading.model.ts
14b — Database schema
CREATE TABLE price_snapshots (
id TEXT PRIMARY KEY,
ticker TEXT NOT NULL,
price REAL NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
source TEXT,
dip_detected BOOLEAN DEFAULT false
);
CREATE TABLE trading_signals (
id TEXT PRIMARY KEY,
ticker TEXT NOT NULL,
signal_type TEXT CHECK (signal_type IN ('strong_buy', 'dip', 'warning')),
entry_price REAL,
detected_at DATETIME DEFAULT CURRENT_TIMESTAMP,
notified BOOLEAN DEFAULT false,
outcome TEXT
);
14c — Real-time price polling
Check watched tickers every 5 seconds. Filter dips ≥5%. Process via DipDetector.
14d — Discord notifications
Send rich embeds with:
- 🔴 5% Dip Detected: TICKER
- Price fell from $X to $Y (-%Z)
- LLM sentiment + recommendation
- Risks
Phase 15 — Day Trading: Trade Journal & Performance Tracking
Goal: Log every decision, track outcomes, measure strategy performance.
Timeline: 1-2 weeks.
15a — Database schema
CREATE TABLE trade_journal (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id),
ticker TEXT NOT NULL,
signal TEXT,
entry_price REAL NOT NULL,
entry_date DATETIME DEFAULT CURRENT_TIMESTAMP,
exit_price REAL,
exit_date DATETIME,
outcome TEXT CHECK (outcome IN ('win', 'loss', 'pending')),
pnl REAL,
reason TEXT,
notes TEXT
);
CREATE INDEX idx_journal_user ON trade_journal(user_id, entry_date DESC);
15b — Trade stats dashboard
Compute daily aggregates:
- Total trades, wins, losses
- Win rate, total P&L
- Average win/loss, best/worst signal
15c — UI: Trade Stats Dashboard
Display stats + trade history with filtering.
Phase 16 — Multi-LLM Support (Optional)
Goal: Support Claude, OpenAI, optionally Llama for cost optimization.
Timeline: 2-3 weeks (do after Phase 14).
Minimal implementation
const MODELS = {
'claude-opus': { cost: 0.015, speed: 'slow', quality: 'best' },
'claude-sonnet': { cost: 0.003, speed: 'fast', quality: 'good' },
'gpt-4': { cost: 0.03, speed: 'medium', quality: 'excellent' },
};
async analyze(ticker: string, preferredModel?: string) {
const model = preferredModel || 'claude-sonnet';
return await routers[model].analyze(ticker);
}
Production Readiness Checklist
Before going live:
- Environment variables locked down (.env.production, no secrets in code)
- Database: Migrate SQLite → Postgres if expect >10 concurrent users
- Job Queue: Set up BullMQ with Redis
- Logging: Add structured logging (Winston, Pino) to track LLM calls + costs
- Rate Limiting: Enabled on all public endpoints (@fastify/rate-limit)
- Discord Webhook: Test alerts with real market data
- Auth: JWT secret rotated, session timeout 1h
- SSL/TLS: HTTPS enforced
- Monitoring: Alerts for job backlog, API latency, cache hit rate, webhook failures
- Alpaca price feed staleness: Should be <5s
Cost estimation (steady state):
| Service | Cost | Notes |
|---|---|---|
| Polygon.io (real-time news + quotes) | $200 | Required for webhooks |
| Anthropic Claude API (w/ prompt caching) | $50–100 | Most cached; 90% cost reduction |
| OpenAI API (fallback, optional) | $50 | Only if GPT-4 fallback added |
| Alpaca/Interactive Brokers | $30–100 | Depends on which feed |
| BullMQ (Redis queue, if scaled) | $0–30 | Free if self-hosted |
| Total | ~$330–450/month | Scales well (no per-user seat cost) |
Final Architecture Summary
| Layer | Tech | Status |
|---|---|---|
| Auth | JWT + RBAC | Phase 11 (weeks 1-2) |
| Data | SQLite → Postgres if 1000+ users | Phase 11 |
| News | Polygon.io webhooks | Phase 12 (weeks 3-4) |
| LLM | Anthropic + OpenAI w/ prompt caching | Phase 13-14 (weeks 5-6) |
| Trading | Real-time price monitoring + Discord | Phase 14 (weeks 7-10) |
| Tracking | Trade journal + stats | Phase 15 (weeks 11-12) |
| UI | Svelte 5 + Phase 10 structure | Phase 10 (weeks 1-5 parallel) |
Total time to "trading ready": 12-16 weeks solo, 8 weeks with 1-2 junior devs.
Go-live target: Q3 2026 (July–September).
Postgres Migration Path (When Needed)
If you grow to 10+ active traders:
- Create Postgres RDS instance (AWS: ~$15/mo, db.t3.micro)
- Update connection string to point to Postgres
- Run schema dump SQLite → Postgres
- Test on staging first
- Blue-green deploy: run both DBs in parallel for 1 day, switch, keep SQLite as backup
Time: 2–4 hours. No code changes needed.
Frequently Asked Questions
Q: How many traders can this system handle?
A:
- 10–50 traders: Single instance. Costs ~$450/mo.
- 50–500 traders: Add Postgres + Redis queue. Costs ~$1000/mo.
- 500+ traders: Add Kubernetes + load balancing. Costs ~$5000+/mo.
Q: What if Polygon.io goes down?
A: Have fallback plan:
- Switch to Finnhub webhooks (similar API, different provider)
- Or fall back to polling (5s instead of real-time, less expensive)
- Add circuit breaker: if Polygon fails for >5 min, automatically switch
Q: Can I trade with real money?
A: Yes, but:
- Start with paper trading (Alpaca's paper account, no real money)
- Test for 2+ weeks on real market conditions
- Once you hit 55%+ win rate on paper, go live with small position sizes
- Scale up gradually (1% → 5% → 10%)
- Always have manual kill-switch
Q: Should I use local LLM training?
A: Not yet. Only consider if:
- You have 6+ months of clean trade data
- Your LLM bill is >$1000/mo
- You have $20K+ to spend on GPU infrastructure
For now, optimize prompts instead. Good prompt beats fine-tuned model.
Future Enhancements (Unscheduled)
FE-1 — Pinned Stocks Watchlist
Concept: User can pin any stock from the screener table. Pinned stocks appear in a persistent sidebar or dedicated panel showing:
- Minimal summary: ticker, current price, signal badge, score
- Price-since-pin sparkline — a small inline chart showing how price moved from the day the stock was pinned to today
- Quick unpin button
Data requirements:
- Store
{ ticker, pinnedAt, pinnedPrice }in SQLite (pinned_stockstable) - Fetch daily OHLC history from Yahoo Finance for the period
pinnedAt → nowto power the sparkline - API:
GET /api/pins(list),POST /api/pins(add),DELETE /api/pins/:ticker(remove),GET /api/pins/:ticker/history(OHLC since pin)
UI notes:
- Pin button (📌) appears on hover of each summary row in the screener table
- Pinned panel can live in a collapsible drawer at the bottom, or a fixed right sidebar
- Sparkline: use a lightweight SVG path (no charting library needed); green if price above pin price, red if below
- On click of the sparkline, open a larger chart modal (Phase FE-1b — can use TradingView widget or Chart.js)
Why deferred: Requires persistent per-user state (needs Phase 11 auth to be meaningful across sessions). Build after Phase 11.
FE-2 — Column Header Tooltips ("Why does this matter?")
Concept: Clicking a column header in the screener summary row opens a small popover explaining:
- What the metric measures
- What a good vs bad value looks like
- How the screener uses it in scoring
This turns the table into a learning tool — users understand why P/E or ROE matters, not just what the number is.
Suggested content per column:
| Header | What to explain |
|---|---|
| Score | Weighted sum of all factor scores. >6 = quality, <4 = weak. Gates must pass first — score only fires if gates are cleared. |
| Signal | Compares two scoring lenses (Mkt-Adjusted vs Graham). Strong Buy = passes both. Momentum = passes inflated only. |
| P/E | Price-to-earnings. Lower = cheaper relative to earnings. Gate: <15x (Graham) or <SPY×1.5 (market-adjusted). >30x warrants scrutiny unless high growth. |
| PEG | P/E ÷ growth rate. Normalises valuation for growth. <1.0 = paying less than growth justifies. Lynch's standard. |
| ROE% | Return on equity — how efficiently the company uses shareholder money. >15% is healthy; >30% is exceptional. Weighted 3× in scoring. |
| OpMgn% | Operating margin — profit per dollar of revenue before interest and tax. Measures business efficiency. |
| FCF% | Free cash flow yield — cash the business actually generates relative to price. Negative = cash-burning; gate fails. |
| D/E | Debt-to-equity. Measures leverage. Gate: <1.5× (general), <2.0× (tech). Higher than 2× raises distress risk. |
| 52W Chg | Total price return over last 52 weeks. Positive momentum is healthy; >+50% may signal overextension. |
| From High | % below the 52-week high. -5% to -15% is a typical dip zone; ≤-30% triggers a risk flag. |
| Analyst | Yahoo consensus (1=Strong Buy, 5=Strong Sell). Requires ≥3 analysts to fire. ≤2.0 adds points; >4.0 subtracts. |
| DCF Safety | Margin of safety from a two-stage DCF model. Positive = stock appears undervalued vs intrinsic value. Only fires when FCF > 0. |
| Cap | Market cap tier: Mega (>$200B), Large ($10B+), Mid ($2B+), Small ($300M+), Micro (<$300M). Smaller = higher risk + volatility. |
| Style | Growth classification from revenue + earnings growth rate. High Growth = ≥15% revenue growth. Value = low growth + ≥3% yield. |
Implementation approach:
- Add
data-tipattribute to each<th>inAssetTable.svelte - On click, show a positioned
<div class="col-tip">anchored to the header - Dismiss on outside click or Escape
- No library needed — pure Svelte
$state+ CSS positioning - Mobile: tip opens as a bottom sheet modal
Why not title attribute? title tooltips are unstyled, non-interactive, and don't work on touch. A custom popover lets you format the content properly and include a "Good range" callout.
Why deferred: Nice-to-have educational feature. Build after the core screener UI (Phase 10.5) is stable.