Skip to main content
Your position’s terms (its fees, barrier, take profit and tenor) are locked in when it opens. A later change by the protocol never reaches a position you already hold. Two guardian switches can stop it closing; see settlement. The one thing you can change is its size: you can add to a live position. That averages your entry and moves both price levels with it. See adding to a position. The governed timing settings are read when each step runs: the minimum hold, the grace before a no-price close and the slot delays. A position that stored a funding grace of 0 also reads the live config.params.borrowGraceS (see funding).

An open is an order

Your stake is taken when the open lands, and the venue’s next price sets your entry. The open carries no price. The program records it as an order with a target slot a short, governed delay after the slot in which it landed (d slots). The entry is the first price of the venue’s price feed at or after that target. A filler on the venue’s list writes it, and the order becomes a position. The barrier and the target are distances from that entry.
  • The price on your screen when you tap is not your entry. Your entry is the next price after the delay.
  • An order that is not filled in time is returned. If the first price comes too late or does not meet the venue’s quality rule, if the market closes, or if no filler lands inside the fill window, anyone can return the order, and the venue’s keepers do. You get your whole stake back.
  • You cannot fill your own order. Only a filler on the venue’s list can write an entry.
  • A landed open is not yet a position. Read your entry when the order is filled, never from the screen at the tap.
The timing settings, the events that report each step and the SDK calls are in orders and closes.

The five choices

1

Asset and direction

Which market you are trading, and whether you are long or short. The market names the price feed that decides your outcome — see the price that decides.
2

Stake

Your collateral, between the venue’s minCollateral and maxCollateral. It, and any entry fee paid on it, is the entire amount at risk. The bounds are governed and live on chain; read them from the config account.
3

Target (optional)

A take-profit target you can add, as a multiple of the stake. Any multiple at or above 2.00x is admitted, up to the protocol’s payout cap — the one ceiling, read as config.params.payoutCapMultiple. Without one, the target is the payout cap.
4

1000x

How far the price has to move to reach either end, so how fast the position ends. 1000x on every market. Pass tier on open: 0 means the market’s own. A market’s tier is the ceiling and the venue’s config.minTier is the floor. Above the ceiling is refused (LeverageAboveMarketTier), below the floor is refused (LeverageBelowMinTier), and so is a value the program does not list. Both values are governed: read them from the chain.The 1000x sets the DISTANCE to both levels. The fee terms read the size of the price move, so the same target pays differently at another tier.
5

Timer (optional)

You may set your own expiry. Leave it at zero and you have no clock at all, because the protocol’s tenor is indefinite (see the tenor). A timer you set is at least 26 seconds.Setting a timer is free. A timed open pre-pays nothing. See the fees.

The barrier

Every position has a barrier: a price, fixed at open, a set distance on the losing side of your entry. If the price touches it, the position is knocked out and you get nothing back. Three things to know:
  • It is a distance, not a dollar amount. The distance is set by the position’s 1000x tier and its loss tax rate, both locked in at open. At 1000x the barrier is near, which is why a position ends fast.
  • It is direction-relative. For a long it sits below entry; for a short, above.
  • It is fixed at your open. Its distance is frozen when your order lands, and its price is set when the order is filled. A governance change later does not move a barrier that already exists.
The exact distance for the terms you are about to open is returned by the SDK’s openPreview().barrierMicroBp, and the resulting price by levels(). Read it, show it to the person taking the risk, and do not hand-compute it.
At the barrier the trader receives nothing. Any interface that tells a user they keep some fraction of their stake on a knockout is wrong. Plan your product around the stake being gone.Nothing is meant literally, not “nothing worth rounding”. At the barrier the loss tax takes the whole of the position’s equity, and the crumb the flooring leaves behind — a few base units, a ten-thousandth of a cent — goes to the liquidity pool, not back to the trader. settlePreview returns 0 for a knockout and that zero is exact.

The target

The target is optional. It is the other side of the barrier: a price, also fixed at open, sitting the distance away that corresponds to your chosen multiple. Touch it and the position has a gross gain of the multiple less one, times your stake: a 2.00x target is a gain equal to the stake. What you receive is that gain less the share of it the terms withhold and less the exit fee, so a target pays less than its multiple. Fees and limits has a worked example. A target can be any multiple of your stake at or above 2.00x. The floor is RUNG_FLOOR_BPS, 2.00x. Without a target, the position’s target is the payout cap, the one ceiling, config.params.payoutCapMultiple; a market carries no take-profit ceiling of its own. A value above the live cap is refused RungAbovePayoutCap.

