For the complete documentation index, see llms.txt. This page is also available as Markdown.

Partial Close (coming soon)

Shrink an open leveraged position by selling part of it, without closing the whole thing. The position stays open at a smaller size.

A normal close exits the entire position. A partial close sells only a slice of it: the position stays open at a reduced size and the proportional capital is repaid. It is the symmetric counterpart to a full close — same owner-initiated on-chain trigger, same backend-automatic execution — but it is non-terminal: the position never leaves the open lifecycle.

This is useful for taking partial profit, trimming exposure, or de-risking a position you still want to hold.

Enablement

Partial close is gated and rolling out — it is not yet enabled in production. When it is off, the on-chain requestPartialClose call is accepted and indexed but the backend does not act on it. Check the changelog or your integration contact before building against it. Everything below describes the enabled behaviour.

How it works

One user-initiated on-chain transaction; the backend handles the rest, exactly like a full close.

User action                               Backend (automatic)
───────────                               ───────────────────
Call requestPartialClose(key, units) ───► Detect event
                                          Withdraw the slice off-vault
                                          Sell the slice on the exchange
                                          Repay proportional capital, finalize on-chain
                                          ──► position stays open at reduced size
  1. Request. The position owner calls requestPartialClose(positionKey, closeTokenUnits) on the vault, naming how many position tokens to sell.

  2. Initiate. The backend withdraws that slice from the vault and posts it as a sell order on the exchange. The position is now mid-close — its live size reflects only the survivor (the part not being sold).

  3. Finalize. When the slice sells, the backend repays the proportional borrowed capital, books the proceeds, and returns the position to open at its new smaller size and lower leverage. If the slice only partially fills, the unsold remainder is returned to the position and only the sold fraction is booked. If nothing fills, the request is aborted and the position is restored to its original size — no change.

Throughout, the position's public status stays open (see Position status).

Calling the contract

Only the position owner (the wallet that called createPosition) can request a partial close. The positionKey is the on_chain_position_key field from GET /positions; closeTokenUnits is the number of position tokens to sell, which must be less than the position's current current.position_token_units and leave a non-trivial remainder.

For smart-contract wallets, wrap requestPartialClose in the wallet's batch the same way you would requestClose (see Smart Wallet Integration).

requestPartialClose is the only call your integration makes. initiatePartialClose, finalizePartialClose (and its WithUnwind / WithReturn variants) and abortPartialClose are all executed by the backend — do not call them yourself. The full canonical vault ABI (every function, event, and custom error) is published in the UI repo.

Position status

A partial close never changes the position's public status — it stays open from request through finalize, just like a force-unwind. After it completes, the current.* fields reflect the new, smaller position:

  • current.position_token_units — fewer tokens held.

  • current.notional_* — reduced exposure.

  • current.book_leverage_bps / current.market_leverage_bps — lower leverage.

  • entry.* — unchanged (the original signed open).

Positions mid-partial-close are returned by GET /positions?status=open and counted under the active state group.

Two basis-point fields let you tell an initial partial fill apart from a partial close:

  • entry.initial_fill_bps — how much of the requested size was filled at open. Frozen at open; it does not move when the position is later partially closed. Use it for an "opened at X% of requested" badge.

  • current.remaining_bps — how much of the originally opened size is still held. This drops after each partial close (e.g. 7000 = 70% left after a 30% close). That drop is by design — surface it as "remaining", not as a fill regression.

History

Fetch the executed partial-close history for a position — tokens sold, average realized price, proceeds, capital repaid, owner payout, and the size/leverage left afterwards — from the partial-closes sub-resource:

Only partial closes that finalized on-chain are returned, in chronological order. See Position partial closes for the full field reference.

WebSocket events

Four events track the slice over its lifecycle. The payload of each is the full position object (open shape with current / risk / fees / timing), identical to the REST API — see WebSocket Events. The status on every one is open.

Event
When

position.partial_close_requested

Owner's requestPartialClose was indexed; the slice is queued.

position.partial_close_initiated

The slice was withdrawn and the sell order is in flight.

position.partial_closed

The partial close finalized; current.* now reflect the reduced size.

position.partial_close_aborted

The slice did not fill; the position was restored to its original size.

position.partial_closed fires for every successful finalize, whether the slice fully sold, partially filled (unsold remainder returned), or was deleveraged by a concurrent risk unwind mid-slice — your integration treats them identically: re-render the now-smaller open position from event.data.

Last updated