Trading bot partial fills: reconciliation without overtrading

An order is not a yes-or-no event. It can be accepted, partly filled at several prices, cancelled with some exposure remaining, or completed while your acknowledgement is lost. A production bot must treat execution as a state machine. This guide shows how to calculate the real fill, resize protection, prevent duplicate retries, and recover the truth after a restart.

On this page
  1. What a partial fill changes
  2. Order state model
  3. Stops and bracket orders
  4. Reconciliation loop
  5. Failure tests
  6. FAQ

What a partial fill changes

A partial fill means the venue executed less than the requested quantity. If a bot sends a limit buy for 100 shares and receives fills of 30 at $50.00 and 20 at $50.04, it owns 50 shares, not zero and not 100. The remaining 50 may still be live. The bot's decisions must use confirmed exposure rather than the original request.

The volume-weighted average price is:

fill mathaverage price = Σ(fill quantity × fill price) / Σ(fill quantity)

(30 × $50.00 + 20 × $50.04) / 50 = $50.016

Fees can arrive per fill and may be charged in the quote currency, base asset, or another token. Store the raw fill records, not just a rounded average. They are needed for exact position cost, P&L, and later audits. Read market vs limit orders for why liquidity and order type change fill behavior.

Model the order lifecycle explicitly

A local boolean such as order_complete cannot describe reality. Keep the venue order ID and your own unique client order ID, then track requested quantity, cumulative executed quantity, remaining quantity, average price, fees, and the latest venue status.

StateWhat it meansSafe bot behavior
Pending submitRequest sent; outcome not yet knownDo not send a replacement until queried by client ID
OpenAccepted with no confirmed fillMonitor events and expiry policy
Partially filledSome exposure exists; remainder may be liveProtect actual exposure and track the remainder
FilledRequested quantity completedFinalize cost and protection
Cancelled/expiredNo further fills expected, but earlier fills remainManage the executed quantity; never assume flat
UnknownLocal and venue evidence disagree or is incompleteBlock new risk and reconcile
A timeout does not mean an order failed

The venue may accept an order and the response may be lost on the network. Retrying the submit can double the position. Give each intent a deterministic client order ID, look it up after an ambiguous result, and only create a new ID after proving the first intent was not accepted.

WebSocket execution events are fast but not infallible: connections drop, messages can be delayed, and clients can restart. REST snapshots are slower but useful for repair. A robust bot consumes streaming events for normal operation and periodically checks a venue snapshot to find gaps.

Resize stops and bracket orders to confirmed exposure

Partial entries create a protection problem. If 40% of an entry fills, a stop for the full intended quantity may sell more than the bot owns and open an unintended short. Waiting for a complete fill before placing any stop leaves the first 40% unprotected. The safest policy depends on venue features, but the invariant is simple: protective order quantity should never exceed confirmed reducible exposure.

  1. Receive a fill and update cumulative entry quantity.
  2. Fetch or calculate the authoritative net position.
  3. Create or resize reduce-only stop and take-profit orders for that quantity.
  4. Confirm the new protection before removing the old protection where atomic amendment is unavailable.
  5. When an exit partly fills, reduce its sibling order so the combined exits cannot exceed the remaining position.

Some venues offer native bracket, OCO, or attached orders; their behavior on partial fills differs. Test the exact broker implementation rather than assuming the labels mean the same thing everywhere. The concepts in order types explained and order book depth help predict when partial execution is likely.

Build reconciliation around authoritative facts

Reconciliation asks, “What does the venue say exists right now?” and compares that with local expectations. Run it on startup, on a timer, after reconnecting a stream, after an ambiguous submit or cancel, and before clearing a kill switch.

python · conceptual reconciliationdef reconcile(venue, local):
    remote_orders = venue.fetch_open_orders()
    remote_fills  = venue.fetch_recent_fills()
    positions     = venue.fetch_positions()

    snapshot = merge_by_venue_and_client_id(
        remote_orders, remote_fills, positions
    )
    differences = compare(local, snapshot)

    for diff in differences:
        if diff.increases_unknown_risk:
            safety_gate.trip("reconciliation_mismatch")
        else:
            record_and_repair(diff)

Compare at least these invariants:

Do not “repair” uncertain state by automatically cancelling everything. First determine whether the snapshot is complete and current. A partial API result is not proof that an order disappeared. Retry read-only queries with bounded rate-limit-aware backoff, then escalate if the evidence remains inconsistent.

Failure tests worth running

Injected failureExpected result
Entry fills 30%, then expiresBot manages 30%; it does not wait for or record 100%
Submit response times out after acceptanceClient ID lookup finds the original; no duplicate is sent
Exit partly fills while stop remains liveStop quantity shrinks to the true remaining position
WebSocket drops one fill eventPeriodic snapshot repairs cumulative fill and average price
Process restarts with a live orderStartup reconciliation reconstructs it before signals resume
Trader changes the account manuallyDifference is quarantined or adopted by an explicit policy

Log every state transition with IDs and raw venue status. An order journal makes a race condition reproducible and supports the broader practices in trading bot logging and monitoring. Run the full lifecycle in a sandbox, then at the smallest possible live size; paper venues often simulate fills more cleanly than real books.

Not financial advice. This content is educational. Automated and algorithmic trading carries a real risk of financial loss. Never trade money you cannot afford to lose. Review the SEC investor.gov and CFTC resources before trading.

Frequently asked questions

What is a partial fill in automated trading?

A partial fill occurs when only part of an order executes. The remaining quantity may continue resting, expire, or be cancelled depending on the order's time-in-force. A bot must track cumulative filled quantity, remaining quantity, average fill price, fees, and final status.

How should a trading bot protect a partially filled position?

Protection should match confirmed exposure. After each fill, calculate the actual net position and create or resize the stop and take-profit quantities without leaving a gap or exceeding the position. Confirm the replacement before cancelling old protection whenever the venue's order model allows it.

Can retrying an order create a duplicate trade?

Yes. A request can time out after the venue accepted it, so blindly submitting again may create a second order. Use a unique client order ID, query the original ID after ambiguous failures, and make order submission idempotent before any retry.

What is trading bot order reconciliation?

Reconciliation compares the bot's local records with authoritative venue data: open orders, recent fills, positions, and balances. Differences are classified and repaired or escalated. It should run at startup, periodically, after uncertain requests, and before a halted bot resumes.

MB

Mustafa Bilgic

Algorithmic trading practitioner · Founder, AITradingBot.us

Mustafa builds and backtests automated trading systems and writes about them without the hype. Every tool on this site is free and runs entirely in your browser.