TL;DR
I built a complete swing trading research platform from an empty scaffold to 13,674 lines of Python in a single day. Five phases: data layer and backtesting, fundamentals and sentiment, portfolio construction, ML signals and Monte Carlo, then paper trading with a terminal dashboard. 199 tests across 48 test files. The platform fetches from Yahoo Finance, FRED, SEC EDGAR, and news APIs, runs technical and fundamental analysis, backtests strategies with walk-forward validation, and presents recommendations through a Rich terminal dashboard with human-in-the-loop approval. No cloud dependencies, no subscriptions, no vendor lock-in.
Why Build This
The retail investing tool landscape is bleak. Every platform either charges a monthly subscription for basic technical indicators, locks your data behind their UI, or surveils your portfolio to sell order flow data. I wanted three things:
- Repeatable research – run the same scan tomorrow and get the same answer (given the same data)
- Strategy validation – backtest before I trade, not after
- No recurring cost – the data sources are free (Yahoo Finance, SEC EDGAR, FRED), the compute is my laptop
The alternative was $30/month for TradingView, $50/month for a backtesting platform, and a separate tool for SEC filing research. Over a year, that is $960 for tools that don’t talk to each other and that I can’t customize.
Architecture
The system follows a four-layer architecture:
External Sources Internal Pipeline Output
──────────────── ────────────────── ────────
Yahoo Finance ──┐
FRED ─┤
SEC EDGAR ─┼──▶ Provider ──▶ Normalize ──▶ Cache (parquet)
News RSS ─┤ │
▼
Analysis Engine
(technical, fundamental,
statistical, sentiment)
│
▼
Strategy Engine
(signals, sizing, risk)
│
▼
Human-in-the-Loop
(dashboard, approve/reject,
order tickets)
Every data model uses Pydantic. Every monetary value uses Decimal. Every provider caches to parquet. These constraints were baked into the skill files before a single line of business logic was written, and they held across all five phases.
The Five Phases
Phase 1: Research Foundation
The data layer came first. Yahoo Finance as the primary OHLCV provider with FRED for macro data (10Y yields, VIX, unemployment). A SecurityMaster maps tickers to metadata. The normalizer enforces a canonical OHLCVBar schema so every downstream consumer gets the same shape regardless of source.
Technical indicators wrap pandas_ta: RSI, MACD, Bollinger Bands, ATR, VWAP, Stochastic, OBV. Each function takes a DataFrame and returns it with new columns – stateless and composable.
Two initial strategies: momentum (20/50 SMA crossover with RSI confirmation and ATR-based stops) and mean reversion (Bollinger Band mean reversion with volume confirmation). Both use the same Signal model with entry price, stop loss, take profit, and confidence score.
The backtesting engine runs vectorized event-driven simulation with proper position tracking, commission modeling, and benchmark comparison.
Phase 2: Fundamentals and Sentiment
SEC EDGAR integration for 10-K, 10-Q, and 8-K filings. The parser extracts sections (risk factors, MD&A, financial statements) and stores structured JSON alongside the raw filing. Fundamental analysis computes valuation ratios, growth metrics, and financial health scores.
Sentiment analysis runs a dual pipeline: VADER for fast keyword scoring and an optional FinBERT transformer path for more nuanced analysis. News comes from RSS feeds with configurable source lists.
Insider trading signals from SEC Form 4 filings. The thesis is simple: clusters of insider buys are a stronger signal than any technical indicator.
Phase 3: Composition and Risk
This phase was about making strategies work together. A CompositeStrategy combines multiple signal sources with configurable weights. A StrategyValidator runs sanity checks before any strategy goes live (even in paper mode): minimum Sharpe ratio, maximum drawdown, minimum trade count.
Portfolio construction uses equal-weight, risk-parity, and minimum-variance allocators. Position sizing respects per-position and total portfolio risk limits. A correlation-aware risk manager prevents concentrated sector bets.
Phase 4: Walk-Forward and Monte Carlo
Walk-forward validation splits historical data into training and test windows, optimizes strategy parameters on training, then evaluates on test – sliding forward through time. This catches overfitting that simple train/test splits miss.
Monte Carlo simulation generates thousands of synthetic equity curves by resampling historical returns with replacement. The output is a distribution of outcomes: median terminal wealth, 5th percentile drawdown, probability of loss. If the Monte Carlo says the strategy loses money 40% of the time, it does not get promoted.
An optional ML signal layer uses scikit-learn for regime classification (bull/bear/sideways via rolling volatility and return features). This is deliberately simple – the goal is regime detection, not alpha generation.
Phase 5: Paper Trading and Dashboard
The final phase made it usable. A PaperBroker simulates order execution with configurable slippage and commission models. It tracks positions, fills, and P&L in memory.
The terminal dashboard uses Rich to render four panels: current positions with unrealized P&L, active signals with confidence scores, triggered alerts (price targets, stop losses), and portfolio performance metrics.
Order tickets export to both plain text and JSONL for manual handoff to a real broker. The explicit design choice is human-in-the-loop: the system recommends, the human decides.
An AlertManager watches price levels and triggers notifications when stops or targets are hit. A WatchlistScreener runs the full signal pipeline against a configurable watchlist on demand.
What the Numbers Look Like
| Metric | Count |
|---|---|
| Python source lines | 13,674 |
| Test files | 48 |
| Test functions | 199 |
| Phases completed | 5 |
| PRs merged | 7 |
| External data sources | 4 (Yahoo, FRED, SEC, News) |
| Strategy types | 4 (momentum, mean reversion, composite, ML) |
Design Decisions Worth Noting
Local-first, no database. Everything stores to parquet files and JSON on disk. No PostgreSQL, no SQLite, no DynamoDB. The entire dataset for a portfolio of 50 tickers is under 100MB. Parquet gives columnar compression, fast reads, and plays perfectly with pandas. If I want to back up my research, I rsync a directory.
Decimal for money, float for analytics. Parquet stores float64 for analytical performance (vectorized pandas operations on Decimal are 10x slower). Conversion to Decimal happens at the boundary when values enter monetary calculations – position sizing, P&L, order tickets. This is a deliberate tradeoff documented in the schema design.
Human-in-the-loop, not autonomous. The system generates signals with confidence scores, but a human approves every trade. The paper broker exists for validation, not execution. Order tickets are exported for manual entry into a real brokerage. Full automation is an explicit non-goal.
No real-time data. All data is end-of-day. Swing trading works on daily bars. Real-time data adds complexity (WebSockets, tick handling, market hours logic) for zero benefit at this timeframe.
What I Would Do Differently
The ML signal layer (Phase 4) is the weakest part. Regime detection via rolling volatility thresholds works just as well as the scikit-learn classifier for the timeframes I care about, and it is far more interpretable. I would drop the ML dependency entirely and use simple statistical regime detection.
The SEC filing parser is also overbuilt for swing trading. Reading 10-K risk factors is valuable for deep research, but for a swing trade with a 2-4 week holding period, the fundamental ratios from Yahoo Finance are sufficient. I kept the SEC integration because it is interesting, not because the strategy needs it.
What’s Next
The backlog tracks remaining work: live watchlist monitoring via cron, a Textual TUI replacement for the Rich dashboard, and eventually a k3s deployment for automated daily scans. But the core platform – data ingestion, analysis, backtesting, and paper trading – is complete and usable today.