Latency, Partial Fills & Real Execution
The gap between 'order submitted' and 'position as intended' is where real execution risk lives — partial fills, latency windows, and VWAP corruption are the failure modes that turn a good signal into a bad trade.
The gap between submitted and filled
Submitting an order and having a position are not the same thing. Between the moment your agent calls the exchange API and the moment you hold an actual position at a known size and price, a series of legs unfold — each taking time, each creating a window where your local belief and exchange truth can diverge.
Most execution failures are not signal failures. They are not logic errors in the strategy. They are failures in this gap: the agent believed one thing about the world while the exchange held a different truth, and the agent acted on the wrong version.
Understanding latency, partial fills, and what happens in between is what separates a demo-grade execution engine from one you can trust with real capital.
The round-trip has multiple legs
Every order submission is a sequence of hops: your agent sends the request (SUBMIT), the exchange acknowledges receipt (ACK), your agent polls until the order fills (FILL POLL), and your reconciler queries exchange positions to confirm local state matches (RECONCILE).
The critical observation is the divergence window. It opens the moment SUBMIT leaves your agent and does not close until RECONCILE confirms that local state and exchange state agree. During this entire span — which may be milliseconds or much longer depending on liquidity — your agent's local state is a guess, not a fact.
The FILL POLL leg is the most dangerous. Its duration is not fixed: it depends on available liquidity at the requested price. If liquidity is thin, the order may fill in pieces — different quantities at different prices — and each fill event arrives as a separate callback or poll result.
Partial fills: intent versus reality
Your agent intended to fill 1.0 BTC. The exchange filled 0.5 BTC and left the remainder open. This is a partial fill.
It is not an error. It is a normal outcome in markets with limited depth or during volatile conditions. The failure would be treating the partial as if it were a complete fill.
Three things go wrong when you do not handle partials explicitly:
Wrong position size for the bracket. If you place a stop-loss and take-profit sized for 1.0 BTC when you only hold 0.5 BTC, you are placing an order for twice your actual exposure. Depending on the exchange, this may produce an over-sized bracket, a rejection, or a mismatch that the reconciler will flag on the next run.
Wrong VWAP for the break-even. If the remainder fills later at a different price, the break-even for the position is the volume-weighted average across all fills — not the price of the first fill, not the intended entry price. Using the wrong break-even means every downstream calculation (stop distance, expected R-multiple, fee-adjusted edge) is built on a corrupted foundation.
Hidden fee accumulation. A single clean fill generates one fee event. A partial fill followed by a retry generates multiple fee events — one per fill, possibly one per order. Over a session, the difference between expected fees (one clean fill) and actual fees (multiple partial fills) can erode edge that looked profitable in backtests that assumed clean fills.
The cascade: detect, evaluate, decide
When reconcile detects a partial — local state shows 1.0 BTC, exchange shows 0.5 BTC — the agent has a decision to make. It cannot ignore the discrepancy. It cannot assume the remainder will fill cleanly. It must evaluate conditions and take a deliberate action.
The decision has two branches:
Retry the remainder when price conditions are still favorable. The criteria: price has not drifted significantly from the VWAP of what already filled, and volatility has not spiked. In this case, submit the remainder as a new order with an idempotent clientOrderId (so a network failure on the retry does not double the order), then wait for that fill, recompute the VWAP across all fills, and bracket the final position.
Halt when conditions have deteriorated. If the current market price has moved more than your configured threshold from the VWAP of your existing fills, retrying means chasing — filling the remainder at a significantly worse average than planned. Halt means: bracket the partial position at its actual size and VWAP, surface the situation for review, and do not chase. The cost of holding a half-sized position briefly is lower than the cost of chasing liquidity into a moved market.
VWAP across partial and multi-leg entries
VWAP — volume-weighted average price — is straightforward to compute but easy to corrupt:
VWAP = sum(qty × price) / sum(qty)
The denominator is the sum of actual filled quantities, never the intended quantity. If you fill 0.5 BTC at $60,000 and then 0.5 BTC at $60,200, the VWAP is $(0.5 × 60000 + 0.5 × 60200) / (0.5 + 0.5) = $60,100$.
If your position is still partial — 0.5 BTC filled, 0.5 BTC pending — the VWAP is just the price of what filled: $60,000. When the remainder fills at $60,200, you update VWAP to $60,100. The break-even your stop and take-profit reference must update with it.
This matters more than it sounds. A stop placed at 1% below $60,000 is a different stop than one placed at 1% below $60,100. Over many trades, bracketing off the wrong VWAP introduces a systematic bias in how much you actually risk per trade versus how much you think you risk.
Fee accrual across retries
Exchanges typically charge per fill event, not per order. Two fills of 0.5 BTC each will often generate two fee charges; a single fill of 1.0 BTC generates one.
When you retry the remainder after a partial, you are submitting a new order. That new order's fill generates another fee. If the retry itself partially fills and you retry again, you accumulate yet another fee.
The practical implication: reconcile should sum fees across all fill events in a sequence and surface the total to whatever monitors your execution quality. A trade that looks profitable at the signal level can have its edge compressed by unexpected fee accumulation if your backtesting assumed clean fills. Track actual fees per fill sequence, not just per trade intent.
Reconcile lag and in-flight orders
There is one more subtlety in the latency picture. Your reconciler queries the exchange periodically. But orders can be in flight — submitted but not yet filled — during a reconcile run. When reconcile polls the exchange and sees a position smaller than local state believes, there are two possible explanations: a genuine partial fill that needs action, or a fill in progress that will complete before the next reconcile run.
A well-designed reconciler distinguishes these by tracking which orders are currently in-flight. An in-flight order that has not yet been acknowledged as fully filled or rejected is in a different category than a confirmed partial. The action on a confirmed partial is to evaluate and decide. The action on an in-flight order is to wait for the next poll before declaring a discrepancy.
This is why the reconciler must track order state, not just position state. Position-only reconcile misses the in-flight window entirely.
Build it: handle the partial fill decision
Implement handlePartialFill() — the function that aggregates actual fills, computes VWAP from exchange truth, decides whether to retry the remainder or halt, and returns the correct action with the size to bracket.
What sound execution actually guarantees
The tools in this lesson — latency-aware reconcile, VWAP aggregation across fills, retry-or-halt decision logic, idempotent retry IDs, and fee surfacing — do not make partial fills go away. They make partial fills safe to handle without corrupting downstream state.
Sound execution does not mean clean fills every time. It means that when fills are messy — partial, multi-leg, delayed, interrupted — the agent knows what actually happened, acts on exchange truth rather than local belief, and never brackets a position at the wrong size or break-even.
That is the difference between an execution engine that compounds errors and one that contains them.