Adding to a position

A trader may add collateral to a position that is already live. It is one order, not two: the stake grows, the entry becomes the average of the prices actually paid, and both levels move with it. An add is an order too. It takes the added stake when it lands (CollateralCommitted) and carries no price. The add is priced at the first price of the venue’s price feed at or after the same short delay, and then one of two things happens:
  • It is filled (CollateralAdded): the entry becomes the blend, and both levels move with it.
  • It is returned (AddReversed), in full: if no price or no filler comes in time, or if at the add’s price the position is already at its barrier or its target. A returned add keeps the time it added to your timer.
Until the add is filled or returned, and for a short, governed time after a filled add’s price (M_add slots), you cannot close the position, move its target or add again (AddPending). In that time a price from before the add that breached the old barrier still proves a knockout: the position is knocked out on its old stake, and the added stake comes back. A touch of the target while an add waits for its price does not count, and it is lost if the add is then filled.
Adding while you are AHEAD moves the barrier TOWARD the live price. This is the single fact an interface must not bury. The 1000x is fixed by the POSITION’s tier, chosen at open and frozen into the slot, so an add buys size at market. It is not “adding margin”: it adds size at the same 1000x.Both levels are distances from the entry, and the entry always moves toward the price the add filled at. So for a long:A short is the mirror in price and the identical statement in distance. A trader who is ahead spends their profit cushion on size. Do not offer this control to a winning position with copy that suggests it protects one.
Adding neither realises nor creates a profit or a loss. A trader who is $0.40 down and adds $1 is still $0.40 down at the add’s price: the entry is the collateral-weighted harmonic mean of the old entry and the fill price, which is the only blend with that property, and it is also the average cost basis every trading interface already shows.
What else it does, all of which a client has to handle:
  • The add is fee-inclusive, exactly as an open is. You hand added_budget — the total to add — the venue takes its entry fee from inside it, and the debit is at most that. min_added_collateral_out is the trader’s mandatory floor on the increment SIZE, the signature over what the add buys.
  • The stake bounds bind on two different quantities. The minimum binds on the increment, the maximum on the total. So a position already holding more than maxCollateral − minCollateral has no legal add at all — quote the admissible range and never render a control whose every value is refused.
  • The clock may only be lengthened. extend_timer_s is a delta in seconds and it only ever goes forward, and never past the tenor frozen on the position. A double-down is the only way to move a position’s own timer, and it cannot shorten it.
  • A position that has touched a level cannot be added to. An add whose price is at or through the barrier, or at or through the take-profit, is returned in full (AddReversed).
  • The hold clock restarts. A filled add re-prices the position, so the minimum hold before a close you ask for runs again from the add’s price.
Funding, below, is the one clock an add does not reset: it is measured from the original open.

Sizing an add from a total

The trader can name what leaves their wallet instead of naming the increment. sizeAddForTotal is that inverse, and it is not sizeForTotal with a different argument.
Two things differ from the open path’s answer, and each is money:
  • setsTimer does not change the debit. Setting a timer is free, on an open or an extension.
  • The bounds are admissibleAdd’s interval, not config.params. The ceiling binds on the total, so what a position may still take is the governed ceiling less what it already holds.
size.debit is never above the total, so the number the trader typed is the added_budget you sign and the chain debits at most that. A fee change landing between the quote and the signature can only shrink the increment, and the min_added_collateral_out floor you set from size.collateral turns that into a BudgetBelowCollateralFloor revert rather than a silent shrink.
addPreview, admissibleAdd, addCollateral and addImpact answer “what would this add do” and “what increments are legal”, which covers the blended entry, the admissible range and the barrier’s new price. Quote with them; do not derive the blend by hand from the formula above, and do not offer the control without showing where the barrier moves to. addGates is the gate report for an add — see reading gates.

The tenor

