Type/to search
Built-in Functions
Structures
Built-in Variables and Constants
Conventions
Exchanges
Capability Matrix
Centralized Exchanges
Decentralized Exchanges (DEX)
Prediction Markets
Securities and Futures
On-chain & Custom

Work out a position plan from a budget, a price range and a shape: which pool, how many positions, how much of each token, whether to swap first and how much rent to lock. Read-only, no transaction.

exchange.IO(k, params)

Examples

  • Plan a position 5% around the SOL/USDC price and check whether a swap is needed first.

    javascript
    function main() { var plan = exchange.IO("lp.plan", {symbol: "SOL_USDC", budget: 100, range: {pct: [-5, 5]}}) if (!plan) { Log("Plan failed:", GetLastError()) return } Log(plan.protocol, plan.pool, "price", plan.price, "positions", plan.positions.length) plan.positions.forEach(function(p) { Log("range", p.priceLower.toFixed(2), "-", p.priceUpper.toFixed(2), "SOL", p.amountBase, "USDC", p.amountQuote) }) Log("needs", plan.needBase, "SOL", plan.needQuote, "USDC", "rent", plan.rentSol, "SOL", "refundable", plan.rentRefundableSol) if (plan.shortfall) { Log("Not enough funds:", plan.shortfall) } else if (plan.swap) { Log("Swap first:", plan.swap.side, plan.swap.amount, plan.swap.currency) } Log("Estimated daily fees", plan.estDailyFeesUsd, "USD") }
  • Compare spot and curve on a Meteora pool: a wide spot range is split into several positions, while curve must fit in one.

    javascript
    function main() { var pool = exchange.IO("lp.select", "SOL", 100, {quote: "USDC", protocol: "meteora"}) if (!pool) { Log("Selection failed:", GetLastError()) return } // about 60 bins: fits in one position var pct = 30 * pool.binStep / 100 var curve = exchange.IO("lp.plan", {symbol: "SOL_USDC", budget: 100, pool: pool.id, range: {pct: [-pct, pct]}, shape: "curve"}) if (curve) { Log(curve.strategy, "bins", curve.bins, "positions", curve.positions.length) } var spot = exchange.IO("lp.plan", {symbol: "SOL_USDC", budget: 100, pool: pool.id, range: {pct: [-10, 10]}, shape: "spot"}) if (spot) { Log(spot.strategy, "bins", spot.bins, "positions", spot.positions.length, spot.warning) } }
  • Plan a 4-layer curve on a Raydium pool.

    javascript
    function main() { var plan = exchange.IO("lp.plan", {symbol: "SOL_USDC", budget: 100, protocol: "raydium", range: {pct: [-5, 5]}, shape: "curve", layers: 4}) if (!plan) { Log("Plan failed:", GetLastError()) return } Log("tickSpacing", plan.tickSpacing, "layers", plan.layers, "/", plan.layersRequested) plan.positions.forEach(function(p) { Log("ticks", p.tickLower, p.tickUpper, "share", (p.share * 100).toFixed(1) + "%") }) }

Returns

TypeDescription

object / null value

The plan object, fields in the remarks; it can be handed to lp.simulate and lp.add as is. A null value is returned for bad parameters, when no pool can be picked, for an empty pool or a range beyond what one plan can hold, with the reason in GetLastError(). A wallet that falls short is not an error: the plan is still returned, with shortfall explaining it.

Arguments

NameTypeRequiredDescription

k

string

Yes

Set to lp.plan to build a position plan.

params

object

Yes

The plan parameters {symbol, budget, range, shape, pool, protocol, fee, binStep, tickSpacing, slippage, layers}, see the remarks. A plan may be passed too (its params are used).

See Also

Remarks

Plan parameters
KeyRequiredDescription
symbolyesThe pair, such as SOL_USDC: sets the price orientation (quote per base) and the currency of budget
budgetyesThe budget in the quote token: the total value of both tokens
rangeyesThe price range, in one of four forms below
shapenoThe shape: spot (default), curve or bidask, see below
poolnoThe pool address or protocol:address; without it a pool is picked by the rule of lp.select
protocol, fee, binStep, tickSpacingnoPool filters when pool is not given, as the options of lp.select
slippagenoPrice protection ratio, default 0.01, at most 0.5, see below
layersnoLayers of curve and bidask on Raydium and Orca, default 3, at most 20

Forms of range:

  • {lower, upper}: prices (quote per base).
  • {pct: [-5, 5]}: percent around the current price.
  • {bins: [lowerBinId, upperBinId]}: DLMM bin IDs (both ends included).
  • {ticks: [tickLower, tickUpper]}: Raydium and Orca ticks. bins and ticks are synonyms, read as bin IDs or ticks by the pool's protocol.

Prices are rounded outward to bins, or aligned outward to the tick spacing.

Shapes

Meteora DLMM distributes by bin with DLMM's three strategies (strategy): spot is SpotImBalanced (equal value per bin), curve is CurveImBalanced (thicker towards the price on both sides) and bidask is BidAskImBalanced (thicker away from it). The active bin and those below hold the pool's Y, those above hold X. A position holds at most 70 bins: a wider spot range is split into several positions (one transaction and one rent deposit each, at most 10, explained in warning), while curve and bidask ranges wider than 70 bins are an error.

Raydium CLMM and Orca Whirlpools are planned the Uniswap way: spot is one range, curve nested ranges in layers (thickest at the price) and bidask segments on both sides of the price (thicker further out, the cell holding the price left empty). layers is the number of layers; in a narrow range the layers actually built (layers) can be fewer than layersRequested. Each position is one NFT and one transaction, at most 20 positions per plan. Orca's Splash pools (tick spacing of 32768 or more) take full-range positions only, so range and shape are ignored (explained in warning).

Price protection (slippage)
  • DLMM: converted into the allowed drift of the active bin in bins, maxActiveBinSlippage; the transaction fails when the active bin moves further at execution.
  • Raydium: caps on the token amounts: one token's amount is fixed (the SOL side first) and the other is capped at the most it can take while the price moves within this ratio.
  • Orca: bounds the execution price; priceBounds are the resulting limits, beyond which the transaction fails.

Result fields
  • Common: params (the parameters as given), symbol, pool, protocol, fee, feeRate, price (quote per base), shape, budget, budgetUsd, slippageBps, volume24hUsd.
  • positions: each position's {lowerBinId, upperBinId, priceLower, priceUpper, amountBase, amountQuote, share} (tickLower and tickUpper on Raydium and Orca), share being its share of the plan's total value.
  • needBase and needQuote are the amounts the plan deposits, haveBase and haveQuote what the wallet has available (the SOL side net of the reserve for rent and fees, wSOL in the wallet counted as SOL); swap is the swap needed before adding, {side, amount, currency} (1% extra for headroom), or null; when both tokens together are not enough, swap is null and shortfall describes the gap: the two amounts the plan deposits (the same as needBase and needQuote), what the wallet holds and its value in the quote token at the pool price, and, when the missing token has to be bought with the other one, how much of that other token is needed in all (deposit plus swap cost, with 1% headroom) and how much is missing; on the SOL side it also states what is kept back for rent and fees.
  • rentSol is the rent the positions lock, of which rentRefundableSol is that of the position accounts (plus the NFT mint and NFT account on Raydium and Orca, and two ticks on Orca), returned on closing; the rest is for new BinArrays and TickArrays and is not returned.
  • shareOfPool is the plan's share of the liquidity at the price (on DLMM, of the active bin), and estDailyFeesUsd = volume24hUsd × feeRate × shareOfPool (assuming the price stays in range); both are 0 for a plan with no liquidity at the price (such as bidask).
  • DLMM adds strategy, binStep, activeId, lowerBinId, upperBinId, bins (number of bins), maxActiveBinSlippage and binArraysToCreate (indexes of BinArrays to create); Raydium and Orca add tickSpacing, tick, tickLower, tickUpper (the whole range), layers, layersRequested and tickArraysToCreate (start ticks of TickArrays to create), plus liquidities (each position's liquidity) on Raydium and priceBounds on Orca; warning appears when something needs explaining.

A plan is a snapshot: lp.simulate and lp.add re-plan from its params at the latest pool price and balances. The fee estimate uses the past 24h volume and is no promise of future income.