QuoteStatus
The on-chain status of a quote. Names and ordering mirror enum QuoteStatus in QuoteStorage.sol (perps-core v0.8.6) exactly — all 11 members — so the uint8 carried on a Quote casts straight to this enum without translation.
This is the field that says what a quote is and what can be done to it. A UnifiedQuote carries it as quoteStatus whenever the row is anchored on-chain.
QuoteStatus is not QuoteLifecycle. Lifecycle is an SDK-side overlay tracking
the write progression (submitted → hedger filled → anchored on-chain); once a row is anchored it is simply
ONCHAIN, whether the quote is LOCKED, OPENED, or CLOSE_PENDING. Read quoteStatus for the protocol’s own
answer, and lifecycle for how far the write has travelled.
enum QuoteStatus {
PENDING = 0,
LOCKED = 1,
CANCEL_PENDING = 2,
CANCELED = 3,
OPENED = 4,
CLOSE_PENDING = 5,
CANCEL_CLOSE_PENDING = 6,
CLOSED = 7,
LIQUIDATED = 8,
EXPIRED = 9,
LIQUIDATED_PENDING = 10,
}Members
PENDING0Created, awaiting a partyB to lock it. No counterparty is committed yet.
LOCKED1A partyB locked the quote and is preparing to open it. Still not a position — nothing has been opened.
CANCEL_PENDING2PartyA requested cancellation of a LOCKED quote and is waiting for partyB to acknowledge.
CANCELED3The pending quote was cancelled before opening. Terminal.
OPENED4Opened into a live position.
CLOSE_PENDING5PartyA requested a full or partial close of the open position; quantityToClose holds the size.
CANCEL_CLOSE_PENDING6PartyA requested cancellation of a pending close and is waiting for partyB.
CLOSED7The position was fully closed. Terminal.
LIQUIDATED8The position was liquidated. Terminal.
EXPIRED9The quote passed its deadline before being settled. Terminal.
LIQUIDATED_PENDING10Liquidation is in progress for the position.
What each state allows
Every write below reverts with PartyAFacet: Invalid state when the quote is in any other status, so this table is the gate a UI should render against — not a guess derived from amounts.
| Status | Bucket | What partyA can do |
|---|---|---|
PENDING | order | requestToCancelQuote — no partyB is committed, so it cancels outright to CANCELED. |
LOCKED | order | requestToCancelQuote — a partyB is committed, so this only requests the cancel: the quote moves to CANCEL_PENDING. There is nothing to close; the position does not exist yet. |
CANCEL_PENDING | order | Wait for partyB. Once now > statusModifyTimestamp + getCoolDownsOfMA()[1] and it still has not answered, forceCancelQuote finishes the cancel unilaterally. |
CANCELED | terminal | Nothing. |
OPENED | position | Close it — instant close or requestToClosePosition, which moves it to CLOSE_PENDING. Also add/remove margin and set TP/SL. |
CLOSE_PENDING | position | requestToCancelCloseRequest → CANCEL_CLOSE_PENDING. A LIMIT close can instead be pushed through with force close once its cooldowns and price window allow. |
CANCEL_CLOSE_PENDING | position | Once now > statusModifyTimestamp + getCoolDownsOfMA()[2], forceCancelCloseRequest returns the quote to OPENED. |
CLOSED | terminal | Nothing. |
LIQUIDATED | terminal | Nothing. |
EXPIRED | terminal | Nothing. |
LIQUIDATED_PENDING | position | Nothing — liquidation is in progress. |
Both cancel requests double as the expiry path: called on a quote already past its deadline, requestToCancelQuote and requestToCancelCloseRequest expire it instead. PENDING, LOCKED, CANCEL_PENDING, CLOSE_PENDING, and CANCEL_CLOSE_PENDING are the statuses a quote can expire out of.
The two getCoolDownsOfMA indexes above are the cancel cooldowns — index 1 gates forceCancelQuote, index 2 gates forceCancelCloseRequest. See getCoolDownsOfMA.
Order or position? Ask the status
The reads are already split along this line: getPartyAPendingQuotes returns only PENDING / LOCKED / CANCEL_PENDING, and getPartyAOpenPositions only OPENED / CLOSE_PENDING / CANCEL_CLOSE_PENDING / LIQUIDATED_PENDING. After reconciliation both sets arrive in one flat list, so classify with the predicates rather than re-deriving the split:
import { isActivePosition, isPendingOrder, partitionQuotes } from "@symmio/trading-core";
const { positions, pending } = partitionQuotes(quotes);Never infer “is this a position?” from openQuantity. UnifiedQuote.openQuantity is quantity − closedAmount —
how much is left to close — so a quote that never opened still reports its full size. Split a positions tab
from an orders tab on openQuantity > 0 and every resting LOCKED limit order lands among the positions behind a
Close button that cannot work: the quote has no openedPrice, so it also prices its whole notional as profit against
an entry of zero. Use isPendingOrder /
isActivePosition, which read this enum.
Related
- UnifiedQuote — carries
quoteStatuson every anchored row. - QuoteLifecycle — the SDK’s write-progression overlay, which this is not.
- partitionQuotes — the positions/orders split, built on these statuses.
- Cancel a quote · Cancel a close — the full write flows.