The tenor is the protocol’s own clock for a position. It is config.params.tMaxSeconds, a governed parameter, and it is frozen onto your position at open. The tenor is the indefinite value, so a position has no protocol clock: it ends by its barrier, its target, your own close, your own timer if you set one, the close of its session on a market with trading hours, or the no-price close under How a position ends. A position keeps the tenor it froze at open. The SDK tests for it with isIndefiniteTenor(); show “no limit”, not a date. A position that froze a finite tenor is closable by anyone when the tenor runs out. On a market with trading hours, the tenor is shorter. It is config.params.tMaxSeconds or the time left until market.sessionBufferS before the session’s close, whichever is shorter, so the position ends before its session closes. No live market has trading hours; see markets. The freeze is a rule of the program: settlement reads the tenor stamped on your position, so a governance change afterwards cannot change a position you already hold. Read the live value rather than typing it, and do not build an interface that promises a reader a duration from a constant. You may also set a shorter timer of your own. The two clocks are independent, and the earlier one is the moment your position first becomes closable by a stranger. A double-down can push your own timer out, never in — see adding to a position. The SDK computes both from the position itself:

How a position ends

The position is over and the trader gets nothing back. Plan for it: at 1000x the barrier sits close enough to entry that an ordinary move reaches it. A knockout settles at the barrier no matter how far past it the price ran, so it needs no exit price to price it.Anyone may prove it with any signed price that is newer than the entry and at most W_ko slots old, a governed setting. So a position can be knocked out by a recent price that has already moved back on your screen. After a close request or an expiry mark, only a price that is not later than the exit price can knock the position out.
The position closes at its target, with a gross gain of the multiple less one, times your stake. You receive that gain less the share the terms withhold and less the exit fee, within the payout cap. It is proved the same way as a knockout, and the price that proves it must also clear the minimum hold. A touch while a double-down waits for its price does not count. After you move your target, only a price at least d slots later proves the new one.
Either your own timer or the protocol’s finite tenor (an indefinite tenor never expires). From the deadline, anyone may mark the expiry (mark_expiry). The position settles at the first price at or after d slots from the mark, by the same window rule as a close you ask for, whatever that price is: a position expiring in profit is paid, and one expiring in loss is settled where it stands.
You ask for the close with request_close, signed by you or by a session key you authorised. The request carries no price, so it cannot fail on a stale one, and it cannot be withdrawn. The exit price is the first price of the venue’s price feed at or after d slots from the request. Anyone may then send the settle that pays it, and the venue’s keepers do.The window rule. If nobody settles inside the exit window (W slots) from that target, the price becomes the first price of the next window, and so on, and a knockout price in between still counts. So the close price is not locked when you ask, and no interface should say it is. You cannot ask inside the minimum hold.
If no price can be obtained for a long time, a position can be closed with no quote, only while no fresh price exists.A live position is paid its stake back with the exit fee waived; a position already stamped at a level is paid that level, and a target level pays the exit fee. The close is refused while the oracle is frozen, on an order that is not filled, and while an add waits for its price.A position that is not settled before its session’s close ends at par, on a market with trading hours: a quote from a later session never prices it.
The SDK names seven close reasons, because expiry has two flavours: voluntary, knockout, takeProfit, userExpiry, tenorExpiry, lastResort and borrowSpent. The last is a close that funding makes possible once it has used a position up; it cannot arise while the funding rate is 0.

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) slots, and a market can have a longer one, market.closeHoldSlots. The longer of the two applies. Both are governed settings: read them from the chain.
  • The hold starts at your entry price, and again at the price of each double-down.
  • A close request inside it is refused (MinimumHoldNotMet, MarketHoldNotMet). The position is not affected. Send the request again from the first slot the hold allows: closableFromSlotOf(position, config, market) gives that slot from three public accounts, so an app can show “can close in N seconds” with no guess.
  • A take-profit must also clear the hold, measured to the slot of the price that proves it. The venue’s hold also applies to the end of your own timer.
  • A knockout is never held back, and a market’s own hold never holds back a timer end.

Settlement is permissionless

Every close ends with a settle that anyone may send. Only one step needs the owner: the request for a voluntary close. A knockout and a take-profit are proved by the price, and the two expiries and lastResort by the clock. Every priced close carries a signed quote from the venue’s price feed; lastResort needs none. Two switches can stop closes: the guardian’s oracle freeze starves every priced close of a price and refuses lastResort, and its settle kill-switch refuses every close. Only the config authority lifts either. Read config.oracleFrozen and config.settlesDisabled. Nobody is paid for sending a close. The protocol pays its submitter nothing — no fee, no share of the settlement, no gas rebate. If you run a keeper, budget for it as a cost.

