open and an add_collateral carry no price. Each one takes the stake when it lands and becomes
an order that waits for the venue’s next price. A close is a request that carries no price,
and anyone may complete it with a priced settle.
This page is the contract a client or a bot builds against. A bot that opens, adds and requests
closes needs a wallet and an RPC endpoint. It needs no access to the price feed, no token and no key
from us. Only settle, stamp_book, set_take_profit and prove_price carry a quote: see
getting prices.
Build an open with
ix.open(accounts, args, priceAccountsFor(market, config, 'open')), as
opening a position shows, an add with
addCollateralFor(market, config, accounts, args) and a close request with
requestCloseFor(market, accounts, seq). Each reads the market’s feed kind and the governed
config: decode both on every request. On a feed-kind-3 market (isKind3Market(market)) the open,
the add and the close request each carry the governance table, and the program refuses them
without it (GovTableRequired). These three add it. Never build an open with openForMode: it
leaves out the market’s fee-terms account and the governance table.The life of a position
An open lands as an order. A fill makes it a live position, or the order is returned with the whole stake. A live position ends at a close request, its barrier, its target or a timer, and a settle closes it. How a position ends has every close reason, and the glossary has the words.The timing settings
Every wait on this page is a count of slots, and every count is a governed setting. Read them through the SDK on each request. Do not compile one into a client.
Each setting can change by a governed write with no program upgrade. The SDK is the source:
Opening: an order, then a fill
- Send
open. It carries no quote. Its remaining accounts are the governance table on a feed-kind-3 market, then the market’s fee-terms account when the market has terms (priceAccountsFor(market, config, 'open')). When it lands, the stake is taken, the slot holds the order withentryPrice0, andPositionOpenedcarries the sequence number. It is an order, not yet a position. - The entry price is the first price of the venue’s price feed at or after
dslots from the slot in which theopenlanded. On a feed-kind-3 market the delay is clock time from the second theopenlanded, and the entry is the first signed tick at or after it. A listed filler writes it. Read it from the eventEntryFilled(entryPrice,quoteSlot), or from a non-zeroentryPriceon your own book. On a feed-kind-3 marketquoteSlotis the slot the fill landed in. - Or the order is returned. The event is
OpenVoided:cause,refundTotal,feeKeptanddelivered. An order is returned when the first price came too late or did not meet the venue’s quality rule, when the market closed, or when nothing filled it insided + T_fillslots of the order (for example, when the pool could not cover it).refundTotalis the whole stake, andfeeKeptreads 0.deliveredfalse means the payout destination could not receive the refund, and it is held on the book: claim it withclaimPayout. - You cannot fill your own order (
NotAListedFiller, 6205). You need not return it either: afterd + T_fillslots anyone may sendvoid_open, and the venue’s keepers do.
voidReasonOf(cause) names the cause, and VOID_REASON_TEXT has the words for a trader. The fill
is the step that judges the price. So the price rows of openGates do not decide an order, and the
quote refusals of quotes and freshness cannot refuse one.
Build with ix.open and priceAccountsFor(market, config, 'open'), send, and then watch the book. The EntryFilled or OpenVoided event is
in the filler’s or the keeper’s transaction, not in yours:
Doubling down
Sendadd_collateral with no quote. CollateralCommitted is in your own transaction:
the added stake is taken (addedCollateral, totalDebit), the timer extension is written at once
(timerAfterS), and addTarget is the slot d after the add. Then one of two:
CollateralAdded. A filler priced the add at the first price at or afteraddTarget. It carries the new entry (entryPrice) and the entry before it (entryPriceBefore).AddReversed. The added stake is returned in full (refundTotal), as for a returned order.causeis one of the open’s causes, or 8 (the price was outside the position’s limits: at the add’s price the position was already at its barrier or its target) or 9 (the position was knocked out first).
- Until one of the two arrives, and then for
M_addslots after the price of a filled add, a close request, a retarget, an expiry mark and a second add returnAddPending(6211).closableFromSlotOfincludes that time. - A reversed add keeps the time it added to the timer. Its stake comes back.
- A knockout price from before the add still counts in that time. A price that breached the
barrier of the entry in force at its slot can still prove the knockout.
KnockoutBeforeAddreports it, the position is knocked out on its old stake, and the added stake comes back (AddReversed, cause 9). - A touch of the take-profit target while an add waits for its price cannot be stamped, and it is lost if the add is then filled.
addCollateralForrefuses to build an add while adds are off (AddsDisabled, 6212). On a feed-kind-3 market the add carries the governance table: build it withaddCollateralFor, notaddCollateralForMode, which refuses a kind-3 list.
Closing: a request, then a settle
- Send
request_close, signed by the owner or a session key. It carries no quote, so it cannot fail on a stale price. On a feed-kind-3 market it carries the governance table (requestCloseForadds it).CloseRequested.exitTargetis the slotdafter the request; on a feed-kind-3 market it reads 0, because that market’s exit target is a clock second, not a slot. - The exit price is the first price of the venue’s price feed at or after
exitTarget. The close is done whenSettlementPricedandSettlementReceiptarrive. - Anyone may send that settle, and the venue’s keepers do.
isPermissionless('voluntary', phase)is true once the phase isExitRequested. - The window rule. If nobody settles inside
Wslots of the target, the price becomes the first price of the next window ofWslots, and so on.SettlementReceipt.windowis the count of windows that rolled: 0 for a close settled in its first window. - A request cannot be withdrawn. A second request returns
ExitPending(6213): treat it as success. SettlementPriced.unverifiedreadstruewhile a market lists one price feed. A price from a single feed isunverifiedby construction; it does not mean the price was wrong. Do not show a warning or a badge from it.
request_close, mark_expiry and open need no quote instruction: send each one with the
compute-budget pair. computeUnitLimit(built.name) gives the declared limit, and for the
open computeUnitLimit('open').
The minimum hold
A position cannot be closed by its owner in the first slots after its entry price.- The venue has one hold for every market:
resolveMinHoldSlots(config.minHoldSlots). A market can have a longer one:market.closeHoldSlots(0 is none). The hold in force is the larger of the two (holdInForce). - It counts from the entry price:
EntryFilled.quoteSlot, and after a filled double-down from the add’s price, which is the position’sopenedSloton the book. - A close request inside it returns
MinimumHoldNotMet(6103) for the venue’s hold orMarketHoldNotMet(6236) for the market’s. Neither one is a failure of the position. - “Can close in N seconds”, with no guess:
closableFromSlotOf(position, config, market)is the first slot at which a request can land, from three public accounts. It isnullwhile the position is an order or an add waits.slotsUntil(slot, nowSlot)counts the slots, andestimatedSecondsUntil(slots, slotMs)turns them into an estimate that you label as one. Pass a slot time you measured (getRecentPerformanceSamples), notconfig.params.slotMs, which is longer than a mainnet slot. - The request must land at or after that slot.
- A take-profit proof must also clear both holds, on the slot of its own price.
- A knockout is never held back. A market’s own hold never holds back a timer end.
Timers
- From the deadline, anyone may send
mark_expirywith reason 3 (userExpiry) or 4 (tenorExpiry).ExpiryMarked.exitTargetis the slotdafter the mark, and the exit price is the first price at or after it, by the same window rule as a close. - A settle with an expiry reason on a position that has no exit target returns
ExitNotAnchored(6214): mark it first. - A wrapper such as Minute sends the mark and then its own settle from slot
exitTargeton: two transactions, at leastdslots apart. - A timer is 0 or at least 26 seconds at the open. After an add, the position’s deadline must be at
least 26 seconds after the add lands (
TimerBelowFloor, 6225).
Knockouts and take-profits
- Anyone may prove a level with any signed price that is newer than the entry and at most
W_koslots old. So a position can be knocked out by a price up toW_koslots old. - After a close request or an expiry mark, only a price that is not later than the exit price can
prove a level (
ExitPendingotherwise). - While an add waits for its price, a touch of the take-profit target cannot be stamped, and it is lost if the add is then filled.
- After a take-profit retarget, only a price at least
dslots after the retarget proves the new target (TakeProfitProofTooEarly, 6220). stamp_bookrecords the level on the book; the settle that pays it follows. It is the only stamp that can prove a knockout with a price from before a filled double-down.
Settling or stamping for others
This is optional. The venue’s keepers send settles and stamps.- A settle of a requested or marked exit needs the window quote: the first price of the venue’s
price feed at or after the start of the current window,
exitTarget + K × W, whereKis the count of whole windows since the target. Any other quote returnsNotWindowQuote(6217). stamp_bookneeds any signed price that breaches the level, inside theW_kolook-back.- To obtain a quote, see Getting prices.
Two more rules
- A client may settle its own requested close in the first window, with the window quote. That
keeps the first price. If nobody settles in
Wslots, the next window’s price applies, and a knockout price in between counts. The count of rolled exits is public:SettlementReceipt.windowis not 0 for them. - Read a position through the SDK’s accessors:
phase,isUnfilled,pendingAdd,addTargetSlot,addMemo,memoIsLive,exitTargetSlot,exitReasonandtpFloorSlot. The same raw bytes of a position mean different things in different states. See reading positions.
What a client must never assume
- That the price on screen at the tap is the entry or the exit. It is the next price after the delay.
- That a landed
openis a position. It is an order untilEntryFilled. - That a landed
request_closeis a close. It is a close atSettlementPriced. - That
PositionOpenedcarries the entry. It has no price field. A position’sentryPriceon the book is 0 untilEntryFilled. Take entries fromEntryFilledandCollateralAdded, and returns fromOpenVoidedandAddReversed. - That a price in one of Minute’s round events is a price. See integrating Minute.
- That a second transaction is safe after a timeout. Never sign a second order for the same intent inside one blockhash life. Send the same signed bytes again.
- That adds are on.
addCollateralForrefuses to build one while they are off. - That a void or a reversal is an error. It is a normal outcome, and the stake comes back
(
refundTotal). - That two events in one slot are in time order. Order by
quoteSlot.
openerTag.
Events
Each has a decoder:
decodeEntryFilled, decodeOpenVoided, decodeCollateralCommitted,
decodeAddReversed, decodeCloseRequested, decodeExpiryMarked, decodeKnockoutBeforeAdd and
decodeSettlementReceipt. On a landed transaction, receiptOf and
venueEventsNamed(receipt, 'EntryFilled') read them all. Every event carries owner, market
and seq, and the events of a position carry its openerTag.
Refusals a client can get
The codes are fixed.retryClassOf(code) gives the class of each one, and RETRY_CLASS_LABEL the
words: see error handling.
When the venue stops taking orders,
open and add_collateral return ProtocolPaused (6046) and
config.paused is true, as for any pause. set_take_profit returns it too. Closes, timer ends and
knockouts keep working, and an order already placed is filled or returned as usual. See risk.