BelethBELETH
Operating the agent
Markdown

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

image

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.