The payout cap

No target can be set above the payout cap. The cap is the governed payoutCapMultiple, the largest multiple the program lets a target take. A position with no target of its own has the cap as its target. Read it rather than typing it. A win is paid in full. Part of it may be paid later, in order. There is no guaranteed time. The amount queued ahead of you is shown. How, and what a client shows, is in how a win is paid.

The fees

Four, all on chain. Three are charged on an event; funding is charged on time. Setting a timer is free. The rates, with the date they were read, are on Fees and limits.
  • an entry fee (config.params.entryFeeBps, basis points of the stake), taken from inside your budget when you open, and from the increment when you add. It is 0, so an open costs the stake and nothing more. The venue is fee-inclusive: you hand a total, it takes the fee from within it, and the debit is at most the number you signed;
  • a loss tax, charged at a close at a loss and taken from the position’s equity before the exit fee. It is a share of the loss (config.params.lossTaxBps), frozen onto the position at open. Fees and limits has the rate, the steps and a worked example;
  • an exit fee, charged at every close except a knockout, and waived on a live position closed by lastResort. It is a share of the exit value, the money the position closes with before the fee. The rate depends on the price move at the close: one rate when the close is a gain beyond a small band around the entry price, a higher rate when it is a loss beyond the band, and a rate between the two inside the band. On a gain, the terms also withhold part of the gain: the share you keep is smaller for a small move and rises with the size of the move. Fees and limits has the values, dated, and a worked example. A position freezes its terms when it opens, so a later change to a market’s terms never reaches a position that is already open. The terms of a market are read from its fee-terms account (feeTermsTablePda in the SDK), and the share of each close is on its SettlementFeeTerms event. A wallet linked to a referrer pays less exit fee (Referrals);
  • funding, a rate on the time held. The rate is 0: nothing is charged for holding.
A knockout pays no exit fee. The total that leaves your wallet is at most the budget you hand the venue — it takes its entry fee from inside it, and setting a timer is free, so nothing is added on top. The SDK sizes the position for you:

Funding

The funding rate is 0, so nothing is charged for holding a position. It is a governed field, and a position freezes the rate it opens with. Funding is charged out of the payout at settlement, after the loss tax and the exit fee, never out of your equity, so it does not move the price that ends your position. All of it goes to the pool. Its fields are named borrow in the program and the SDK (borrowRateBpsPerHour, borrowGraceS). A position freezes its rate when it opens, and a stored grace of 0 reads the live config.params.borrowGraceS. Fees and limits has the rate in force; read the position’s own borrowRateBpsPerHour to price its settlement.
Every one of these parameters is governed: they can be changed on chain by the protocol’s authority. Do not cache them. Read them from the config account, and show the reader how old your copy is.

The price that decides your outcome

It is the price feed of the market you traded, not “the price of SOL” on some other screen. Every venue reports a slightly different number. Every price is a signed update from the market’s price feed, and the program checks the signature before it uses it.
  • Your entry is the first price at or after your order’s target slot. An order is filled only by a price that meets the venue’s quality rule for an entry. Otherwise it is returned.
  • Your exit is the first price at or after your close’s target slot, by the window rule, or the price that proves a level.
  • If no quote can be obtained at all, lastResort is the backstop.
Markets can also close. BTC, ETH, SOL, XRP and HYPE trade around the clock. A scheduled event (a rate decision, a print) can also close a market to new opens for a window, and a market can be paused or halted. See markets for the rules at each close. The SDK models this as a state, never a boolean. Branch on the state:

Your positions live in one account

A trader has a single account holding several position slots — twelve by default in the SDK, and a client may ask for anywhere from 1 to 12 when the account is created, or resize it later. That account is created by its own instruction, not by opening a position — but account creation and your first open fit in one transaction, so a first-time trader signs once and pays the account’s rent only on that first trade. The rent comes back to whoever paid it when the account is closed. That transaction is one an interface composes; the SDK’s own composer will not build it for you. Opening a position has the recipe. Slots are reused, so a slot index is not a position handle. The SDK gives you { trader, slot, seq } and settlement addresses positions by seq, so a stale handle finds nothing instead of closing a neighbour. See reading positions.