Back to work

Autonomous Trading Agent

An AI system that makes financial decisions — scanning markets, executing trades, and managing risk without human intervention

AI AgentsPythonFinanceRisk ManagementMachine Learning

At a Glance

Role
Architecture & Engineering
Timeline
2 months
Tech Stack
PythonAlpaca APIXGBoostNext.jsRechartsTailwind CSS

The Problem

Intraday trading requires constant attention. You need to scan dozens of stocks for setups, calculate entry points and position sizes, place orders with protective stops, monitor positions for exit signals, and close everything before market close. Every 15 minutes, all day.

Humans are bad at this. We get emotional, override our own rules, miss exits, and revenge-trade after losses. The mechanics of intraday trading are almost entirely systematic — which makes them a natural fit for automation.

The question isn't whether to automate. It's how to build a system that makes real financial decisions reliably, with guardrails that prevent catastrophic outcomes.

What We Built

An autonomous trading agent that scans the S&P 500 for momentum and mean-reversion setups, executes trades through Alpaca's API, manages positions with ATR-based stops, and closes everything before market close. It runs on a cron schedule during market hours with zero human intervention required.

The Scanner

Every 15 minutes during market hours, the scanner evaluates 40 liquid S&P 500 stocks:

Technical indicators calculated per stock:

  • RSI (14-period) for overbought/oversold detection
  • MACD (12/26/9) for momentum direction
  • Bollinger Bands (20-period, 2σ) for mean-reversion setups
  • ATR (14-period) for volatility-based sizing and stops
  • Volume analysis for confirmation

Two signal types:

Momentum — stock breaking out with volume confirmation, MACD histogram positive and expanding, RSI between 40-70 (not overbought). The agent is buying strength with the expectation of continuation.

Mean reversion — stock trading below the lower Bollinger Band or at RSI extremes (under 30), with the expectation of a snap-back to the mean. Higher risk, higher reward, shorter hold times.

Each signal goes through an ML filter before execution.

The ML Model

An XGBoost classifier trained on historical trade outcomes. It takes the scanner's feature vector (RSI, MACD, Bollinger position, volume ratio, ATR, regime) and predicts whether the setup will be profitable.

Signals below a 60% confidence threshold get rejected. In practice, most executed trades show 70-85% ML confidence — the model is conservative by design.

The model was trained on historical data and periodically retrained as the trade log grows. It's not predicting price direction. It's predicting whether this specific type of setup, in this specific regime, tends to work.

Risk Management

This is the most important part of the system. Every other component exists to find and execute trades. Risk management exists to survive.

Hard limits (never overridden):

RuleLimitWhy
Equity cap$5,000Limits total capital at risk regardless of account size ($87K paper account)
Max positions5Prevents overexposure
Risk per trade2%Standard risk management — max $100 risk per trade
Daily loss limit5%Halts all trading if daily losses exceed $250
Max drawdown15%Circuit breaker — shuts down if peak-to-trough exceeds $750
Sector limit2 per sectorPrevents concentration in one industry

ATR-based position management:

Every position gets a bracket order at entry with three price levels calculated from ATR:

  • Stop loss at 1.0× ATR below entry — the "I was wrong" exit
  • Take profit at 2.0× ATR above entry — the natural target (2:1 reward-to-risk)
  • Breakeven stop triggered at 1.0× ATR profit — eliminates downside after initial move
  • Trailing stop starts at 1.5× ATR profit, trails at 0.75× ATR distance — locks in gains on runners

The position manager checks every position against these levels every scan cycle. Stops only move up, never down.

Market Regime Detection

Not all market conditions are equal. The agent detects three regimes:

  • Trending — SMA slope positive, volatility normal. Full position sizes, favor momentum signals.
  • Choppy — No clear direction. Mixed signals. Slightly reduced position sizes.
  • High volatility — ATR exceeds the 75th percentile of its 20-day range. Half position sizes, wider stops, fewer trades.

The regime detector uses a 20-period SMA slope and volatility lookback to classify current conditions. Position sizing and stop placement adjust automatically.

End-of-Day Close

At 3:55 PM ET, the agent force-closes all open positions. No overnight risk. This is the strictest rule in the system — it runs regardless of whether positions are profitable.

The force-close process:

  1. Cancel all pending bracket orders (stop/take-profit legs)
  2. Submit market sell orders for each position
  3. Log all exits with final P&L
  4. Reset daily state for next session

A bug in the bracket order system taught us this lesson the hard way: pending bracket legs hold shares, so you can't sell until you cancel them first. Simple in hindsight, but it caused a 4-hour loop on day 1 before we identified it.

The Dashboard

An autonomous system making financial decisions needs visibility. We built a real-time monitoring dashboard.

Design

Linear/Stripe aesthetic — pure zinc dark mode, glass cards with backdrop blur, monospace numbers throughout. Financial data demands precision, so every number uses tabular-nums for alignment and the Geist Mono typeface.

Pages

Overview — four stat cards (equity, daily P&L, win rate, active positions), equity curve chart tracking performance over time, live positions table with unrealized P&L, and recent trades feed.

Trades — full history of every trade with sortable columns, filtering by symbol and signal type, running P&L tracking, and summary statistics (win rate, average win/loss, profit factor, largest win/loss).

Risk — real-time view of risk limit utilization. Progress bars show how close the agent is to each hard limit. Circuit breaker status, current market regime with description, and sector allocation breakdown.

Scanner — the 40-stock universe displayed as a grid. Each stock shows its current signal (if any), ML confidence, and key technicals. Green border for momentum signals, blue for mean reversion.

Settings — read-only display of all configuration parameters, parsed directly from the Python config file.

Technical Implementation

Next.js app on localhost:3334. API routes proxy to Alpaca for live account/position/order data and read the trading bot's local state files for configuration and trade history. No database — the dashboard is a real-time lens over the bot's existing data.

Results

Three days of paper trading data:

  • 36 trades executed across 3 trading days
  • Stocks traded: AAPL, AMZN, BAC, CVX, GS, HD, INTC, JPM, LOW, MA, MRK, MSFT, MU, NKE, PFE, QCOM, SHOP, TSLA, UNH, WFC, XOM
  • Signal split: ~60% mean reversion, ~40% momentum
  • ML confidence range: 0.63 - 0.85 (all above 0.60 threshold)
  • Regime: choppy throughout (market uncertainty)
  • Force-close exits: 5 positions closed at EOD on March 4 — all profitable (JPM +$13.90, MU +$14.34, QCOM +$1.65, MA +$2.96, HD +$2.61)
  • Zero circuit breaker triggers — risk system never activated
  • Zero manual interventions — fully autonomous for all 3 days

Bugs Found in Production

Bracket order locking (Day 1): Pending bracket legs (stop-loss and take-profit orders) hold shares. Submitting a separate sell order fails because the shares are "already committed." Fix: cancel bracket legs before submitting close orders.

Force-close timing (Day 2): The force-close routine ran but didn't account for order fill latency. Orders submitted at 3:55 PM sometimes filled after the state was reset. Fix: added a verification loop that confirms all positions are actually flat before resetting state.

Fabricated reporting (Day 3): The AI agent reporting results to Discord fabricated trading data in 4 separate messages — claiming trades and P&L that never happened. This is a context engineering problem, not a trading problem. Fix: hard rule that zero trading data can be reported without a tool call in the same response. Documented as regression R016.

Architecture

The system has four layers, each running independently:

Layer 1: Scanner + Execution Pipeline Cron triggers every 15 minutes during market hours. The scanner evaluates 40 stocks, passes signals through the XGBoost ML filter (greater than 60% confidence required), checks all risk limits (equity cap, sector max, position count), and submits bracket orders to Alpaca with stop-loss and take-profit legs.

Layer 2: Position Management Runs on the same scan cycle. Checks every open position against ATR-based levels. Moves stops to breakeven, starts trailing stops, and handles target/stop exits. Force-closes everything at 3:55 PM ET.

Layer 3: State Management Persists to three files: state.json (positions, daily stats, regime), trades.csv (complete trade log), and scan.log (debug output). No database. State survives restarts.

Layer 4: Dashboard Next.js app reading from Alpaca API and the bot's state files. Five pages showing account status, trade history, risk utilization, scanner signals, and configuration. Real-time data, no write access.

What We Learned

Risk management is the product. The scanner and ML model are interesting, but they're not what keeps the system alive. The risk manager is. Every dollar of the $35.46 net profit on day 3 was protected by bracket orders, ATR stops, and hard equity limits. Without those, the same trades could have easily produced a loss.

Bugs in autonomous systems are different. When a human trader makes a mistake, they notice and correct. When an autonomous agent hits a bug, it loops. The bracket-order locking bug ran for 4 hours before we caught it — the agent kept trying to sell, getting rejected, and trying again. Autonomous systems need circuit breakers at every level, not just for trading.

Report what you can verify. The fabricated reporting incident (R016) was a reminder that AI agents will confidently state things that aren't true. The fix isn't better prompting — it's structural. The system now requires a tool call (actual data fetch) in the same response as any data claim. Architecture beats instructions.

Paper trading first, always. Running on a paper account with a $5,000 virtual cap means every bug is a learning opportunity, not a financial loss. The $87K account balance is irrelevant — the agent only sees $5,000. When the system has enough track record, raising the cap is one line of config.


This system was designed and built by Parallel Studio. We build autonomous AI systems with the risk management and observability required for production deployment. Tell us about your project.