Order Lifecycle
Every trade Balchemy executes — whether triggered by a chat command, an automation rule, or an external agent — follows the same lifecycle managed by the Rust trading engine. Understanding this lifecycle helps you interpret order statuses in the Studio dashboard, debug failed trades, and design automations that react to order outcomes.
The execution pipeline
Before an order ever enters the state machine, it passes through a five-stage pipeline:
- Intent — The user's natural-language command or agent tool call is parsed into a fully-resolved trading intent: action, token, amount, chain, wallet, and order type.
- Pre-trade policy — The trading engine evaluates four policy gates: global kill switch, per-chain kill switch, circuit breaker for the primary provider, and per-user trading lock. If any gate is active, the order is rejected immediately — no database write occurs.
- Policy — The order is checked against the bot's risk policy: maximum position size, stop-loss thresholds, allowed token list, and any active guardrails. Violations return a clear error before execution.
- Execute — The order is submitted to the chain (Jupiter for Solana, 0x for EVM). The engine acquires a Redis idempotency lock for 180 seconds to prevent double-execution during retries.
- Verify and Notify — On-chain confirmation is received, the order is marked as filled, and the result is sent back to the originating channel (chat, webhook, or API callback).
Order states
The trading engine uses an eight-state finite state machine (FSM). Each state has a precise string value stored in the database.
| State | Stored as | Meaning |
|---|---|---|
| Pending | pending | Created and awaiting execution. |
| Pending Approval | pending_approval | Waiting for explicit user approval before proceeding. |
| Executing | executing | Being submitted to the chain right now. |
| Filled | completed | Fully confirmed on-chain. Terminal state. |
| Partially Filled | partial | Some legs executed; watching for remaining (partial-sell orders). |
| Cancelled | cancelled | Cancelled by user or system policy. Terminal state. |
| Failed | failed | All retries exhausted or a hard error occurred. Terminal state. |
| Expired | expired | Limit or DCA order passed its expiry time without triggering. Terminal state. |
Only completed, cancelled, failed, and expired are terminal states. The FSM rejects any transition that is not in the allowed table — for example, you cannot re-open a completed order.
State transition table
The following table shows every valid state transition. Any event applied to a state not listed here returns an InvalidTransition error.
| From | Event | To |
|---|---|---|
pending | StartExecution | executing |
pending | Cancel | cancelled |
pending | Expire | expired |
pending_approval | Approve | pending |
pending_approval | Cancel | cancelled |
executing | Fill | completed |
executing | PartialFill | partial |
executing | Fail | failed |
executing | Cancel | cancelled |
partial | Fill | completed |
partial | Cancel | cancelled |
failed | StartExecution | executing (retry) |
Market order flow
A standard market order (buy or sell) follows this path:
Step 1 — Command received. You type "buy 0.1 SOL of BONK" in chat, or an agent calls trade_command.
Step 2 — Intent resolved. The backend parses the command into a ParsedTradingIntent with action: "buy", token_mint: <BONK mint>, order_type: "market", and your configured wallet.
Step 3 — Policy gates pass. The trading engine checks kill switches, circuit breaker, and trading locks. All pass.
Step 4 — Order created as pending. The order document is written to MongoDB with status: "pending". An execution history entry is appended: created from web.
Step 5 — Execution lock acquired. The engine sets a Redis SETNX lock (order:<id>:executing with 180s TTL) and transitions the order to executing.
Step 6 — Swap submitted. For Solana, Jupiter is queried for a route and the swap transaction is sent. For EVM, 0x is queried and the swap is submitted.
Step 7 — On-chain confirmation. The transaction signature is received. The engine calls mark_filled, which transitions the order to completed, records the transaction signature and actual output amount, and releases the execution lock.
Step 8 — Callback sent. The backend receives the fill callback from the trading engine and notifies the originating channel (chat message, webhook event, etc.).
Approval flow (pending_approval)
Some orders require explicit approval before execution begins. An order enters pending_approval when:
- The bot's risk policy requires confirmation above a certain dollar threshold.
- An automation or external agent creates an order but the bot is configured to require human sign-off.
To approve a pending order:
- Open Studio and navigate to the bot's trading dashboard.
- Find the order in the Pending Orders section — it shows an "Approve" or "Cancel" action button.
- Click Approve. The order transitions from
pending_approvaltopendingand immediately proceeds to execution.
To cancel a pending order:
Click Cancel on the pending order. You can also cancel via the API or the MCP tool trading_orders_cancel:
{
"method": "tools/call",
"params": {
"name": "trading_orders_cancel",
"arguments": {
"order_id": "ORDER_ID_HERE"
}
}
}Cancelling an order in pending or pending_approval transitions it to cancelled. You cannot cancel an order that is already executing — the blockchain transaction is in flight.
DCA order lifecycle
DCA (Dollar-Cost Averaging) orders split a large position into multiple smaller executions over time or at defined price intervals. Each execution leg is a separate TradingOrder document linked by a parent_order_id and tracked with dca_leg_index.
The parent DCA order remains in pending until all legs complete. Each leg follows the standard market order flow independently. When the last leg fills, the parent order transitions to completed. If any leg fails after exhausting retries, that leg is marked failed but the remaining legs continue unless you cancel the parent.
To monitor a DCA order, filter the trading dashboard by the parent order ID. All associated legs appear as child entries.
Order types
Balchemy's trading engine accepts six order types, set in the order_type field of the parsed intent:
| Type | Description |
|---|---|
market | Immediate execution at the current best price. |
limit | Execute only when the token price reaches a specified target. Expires if not triggered. |
dca | Split into multiple market legs executed over time. |
trailing_stop | Follow price upward and execute when price drops by a configured percentage from peak. |
partial_sell | Sell a position in multiple tranches at predefined price levels. |
stop_loss | Sell immediately when price drops to a specified level to limit losses. |
Failure scenarios
Slippage exceeded
If the on-chain swap price moves beyond your configured slippage tolerance before the transaction confirms, the transaction fails. The order transitions to failed. You can retry by approving a new order at the current price, or increase your slippage tolerance in the bot's trading configuration.
Insufficient funds
If the wallet does not have enough balance to cover the trade amount plus network fees, the execution attempt fails immediately. The order transitions to failed with an error message describing the shortfall. Fund your wallet and place a new order.
Circuit breaker open
If Jupiter (Solana) or 0x (EVM) is experiencing elevated error rates, the circuit breaker opens and new orders for that chain are rejected at the policy gate — before the pending state is even created. You will see an error message: "Circuit breaker is open for provider: jupiter." The circuit breaker closes automatically when error rates normalize.
Kill switch active
If an administrator activates the global or per-chain kill switch, all new orders are rejected. This is an emergency control used during severe market events or platform maintenance. Existing orders in executing state continue to completion; only new order creation is blocked.
Retry behavior
When an order in executing transitions to failed, the engine checks whether retry_count < max_retries (default maximum: 3). If retries remain, the order is automatically reset to pending and re-queued for execution. Each retry is recorded in the execution history with a retry_scheduled entry. After all retries are exhausted, the order stays in failed permanently.
Checking order status
In Studio: Navigate to your bot and open the Trading tab. The order table shows real-time status with color-coded badges. Click any row to open the order detail drawer, which displays the full execution history, transaction signature, actual output, fees, and error messages if applicable.
Via API (MCP): Use the trading_orders_list tool to fetch orders by status:
{
"method": "tools/call",
"params": {
"name": "trading_orders_list",
"arguments": {
"status": "pending",
"limit": 20
}
}
}The response includes the full order document for each matching record, including the execution history array that shows every state transition with timestamps.
Via REST API: Send GET /api/trading/orders?status=completed&limit=50 with your session token. See API Endpoints for the full parameter reference.
Execution history
Every order maintains an execution_history array — an append-only audit trail of every state transition. Each entry contains:
| Field | Description |
|---|---|
status | The step name (e.g., created, approved, executing, completed, failed, retry_scheduled). |
timestamp | UTC ISO timestamp of the transition. |
details | Human-readable description of what happened. |
tx_signature | Transaction signature, present only on fill events. |
error | Error message, present only on fail events. |
The execution history is read-only and append-only — it cannot be modified after creation. Use it as your audit trail when investigating order issues.
Related pages
- Solana Trading — chain-specific execution details for Solana
- EVM Trading — chain-specific execution details for Base and Ethereum
- Trading Dashboard — how to view and manage orders in Studio
- Tool Catalog —
trading_orders_list,trading_orders_approve,trading_orders_cancel - Glossary — definitions for circuit breaker, kill switch, slippage, DCA, and more