LP Range Orders
Last updated
Last updated
Terms and Disclaimers
Privacy PolicyRange orders are at the core of concentrated liquidity, they allow LPs to deposit over a specific price range defined by a lower price bound and upper price bound. Orders that span the minimum price width can replicate limit orders similar to orders placed on a limit order-book (if only crossed a single time), while orders spanning across multiple price points (called ticks) replicate orders placed linearly over multiple adjacent prices. There is no commitment period for LP deposits. They can be withdrawn prior to expiration.
Premia v3 uses an accounting method called Split-User Accounting. At the core, this accounting process allows LPs to define directional exposure of a range order. This is automatically determined based on the asset that is deposited (Collateral or Option Contracts) along with the orientation of the order relative to the current market price. Simply put, range orders provide uni-directional exposure (either buy or sell). More information about how Split-User Accounting works can be found in Section 4 of the Premia v3 whitepaper here.
In order to define a range order, 3 primary inputs must be provided to the deposit
function in the IPool
interface:
Lower Tick ⇒ lower price bound
Upper Tick ⇒ upper price bound
Size (liquidity) ⇒ size of Collateral OR Option Contracts
To find the pool address, users can query the getPoolAddress
function in IPoolFactory
.
When liquidity is deposited above the market price, this order can be initially viewed as ask-side liquidity. Similarly, deposits below the market price can be initially viewed as bid-side liquidity. As the market price moves up and down through a range, the liquidity provider collects maker fees, in addition to any exposure changes.
If an LP deposits collateral, they are suggesting that they would like to get long option contracts if they are posting a bid-side range order and short contracts if they are posting an ask-side range order. These range orders can be thought of as “buy-to-open” or “sell-to-open” order types initially. As the market price traverses through these range orders, they will become “sell-to-close” and “buy-to-close” range orders respectively.
An ask-side range order with collateral can never be net long options just as a bid-side order with collateral can never be short options.
If an LP deposits option contracts, they are suggesting that they would like to close their position. Bid-side orders can be made with short option contracts, and Ask-side orders can be made with long option contracts. These order types can be thought of as “buy-to-close” and “sell-to-close” order types initially. As markets traverse back and forth through these range orders, an LP may re-establish the original option exposure they deposited with.
An ask-side range order with long options can never get net short options. A bid-side order with short options can never get net long options.
It is best to illustrate a detailed view of a range order through an example. Here, we will be focusing on a range order with the following details:
Option type ⇒ Call Option
Current Market Price ⇒ 0.35
Initial Range Order Type ⇒ bid-side liquidity
Lower Tick ⇒ 0.25
Upper Tick ⇒ 0.30
Deposit ⇒ 1 Unit of Base (Underlying) Asset as collateral
As the price of the option moves down (right to left), the collateral which was initially deposited (1 unit) begins to decrease (transferred to takers) as the market price enters and traverses through the order range. The collateral is used to purchase long option contracts from traders.
Once this order is fully traversed (ie. market price is now 0.20), the range order consists of 3.63 options and 0 collateral. Note, any fees collected are not part of the collateral balance and are separately redeemable. At this point, the order is considered sell-side liquidity, which consists of Option Contracts that will be sold to collateral if the price of the option were to go up (left to right).
It is worth noting that pricing is linear, which means the average execution price of a range is simple (Upper Tick + Lower Tick)/2
. In this example of a sell-side order, the average execution price is 0.275.
While the minimum tick precision for a market is 0.001, a range order must have a compatible RangeWidth. Since liquidity for a range order is distributed evenly in between the ticks of a range order, it is possible to generate rounding errors using certain range widths. While this is automatically handled on the front end to prevent bad order ranges, care must be taken for direct interaction with smart contracts to abide by
A valid RangeWidth for any order type must be one of the following values:
A deposit of 1 unit of collateral (ie. WETH) for bid-side liquidity is desired. The lower tick is .001 and corresponding upper tick is .004 (RangeWidth = 0.003). This would mean that 1/3 of the liquidity would be distributed across each of the 3 ticks within the range. This produces rounding errors that are undesirable even with 18 decimal places of precision for most ERC-20 assets.
In order to directly process a range order deposit
, a position Key
struct must be passed in. This can be found in the Position
library. Here, a user can specify both an owner
and operator
addresses for the position. This is primarily intended for 3rd party integrations, otherwise both will be the user’s address.
Additionally, a user will specify the lower
and upper
ticks for their range order and define the order using the OrderType
enum as follows:
uint8
0 → CSUP (Collateral ↔ Short, Use Premiums) - Convert collateral to short exposure or vice versa and use the premiums towards collateral requirements (fully filled spends any premiums collected).
uint8
1 → CS (Collateral ↔ Short) - Convert collateral to short exposure or vice versa and collect (pay) premiums separately (fully filled retains all premiums collected).
uint8
2 → LC (Long ↔ Collateral) - Convert collateral to long exposure or vice versa and pay (collect) premiums separately (fully filled spends all collateral as premiums).
Providing the belowLower
and belowUpper
tick values can easily be done by first calling getNearestTicksBelow
view function with the lower
and upper
of the Position to be deposited. The pool will verify that the ticks are in the correct location before inserting and confirming the deposit. The following relationship is verified:
The insert location is not searched for within each deposit
transaction, because of the gas costs associated with inserting into the doubly linked list of ticks. By first finding the nearest ticks for the function off-chain (view function) and then verifying correctness on-chain, users can significantly reduce transaction costs per deposit
.
In addition to belowLower
, belowUpper
, and size
, users must also supply a minMarketPrice
and maxMarketPrice
when directly interacting with the deposit
function. Market price here refers to the option’s price. By specifying a min/max market price for the option, LPs can get granular control of the composition of asset(s) that are used for collateral. It can be thought of as slippage control for asset composition.
These slippage controls are primarily helpful for orders that are intended to be solely bid-side or ask-side liquidity with a single collateral type (option OR collateral). When LPs hold a mixture of options and collateral in their wallet, there is risk of using an unintended collateral type if a range order is placed close to the market price. It is possible for a single sided range order to be an unintended straddle (more on straddles below). This risk can be prevented by correcting defining the minMarketPrice
and maxMarketPrice
parameters.
If no slippage controls are desired, the values can be set to the minimum and maximum tick for a pool.
It is possible to initialize a range order that straddles the current market price. This would mean Lower *Tick < Market Price < Upper Tick*
. In this case, a deposit will need to consist of both options AND collateral (linearly interpolated based on the core order inputs and market price).
Setting the minMarketPrice
and maxMarketPrice
to a narrow range ensures the composition of the order is within a user’s tolerance.
Let’s imagine for example an LP holds ETH and short options in their wallet. Their intention is to create a bid-side range order to close the existing short exposure in the price range of 0.4 ↔ 0.5. The range is set just below the current market price of 0.55 (Upper Tick < Market Price). In the process of creating the range order, the market price moves down to 0.45, into the LP’s order range (thus creating an unintended straddle).
In this case, the deposit
function would seek a small portion of the LP’s ETH and leave behind a small portion of the short option contract in the user’s wallet, to correctly open the position.
Setting the minMarketPrice
to the Upper Tick (0.5) would cause the transaction to revert if price moved below 0.5, allowing the LP to adjust the range order and re-submit. In this example maxMarketPrice
is trivial, but still requires a value greater than or equal to the market price at time of deposit.
Withdrawing a range order is very similar to a deposit
. The only difference is that belowLower
and belowUpper
are NOT required function inputs, as no linked list insertion is required. The minMarketPrice
and maxMarketPrice
parameters are still used to manage slippage and collateral control as mentioned in Range Order (Technical) - Deposits.