Order failures and partial fills
One multi-leg order per spread makes a half-filled spread structurally impossible; what the agent does when a submission is rejected or the account ends up unbalanced anyway.
Last updated August 31, 2026

One order, both legs
Every entry and every exit is a single multi-leg (mleg) order with both
legs inside it — never two separate orders, never a naked leg. Alpaca fills the
package against one net limit price, which is what makes the classic failure —
one leg fills, the other does not, leaving unhedged exposure — structurally
unavailable here. Alpaca's own documentation puts it as reducing "the chance of
partial fills that could distort the intended strategy". Options orders are
day-only, so an unfilled order simply expires at the close and the next cycle
re-evaluates from scratch.
When a submission is rejected
submit_mleg_order wraps any API error as one OrderSubmissionError and does
not retry — a silent retry could double a position. The cycle catches it and
persists a trades row with status = 'submission_failed' and the broker's
message, emits an order_failed (or exit_failed) event, and prints the error.
The rejection is a first-class row in the decision log and shows in the
dashboard with the same weight as a fill.
Several checks stop an order before it is even built, each logging its reason instead of sending: no fillable net-credit limit exists; being marketable would concede more than half the measured credit; or the sized quantity came out at zero.
No silent stacking
Because the loop runs every few minutes, a resting order is committed risk the position count cannot see. So if an entry order is already resting on the account, R10 rejects every new entry that cycle — no stacking of unfilled entries (this was a real day-one incident, since fixed). An order whose legs cannot be read is treated as opening risk, not ignored. On the exit side, a triggered close whose spread already has a working closing order is skipped, not duplicated.
If the account still ends up unbalanced
Each cycle pairs the account's open option legs back into spreads. Anything that
does not pair — a lone short leg (naked exposure), an unpaired long, a
non-option position — is surfaced as an anomaly: it is written to the log,
raised as a position_anomaly event, and blocks all new entries until it is
resolved. There is no automatic unwind. The agent's response to an unexpected
account state is to stop adding risk and make the problem visible, not to trade
its way out of it — clearing it is an operator action.