Advertisement

Most so-called AI trading agents fall into one of two camps: either they are rigid rule-based bots that never adapt, or they are one-off AI sessions that generate a few ideas and then disappear. Neither is what most builders actually want.

A practical AI trading agent should run on a schedule, inspect market conditions, reason through possible actions, place trades through a broker API, and write a clean journal explaining what it did and why. That is the system this guide helps you build.

This setup uses Claude Code for reasoning and workflow execution, and Alpaca for paper trading infrastructure. Alpaca’s paper trading environment simulates live trading behavior while keeping real money out of the loop, which makes it the safest place to begin. The result is a 24/7 agent that handles three jobs automatically: research, trading, and journaling.[docs.alpaca]

Advertisement

What This Agent Actually Does

This agent is built around three recurring responsibilities:

  • Research the market using watchlist symbols, recent price data, and news.
  • Decide whether to buy, sell, or hold based on predefined rules.
  • Write a journal entry that records every action, including days when no trade is placed.

That last part matters more than most people think. A trading agent without a journal is just an automated black box. A trading agent with a structured journal becomes something you can review, debug, and improve over time.

What You Need Before You Start

Before writing any code, put these pieces in place:

Advertisement

  • A Claude Code setup that is already working locally.
  • An Alpaca account with paper trading API credentials.
  • A project folder with a simple, organized structure.
  • A commitment to begin with paper trading only.

Alpaca documents that paper accounts use different API keys from live accounts and run on a separate paper endpoint, while keeping the API structure largely the same for testing and later migration. That means you can build and validate the workflow safely first, then switch environments later without rewriting the whole system.[docs.alpaca]

Your .env file should look like this:

textAPCA_API_KEY_ID=your_paper_key_id
APCA_API_SECRET_KEY=your_paper_secret_key
APCA_BASE_URL=https://paper-api.alpaca.markets

A clean starter folder looks like this:

text/trading-agent
  CLAUDE.md
  watchlist.json
  journal/
  scripts/
    research.py
    trade.py
    notify.py
  .env

Step 1: Define the Agent in CLAUDE.md

The CLAUDE.md file acts as the agent’s operating manual. Claude Code reads these instructions at the start of each session, so this is where you define behavior, risk tolerance, and decision rules.

Use this as a strong starting point:

text# Trading Agent Instructions

You are an autonomous trading agent managing a paper portfolio.

## Core Responsibilities
- Every market day at 9:45 AM ET: Run the research routine
- Every market day at 10:00 AM ET: Evaluate research and place trades
- Every market day at 4:15 PM ET: Write a journal entry covering the day

## Rules You Must Always Follow
- Never invest more than 5% of total portfolio value in a single position
- Never place a market order
- Use limit orders within 0.2% of ask
- If a position drops 8% from entry, close it
- Always write a journal entry, even if no trades happen
- Never place trades when the market is closed

## Decision Framework
Before placing any trade, answer:
1. What is the current cash balance?
2. What positions are already open?
3. What does recent news say about this ticker?
4. What do the 20-day and 50-day moving averages suggest?
5. What is the downside if this trade fails?

## Output Format
Every action must be logged to journal/YYYY-MM-DD.md in structured format.

Specific instructions produce more stable agent behavior. Vague instructions create vague decisions.

Step 2: Build the Research Script

Your agent needs a small helper script that can fetch account data, open positions, historical bars, and recent news before making any decision. Alpaca’s Trading API and paper environment are built for this kind of algorithmic testing flow, including account access and market-related requests.[alpaca]

Create scripts/research.py:

pythonimport os
import requests
import json

ALPACA_KEY = os.getenv("APCA_API_KEY_ID")
ALPACA_SECRET = os.getenv("APCA_API_SECRET_KEY")
BASE_URL = os.getenv("APCA_BASE_URL")

def headers():
    return {
        "APCA-API-KEY-ID": ALPACA_KEY,
        "APCA-API-SECRET-KEY": ALPACA_SECRET,
    }

def get_bars(symbol, timeframe="1Day", limit=60):
    url = f"https://data.alpaca.markets/v2/stocks/{symbol}/bars"
    params = {
        "timeframe": timeframe,
        "limit": limit,
        "adjustment": "raw"
    }
    response = requests.get(url, headers=headers(), params=params)
    return response.json()

def get_account():
    url = f"{BASE_URL}/v2/account"
    response = requests.get(url, headers=headers())
    return response.json()

def get_positions():
    url = f"{BASE_URL}/v2/positions"
    response = requests.get(url, headers=headers())
    return response.json()

def get_news(symbol):
    url = "https://data.alpaca.markets/v1beta1/news"
    params = {
        "symbols": symbol,
        "limit": 5,
        "sort": "desc"
    }
    response = requests.get(url, headers=headers(), params=params)
    return response.json()

