0 reads the live config.params.borrowGraceS (see the borrow fee).
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 rung 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 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
Rung
Your take-profit target, 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. The presets are presetRungsBps(ceilingBps) at the live
payout cap — not the only choices.4
Leverage
How far the price has to move to reach either end — so, how fast the position resolves.
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.It changes nothing about what you can win or lose. There is no leverage term in the payout.
What leverage changes is the DISTANCE to both levels.The barrier
Every position has a barrier: a price, fixed at open, sitting a set distance on the losing side of your entry. If the market touches it, the position is over and you receive nothing. Three things about it are worth internalising:- It is a distance, not a dollar amount. The distance is set by the leverage you chose, which the market’s tier caps. More leverage means a nearer barrier, which is exactly what makes the position resolve fast. Two positions on the same asset can sit at different distances.
- 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.
openPreview().barrierMicroBp, and the resulting price by levels(). Read it, show it to the
person taking the risk, and do not hand-compute it.
The rung
The rung is the other side of the same idea: 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 rung 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 rung pays less than its multiple. Fees and limits has a worked example. Your rung can be any multiple of your stake at or above 2.00x — not a fixed list. The floor isRUNG_FLOOR_BPS, 2.00x.
The presets are presetRungsBps(ceilingBps), which returns four values in bps of stake and ends
at the live payout cap. A preset above the live cap is refused RungAbovePayoutCap: compute the
presets from the live cap. The SDK’s RUNGS export — [2, 3, 5, 10] — is not the preset list.
A client may offer any value at or above the floor.
There is exactly one ceiling above that floor, and it is config.params.payoutCapMultiple.
Read it from the config account; a market carries no take-profit ceiling of its own.
RungAboveMarketCap (6050) keeps its code in the error catalogue, but nothing in the program
raises it: a client branching on 6050 is handling a refusal that cannot arrive.
RungAbovePayoutCap is the live one.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. Opening a second position instead would leave a trader holding two trades that knock out at two different prices, which is the outcome this instruction exists to prevent. 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.
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 neither realises nor creates a profit or a loss. A trader who is 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.
- 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_outis 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 − minCollateralhas 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_sis 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.
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.
setsTimerdoes not change the debit. Setting a timer is free, on an open or an extension.- The bounds are
admissibleAdd’s interval, notconfig.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 isconfig.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 24 hour refund 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 gold, 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 every gold position’s
tenor ends before its session closes. See markets.
Frozen is the part that is a guarantee: 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.
Read
tMaxSeconds and borrowRateBpsPerHour together; neither one alone tells you how long a
position can live. An LP’s withdrawal wait does not follow the tenor — see
providing liquidity.How a position ends
Knockout — the price touched the barrier
Knockout — the price touched the barrier
The position is over and the trader receives nothing. Plan for it: at high leverage 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.Take-profit — the price reached your rung
Take-profit — the price reached your rung
The position pays the rung’s multiple of your stake, subject to the overall 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.Expiry — a clock ran out
Expiry — a clock ran out
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.Voluntary — you closed it
Voluntary — you closed it
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.Last resort — no price could be obtained
Last resort — no price could be obtained
Anybody may close a position with no quote, but only while no fresh price exists, and only on one
of two time grounds.A market with no posted price for 24 hours is dark (the governed
dark_after_min, read from
the governance table as GovTable.darkAfterMin). On a dark market anyone can close any position
on it, and the position gets its stake back. Anyone can post a signed price with provePrice to
reset the clock.A position with a finite tenor can also be closed once its frozen tenor plus a governed
grace (config.lastResortGraceS) has elapsed. A position with the indefinite tenor has only the
dark ground.A live position is paid its stake back with the exit fee waived; a position already stamped at a
level is paid that level. 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.voluntary, knockout,
takeProfit, userExpiry, tenorExpiry and lastResort.
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 asettle 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 governedpayoutCapMultiple, 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
Three, all on chain. Two are charged on an event; the third 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; - 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 (feeTermsTablePdain the SDK), and the share of each close is on itsSettlementFeeTermsevent; - a borrow fee, charged out of the payout for however long the position was held.
The borrow fee
It is rent on the pool capital your position freezes, charged out of the payout at settlement for the time held beyond a free grace period, at a flat hourly rate on the collateral you opened with. All of it goes to the pool. It is taken after the loss tax and after the exit fee, never out of your equity, so it does not move the price that ends your position. A position freezes its rate when it opens, and a stored grace of0 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 venue’s price feed for the market you traded — not “the price of SOL”, because no such single number exists. Every venue reports a different one. Every price is a quote signed by the venue’s price feed, and the program accepts it only when it is signed by at leastmarket.minSignatures keys that the program recognises.
- 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,
lastResortis the backstop.
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.