← All projects

AI Stock Trading Tool

An end-to-end Python trading system with rigorous backtesting and safe paper execution. Testing found no strategy beat buy-and-hold, so I stopped the project.

Pythonscikit-learnpandasAlpaca APIBacktesting
Trading system diagram showing market data, a scikit-learn model, backtesting and Alpaca paper trading; no strategy beat buy-and-hold after costs, so the project was stopped

The Problem

Could an automated system use price data to predict stock movements and earn modest, steady profits? I set a clear success criterion: beat holding the same stocks after trading costs, on data the model had not seen. All orders went to a paper account, with no real money at risk.

What I Built

I built a Python data pipeline, a backtesting framework and a working trading bot connected to Alpaca's paper-trading API. I tested seven families of strategy and risk experiments, plus three intraday strategies, using free Yahoo Finance and Alpaca data. The bot was tested with $100,000 of simulated cash. As the research progressed, the aim shifted from building a profitable bot to honestly finding out whether any approach was profitable.

Key Features

  • Time-based train/test splits and yearly walk-forward testing from 2017 to 2026, with next-day execution and gaps where targets overlapped to avoid lookahead
  • Trading costs, buy-and-hold benchmarks, stability checks across both halves of the test period and model accuracy compared with the right baseline
  • Random Forest direction forecasts, cross-sectional gradient boosting across 30 stocks, volatility forecasts, risk targeting and intraday tests
  • Swappable strategy function separated from the order-planning and safety layer; sells planned before buys and every action logged
  • Dry run by default, a STOP.txt kill switch, a hard-coded paper account and checks for blocked accounts and closed markets
  • Monday-only rebalancing, blocked when the account is down 3% on the day or earlier orders remain pending; exits checked on every run that clears safety checks
  • 8% maximum allocation per stock, $5,000 maximum order and no trades below $25
  • 15% stop-loss with a 30-day cooldown before rebuying; take-profits disabled after testing showed they reduced returns
  • Total exposure of 95% for equal weight, or 25–95% when volatility targeting is enabled

Tech Stack

Python
Data pipeline, backtests and trading bot in a Windows virtual environment
yfinance
Free daily stock prices and VIX data from Yahoo Finance
pandas + NumPy
Data analysis and time-series feature engineering
scikit-learn
Random Forest, HistGradientBoosting and Ridge regression
matplotlib
Charts and experiment visualisation
alpaca-py
Paper-account orders and 5-minute market data from the free IEX feed
Claude
Coding and analysis assistance during iterative development

Experiments & Results

Moving-average crossover
A 20/50-day crossover on Apple returned 28.0% over five years versus 107.5% for buy-and-hold, across 33 trades.
Random Forest direction model
On Apple from November 2023 to November 2026, accuracy was 52.4% versus 53.7% for always guessing up. Return was 43.5% versus 83.4% for buy-and-hold across 85 trades.
Cross-sectional stock selection
A top-five weekly portfolio from 30 stocks returned 17.5% a year with a Sharpe ratio of 0.79, versus 19.8% and 1.23 for equal weight. Model AUC was 0.512, close to the 0.50 no-skill baseline.
Stop-losses and take-profits
Across 20 combinations, take-profits reduced returns in every setting. A 15% stop alone returned 17.9% a year versus 18.6% without exits, while reducing the worst drop from −31.7% to −26.5%. I retained only the wide stop as a safety net.
Stock-level volatility forecasting
The ML forecast's rank correlation was 0.663, no better than 0.665 for trailing 60-day volatility. Inverse-volatility weighting returned 16.8% a year versus 19.5% for equal weight, with Sharpe ratios of 1.20 and 1.25.
Portfolio volatility targeting
A 12% target using trailing 20-day volatility returned 14.2% a year versus 18.0% fully invested. The worst drop improved from −31.6% to −17.1%, and Sharpe rose from 1.04 to 1.19: lower risk, not higher returns.
Matched-risk comparison
With cash earning an assumed 2%, volatility targeting returned 14.8% a year versus 13.1% for a constant-exposure twin of the same volatility. A block bootstrap estimated a +1.5 percentage-point annual gap, with a 90% range of −0.78 to +3.70 points and an advantage in 87% of resamples. The timing benefit was modest and not conclusive.
Intraday strategies
Across 10 stocks and 493 trading days on 5-minute bars, opening-range breakout, VWAP mean reversion and late-day momentum averaged −5.3, −5.1 and −6.9 basis points per trade after a 0.06% round-trip cost. All lacked an edge. Buying at the open and selling at the close also lost after costs; before costs it returned 5.5% a year versus 24.3% for holding the same stocks.
Paper-account execution
The first run placed 30 buy orders of $3,166.67 each; a repeat dry run planned no trades. With basket volatility at 16.2% against a 12% target, the bot reduced exposure to 74% and sold about $21,000 across 30 positions. A follow-up dry run again planned no trades.

Challenges & What I Learned

The main lesson was how to test a trading idea honestly. A 52.4% direction accuracy sounds useful until compared with 53.7% from always guessing up. Intraday edges below one basis point disappeared after costs, and predictable volatility did not translate into better returns. Comparing risk targeting with a constant-exposure portfolio of the same volatility helped separate timing benefits from simply holding less stock. Dry runs, hard limits and a kill switch proved more important than the strategy when software controlled an account.

Limitations & Caveats

  • The basket used today's large, successful companies, creating survivorship bias that inflates both strategy and benchmark returns.
  • Free daily data and Alpaca's partial-volume IEX feed limit price quality, VWAP accuracy and fill estimates.
  • Costs were flat estimates, shorting was treated as free and cash earned 0% in most tests; 2% was a rough assumption only in the matched-risk comparison.
  • Ten years of daily data and two years of intraday data cover a limited set of market events. Trying multiple variants on the same data introduces selection bias.
  • Weekly portfolio drawdowns understate falls within a week, and simulated fills are cleaner than real execution.
  • This is an engineering and learning project, not investment advice.

Project Outcome

Completed and stopped after no approach beat buying and holding on return. I closed all paper positions, left the kill switch in place and did not deploy the cloud server. Windows Task Scheduler and a small cloud server were designed but never deployed. The code, data pipeline and backtests remain as a reference; the useful result was that rigorous testing caught plausible strategies that did not hold up, without risking real money.