TRADEnosis™ — My Quest to Build a Mythical Stock Trading Platform
Chapter 3: Alpaca Market’s Trade Order Submission Rules
Where are we, Sire?
This is yet another chapter in our ongoing adventure to build a basic, yet functional, web application for viewing stock graphs and entering trades. In Chapter 1, we used Plotly Dash to write a responsive interface. In Chapter 2, we added interactively with Dash callbacks and used the Alpaca Version 2 Python SDK to load data into our candlestick graph.
In this chapter we will explore what is needed to generate trade orders. That’s where things get complicated. There are lots of order parameters and order types and lots of rules we’ll need to follow. So, before we dive into the code, let’s review review what goes into trade orders.
Understanding Orders
A order is an instruction to your broker to buy or sell a security, essentially to “execute a trade” according to rules established by the US Securities & Exchange Commission (SEC) and the exchanges. Brokers may add additional requirements such as how much margin is required or how much commission is charged. As we discussed earlier, there are multiple markets/exchanges. Your trade many not actually get transacted on a major exchange and often not by your broker directly. In fact, routing orders to various market makers and clearing firms, willing to pay fees for the order flow, is how brokers make money. You didn't really think brokers were executing trades for free, did you?
Some brokers use the terms “Buy/Sell to Open” and“Buy/Sell to Close” as a way to distinguish whether a order is intended to establish a new position or to zero-out a previously-entered position. Alpaca does not. For example, you can use an order to buy 10 shares and later enter another order to sell 5 of those shares without having to indicate that it's to partially close the position. That makes trading easier, but it can make explaining orders a bit harder, especially when we get to the Advanced Order Types.
First, let’s discuss the 4 basic order types and a fifth that some brokers support. Be aware that orders can be used either to open or close a position. Also, an “entry/opening order” will establish either a long (by buying shares) or a short (by selling borrowed shares) position, for which you later use a “closing/exiting order” to zero-out your position.
- Market — an order to buy/sell at the price available at the time the order is executed, specifically at the then-current “ask” price if your are buying and at the “bid” price if selling albeit some brokers will try to get “price improvement” by routing your order to achieve a slightly better price. Provided the market is open and the security is available, market orders execute almost immediately. You won’t know the exact price until the order is transacted and there is a risk there could be an unexpected price deviation from the last quote you saw, especially if your quotes are delayed or not from the major exchanges (e.g., IEX rather than SIP), or if the market is moving quickly.
- Limit — an order to buy/sell at the specified price OR BETTER. What may be confusing here is that the stock price could exactly hit your limit price, but not be filled if the order could not be immediately executed at that price, say due to insufficient market liquidity. The risk with a limit order is that you can miss a trading opportunity if the price never hits the trigger. Limit orders are viewable in the Level 2 order book. There’s debate whether or not high-frequency trading firms and market makers over a few seconds, move the market by a combination of aggressive selling and placing large sell orders cancelled immediately afterwards, to “take-out” a standing limit order. I don’t know if that really happens, but it sure feels that way when price suddenly moves to my limit and my trade fills only to immediately revert.
- Stop — this order becomes a market order once the stock price moves past your stop price. It can be used to enter a position, say, when you only want to buy the stock if it breaks above a resistance line on the chart. Conversely, when used to close (as in zero-out your exposure to) an already open position, many think of this as a “protective stop”. But, in a fast moving market, your order could get filled quite far away from the stop price. I remember the Flash Crash of March 2010 when lots of people got “stopped-out” with tremendous loses after the market suddenly dropped hard and the exchanges couldn’t keep up. It hurt all the more when the market fully recovered within just a few minutes, having filled those closing orders near the bottom despite the stops being much higher. I use stops far less often now!
- Stop-Limit — this order will be executed at a specified limit price, or better, after a given stop price has been reached. Using this order type might have limited losses in the Flash Crash, but in the more serious event of a sudden and sustained price change, say a company suddenly goes under, it might not get you out at all. The triggered order might move beyond the limit before it can be filled.
- Trailing Stop — Some brokers, including Alpaca, support this order type which will automatically update the stop as the stock price moves in a favorable direction. For a sell order, the order will convert to a market order if the price drops a specified percentage or dollar amount below what is called the “high water mark” (hwm) after reaching a peak high price. For a buy order, say to close a short trade, the opposite is true, but confusingly, Alpaca still labels the trough as hwm just the same. The advantage of this order type is that you don’t have to monitor the market continuously to move up your stop. Another wonderful advantage is that the stop trigger can be entered either as a trailing price or trailing percentage; the latter makes trade management easier!
Advanced Order Types
Alpaca (and increasingly other brokers) support advanced order types that can be used to add additional “legs” to an order. Some of the legs are not sent to the exchange until after another leg has been filled. This is primarily used to convey your opening and closing instructions within a single order (called a Bracket Order); whereas, normally, you can’t enter contradictory orders. Alpaca supports three types, but unfortunately, in contrast to the trailing stop, they currently support only price targets, not percentage moves.
- One-Cancels-Other (OCO) —This is a set of two orders with the same side (Buy/Buy or Sell/Sell). When you already have an open position, Alpaca allows this order type to enter both a Loss-Stop and a Take-Profit stop as conditional closing orders; be aware that you can’t use this order type to enter a new position, only to exit a current position. For example, let’s say last week you bought shares at $10. Using this order type today, you might establish a stop price at $15 to cash-out your position with a profit should the stock move favorably upward, and AT THE SAME TIME, establish a stop at $8 to close-out your position if the stock moves against you. Alpaca will execute whichever target gets hit first, hopefully your “Take-Profit” order, and automatically cancel the other. In a volatile market, especially when your targets are close together, there is a chance that both orders could trigger. It’s rare, but one of the reasons you shouldn’t let trades go totally unattended.
- One-Triggers-Other (OTO) — This is an opening order, using any of the 5 basic order types, along with a closing leg that has EITHER a stop to prevent the stock from moving against your position ( Loss Stop) OR a stop to extract a profit if the position moves favorably above a predetermined trigger point (Take-Profit stop). This closing leg gets “triggered” only if the opening order was successful.
- Bracket Order (OTOCO) — This is a combination of the above two advanced types where in a single submission you can establish an opening order (using one of the 5 basic types) plus two OCO closing legs, a Take Profit and a Loss Stop. This is an ideal order type to use in an automated trading system; for discretionary trading, you might want to monitor the trade and “move your stops” manually. The current Alpaca documentation is unclear/inconsistent whether you can adjust stops in advanced orders without first cancelling the current order; I’m still exploring how best to do that.
The TIF Order Parameter
Time-in-Force (TIF) instructs Alpaca as to when the trade should be executed. There are several available options:
- Day — A day order is eligible for execution only during REGULAR market hours. In the case of US stocks, that would be 9:30AM to 4:00PM Eastern Time. It can be submitted the evening before, provided it’s submitted after the closing time for regular hours. If it does not get filled (say, if it was an untriggered limit order) or get cancelled by the trader, Alpaca will automatically cancel it at regular market close.
- Good Until Canceled (GTC) — A GTC order remains active until it is filled, or cancelled by the trader. If there is a stock split while an order is open, the number of shares will get adjusted accordingly.
- Extended Hours — this is a separate parameter than can be applied to a Day or GTC order; in the user interface we will treat it like a TIF option and distinguish it in the code only. Alpaca allows orders to be submitted “pre-market” between 4:00AM and 9:30AM Eastern Monday to Friday, and “After Hours” between 4:00PM and 8:00PM ET M-F. The number/speed of transactions (called “liquidity”) of a stock is generally much less outside of regular hours, so spreads (between the bid and ask price) can be much higher. As such, Alpaca will only accept Limit-Day orders during extended hours.
- At the Open (OPG) — Only market and limit orders are allowed to use this TIF option. The order is eligible to be filled at the market’s opening auction price (the “opening price” at 9:30AM) and will be automatically cancelled if not filled. An OPG order must be submitted before 9:28 AM to be valid, and can be submitted any time after 7:00PM for the next session’s opening trade.
- At the Close (CLS) — Market and limit orders submitted with this TIF parameter are eligible to be filled at the maket’s closing auction price. They will be automatically cancelled if unfilled. CLS orders can be submitted until 3:50PM, or after 7:00PM for the next trading session.
- Immediate or Cancel (IOC) —This rarely-used TIF parameter requires that the order be executed immediately, either fully or partially, or it will be automatically cancelled. If partially filled, the unfilled portion is cancelled.
- Fill or Kill (FOK) —A FOK order must be filled for the full requested quantity, otherwise the order is cancelled.
Notable Alpaca Trading Rules:
On top of the design considerations we’ll be making to implement the order types and parameters discussed above, Alpaca adds certain rule. An order that does not comply will get rejected. In our coding, we may want to handle some of these situations to try and avoid an order being rejected, or to prevent the user getting a “fill” that differs from what was expected.
- You must have sufficient “buying power” to execute a trade. Alpaca determines the amount needed to transact the trade by multiplying the limit price by the number of shares. For non-limit orders, they do this calculation using a 2.5% to 4% estimate above/below current price. Buying power takes into account any open orders and whether you are using margin (essentially, agreeing to borrow money); therefore, we may want to show buying power at the time the user is entering a trade.
- A Limit, Stop or Stop-Limit order will be rejected if it has a sub-penny increment (ex., $4.026) when the price is over a dollar or if there are more than 4 decimals ($0.50789) when the price is under a dollar.
- A Stop order when buying will automatically convert into a Stop-Limit order with the limit price set at 4% higher than a Stop price when the price is under $50, or with a limit set at 2.5% higher than the Stop price when the stock transacts at $50 or more. When selling, this automatic conversion does not occur. I’m sure there a reason, but it evades me!
- The Take-Profit stop parameter of advanced order types must be a price, not a percentage; once triggered the order is executed at the market price. The Stop-Loss parameter can be either a stop-price, a limit_price or both; again, unfortunately, it must be entered as a price, not a percentage.
- The Stop-Loss parameter of an advanced order for exiting a position must be at least $0.01 away (below for a sell, above for a buy) from the price of the entry leg as well as the alternate leg.
- Fractional shares and notional (dollars instead of qty) orders are only available during regular hours and only as market orders. Only certain higher-volume stocks are “fractionable”. We check for this parameter in the stock’s data, but few people use notional trades and the added step is probably not worth the added complexity and potential delay.
- Shorting (borrowing to sell) is only allowed with “Easy-to-Borrow” stocks. Additional fees, in some cases substantial, are charged daily. Again, we could check for that parameter in the stock data before submitting the order, but that again adds unnecessary(?) complexity.
- Alpaca will reject an order if it would unintentionally cause a Pattern Day Trader (PDT) designation on an account with less than $25,000 in equity. By exchange rules, a PDT designation results if you make four “day trades” (buy & sell the same stock within asingle market session) within 5 days. To actually day trade, you’ll likely need more than $25K to cover the clearing time before funds are returned to your account after a closing transaction. This parameter is in the user’s account data, something well consider displaying in the future.
Where do we go next?
In the next chapter, we’ll create the order form to handle all these different order parameters. Version 2 of the Alpaca Markets Python API uses a package called Pydantic (on their end, we don’t need to install it) to validate orders. For us, that means we will need to submit orders using a specific API class to match the specific order type. It quickly get’s complex and unfortunately the available documentation is currently incomplete, but we’ll rise to the challenge!
A Summary of our Adventure:
- In Chapter 1, we created the UI for a basic trading webpage using Dash.
- In Chapter 2, we created an Alpaca Settings dialog and added code to load candlestick price data into a stock chart.
- In this chapter, we reviewed Alpaca’s trading rules.
- In Chapter 4 we will create the order form and associated code to compile trade instructions.
- In Chapter 5, we will write the callback needed to submit an order to Alpaca.
- In Chapter 6, we’ll use the Alpaca account data to fill the Orders and Positions tabs.