Order Types by Broker¶
What BotSpot MCP adds
Use Claude, ChatGPT, Codex, Cursor, VS Code or another MCP-compatible assistant? Connect the free BotSpot MCP for:
Skills: build LumiBot strategies.
Free data: news, economics and SEC filings.
Backtests: run tests and read charts.
Cloud hosting: low-cost scheduled bots.
Community: share bots and discuss trades.
Marketplace: discover and publish strategies.
Broker linking: connect accounts through BotSpot.
Which brokers can hold which orders, especially the protective ones (stops, trailing stops, brackets and OCO exits), and what happens to them when your strategy is not running.
The rule every broker follows: your order is placed exactly as you asked, or it is refused with a plain “not supported” error. It is never quietly changed into something else. A trailing stop is never sent as a market order, a bracket is never sent without its stop, and a stop is never sent without its trigger price.
If an order is refused, nothing is sent to the broker. Its status is error, its
error_message says why, and on_error_order is called.
Ask the broker in code¶
Brokers add order types over time, so do not copy this table into your strategy. Ask the broker instead:
order = self.create_order(asset, 10, Order.OrderSide.SELL, trail_percent=2.0)
support = self.broker.supports_order(order)
if not support.supported:
self.log_message(support.reason) # plain reason, nothing was sent
elif support.simulated:
self.log_message("Only works while this strategy is running")
# or describe the order without building it
self.broker.supports_order(order_type="market", order_class="bracket", trailing_leg=True)
support.mode is one of:
"native": the broker holds the order. It keeps working even when your strategy is stopped."simulated": LumiBot watches the market and acts for you. It only works while your strategy is running."unsupported":submit_orderrefuses it withsupport.reasonand sends nothing."unknown": this broker has not declared it yet. LumiBot sends the order as asked.
The answers come from one declaration in each broker class. The same declaration is what
submit_order uses to refuse an order, and a test checks it against each broker’s real
order requests, so the code, the refusals and this page cannot drift apart.
Matrix¶
Native: the broker holds it.
Simulated: LumiBot does part of the work while your strategy runs (see below).
No: refused with a “not supported” error.
Broker |
Market |
Limit |
Stop |
Stop-limit |
Trailing |
Bracket |
OTO |
Trailing leg |
OCO |
|---|---|---|---|---|---|---|---|---|---|
Alpaca |
Native |
Native |
Native |
Native |
Native |
Native |
Native |
No |
Native |
Tradier |
Native |
Native |
Native |
Native |
No |
Native |
Native |
No |
Native |
Schwab |
Native |
Native |
Native |
Native |
No |
Native |
Native |
No |
Native |
Tradovate |
Native |
Native |
Native |
Native |
No |
No |
No |
No |
No |
Bitunix |
Native |
Native |
No |
No |
No |
Native |
Native |
No |
No |
ProjectX |
Native |
Native |
Native |
No |
No |
Simulated |
No |
No |
No |
Interactive Brokers (Client Portal) |
Native |
Native |
Native |
Native |
Native |
No |
No |
No |
No |
CCXT exchanges |
Native |
Native |
Native |
Native |
Some |
No |
No |
No |
No |
tests/test_broker_order_support.py fails if this table and the brokers’ declarations
disagree. tests/test_broker_order_type_contract.py fails if a declaration and a
broker’s real request builder disagree.
Notes¶
Alpaca: a bracket or OTO entry can be market or limit. Alpaca’s stop-loss leg takes a fixed stop price only, so a trailing leg is refused; submit a separate trailing stop after the entry fills.
Bitunix: the exchange’s order endpoint accepts only market and limit orders. Put your stop and target on the entry:
order_class=Order.OrderClass.BRACKET(orOTO) withsecondary_stop_priceandsecondary_limit_price. The exchange holds them as its stop-loss and take-profit. Standalone stops, trailing stops and OCO are refused.Tradovate: single market, limit, stop and stop-limit orders. Trailing stops, brackets, OTO and OCO are refused for now; submit the entry, then the stop and target as separate orders.
Tradier and Schwab: no trailing stops through LumiBot; use a stop or stop-limit. The Schwab row is for stocks.
ProjectX: brackets are simulated (see below). Stop-limit, trailing, OTO and OCO are refused.
Interactive Brokers (Client Portal): single orders of every type are sent natively, including trailing stops. Brackets, OTO and OCO groups are refused until this adapter builds parent and child orders.
CCXT exchanges: trailing stops are sent with CCXT’s
trailingPercentortrailingAmountonly where the exchange says it supports them (for example OKX and Binance for percent, Bybit for amount);supports_orderanswers per exchange. Brackets, OTO and OCO are refused; pass the exchange’s own attached take-profit or stop-loss fields throughcustom_paramsinstead.Interactive Brokers (TWS), Kalshi, Polymarket and MetaMask have not declared their order support yet, so
supports_orderanswers"unknown"for them.
If your strategy is not running all the time¶
Many people run a strategy on a schedule: start it, let it run for a while, stop it, start it again later. That is fine for orders the broker holds. It is not fine for anything LumiBot simulates, because while your strategy process is not running, nothing is watching the market. Only orders resting at the broker protect you.
What LumiBot simulates today, and what happens around a stop and restart:
Simulated by LumiBot |
While the strategy is stopped |
Survives a restart? |
What the strategy sees on its next start |
|---|---|---|---|
ProjectX bracket: placing the |
Not placed. If the entry fills while the |
The entry and any placed exits come |
The filled entry and an open position. If no |
stop and target after the entry |
strategy is stopped, the position has no |
back from the broker and are linked |
exit had been placed yet, none is placed after |
fills |
stop or target. |
again. |
the restart: protect the position yourself. |
ProjectX bracket: cancelling the |
The other exit stays open. If the target |
Yes. The pair is linked again on the |
The filled exit, and the other exit cancelled |
other exit when one fills |
fills, the stop is still resting. |
next start. |
during the first sync. |
Smart limit re-pricing |
Re-pricing stops. The last limit order rests at the broker at its last price. |
Partly. A strategy that restores its saved orders keeps the smart limit settings, but not the step timer. |
With restored orders: re-pricing starts over from the first step. Without them: a plain limit order at the last price. |
Everything marked Native in the matrix rests at the broker and keeps protecting your position while the strategy is stopped. When it starts again, LumiBot reads those orders back from the broker with their prices and links (stop and target of a bracket or OCO stay paired), so a fill that happened while it was stopped reaches your strategy.
Backtesting¶
The backtester supports every order type and class above, including trailing legs on
brackets and OTO orders. Backtests run continuously, so simulated orders always work
there; check supports_order before relying on the same orders live. See
How Orders Fill in a Backtest for exactly how stops, gaps and “both exits touched in
one bar” are filled.
Frequently asked questions¶
What happens when I send an order my broker cannot hold?¶
Nothing is sent. The order’s status becomes error, error_message explains
which part is not supported, and on_error_order is called. LumiBot never changes it
into a different order, such as a market order instead of a trailing stop.
How do I check whether a broker supports an order before sending it?¶
Call self.broker.supports_order(order), or describe the order with
supports_order(order_type=..., order_class=..., trailing_leg=...). Check
support.supported and read support.reason when it is not.
What is the difference between native and simulated orders?¶
A native order rests at the broker and keeps protecting your position even when your strategy is stopped. A simulated order is handled by LumiBot and only works while your strategy is running.
Can I put a trailing stop on a bracket order?¶
In backtests, yes: pass secondary_trail_percent or secondary_trail_price. Live,
no broker in the table above holds a trailing leg on a bracket yet, so it is refused.
Submit a separate trailing stop after the entry fills where the broker supports trailing
stops.
How do I protect a Bitunix position with a stop?¶
Attach it to the entry: use order_class=Order.OrderClass.BRACKET (or OTO) with
secondary_stop_price and, for a target, secondary_limit_price. Bitunix holds them
as its stop-loss and take-profit. Standalone stop orders are refused on Bitunix.
Is it safe to run my strategy on a schedule instead of all the time?¶
For native orders, yes: they keep working at the broker while the strategy is stopped. Simulated orders, such as ProjectX bracket exits and smart limit re-pricing, stop while the strategy is not running. See the restart table above.