Home › Guides › Trading bots & code
Alpaca API stock bot basics: paper trade your first rule
Alpaca is a US broker built around an API, with a free paper trading account that uses fake money. That makes it a common first home for a stock bot. Here's the whole path, with the stop attached from the start.
Why Alpaca shows up in bot tutorials
Alpaca is a US broker designed for code. Three things make it beginner-friendly:
- A paper trading account with fake money, separate from any live account.
- An official Python library, called alpaca-py.
- Free basic market data for testing.
None of that makes a bot safer by itself. It just makes testing easier — and testing is the part to spend time on.
Step 1: Get paper keys
- Sign up on alpaca.markets and open the paper trading dashboard.
- Create API keys there. Paper keys only work on the paper account.
- Store them as environment variables on your computer, named ALPACA_API_KEY and ALPACA_SECRET_KEY.
Never paste keys into your code. If the code gets shared or uploaded, the keys go with it. See API key security for trading bots.
Then install the library: pip install alpaca-py pandas.
Step 2: Connect and read prices
Example code for learning — test it on paper first; it is not a ready-made trading system.
import os
from datetime import datetime, timedelta, timezone
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest, TakeProfitRequest, StopLossRequest
from alpaca.trading.enums import OrderSide, TimeInForce, OrderClass
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
from alpaca.data.enums import DataFeed
KEY = os.environ["ALPACA_API_KEY"] # paper keys, from the environment
SECRET = os.environ["ALPACA_SECRET_KEY"]
trading = TradingClient(KEY, SECRET, paper=True) # paper=True: fake money
data = StockHistoricalDataClient(KEY, SECRET)
request = StockBarsRequest(
symbol_or_symbols="SPY",
timeframe=TimeFrame.Day,
start=datetime.now(timezone.utc) - timedelta(days=90),
feed=DataFeed.IEX, # the free data feed
)
bars = data.get_stock_bars(request).df.loc["SPY"]
print(bars[["open", "high", "low", "close"]].tail(3))
What it does, in plain words:
- The imports bring in the pieces: one client for trading, one for price data, plus the order types.
- TradingClient(..., paper=True) connects to the paper account. Always write paper=True out in full, so nobody has to guess.
- StockBarsRequest asks for daily bars (candles) for SPY, an S&P 500 ETF, from the last 90 days.
- feed=DataFeed.IEX uses the free feed, which comes from one exchange (IEX). It's fine for learning. Free accounts can't pull the most recent 15 minutes of full-market data.
- .df.loc["SPY"] turns the answer into a pandas table, one row per day.
Step 3: Check before every trade
The first thing a bot does isn't looking for a trade. It's checking whether it's allowed to trade.
MAX_DAILY_LOSS_PCT = 2.0
RISK_PCT = 1.0
def ok_to_trade():
clock = trading.get_clock()
if not clock.is_open:
return False # market closed
account = trading.get_account()
equity = float(account.equity)
start_of_day = float(account.last_equity) # yesterday's closing equity
if equity <= start_of_day * (1 - MAX_DAILY_LOSS_PCT / 100):
print("Daily loss limit hit. No new trades today.")
return False
return True
In plain words:
- get_clock() tells you whether the US market is open right now (regular hours are 9:30 am–4:00 pm ET).
- account.equity is the account's value now. account.last_equity is its value at the end of the last trading day. Alpaca sends both as text, so float() turns them into numbers.
- If today's drop reaches 2%, the bot stops opening trades until tomorrow. See daily loss limit.
Step 4: Send a bracket order
A bracket order sends 3 orders together: the entry, a take-profit and a stop. Once the entry fills, the stop and target sit at the broker — even if your bot crashes. When one fills, the other cancels.
def buy_with_bracket(entry, stop, target):
equity = float(trading.get_account().equity)
risk_money = equity * RISK_PCT / 100
qty = int(risk_money // (entry - stop)) # whole shares only
if qty < 1:
return None # stop too wide: skip
order = MarketOrderRequest(
symbol="SPY",
qty=qty,
side=OrderSide.BUY,
time_in_force=TimeInForce.DAY,
order_class=OrderClass.BRACKET,
take_profit=TakeProfitRequest(limit_price=round(target, 2)),
stop_loss=StopLossRequest(stop_price=round(stop, 2)),
)
return trading.submit_order(order)
In plain words:
- risk_money is 1% of the account. entry − stop is what one share loses if the stop is hit. Divide, round down to whole shares — bracket orders use whole shares.
- If even 1 share risks too much, skip the trade. Never shrink the stop to make a trade fit.
- MarketOrderRequest with OrderClass.BRACKET attaches the target (a limit order) and the stop.
- round(..., 2) keeps prices to cents.
Example: 10,000 equity, 1% = 100 at risk. Entry 500, stop 495: 100 ÷ 5 = 20 shares. A math example, not a promise — a gap at the open can fill the stop well past 495. Check with our position size calculator.
The rule that picks entry, stop and target is yours: for example, the stop just past the last place price turned around on the daily chart. See how to set a stop loss.
Step 5: Handle errors and log everything
Orders get rejected. Connections drop. A bot has to expect it.
- Wrap submit_order in try / except. alpaca-py raises APIError (from alpaca.common.exceptions) when Alpaca rejects a request; log the message.
- After any error, check before retrying. Call get_orders() and get_all_positions() — the order may have gone through even if your bot didn't hear back.
- Log every decision: the bars it saw, the rule result, the size, the order ID, the reply.
- Add a kill switch you can use without code — and know that close_all_positions exists for emergencies.
More: trading bot risk controls.
Step 6: Paper trade for real
Run the bot on the paper account for weeks, through calm days and news days like CPI and Fed days.
Paper fills are simulated, so real fills can be worse: Slippage, partial fills and gaps all cost more with real money. Before that, test the rule on past data, watching for look-ahead bias (using a candle's close before it happened) and overfitting (tuning until the past looks perfect). See how to backtest in Python and paper trading a bot.
Switching to live is one word: paper=False. That's exactly why it should never be the default. Keep live and paper keys separate, and make going live a deliberate, written decision — not an edit you make at midnight.
Common mistakes
- Testing on the live account "just once". Use paper keys until the log is boring.
- Fractional sizes in a bracket order. Use whole shares.
- Acting before the bar is finished. Daily bars aren't final until after the close.
- No daily limit. A bug can repeat a bad trade all day.
Questions
Does Alpaca have paper trading?
Yes. Alpaca offers a paper trading account with simulated money and its own API keys. In alpaca-py you connect to it with TradingClient(key, secret, paper=True).
Is the Alpaca API free?
Opening an account and using the trading API costs nothing, and there's a free market data plan. Fuller real-time data from all US exchanges is a paid add-on. Check alpaca.markets for current pricing.
What Python library do I use for Alpaca?
alpaca-py, Alpaca's official Python SDK. Install it with pip install alpaca-py. Older tutorials use alpaca-trade-api, which is the older library.
Can I trade crypto with the Alpaca API?
Alpaca offers crypto trading too, with its own data client in alpaca-py. Crypto trades 24/7, so loss limits and kill switches matter even more.
Want the next step? We'll walk you through it.
Four free lessons by email, one every few days — each with a 10-minute exercise on a real chart.
Send Me the Free Lessons 4 short emails · No spam · Unsubscribe anytimeOr see the whole method, step by step:
See What’s Inside the Course Try everything free first. Decide when you're ready. · Open the free chart tool (works in the TradingView phone app)One chart idea a day, in plain English. Follow along: