In May 2026, Robinhood shipped an official Model Context Protocol server that lets an AI agent place real trades on your behalf. A few weeks earlier, the SEC scrapped the pattern-day-trading rule, so the old four-trade-a-week constraint is gone. Wire those two together and you can run an LLM agent on a schedule: it wakes up, evaluates a strategy, and executes without a human in the loop.
I built one. The setup: a cheap always-on VM, Claude Code as the agent runtime, the Robinhood MCP as the trading rail, cron as the trigger, and Telegram as the reporting channel. The strategy I’m using is a plain 200-day trend filter on QQQ, because the point here is the plumbing, not alpha. I’ll be upfront about cost, risk, and the one design mistake that nearly placed an order I didn’t want.
This is fun-money territory. I funded the account with $1,000. That’s the most you should be willing to set on fire, because an unattended agent moving real money is exactly as serious as it sounds.
The shape of the thing
What happens inside a single run:
The agent doesn’t run continuously. Cron fires a one-shot headless Claude Code invocation ten minutes before the close, the agent reads its rules, checks the market, places an order if the rule says to, and reports back. Then the process exits. Nothing listens between runs. A smaller alive-window means a smaller attack surface for something with trading authority.
Claude does not run on a timer by itself. It acts when invoked. The first-instinct setup is cron on a laptop, and that breaks the moment the laptop sleeps or you leave with it. An always-on VM exists so the trigger fires whether or not you’re around.
Cost, upfront and honest
Stack: GCP
e2-micro(1 vCPU, 1GB RAM) + 30GB standard disk, Ubuntu 24.04, Node 22 LTS, Claude Code 2.1, Robinhood Trading MCP, cron, a Telegram bot. Account funded with $1,000.
The VM is the cheap part. An e2-micro in us-central1 with a 30GB standard disk fits inside GCP’s Always Free tier, one instance per month at no charge. If you outgrow it, an e2-small with 2GB RAM runs a few dollars a month. The Telegram bot is free. Claude Code runs on your existing subscription.
Recurring infrastructure cost is roughly zero. The only money at risk is the account balance. You can run this experiment for the cost of the funds you put in, nothing else. The $1,000 is real; the VM being free doesn’t change that.
e2-micro is shared-core and burstable, and 1GB of RAM is tight for Node plus Claude Code plus an MCP subprocess. Add a 2GB swap file as an OOM safety net:
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Run timeouts or OOM kills in your alerts mean it’s time to bump to e2-small. I haven’t needed to, but I sized the timeouts expecting occasional slow runs.
Provisioning the VM
I used GCP because the free tier covers it, but nothing here is provider-specific. Any always-on Linux box works. Create the instance, then SSH in:
gcloud compute ssh rh-autotrader --zone=us-central1-a
A few hardening basics matter more here, because this box holds a credential that can move money. GCP blocks all inbound by default and you don’t need any ingress (the agent only makes outbound calls to the MCP and the Anthropic API), so leave the firewall closed. Use SSH keys only, no password login. Enable unattended security upgrades. The threat is someone getting shell on the box and inheriting the agent’s trading authority, so treat it accordingly.
Install Node and Claude Code:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
sudo npm install -g @anthropic-ai/claude-code
Connecting the Robinhood MCP
From inside your project directory, one command connects the MCP:
claude mcp add --transport http robinhood-trading https://agent.robinhood.com/mcp/trading
Then launch claude, run /mcp, select robinhood-trading, and complete the OAuth flow. On a headless VM you copy the printed URL into your local browser, approve the connection to your Agentic account there, and the token lands back on the box.
Two things about the Robinhood side worth knowing upfront. First, the agent trades out of a dedicated, segregated account funded only with money you move into it. That account balance is a hard cap on what the agent can ever touch. Second, you choose between manual-approval mode (you confirm each order) and auto-execute (it just trades). True unattended automation requires auto-execute, and that’s a real step up in risk: a box you aren’t watching can place real orders with no human gate. I run auto-execute because the segregated account caps the downside, but understand that trade-off before you flip it on.
Before trusting any of it, do a read-only shakedown. The MCP exposes a simulate/review path that constructs an order without placing it:
Using the robinhood-trading MCP, REVIEW (do not place) a market buy of
$50 of BIL. Show me the simulated fill, fees, and resulting buying power.
If that returns a clean preview with no order placed, your plumbing works and you’ve confirmed the agent can read quotes and build orders before a single real dollar moves.
The strategy: a 200-day trend filter on QQQ
The strategy is deliberately boring: hold QQQ in an uptrend, sit in BIL when it isn’t.
Every run, the agent computes QQQ’s 200-day simple moving average and compares it to the latest close:
- If QQQ closes above its 200-day SMA and you’re not already holding it: sell BIL, buy QQQ with the full bucket.
- If QQQ closes below its 200-day SMA and you are holding it: sell QQQ, move to BIL.
- Otherwise: do nothing.
To avoid whipsawing when price hugs the line, the agent only acts outside a ±1% band around the SMA. This trades maybe a handful of times a year and sidesteps the worst drawdowns historically. It’s also simple enough to verify by hand, which matters.
The rules live in a CLAUDE.md file in the project directory, which headless Claude Code reads automatically on every invocation. In prose, the relevant chunk reads roughly:
## Strategy: Trend Filter (QQQ / 200-day SMA)
Universe: QQQ, BIL. Evaluate once per run.
- SMA200 = average of QQQ's last 200 daily closes.
- If QQQ > SMA200 * 1.01 and not holding QQQ: sell BIL, buy QQQ (100%).
- If QQQ < SMA200 * 0.99 and holding QQQ: sell QQQ, buy BIL (100%).
- Inside the band, hold current position.
- You MUST compute the indicator yourself from price history fetched via
the MCP, and SHOW the inputs before acting.
That last instruction in the CLAUDE.md isn’t decoration. An LLM will happily claim an indicator value. You want it to show the price series and the arithmetic so you can check. After my first successful run I recomputed the moving averages from the same closes in a throwaway script and confirmed they matched to the decimal. Do this before you let it trade. Agents are confidently wrong about indicator math more often than you’d expect, and a silently miscalculated number breaks the whole strategy without throwing an error.
The trigger and the reporting
The cron entry runs the wrapper script ten minutes before the close on weekdays (set the VM’s timezone first so the schedule reads in your local market time):
50 14 * * 1-5 /home/you/trading/run_check.sh daily >> /home/you/trading/logs/cron.log 2>&1
The wrapper does three things: gates on settlement (next section), runs the agent headless, and pings a Telegram bot with the result. Telegram is what makes an unattended system tolerable. Every run, success or failure, you get a message. A heartbeat job on Sundays tells you the box is still alive. With automation, the dangerous failure isn’t a loud crash. It’s a lapsed credential from three weeks ago you never noticed.
Getting a bot token from @BotFather and your chat ID takes two minutes, and the send is a one-liner:
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
-d chat_id="${TG_CHAT}" --data-urlencode text="your message"
The headless invocation restricts the agent’s tools to just what it needs (the Robinhood MCP plus read-only file access) rather than handing it unrestricted access. On a machine that can spend money, scope the agent down to its job.
The safety harness
The wrapper script is no longer just a settlement check and a Claude invocation. It’s a multi-stage pipeline where deterministic Python brackets the LLM on both sides.
Before the agent runs, state.py evaluates a 15% trailing stop in pure Python with no LLM involved. It tracks a high-water mark per account and compares today’s value against it. If the account falls more than 15% below its peak, the script fires a 🛑 Telegram alert with the exact sell instruction and halts — no new buys, ever, without you manually clearing the flag. Crucially, it runs before the trading stage: a halted account never reaches the agent at all. Both the threshold and the halt logic live in testable Python, not in the prompt.
After the agent runs, reconcile.py re-fetches real positions from the broker via a read-only MCP call and rebuilds the ledger from what actually filled. The agent’s text output is not trusted as the record of what happened; the broker is. If there’s drift between what the agent claimed and what’s actually in the account, reconcile catches it and flags it in the Telegram report.
The design principle is the same one the settlement gate taught: let the LLM do judgment (read prices, compute the indicator, decide direction) and let plain code enforce hard constraints. state.py and reconcile.py are both unit-testable with synthetic prices before they touch a real account.
The mistake worth learning from
I built a “don’t trade with unsettled cash” rule as a line in CLAUDE.md. Freshly deposited funds take a day or two to settle, and buying against unsettled cash in a cash account risks a good-faith violation. The rule worked on the first run: the agent recognized the deposit was unsettled and declined. On a later run, identical conditions, it placed the order anyway.
Nothing bad happened (I caught and canceled it). But a prose instruction is not a guardrail for anything that matters. An LLM following natural-language rules is probabilistic. It’ll honor them most of the time, which is precisely what makes the occasional miss dangerous: you stop watching.
The fix was to move the check out of the agent’s judgment and into the wrapper script as a hard gate. A cheap read-only call reports settlement status as a machine-readable flag. The script greps for it and only invokes the trading stage if funds are settled:
if ! grep -q "SETTLED=YES" "$GATELOG"; then
tg "Funds not settled — gate held, no trading agent invoked."
exit 0
fi
# ... trading stage only runs past this point
Anything that must always happen or never happen belongs in deterministic code surrounding the agent, not in the agent’s instructions. Let the LLM do judgment (reading prices, computing signals, deciding direction) and let plain code enforce the hard constraints. That split is the design philosophy here.
Risk and reward, stated plainly
The reward case is modest. A trend filter on a single index ETF historically reduces drawdowns; it’s not a money printer, and on a $1,000 stake a good year is measured in tens of dollars. The more likely outcome over any given year is that it trails a plain buy-and-hold of the same index, because trend strategies pay an insurance premium in sideways markets you only recoup in crashes. Build a paper “just hold the index” comparison alongside it.
Auto-execute means no human reviews orders in real time (your Telegram message is after-the-fact). An LLM can misread data or get prompt-injected if it touches the open web mid-session, which is why the rules say to ignore everything that isn’t price data. The OAuth token expires and re-authentication is manual, by design. A free-tier VM under memory pressure can time out a run, so you’ll occasionally miss a signal.
Build it small. Instrument it heavily. Cap the funds at what you’d shrug off and keep the kill switches one command away. For $1,000 you get a hands-on understanding of how agentic systems behave when they have real authority and you’re not watching. That’s worth more than the trades.
Where this goes
The interesting work stays in the harness. The next layer I’m thinking about is a proper backtest the agent reasons against before acting — right now it computes the indicator live and acts; a backtest would let it sanity-check the signal against history before placing anything. Richer trade logs (structured JSON per run, queryable after the fact) would make drift and performance analysis easier over time.
The trading logic in this post is intentionally trivial so the infrastructure stands on its own. Swap in whatever signal you trust. The scaffolding — VM, MCP, headless agent, deterministic gates, Telegram — is the reusable part.
If you build something on top of this, I’d like to hear how you structured the guardrails. Getting the trades right is simpler than keeping an autonomous agent honest when real money is on the line.