if __name__ == "__main__":
    import sys
    action = sys.argv[1] if len(sys.argv) > 1 else "account"
    symbol = sys.argv[2] if len(sys.argv) > 2 else None

    if action == "bars" and symbol:
        print(json.dumps(get_bars(symbol)))
    elif action == "news" and symbol:
        print(json.dumps(get_news(symbol)))
    elif action == "positions":
        print(json.dumps(get_positions()))
    else:
        print(json.dumps(get_account()))

This script gives Claude a practical tool interface. For example:

bashpython scripts/research.py bars AAPL
python scripts/research.py news NVDA
python scripts/research.py positions

Step 3: Build the Trade Execution Script

Next, create a separate file responsible for market status checks and order placement. Keeping execution separate from research makes the whole workflow easier to audit.

Create scripts/trade.py:

pythonimport os
import requests
import json
import sys

ALPACA_KEY = os.getenv("APCA_API_KEY_ID")
ALPACA_SECRET = os.getenv("APCA_API_SECRET_KEY")
BASE_URL = os.getenv("APCA_BASE_URL")

def headers(json_mode=False):
    base = {
        "APCA-API-KEY-ID": ALPACA_KEY,
        "APCA-API-SECRET-KEY": ALPACA_SECRET,
    }
    if json_mode:
        base["Content-Type"] = "application/json"
    return base

def place_order(symbol, qty, side, limit_price):
    order_data = {
        "symbol": symbol,
        "qty": qty,
        "side": side,
        "type": "limit",
        "time_in_force": "day",
        "limit_price": str(limit_price)
    }
    url = f"{BASE_URL}/v2/orders"
    response = requests.post(url, headers=headers(json_mode=True), json=order_data)
    return response.json()

def cancel_all_orders():
    url = f"{BASE_URL}/v2/orders"
    response = requests.delete(url, headers=headers())
    return response.status_code

def get_market_status():
    url = f"{BASE_URL}/v2/clock"
    response = requests.get(url, headers=headers())
    return response.json()

if __name__ == "__main__":
    action = sys.argv[1]

    if action == "status":
        print(json.dumps(get_market_status()))
    elif action == "order":
        symbol = sys.argv[2]
        qty = sys.argv[3]
        side = sys.argv[4]
        limit_price = sys.argv[5]
        print(json.dumps(place_order(symbol, qty, side, limit_price)))
    elif action == "cancel":
        print(cancel_all_orders())

This version intentionally uses limit orders only. That makes the tutorial cleaner and aligns better with basic risk discipline.

Step 4: Set Up the Trade Journal

The journal is the most valuable output in the whole system. It tells you not only what the agent did, but how it reasoned about market context.

A sample journal file might look like this:

text# Trade Journal — 2026-07-08

## Portfolio Status
- Cash: $12,450.00
- Positions: NVDA (10 shares), SPY (8 shares)
- Total Value: $23,891.80

## Market Research
### NVDA
- 20-day MA: $838.50
- 50-day MA: $812.00
- News: Positive momentum remains intact

### AAPL
- 20-day MA: $195.20
- 50-day MA: $198.80
- News: Short-term weakness, no clean setup

## Trades Executed
| Time | Symbol | Action | Qty | Price | Reason |
|------|--------|--------|-----|-------|--------|
| 10:03 | NVDA | BUY | 2 | $847.50 | Trend aligned and risk acceptable |

## Positions Closed
None

## End-of-Day Reflection
Stayed disciplined and avoided forcing weak trades. Need to monitor follow-through tomorrow.

This turns your trading system into a reviewable decision engine instead of a silent automation.

Step 5: Schedule the Agent with Claude Code Routines

This is where the setup becomes truly autonomous. Claude Code routines are designed for scheduled AI tasks that run without constant user supervision, including recurring workflows triggered on a timetable.[mindstudio][youtube]

Create .claude/routines.json:

json{
  "routines": [
    {
      "name": "Morning Research",
      "schedule": "45 9 * * 1-5",
      "timezone": "America/New_York",
      "prompt": "Run the morning research routine. Check market status first. If the market is open, pull bars and news for every symbol in watchlist.json. Summarize findings in a structured format and save them to journal/YYYY-MM-DD.md under the Research section.",
      "allowed_tools": ["bash", "read", "write"]
    },
    {
      "name": "Trading Session",
      "schedule": "0 10 * * 1-5",
      "timezone": "America/New_York",
      "prompt": "Run the trading session. Read today's research from the journal file. Check current positions and cash balance. For each symbol in the watchlist, decide whether to buy, sell, or hold based on the research and your rules in CLAUDE.md. Place limit orders for any decisions. Log all actions and reasoning to the journal.",
      "allowed_tools": ["bash", "read", "write"]
    },
    {
      "name": "End of Day Journal",
      "schedule": "15 16 * * 1-5",
      "timezone": "America/New_York",
      "prompt": "Run the end-of-day routine. Pull final positions and account value. Read all orders placed today. Complete the journal entry with a reflection section, including what worked, what failed, and what to watch tomorrow.",
      "allowed_tools": ["bash", "read", "write"]
    }
  ]
}

This schedule runs only on weekdays, and your trading routine should still verify market status before placing any order.

