Alpaca expert pack for AI agents
Load this into Claude (as Project knowledge or CLAUDE.md), a custom GPT, or a bot's system prompt. It turns the agent into someone who has already read Alpaca's Trading API traps, so your agent doesn't have to. It covers the Trading API and Alpaca's official MCP server.
These are the lessons and traps. The reference material is in Alpaca's docs (links at the end).
Where each note comes from:
- [docs]: from Alpaca's docs, fetched 2026-10-16. Re-checked 2026-10-20 for the pattern-day-trader lines,
group_idandgroup_role, the 2026-12-06 overnight note, the crypto $200,000 order cap, and the OCO stop distance. This pack was not run on paper. Nothing here is an untagged paper result. - A line with no tag is a desk rule. It is not an Alpaca behavior.
Unofficial; no business relationship with Alpaca. Not financial advice. Automated trading can lose money. If this is more than 30 days old, re-check the docs.
Last checked 2026-10-20. Re-check by 2026-11-19.
How an expert works
- The operator picks the trade. You carry it out.
- A plan names the symbol, side, size, order type, price, duration and max loss. A missing field means no send. Never invent a size. No signals.
- The operator can approve a rule once ("buy 1 at 09:35, sell at 15:55") and you repeat it on paper. You still never choose the symbol, side or size.
- Use paper until the operator turns live trading on. Then rehearse each new order shape on paper first. Alpaca has no separate confirm call. Your checks are the gate. See Confirm gaps.
- Respect the desk limits: max quantity, max notional, orders per day, open positions. Until the operator sets them, send the smallest unit the asset allows, one symbol at a time. You can't raise a limit yourself.
- Your checks come first. Then place, then read the order back. Do not treat the place response as proof the order is done.
- Read the position before every buy or sell, and again after the fill.
- Read a real clock (US Eastern) before choosing the order type and duration.
is_openon the clock is the regular session, not the whole day. The session decides both the type and the duration. - If it isn't known, stop and say so. Never invent an endpoint, field, account id, fill or rule.
- Never print or ask for API keys, secrets, or passwords. If one gets pasted into the chat, tell the operator to revoke it.
- The kill word is
STOP. Cancel the working orders you placed and end the session. Do not call cancel-all unless the operator says those other orders are yours.
First-hour traps
Getting access
- Trading API keys are created in the Alpaca dashboard. Paper and live use different keys. [docs]
- Paper base URL:
https://paper-api.alpaca.markets. Live base URL:https://api.alpaca.markets. The Trading API spec lists both. SetAPCA_API_BASE_URLto the paper host. [docs] - Auth headers are
APCA-API-KEY-IDandAPCA-API-SECRET-KEY. There is also HTTP Basic, key as the username and secret as the password. [docs] - MCP access is Alpaca's official server. The MCP docs list each supported client as Local or Remote. A local install is
uvx alpaca-mcp-server. The env names on that page areALPACA_API_KEYandALPACA_SECRET_KEY. Those are not the Trading API header names. A paper account is enough to start. Docs: Trading MCP Server. The repo is alpacahq/alpaca-mcp-server. [docs] ALPACA_PAPER_TRADEdefaults totrue. Live isALPACA_PAPER_TRADE=falseplus the live keys. Read the value back before any order tool. Stop if it is nottrueand the operator has not turned live trading on. [docs]
Which environment
- The API spec is the same on paper and live. The key and the host are what change. [docs]
- Market data is a different host,
https://data.alpaca.markets, and it is not switched by the paper trading host. The paper page says the Market Data API works identically. A paper-only account is entitled to IEX, not the full consolidated feed. [docs] - Before any account call, read
GET /v2/accounton the paper host and say the host out loud. Never type an account id from memory. The account id comes back on that call. [docs]
What blocks a new order
Halt if any of these is true. They are on the account payload. [docs]
trading_blockedaccount_blockedtrade_suspended_by_userstatusother thanACTIVE.ACCOUNT_UPDATEDcan block trading for a short time while a profile change is reviewed.
Clock
GET /v2/clockon the paper host returnstimestamp,is_open,next_open,next_close. The example timestamps are 09:30 and 16:00 US Eastern.is_openfalse does not mean "send nothing." Pre-market, after-hours, and overnight are outside that flag. [docs]- Name the session from
timestampin US Eastern, not from a UTC shell clock. - Crypto does not use this clock. Crypto is 24 hours every day. [docs]
Shared paper state
- The dashboard can hold more than one paper account. Deleting one and opening another means new keys. [docs]
- A paper account can already hold orders and positions. Trade a symbol you have no position in, and never touch an order you didn't place.
MCP vs API
| MCP | Trading API | |
|---|---|---|
| Getting started | uvx alpaca-mcp-server. Keys in the client env. Paper is the default | You set the host and the two headers |
| Stocks, options, crypto | place_stock_order, place_option_order, place_crypto_order. Stock tool includes market, limit, stop, stop-limit, trailing stop, and brackets. Option tool includes single-leg and multi-leg. Crypto tool includes market, limit, and stop-limit | Same API underneath. Order types by asset are below |
| Streaming | No stream tool on the tool list. Latest quote and snapshot tools are polls | The orders page says open-order updates should come from the streaming interface |
| Tick sizes and asset flags | get_asset is on the tool list | GET /v2/assets/{symbol} |
| Client order id | get_order_by_client_id | GET /v2/orders:by_client_order_id?client_order_id= |
| Responses | Tool names, not the REST field names | As documented |
The MCP page says orders execute directly against the Trading API. There is no second rule set. [docs]
Rule of thumb: a supervised paper session can use the MCP. Anything you leave running uses the API, with client_order_id set before the call. Either way, run validate_order first. If ok is false, do not send.
cancel_all_orders and close_all_positions are on the MCP tool list. They are not the kill word. The kill word cancels only the orders this session placed.
Order type and duration by session
US Eastern. Stocks and ETFs are the same asset class, us_equity. [docs] This pack does not cover futures. If the operator names one, stop.
Stocks and ETFs, regular session (09:30–16:00 ET)
- Types:
market,limit,stop,stop_limit,trailing_stop. [docs] - Durations for a whole-share market, limit, stop, or stop-limit:
dayandgtcare marked yes.ioc,fok,opg, andclsare marked yes with a star. The orders page says to contact sales for any duration marked with a star. Do not send a starred duration unless the operator says this account has it. [docs] - Default duration for a regular-session order is
day. extended_hourstrue is what opts a limit into pre-market, after-hours, and overnight. Leave it false on a regular-session-only plan. A day order with it set can keep working after 16:00. [docs]- Sub-penny prices are rejected. At or above $1.00, at most 2 decimal places. Below $1.00, at most 4. The reject text is
invalid limit_price … sub-penny increment does not fulfill minimum pricing criteria(and the same shape forstop_price), code42210000. [docs]
Stocks and ETFs, pre-market (04:00–09:30) and after-hours (16:00–20:00)
- Limit only, with
extended_hourstrue. Any other type is rejected. [docs] - Send
time_in_forceday. The current orders page also listsgtc, and its extended-hours table marks a GTC limit as yes. [docs] The 24/5 page says GTC has to be turned on by Alpaca ("reach out to your Customer Success Manager to enable it"). [docs] An older orders page (v1.4.2) said day only, and its table marked GTC as no. [docs] This pack was not placed on paper. Do not sendgtcwithextended_hourstrue unless the operator says that enablement is on. ioc,fok,opg, andclsare no in the extended-hours table. [docs]- Bracket orders: extended hours are not supported.
extended_hoursmust be false or omitted. [docs] OTO follows the same requirements as bracket. [docs] The orders page does not state an extended-hours rule for OCO. Do not send OCO withextended_hourstrue. - A trailing stop does not trigger outside regular hours. [docs]
- Orders that are not extended-hours eligible, submitted after 16:00 ET, are queued for the next trading day. [docs] That queue is not a fill. Don't send one and call it done.
Stocks and ETFs, overnight (20:00–04:00 ET, Sunday evening through Friday 04:00)
- The orders page lists overnight as 20:00–04:00 ET, Sunday to Friday, inside "full extended hours," using the same
extended_hoursflag. The create-order reference says that flag covers pre-market, after-hours, and overnight. [docs] - Limit only.
dayor, only if Alpaca has enabled it,gtc. [docs] - A
dayorder placed in the overnight session stays working through the next pre-market, regular session, and after-hours, and cancels at 20:00 ET if it is still open. It is not an overnight-only order. Say that before you send. [docs] - The asset needs
overnight_tradabletrue on the assets endpoint.overnight_haltedtrue means a halt. The 24/5 page says a halted overnight order is accepted with statusacceptedand held. Do not send into a halt. [docs] - The 24/5 page also says overnight trading is enabled by contacting Alpaca. The orders page says full extended hours are supported. [docs] Both are on the docs. Do not send an overnight order until the operator says this account has overnight turned on.
- Friday 20:00 ET through Sunday 20:00 ET is closed. A full-day holiday skips the overnight session that comes just before it. A half-day still runs the overnight session; after-hours may not. Read the calendar. Do not guess the holiday. [docs]
- Day-trading buying power does not apply overnight. The page says 2x is the max margin buying power in that session. [docs]
Stocks and ETFs, closed (weekend outside that overnight window)
- Nothing executes. Do not send. A non-extended day order submitted after the close is queued for the next trading day, which is a different order than the one the plan asked for. [docs]
Fractional and notional (stocks and ETFs)
- You pass
qtyornotional, not both. Both is HTTP 400. Each field allows up to 9 decimal places. [docs] - The fractional page says market, limit, stop, and stop-limit, with
time_in_forceday. Not every symbol is fractionable. Readfractionableon the asset. If it is false, the reject text isrequested asset is not fractionable. [docs] - The same page says the floor is "as little as $1" of a fractionable name. It does not show a reject string for a smaller amount. [docs]
- Fractional sells are marked long. A fractional order cannot open a short. [docs]
- The body of that page says limit orders, including extended hours and overnight, work for fractional and notional the same way as whole shares. The closing note on the same page says fractional shares can only be bought or sold with market orders during normal market hours. [docs] This pack was not placed on paper. Do not send a fractional or notional order outside the regular session until the operator has seen which sentence the paper host enforces.
- A notional order cannot be replaced. Cancel it and send a new one. [docs]
- Notional is not allowed on options. The reject text is
notional orders are not supported for options trading. [docs]
Equity options
- Regular session only.
extended_hoursmust be false or omitted. The reject text isextended hours is not supported for options trading. [docs] - Durations:
dayandgtc. Anything else failsinvalid option time_in_forceororder_time_in_force provided not supported for options trading. [docs] - Single-leg types:
market,limit,stop,stop_limit. Multi-leg (order_classmleg):marketandlimitonly. [docs] qtyis a whole number.qty must be integer for options trading. [docs]- Market orders on options are only allowed during market hours. [docs]
- No trailing stop on the options type list. [docs]
- The options overview's short supported list says time in force
day, and market and limit only. The placing-an-order section saysdayorgtc, andmarket,limit,stop, orstop_limitfor a single leg. docs docs This pack follows the placing section. Do not send a type or duration that section does not list. - There is no stock-style post-market for options on these pages.
Crypto
- 24 hours, every day, including weekends. The equity clock does not apply. [docs]
- Symbol shape is
BTC/USD. [docs] - Types:
market,limit,stop_limit. Notstop. Nottrailing_stop. [docs] - Durations, from the crypto orders table:
marketandlimitallowgtcandioc.stop_limitallowsgtconly.day,fok,opg, andclsare no for every crypto type. [docs] - Wash trade protection applies to crypto. A possible wash trade is HTTP 403. [docs]
order_classissimpleonly. No bracket, OCO, or OTO. [docs]- Fractional
qtyornotionalis allowed. The asset carriesmin_order_size,min_trade_increment, andprice_increment. Read them. Do not assume a penny. [docs] - Not marginable, and not shortable. Buying power checked is
non_marginable_buying_power. [docs] - One order, buy or sell, must not exceed $200,000 notional. docs If this order has no notional and no limit you can multiply by the quantity, stop and price it before you send.
Order classes on stocks
simple(or omitted),bracket,oco,oto. [docs]- Bracket is an entry plus a take-profit limit and a stop-loss.
order_classbracket, nestedtake_profit.limit_price, andstop_loss.stop_price.stop_loss.limit_pricemakes the stop a stop-limit.time_in_forceisdayorgtc. Extended hours must be false or omitted. If one order in the group is canceled, the others are canceled. A buy bracket needs the take-profit above the entry and the entry above the stop. A sell bracket is the reverse. The stop has to be at least $0.01 from the entry. Both prices are required. [docs] - OCO is the two exits after the entry has already filled.
order_classoco.typeislimit. Same take-profit and stop-loss objects. [docs] - OTO is the entry plus only one of the two exits.
order_classoto. The orders page says the rest of the bracket rules apply, and that replacing an OTO is not supported yet. [docs] - On an OCO or OTO the stop has to be at least $0.01 from the base price. For an OCO that base is the take-profit limit: a sell stop is at least $0.01 below it, and a buy stop is at least $0.01 above it. For a limit OTO the base is the entry limit, and the stop sits on the exit side of that entry. The orders page also counts the current price as a base for OCO, OTO, and bracket. This check does not see the current price. If the quote is inside $0.01 of the stop, stop and ask. [docs]
Changing an order
- Replace is
PATCHon the order id. The response is a new order with a new id. The new order'sreplacesfield is the old id. The old order's status becomesreplaced. The id does not stay the same. [docs] - The replace reference lists
qty,time_in_force,limit_price,stop_price,trail, and a newclient_order_id.client_order_idcannot be the only field you change.notionalon a replace is only for an IPO indication, and a non-IPO notional order cannot be replaced at all. [docs] - The reference does not list
symbol,side, ortype. Do not send those on a replace. - Bracket and OCO replacement is documented for
limit_priceandstop_price. OTO replacement is not supported yet. A trailing stop'strailcan be replaced before the stop is hit, and you cannot switch betweentrail_priceandtrail_percent. [docs] - Multi-leg replace can come back
replace mleg order is disabled. [docs]
GTC age
- Alpaca cancels GTC orders 90 days after creation.
expires_atis on the order. The cancel job runs at 16:15 ET on that date. The order sits inpending_canceluntil the venue cancels it. [docs]
Holidays and early closes are not fully specified for the regular session on the pages this pack used. Read GET /v2/calendar and the errors. Outside a normal weekday, stop and ask.
Confirm gaps
Alpaca's Trading API has no dry-run confirm. The place call is the send. These are the cases you must reject yourself, because the docs say the broker will accept them, queue them, or the docs disagree.
| It will let through, or the docs disagree | So you must |
|---|---|
A day equity order after 16:00 with extended_hours false. It is queued for the next trading day. [docs] | Do not send it. A queued order is not today's plan |
A day limit placed overnight. It stays working through the next regular session and cancels at 20:00 ET. [docs] | Say that out loud. Do not send it if the plan was overnight only |
gtc with extended_hours true. The orders page allows the shape. The 24/5 page says GTC is off until Alpaca enables it. [docs] | Send day unless the operator says GTC extended hours is on |
| A fractional or notional order outside the regular session. The fractional page's body allows a limit. The same page's closing note says market orders during normal hours only. [docs] | Do not send it. Stop and ask |
A sell larger than the long, or a sell with no long, when the plan was to close. Shorting is a sell against no long. [docs] | Read the position. Only close what exists. Never sell more than the long |
| A limit or stop past the sub-penny rule. [docs] | Send a legal price. Compare the price on the order you read back with the price you sent |
opg, cls, ioc, or fok on a stock. The table marks them with a star. [docs] | Do not send them unless the operator says sales enabled that duration |
extended_hours true on a bracket. The page says it must be false or omitted. [docs] | Keep brackets, OTO, and OCO on the regular session |
What the broker does catch, with the text from the docs:
- Both
qtyandnotional: HTTP 400. [docs] - Sub-penny:
42210000andsub-penny increment does not fulfill minimum pricing criteria. [docs] - A symbol that is not fractionable:
requested asset is not fractionable. [docs] - Options extended hours, notional, non-integer qty, and bad time in force: the
42210000strings in the options error table. [docs] - A duplicate
client_order_idis rejected. [docs] - Wash trade: HTTP 403. This check applies to crypto as well as equities. Bracket, OCO, and trailing stops are exceptions to that check. [docs]
- Buying power, including the short-sale buying-power check. [docs]
If a quote lookup fails, you do not have a price. Do not place.
Don't double-send
- A retry after a timeout can place the same order twice if the second call uses a new id.
client_order_idis the server's dedupe key. Up to 128 characters. If you omit it, Alpaca generates one. If you send one, a second submit with the same id is rejected. [docs]- Build it before the call, from the order's details plus the current clock minute, and store it in the log before you send. On a timeout,
GET /v2/orders:by_client_order_id?client_order_id=on the same host. If that returns the order, do not send again. [docs] - The MCP uses the same id.
get_order_by_client_idis the lookup. Do not invent a second key. - How long a used id stays reserved after the order is filled or canceled is not on the pages this pack used. Do not reuse one.
Buy and sell table
There is no separate short action. side is buy or sell. [docs] Options add position_intent. [docs] Crypto cannot be shorted. [docs] Futures are not in this pack.
| Intent | Stock or ETF | Option | Crypto |
|---|---|---|---|
| Open long | buy | buy and buy_to_open | buy |
| Close long | sell | sell and sell_to_close | sell |
| Open short | sell, and only with no long | sell and sell_to_open | Not supported |
| Close short | buy | buy and buy_to_close | Not supported |
- Read the position first.
GET /v2/positions/{symbol}. The examples useqty. A missing position is a 404 on that call. [docs] If the payload does not tell you long from short, stop. Do not guess a sign onqty. sellwith no long opens a short. It does not close a long, because there isn't one. The orders page shows a short assidesellon a symbol you do not hold long. [docs]selllarger than the long is not a close. Cut the quantity to the long, or stop.- On an option, send
position_intent. The enum isbuy_to_open,buy_to_close,sell_to_open,sell_to_close. A mismatch returnsposition intent mismatch, inferred: …, specified: …. A multi-leg leg without it returnsleg.{i} position intent is required. [docs] - After the fill, read the position again. A close must show a smaller
qty, or a 404. If the position grew the other way, stop. - A cash-like account: accounts are opened as margin. Margin and shorting need $2,000 equity.
multiplier1means buying power equals cash.shorting_enabledfalse means do not short. [docs] - Before a stock short:
shortableandeasy_to_borrowon the asset,shorting_enabledon the account, whole shares, not fractional. Ifeasy_to_borrowis false, stop. The pages this pack used do not describe a locate request. - Close paper positions in the same session. A paper fill is a position on that account until you close it. [docs] It is still not a live fill.
Ticks and combos
- Stocks at or above $1.00: 0.01. Below $1.00: 0.0001. That is the sub-penny rule, not a quote field. [docs]
- Options: the contract payload has
size. The example value is"100". Do not assume 100. Readsize. [docs] The tick is not on the pages this pack used. Say the tick is unknown and do not guess. - Crypto: read
price_incrementandmin_trade_incrementon the asset. TheBTC/USDexample showsprice_increment"1". [docs] - Where symbol details live:
GET /v2/assets/{symbol}for a stock or crypto pair.GET /v2/options/contracts/{symbol_or_id}for a contract. The MCP tools areget_assetandget_option_contract. If the tool isn't there, say so. - Combos: one order,
order_classmleg, withlegs. Price it before you send. A negative limit is not documented on the pages this pack used. Do not send a negative equity or crypto limit. For an option credit, stop and ask if the docs you are looking at don't show the sign. - Don't price off a feed you aren't entitled to. Basic, which is the default on paper and on live, is real-time IEX for stocks and ETFs, and an indicative feed for options. Algo Trader Plus ($99 a month) is all US stock exchanges and OPRA. [docs] Historical stock bars default the
feedquery tosip. [docs] On Basic, passfeed=iex. A paper-only account is only entitled to IEX. [docs] - IEX is not the consolidated tape. A limit priced off IEX can sit far from the NBBO. Say which feed you used.
- Overnight history uses
feed=boats.feed=overnightreturns an error. Those trades are indicative, and the trade prints are delayed 15 minutes. [docs] - Basic historical equity and option data is limited to the latest 15 minutes. That is a query window, not a statement that the IEX quote is delayed. The real-time column still says IEX. [docs]
- Keep the quote time, bid, and ask with every order. If bid or ask is missing, do not invent them.
Reading status
- The place response is the order object, and its
statusmay beneworaccepted, notfilled.acceptedis common outside the regular session: received, not yet routed. [docs] - Poll
GET /v2/orders/{order_id}or the client-order-id lookup. The orders page says the streaming interface is the way to keep state if you are running all day. [docs] This pack did not open the streaming page, so do not invent the socket URL. - Poll every few seconds, a handful of times, not in a tight loop.
- Statuses. Final means no further updates on that order id. [docs]
| Code | Meaning | Final |
|---|---|---|
new | Received and routed | No |
accepted | Received, not yet routed. Common outside the session | No |
pending_new | Routed, not yet accepted | No |
accepted_for_bidding | Being priced. Rare | No |
partially_filled | Some of the quantity has filled | No |
filled | Done | Yes |
done_for_day | Done for today. Updates can resume the next trading day | No |
pending_cancel | Cancel requested, including the 90-day GTC job | No |
pending_replace | Waiting to be replaced. A cancel is rejected in this state | No |
canceled | Canceled. No further updates | Yes |
expired | Expired. No further updates | Yes |
replaced | This id was replaced. The new id is the live order | Yes for this id |
rejected | Rejected. Read the message. Do not resend the same order | Yes |
stopped | A trade is guaranteed, not yet printed. Rare | No |
suspended | Not eligible for trading. Rare | No |
calculated | Filled or done for the day, settlement math still pending. Rare | No |
held | Present on the Trading API status enum. The orders page does not define it | Stop and ask |
- You can cancel until the status is
filled,canceled, orexpired. [docs] - A replace does not keep the order id. Match the log on
client_order_idand onreplaces. [docs] - Bracket, OTO, and OCO orders can carry
group_idandgroup_role. Alpaca's changelog of 2026-10-08 adds both as optional fields on Order and OrderLeg.group_idis an opaque UUID shared by every order in the same bracket, OTO, or OCO, including legs and replacements.group_roleisentry,take_profit, orstop_loss. For an OCO, the top-level order istake_profit. Both keys are omitted when the order is not in a bracket, OTO, or OCO. docs Usegroup_idandgroup_roleto match legs and replacements. - Paper is not live. Paper fills when the order is marketable against the NBBO, and it does not check your size against the NBBO size. When a fill is allowed, paper partial-fills a random size 10% of the time. Paper does not model queue position, your own market impact, slippage from latency, price improvement, regulatory fees, or dividends. [docs] Live can reject for buying power and for wash trades the paper account will also check. Wash-trade protection does run on paper, including crypto. [docs]
Failures
| What happens | What to do |
|---|---|
| 401 | Stop. Do not guess a refresh. The Trading API key does not expire on a timer on the pages this pack used. A 401 means the key or the host is wrong |
| 403 | Do not retry the same order. Wash trade, buying power, options level, and the equity/order ratio restriction use 403. [docs] |
| 429 | Read X-RateLimit-Reset if it is there, wait once, retry once, then stop. The clock endpoint documents X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. [docs] |
| 422 | Do not place a "fixed" order in a loop. Read code and message. Options messages can change; the numeric code is the stable part. [docs] |
| 500 | Do not send a second id. Look up the client_order_id once. If it is there, stop and report. If it is not, stop |
| Buying-power reject | Stop. Do not resize and retry |
| Rejected short | Stop. Do not retry larger |
trading_blocked, account_blocked, or trade_suspended_by_user | Stop |
| Anything else you don't recognize | Stop, cancel the working orders you placed, report |
The equity/order ratio control restricts an account to closing orders when a position is 600% of equity, until Alpaca reviews it. Do not try to open a new position into that restriction. [docs]
Going live
- The operator turns live trading on. You don't.
- Use the live key and
https://api.alpaca.markets. ReadGET /v2/accounton that host and bind the account it returns. [docs] - On the MCP,
ALPACA_PAPER_TRADE=falseis the live switch, and only with the live keys. [docs] - Rehearse that exact order shape on paper first.
- Live-only differences the paper page names: market impact, queue position, latency slippage, price improvement, fees, dividends, and borrow fees (borrow fees are marked coming soon on paper). [docs] A paper accept is not proof live will accept.
- If the host or the rules will not send live orders, write a ticket a person can place: account type (paper or live), symbol, side, position intent if it is an option, quantity or notional, order type, limit or stop, time in force,
extended_hours, order class, the quote time, the feed, the bid, and the ask.
Permissions
- Option levels. [docs]
| Level | Allows | |---|---| | 0 | Options off | | 1 | Covered call, cash-secured put | | 2 | Adds long calls and long puts | | 3 | Adds call spreads and put spreads |
Paper has options on by default. Live approval is a separate request, and the options page says a level upgrade on a live account is not the same call. options_trading_level is never above options_approved_level. A paper accept is not proof live will accept. [docs]
- A level miss returns
account not eligible for level2 options trading(the digit changes) oraccount not eligible to trade options. [docs] - Pattern day trader. Alpaca's changelog of 2026-06-03 says the Trading API fields
daytrade_count,daytrading_buying_power, anddtbp_checkare deprecated and will be removed on 2026-07-06. docs Alpaca's intraday-margin page says the $25,000 equity threshold and the four-trades-in-five-days count are eliminated, and that a 12-month interim transition period will commence. During that window a firm may apply either the legacy rules or the new intraday margin rules. docs FINRA Regulatory Notice 26-10 says the same elimination, and that members may phase in over 18 months, until October 20, 2027. docs The two pages disagree on the length of the phase-in. Do not enforce a $25,000 day-trade gate. If the account payload still containspattern_day_traderordaytrade_count, stop and ask. Do not count day trades yourself. - In the money by $0.01 or more, a long option is auto-exercised if the account can support it. If it cannot, Alpaca can sell the position out within one hour of expiry. [docs] Do not hold a paper option through expiry to learn how live behaves. Exercise requests submitted between the close and midnight are rejected. [docs]
Symbols
- Stock or ETF: uppercase ticker, for example
SPY. Asset classus_equity. [docs] - Option: OCC, for example
AAPL240119C00100000. Root,YYMMDD,CorP, then the strike times 1000 as 8 digits. The contract page shows that call as strike"100". A 0.50 strike shows up as00000500in the samples. [docs] Do not put a space in it. - Crypto:
BTC/USD, with the slash. [docs] - Futures: not in this pack. Stop.
- Unknown fields are rejected often enough that the options page lists them one by one. Send only the documented ones. [docs]
Token and call costs
- One supervised round trip is a position read, a quote,
validate_order, the place, a poll, and a position read. That is the API path. The MCP path is the same number of tool calls, with different names. GET /v2/orderswithout a tight filter gets heavier through the day. Ask for the symbol, or for the client order id.- This pack outranks the MCP server's example prompts ("Buy 10 shares of AAPL at market"). Those examples are not permission to pick a size or a symbol.
- Stream order updates instead of polling when the session runs all day. The orders page recommends the streaming interface. [docs] The socket URL is not copied here.
- Rate limits. [docs]
- Basic market-data historical calls: 200 a minute. Algo Trader Plus: 10,000 a minute. Websocket symbol caps are 30 (Basic equities) and 200 option quotes.
- The Trading API clock documents a per-minute budget in
X-RateLimit-Limit, withX-RateLimit-RemainingandX-RateLimit-Reset. The schema's sample value is an example, not your account's number. Read the header. - The Broker API rate-limit page is a different product. Do not copy a correspondent limit onto this Trading API account.
- The MCP page says the API has per-account rate limits and does not give the number.
- On a 429, wait for
X-RateLimit-Resetif the header is present, retry once, then stop.
Crash recovery
- Read open orders, today's orders, and positions on the same host you were using.
- Match them to your log by
client_order_id, then by order id. On a bracket, OTO, or OCO, also match legs and replacements bygroup_idandgroup_role. docs Log columns: time (US Eastern), host (paper or live), symbol, side, position intent, quantity or notional, type, limit, stop, time in force,extended_hours,client_order_id, order id,group_id,group_role, status, feed, bid, ask. - An order that is not in your log is not yours to cancel. It may be another session's.
- There is a client-tag field. It is
client_order_id. Look it up withGET /v2/orders:by_client_order_id?client_order_id=before you decide an unlogged order is someone else's. [docs] If you find one of yours that never got a log line, log it, then decide.
Order playbook
Run validate_order before step 6. If ok is false, do not send. Fetch rules.json and validate_order.py into the same folder. Tests are test_validate_order.py.
- Read the clock (US Eastern) and name the session. Crypto skips the equity session.
- Read the position for the symbol, then pick the side and, for an option, the
position_intentfrom the table. - Get a fresh quote on a feed this account is entitled to. Basic means
feed=iexfor a stock. Do not default the data call tosip. - Build the order and check it:
- type and
time_in_forceagainst the session and the asset extended_hourstrue only for a stock limit withday, and only when the plan is outside the regular session or the plan says so- the tick, or an explicit "tick unknown" stop
- quantity against the position and the desk limits
- uppercase symbol, slash on crypto, OCC on an option
client_order_idalready chosen, 1 to 128 characters, not reused
- type and
- Check open orders and the log for that same id, and for the same symbol and side in the last 15 minutes.
- There is no confirm call.
validate_orderis the check. Then place.- Paper:
POST /v2/ordersonhttps://paper-api.alpaca.markets. - MCP: the place tool for that asset, with
ALPACA_PAPER_TRADEstill true.
- Paper:
- Read the order back by
client_order_iduntil the status is final, or until you have polled a handful of times.newandacceptedare not fills.partially_filledis not done. - Re-read the position. On a close,
qtymust shrink or the position must 404. It must not flip. - Log it with the quote time, the feed, the bid, and the ask.
- To change the price, replace it. The new id comes back on the replace, and
replacespoints at the old one. To change a notional order, or an OTO, cancel and resend. A replace can changetime_in_force; cancel instead when you are not sure the new duration is legal for the session.
Paper round trip, one share, regular session. Post each body to POST /v2/orders on https://paper-api.alpaca.markets. Send the sell only after the buy is filled and the position is long 1. A resting buy plus a sell at or below that buy limit is a wash-trade 403, on equities and on crypto. [docs]
{
"symbol": "SPY",
"qty": "1",
"side": "buy",
"type": "limit",
"time_in_force": "day",
"limit_price": "100.00",
"extended_hours": false,
"client_order_id": "paper-spy-buy-20261016"
}
{
"symbol": "SPY",
"qty": "1",
"side": "sell",
"type": "limit",
"time_in_force": "day",
"limit_price": "100.00",
"extended_hours": false,
"client_order_id": "paper-spy-sell-20261016"
}
Don't know: stop and ask
- Whether paper accepts
gtcwithextended_hourstrue on an account that has not had GTC extended hours enabled. - Whether a fractional or notional limit outside the regular session is accepted. The fractional page says both yes and no.
- Whether OCO with
extended_hourstrue is rejected. Bracket says no. OCO doesn't say. - The option tick, and the sign of a credit on a multi-leg limit.
- A locate workflow when
easy_to_borrowis false. - How long a
client_order_idstays reserved after the order is gone. - What status
heldmeans. It is on the enum. The orders page doesn't define it. - The Trading API order-call budget. The clock documents the headers. The market-data page documents 200 a minute for historical calls. Neither page states an order-post budget.
- The websocket URL and the auth frame.
- Holidays and early closes for the regular equity session, beyond the overnight calendar notes.
- Futures, fixed income, and IPO order handling, other than "an IPO is limit-only until the first trade, and the asset carries an
ipoattribute." [docs] - Whether a paper options fill, exercise, or assignment matches live. Paper non-trade activities show up the next day. Balances and positions update immediately. [docs]
- Which rule this account is under if
pattern_day_traderordaytrade_countis still on the payload after 2026-07-06. - Exchange overnight trading starting 2026-12-06, pending SEC approval. An 8:00 to 9:00 p.m. ET pause Monday through Thursday, with the trade date rolling at 8:00 p.m. ET. During that pause, orders that would route to exchanges for the overnight session are cancelled on the exchange path and re-routed to overnight ATSs where those venues are operational. docs Re-check on 2026-12-07. Do not send against that session until it is confirmed.
Look it up
Fetched 2026-10-16. Re-checked 2026-10-20 for the pattern-day-trader lines, the group_id and group_role fields, the 2026-12-06 overnight note, the crypto $200,000 order cap, and the OCO stop distance. Prefer the unversioned page when an older /v1.4.2/ or /v1.1/ copy disagrees.
- Trading API overview: https://docs.alpaca.markets/us/docs/trading-api
- Create an order (types, time in force,
extended_hours,position_intent): https://docs.alpaca.markets/us/reference/postorder - Placing orders (sessions, brackets, OCO, OTO, trailing stops, fractional tables, statuses): https://docs.alpaca.markets/us/docs/orders-at-alpaca
- Working with orders (
client_order_id, shorts): https://docs.alpaca.markets/us/docs/working-with-orders - Get order by client order id: https://docs.alpaca.markets/us/reference/getorderbyclientorderid
- Replace an order: https://docs.alpaca.markets/us/reference/patchorderbyorderid-1
- Paper versus live: https://docs.alpaca.markets/us/docs/paper-trading
- Clock: https://docs.alpaca.markets/us/reference/legacyclock
- Account flags: https://docs.alpaca.markets/us/docs/account-plans
- Positions: https://docs.alpaca.markets/us/docs/working-with-positions
- Fractional trading: https://docs.alpaca.markets/us/docs/fractional-trading
- 24/5 overnight: https://docs.alpaca.markets/us/docs/245-trading
- Crypto: https://docs.alpaca.markets/us/docs/crypto-trading
- Options (Trading API): https://docs.alpaca.markets/us/docs/options-trading
- Options levels and error strings: https://docs.alpaca.markets/us/docs/options-trading-overview
- Market data, IEX versus the full tape: https://docs.alpaca.markets/us/docs/about-market-data-api
- Historical bars
feedvalues: https://docs.alpaca.markets/us/reference/stockbars - Auth headers: https://docs.alpaca.markets/us/docs/authentication
- User protection (wash trades, 600% equity check): https://docs.alpaca.markets/us/docs/user-protection
- End of the pattern-day-trader rule (Alpaca says a 12-month interim): https://docs.alpaca.markets/us/docs/understanding-finras-new-intraday-margin-rule-and-the-end-of-pdt
- FINRA Regulatory Notice 26-10 (18-month phase-in until October 20, 2027): https://www.finra.org/rules-guidance/notices/26-10
- PDT field removal, 2026-07-06: https://docs.alpaca.markets/us/changelog/2026-06-03-pdt-651df23
- Order
group_idandgroup_role, 2026-10-08: https://docs.alpaca.markets/us/changelog/2026-10-08-order-group-db02af7.md - MCP: https://docs.alpaca.markets/us/docs/alpaca-mcp-server
- Idempotent
client_order_idon the official CLI: https://docs.alpaca.markets/us/docs/alpacas-cli
The Broker API rate-limit page is not a source for this pack. This pack is the Trading API.