AuditsControls › Order rejection handling

Accounting and reconciliation · control 12 of 21

Order rejection handling: does your trading bot have it?

What happens when the exchange rejects an order or fills it partially: bounded retry, log entry, alert, or silent loop?

Why it matters

Unbounded retries multiply exposure. Partial fills that are treated as full fills corrupt the position size.

How to check it in the code

  1. Find the create-order call and read its error handling.
  2. Check retries have a maximum and a delay.
  3. Check partial fills update the position with the filled amount, not the requested one.

This is what the audit does for every bot in the list, on public code, without installing or running anything. Verdict values: present, partial, absent, not applicable, not verified. See the method.

Results across audited bots

Across 10 audited bots: 6 present, 3 partial, 0 absent, 0 not applicable, 1 not verified.

BotVerdictEvidenceNote
HKUDS/Vibe-TradingPresentagent/src/live/order_guard.py _allow(): inspects the broker response envelope; an error envelope is audited as kind='order_rejected'/outcome='error' and does NOT increment the daily counter; class attribute 'repeatable = False' documented as 'a live order must never be silently re-issued'No automatic retry on rejection; explicit no-retry design avoids exposure multiplication.
hummingbot/hummingbotPresenthummingbot/connector/client_order_tracker.py, process_order_not_found()Counts 'order not found' failures with a bounded limit (3) before marking the order FAILED and firing MarketOrderFailureEvent; it does not retry indefinitely.
Drakkar-Software/OctoBotPresentindex_trading.py IndexTradingModeConsumer._rebalance_portfolio(): except (trading_errors.MissingMinimalExchangeTradeVolume, copy_errors.RebalanceAborted) as err: logs a warning/error and marks the rebalance as REBALANCING_SKIPPED instead of retrying blindly or crashing.Structured typed-error handling with logging and a bounded abort path was found for at least the index/rebalance order path.
freqtrade/freqtradePresentfreqtrade/exchange/common.py, @retrier / @retrier_async decorators (API_RETRY_COUNT=4, calculate_backoff exponential delay, logs 'Retrying still for N times' / 'Giving up.') -- https://raw.githubusercontent.com/freqtrade/freqtrade/3774521e7028fa666770e7c7a8da323af5c4cefa/freqtrade/exchange/common.pyRetries are bounded (default 4 attempts) and logged on every attempt; the RetryableOrderError path is used specifically for order operations.
jesse-ai/jesseNot verifiedjesse/services/order_service.py exists (order state transitions) but exchange-specific rejection/partial-fill handling for real accounts is implemented in the per-exchange live drivers inside jesse_live, not present in this repositoryCould not verify retry/backoff behavior on exchange rejection within the audited repo.
chrisleekr/binance-trading-botPresentdocs/concepts/notifiers.md, order-failed rowExplicit, bounded backoff: repeated failures for the same symbol collapse into one alert per 15-minute window; a non-recoverable placement refused three times identically triggers a dedicated alert that the bot has throttled that exact request to one probe per minute — not unbounded retries.
alsk1992/CloddsBotPartialsrc/trading/orchestrator.ts guardMethod() wraps every guarded order call in try/catch, logs the error via logger.error and returns a structured {success:false, status:'rejected'} result. No bounded-retry-on-partial-fill logic was located within budget.Rejections are caught, logged and reported cleanly; retry behavior on partial fills is unverified.
Lumiwealth/lumibotPartialdocs/FAST_ORDER_LIFECYCLE_GUIDE.md testing matrix explicitly lists 'rejected hedge and bounded retry' as something a regression suite 'should cover', and states 'the strategy still owns its deadline, replacement, hedge, and conflict policy. Do not turn one strategy's timeout or risk rule into a global LumiBot default.'
YizhiSong/FriesTraderPresentPHASE_B_TASK.md Step 6: review_equity_order is always called first as a preview; a blocking alert is logged verbatim and treated as rejected without placing; after placement, get_equity_orders is polled up to twice (~15s apart) for a terminal state, whatever state comes back is logged, and a failed tool call is logged and the candidate skipped.No unbounded retry loop found that could multiply exposure on repeated rejection.
c9s/bbgoPartialpkg/types/order.go (OrderStatusRejected); pkg/exchange/binance/stream.go (handleExecutionReportEvent)Order rejection is a modeled, observable status propagated through execution-report stream events, but no universal bounded-retry policy at the framework/OrderExecutor level was found.

If your bot does not have it

The watchdog enforces daily loss, drawdown, position size and heartbeat limits from outside the bot, with a read-only key, whatever the bot's own code does.