Step 6: Keep the Watchlist Tight

A small watchlist is easier to manage, cheaper in token usage, and less likely to overload the agent with weak signals. Start with a handful of liquid symbols.

Example watchlist.json:

json{
  "watchlist": [
    {
      "symbol": "SPY",
      "description": "S&P 500 ETF",
      "max_allocation_pct": 15
    },
    {
      "symbol": "QQQ",
      "description": "Nasdaq ETF",
      "max_allocation_pct": 10
    },
    {
      "symbol": "NVDA",
      "description": "AI infrastructure leader",
      "max_allocation_pct": 8
    },
    {
      "symbol": "AAPL",
      "description": "Large-cap stability anchor",
      "max_allocation_pct": 8
    },
    {
      "symbol": "MSFT",
      "description": "Cloud and enterprise AI exposure",
      "max_allocation_pct": 8
    }
  ],
  "cash_reserve_pct": 20
}

These constraints stop the agent from taking individually reasonable trades that become reckless when combined.

Step 7: Add Risk Controls That Actually Matter

Risk controls should exist in more than one place. If you rely only on prompting, eventually the system will fail in an edge case.

Use three layers.

Layer 1: Rules in CLAUDE.md

These are the strategic rules the agent reads every session.

Layer 2: Validation in Python

Add pre-trade validation so the script can block dangerous orders:

pythondef validate_order(symbol, qty, side, current_price, account_value, current_positions):
    order_value = qty * current_price
    allocation_pct = (order_value / account_value) * 100

    if allocation_pct > 10:
        return False, f"Order exceeds 10% allocation limit: {allocation_pct:.1f}%"

    total_invested = sum(float(p["market_value"]) for p in current_positions)
    if (total_invested + order_value) / account_value > 0.80:
        return False, "Order would violate 20% cash reserve requirement"

    return True, "Order validated"

Layer 3: Broker-Level Protection

Alpaca’s paper environment gives you a safe simulation of how the algorithm would behave, including the practical separation between paper and live credentials and endpoints. That makes it a solid final safety layer while the system is still being refined.[github]

Step 8: Add Notifications

You probably do not want to stare at logs every day. A simple email digest solves that.

Create scripts/notify.py:

pythonimport os
import sys
import smtplib
from email.mime.text import MIMEText

def send_digest(journal_path):
    with open(journal_path, "r") as f:
        content = f.read()

    msg = MIMEText(content)
    msg["Subject"] = f"Trading Agent Report — {journal_path.split('/')[-1]}"
    msg["From"] = os.getenv("SMTP_FROM")
    msg["To"] = os.getenv("NOTIFY_EMAIL")

    with smtplib.SMTP(os.getenv("SMTP_HOST"), int(os.getenv("SMTP_PORT", "587"))) as server:
        server.starttls()
        server.login(os.getenv("SMTP_USER"), os.getenv("SMTP_PASS"))
        server.send_message(msg)

if __name__ == "__main__":
    send_digest(sys.argv[1])

Now the end-of-day routine can trigger a digest automatically.

What Realistic Results Look Like

It is important to keep expectations grounded. This kind of agent is good at consistent execution, repeatable workflows, and logging decisions. It is not automatically good at predicting market direction.

A realistic expectation looks like this:

  • The agent follows rules more consistently than a human.
  • The strategy can still lose money if the logic is weak.
  • Paper trading will often look better than live trading.
  • The biggest long-term advantage is the journal, not the first month of returns.

Alpaca’s paper trading setup is specifically meant for testing strategies in a simulated environment before switching to live accounts, with the same general API structure across both environments. That is why this build should stay in paper mode until the workflow behaves exactly the way you want.[docs.alpaca]

Frequently Asked Questions

Can I switch this to live trading later?

Yes. Alpaca uses separate credentials and a separate base URL for live and paper accounts, so moving from one to the other is mainly an environment configuration change once the workflow is stable.[docs.alpaca]

How much does it cost to run?

The main ongoing costs are Claude usage and any machine or hosting environment you use to run the project. Alpaca paper trading itself is meant for free strategy testing and gives developers a practical way to validate workflows without real capital at risk.[alpaca]

What if the agent makes a bad trade?

That is exactly why position limits, stop-loss logic, cash reserve rules, and journaling exist. A good setup contains damage and makes mistakes visible.

Can this trade options or short stocks?

It can be extended, but this tutorial is intentionally long-only. That keeps the strategy simpler, the risk model easier to control, and the debugging process much cleaner.

Does the market need to be open for the routine to run?

No. The routine can still trigger on schedule, but the trading step should check market status first and exit cleanly if the market is closed.

Final Thoughts

A useful AI trading agent is not just an LLM with a broker key. It is a controlled system built from routines, helper scripts, hard risk limits, and a journal that explains every decision. Claude Code gives you the reasoning layer, Alpaca gives you the paper trading infrastructure, and the combination is enough to build a fully autonomous test environment before you ever consider using live capital.[youtube][docs.alpaca]