@devasher/kuru-sdk
Version:
Ethers v6 SDK to interact with Kuru (forked from @kuru-labs/kuru-sdk)
523 lines • 30.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PositionViewer = void 0;
const FEE_DENOMINATOR = BigInt(10000);
class PositionViewer {
/**
* @dev Retrieves details for concentrated liquidity positions within a price range.
* @param minFeesBps - The minimum fees to filter positions by.
* @param startPrice - The lower bound of the price range to query.
* @param endPrice - The upper bound of the price range to query.
* @param bestAskPrice - The current market price.
* @param pricePrecision - The precision of the price.
* @param tickSize - The size of a tick.
* @param quoteLiquidity - The total quote liquidity in the market.
* @param baseLiquidity - The total base liquidity in the market.
* @returns A promise that resolves to the batch order details.
*/
static async getSpotBatchLPDetails(minFeesBps, startPrice, endPrice, bestAskPrice, pricePrecision, sizePrecision, quoteAssetDecimals, baseAssetDecimals, tickSize, minSize, quoteLiquidity, // In quote asset decimals
baseLiquidity) {
// don't allow both quoteLiquidity and baseLiquidity to be undefined
if (quoteLiquidity === undefined && baseLiquidity === undefined) {
throw new Error('quoteLiquidity and baseLiquidity cannot be undefined');
}
startPrice = startPrice - (startPrice % tickSize);
var numBids = BigInt(0);
var numAsks = BigInt(0);
const bids = [];
const asks = [];
const maxPrice = Math.min(Number(bestAskPrice), Number(endPrice));
while (startPrice < maxPrice) {
numBids++;
var nextPrice = (startPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice == startPrice) {
nextPrice = startPrice + tickSize;
}
nextPrice = nextPrice - (nextPrice % tickSize);
var flipPrice = (nextPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == nextPrice) {
flipPrice = nextPrice + tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
const position = {
price: startPrice,
liquidity: BigInt(0),
flipPrice,
};
bids.push(position);
startPrice = nextPrice;
}
while (startPrice < endPrice) {
numAsks++;
var nextPrice = (startPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice == startPrice) {
nextPrice = startPrice + tickSize;
}
nextPrice = nextPrice - (nextPrice % tickSize);
var flipPrice = (startPrice * (FEE_DENOMINATOR - minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == startPrice) {
flipPrice = startPrice - tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
// Prevent asks from exceeding the specified endPrice
if (nextPrice > endPrice) {
break;
}
const position = {
price: nextPrice,
liquidity: BigInt(0),
flipPrice,
};
asks.push(position);
startPrice = nextPrice;
}
if (quoteLiquidity !== undefined && baseLiquidity == undefined) {
baseLiquidity = BigInt(0);
const quotePerTick = quoteLiquidity / numBids;
for (const bid of bids) {
bid.liquidity =
(quotePerTick * sizePrecision * pricePrecision) / (bid.price * BigInt(10) ** quoteAssetDecimals);
if (bid.liquidity < minSize) {
throw new Error('bid liquidity is less than minSize');
}
}
for (const ask of asks) {
ask.liquidity =
(quotePerTick * sizePrecision * pricePrecision) / (ask.price * BigInt(10) ** quoteAssetDecimals);
baseLiquidity += (ask.liquidity * BigInt(10) ** baseAssetDecimals) / sizePrecision;
if (ask.liquidity < minSize) {
throw new Error('ask liquidity is less than minSize');
}
}
return {
bids: bids.sort((a, b) => Number(b.price - a.price)),
asks: asks.sort((a, b) => Number(b.price - a.price)),
quoteLiquidity: quoteLiquidity !== null && quoteLiquidity !== void 0 ? quoteLiquidity : BigInt(0),
baseLiquidity: baseLiquidity !== null && baseLiquidity !== void 0 ? baseLiquidity : BigInt(0),
};
}
if (baseLiquidity !== undefined && quoteLiquidity == undefined) {
// We have total base liquidity but need to infer the amount of quote
// per price-point (i.e. quotePerTick) such that each bid/ask bucket
// receives the same amount of quote. The relationship between the
// base size at the first ask (b₁) and the total base B is
// B = Σ_{i=1}^{N} (b₁ * p₁) / p_i
// Solving for the constant quotePerTick = b₁ * p₁ gives
// quotePerTick = B / (Σ 1/p_i)
// We implement this in integer arithmetic by scaling the reciprocal
// terms with `pricePrecision ** 2` so that we avoid fractional
// values while maintaining precision.
// ------------------------------------------
// 1. Compute the scaled reciprocal sum Σ pricePrecision^2 / p_i
// ------------------------------------------
const reciprocalSumScaled = asks.reduce((sum, ask) => sum + (pricePrecision * pricePrecision) / ask.price, BigInt(0));
if (reciprocalSumScaled === BigInt(0)) {
throw new Error('reciprocalSumScaled is zero – check price inputs');
}
// ------------------------------------------
// 2. Compute ask liquidity in sizePrecision units
// Formula: L_i = (B * sizePrecision * pricePrecision^2) /
// (ΣRecipScaled * p_i * 10^{baseDecimals})
// ------------------------------------------
for (const ask of asks) {
ask.liquidity =
(baseLiquidity * sizePrecision * pricePrecision * pricePrecision) /
(reciprocalSumScaled * ask.price * BigInt(10) ** baseAssetDecimals);
if (ask.liquidity < minSize) {
throw new Error('ask liquidity is less than minSize');
}
}
// ------------------------------------------
// 3. Derive the constant quotePerTick from the first ask bucket.
// quotePerTick = L_1 * p_1 * 10^{quoteDecimals} / (sizePrecision * pricePrecision)
// ------------------------------------------
const firstAsk = asks[0];
const quotePerTick = (firstAsk.liquidity * firstAsk.price * BigInt(10) ** quoteAssetDecimals) /
(sizePrecision * pricePrecision);
// ------------------------------------------
// 4. Allocate liquidity for bids (inverse conversion)
// L_bid = quotePerTick * sizePrecision * pricePrecision /
// (p_bid * 10^{quoteDecimals})
// ------------------------------------------
let inferredQuoteLiquidity = BigInt(0);
for (const bid of bids) {
bid.liquidity =
(quotePerTick * sizePrecision * pricePrecision) / (bid.price * BigInt(10) ** quoteAssetDecimals);
inferredQuoteLiquidity += quotePerTick; // one quotePerTick per bid
if (bid.liquidity < minSize) {
throw new Error('bid liquidity is less than minSize');
}
}
return {
bids: bids.sort((a, b) => Number(b.price - a.price)),
asks: asks.sort((a, b) => Number(b.price - a.price)),
quoteLiquidity: inferredQuoteLiquidity,
baseLiquidity: baseLiquidity,
};
}
if (baseLiquidity !== undefined && quoteLiquidity !== undefined) {
for (const ask of asks) {
ask.liquidity = (baseLiquidity * sizePrecision) / (numAsks * BigInt(10) ** baseAssetDecimals);
if (ask.liquidity < minSize) {
throw new Error('ask liquidity is less than minSize');
}
}
for (const bid of bids) {
bid.liquidity =
(quoteLiquidity * sizePrecision * pricePrecision) /
(numBids * bid.price * BigInt(10) ** quoteAssetDecimals);
if (bid.liquidity < minSize) {
throw new Error('bid liquidity is less than minSize');
}
}
return {
bids: bids.sort((a, b) => Number(b.price - a.price)),
asks: asks.sort((a, b) => Number(b.price - a.price)),
quoteLiquidity: quoteLiquidity !== null && quoteLiquidity !== void 0 ? quoteLiquidity : BigInt(0),
baseLiquidity: baseLiquidity !== null && baseLiquidity !== void 0 ? baseLiquidity : BigInt(0),
};
}
return {
bids: [],
asks: [],
quoteLiquidity: BigInt(0),
baseLiquidity: BigInt(0),
};
}
/**
* Calculates the liquidity distribution for a batch of limit orders on a "Curve" or "U-shape".
* The core principle is that quote currency value is lowest at the outer edges of the
* price range and increases in an arithmetic progression towards the center (bestAskPrice).
*
* @param minFeesBps - Minimum fees in basis points.
* @param startPrice - The starting price for placing liquidity (farthest bid).
* @param endPrice - The ending price for placing liquidity (farthest ask).
* @param bestAskPrice - The current best ask price on the market, which is the center of the curve.
* @param pricePrecision - The precision factor for prices (e.g., 10^18).
* @param sizePrecision - The precision factor for position sizes (e.g., 10^6).
* @param quoteAssetDecimals - The number of decimals for the quote asset.
* @param baseAssetDecimals - The number of decimals for the base asset.
* @param tickSize - The minimum price increment.
* @param minSize - The minimum position size allowed.
* @param quoteLiquidity - The total liquidity for one side of the curve, denominated in the quote asset.
* @param baseLiquidity - The total liquidity for the asks side of the curve, denominated in the base asset.
* @returns A promise resolving to an object with bid and ask positions and total liquidity.
*/
static async getCurveBatchLPDetails(minFeesBps, startPrice, endPrice, bestAskPrice, pricePrecision, sizePrecision, quoteAssetDecimals, baseAssetDecimals, tickSize, minSize, quoteLiquidity, // In quote asset decimals
baseLiquidity) {
if (quoteLiquidity === undefined && baseLiquidity === undefined) {
throw new Error('Either quoteLiquidity or baseLiquidity must be provided.');
}
startPrice = startPrice - (startPrice % tickSize);
const bids = [];
const asks = [];
let currentPrice = startPrice;
const maxPrice = Math.min(Number(bestAskPrice), Number(endPrice));
// #############################################################
// # 1. Generate Bid & Ask Position Grids
// #############################################################
while (currentPrice < maxPrice) {
let nextPrice = (currentPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice === currentPrice)
nextPrice = currentPrice + tickSize;
nextPrice = nextPrice - (nextPrice % tickSize);
var flipPrice = (nextPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == nextPrice) {
flipPrice = nextPrice + tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
bids.push({ price: currentPrice, liquidity: BigInt(0), flipPrice });
currentPrice = nextPrice;
}
while (currentPrice < endPrice) {
let nextPrice = (currentPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice === currentPrice)
nextPrice = currentPrice + tickSize;
nextPrice = nextPrice - (nextPrice % tickSize);
// Prevent asks from exceeding endPrice
if (nextPrice > endPrice) {
break;
}
var flipPrice = (startPrice * (FEE_DENOMINATOR - minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == startPrice) {
flipPrice = startPrice - tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
asks.push({ price: nextPrice, liquidity: BigInt(0), flipPrice });
currentPrice = nextPrice;
}
const numBids = BigInt(bids.length);
const numAsks = BigInt(asks.length);
// #############################################################
// # 2. Distribute Liquidity
// #############################################################
if (quoteLiquidity !== undefined) {
// Scenario A: Total Quote Liquidity is provided.
const quoteUnitForBids = numBids > 0 ? (BigInt(2) * quoteLiquidity) / (numBids * (numBids + BigInt(1))) : BigInt(0);
const quoteUnitForAsks = numAsks > 0 ? (BigInt(2) * quoteLiquidity) / (numAsks * (numAsks + BigInt(1))) : BigInt(0);
let totalBaseLiquidity = BigInt(0);
// Distribute across bids: liquidity increases towards the center.
for (let i = 0; i < bids.length; i++) {
const bid = bids[i];
// Farthest bid (i=0) gets 1 unit; closest bid (i=numBids-1) gets numBids units.
const quoteMultiplier = BigInt(i + 1);
const quoteForThisBid = quoteUnitForBids * quoteMultiplier;
bid.liquidity =
(quoteForThisBid * pricePrecision * sizePrecision) / (BigInt(10) ** quoteAssetDecimals * bid.price);
if (bid.liquidity < minSize)
throw new Error('Calculated bid liquidity is less than minSize.');
}
// Distribute across asks: liquidity increases towards the center.
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
// CORRECTED: Closest ask (i=0) gets numAsks units; farthest ask (i=numAsks-1) gets 1 unit.
const quoteMultiplier = numAsks - BigInt(i);
const quoteForThisAsk = quoteUnitForAsks * quoteMultiplier;
ask.liquidity =
(quoteForThisAsk * sizePrecision * pricePrecision) / (BigInt(10) ** quoteAssetDecimals * ask.price);
// Accumulate the resulting base liquidity from asks.
totalBaseLiquidity +=
(quoteForThisAsk * BigInt(10) ** baseAssetDecimals * pricePrecision) /
(BigInt(10) ** quoteAssetDecimals * ask.price);
if (ask.liquidity < minSize)
throw new Error('Calculated ask liquidity is less than minSize.');
}
baseLiquidity = totalBaseLiquidity;
}
else if (baseLiquidity !== undefined) {
// Scenario B: Total Base Liquidity is provided (for asks).
if (numAsks === BigInt(0)) {
throw new Error('Cannot provide baseLiquidity when there are no asks to place it in.');
}
// CORRECTED INVARIANT: quote_i = (numAsks - i) * quote_unit for asks
// quote_unit corresponds to the farthest ask (q_f = b_f * p_f)
// base_i * price_i = (numAsks - i) * b_f * p_f => base_i = (numAsks - i) * b_f * (p_f / p_i)
// Total Base B = b_f * sum((numAsks - i) * (p_f / p_i))
const farthestAsk = asks[asks.length - 1];
const farthestAskPrice = farthestAsk.price;
let weightedSumOfBaseRatios = BigInt(0);
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
// Multiplier is (numAsks - i)
const ratio = ((numAsks - BigInt(i)) * farthestAskPrice * pricePrecision) / ask.price;
weightedSumOfBaseRatios += ratio;
}
// Solve for b_f (base liquidity in the FARTHEST ask).
const baseInFarthestAsk = weightedSumOfBaseRatios > 0 ? (baseLiquidity * pricePrecision) / weightedSumOfBaseRatios : BigInt(0);
let totalQuoteLiquidity = BigInt(0);
// Distribute liquidity across all asks based on b_f.
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
const baseForThisAsk = ((numAsks - BigInt(i)) * baseInFarthestAsk * farthestAskPrice) / ask.price;
ask.liquidity = (baseForThisAsk * sizePrecision) / BigInt(10) ** baseAssetDecimals;
if (ask.liquidity < minSize)
throw new Error('Calculated ask liquidity is less than minSize.');
const quoteForThisAsk = (baseForThisAsk * ask.price * BigInt(10) ** quoteAssetDecimals) /
(BigInt(10) ** baseAssetDecimals * pricePrecision);
totalQuoteLiquidity += quoteForThisAsk;
}
// Now use the total quote from asks to provision bids with the same curve shape.
if (numBids > 0) {
const quoteUnitForBids = (BigInt(2) * totalQuoteLiquidity) / (numBids * (numBids + BigInt(1)));
for (let i = 0; i < bids.length; i++) {
const bid = bids[i];
// Farthest bid (i=0) gets 1 unit, closest gets numBids units.
const quoteMultiplier = BigInt(i + 1);
const quoteForThisBid = quoteUnitForBids * quoteMultiplier;
bid.liquidity =
(quoteForThisBid * pricePrecision * sizePrecision) /
(BigInt(10) ** quoteAssetDecimals * bid.price);
if (bid.liquidity < minSize)
throw new Error('Calculated bid liquidity is less than minSize.');
}
}
quoteLiquidity = totalQuoteLiquidity;
}
// #############################################################
// # 3. Finalize and Return
// #############################################################
return {
bids: bids.sort((a, b) => Number(b.price - a.price)), // highest price first
asks: asks.sort((a, b) => Number(a.price - b.price)), // lowest price first
quoteLiquidity: quoteLiquidity !== null && quoteLiquidity !== void 0 ? quoteLiquidity : BigInt(0),
baseLiquidity: baseLiquidity !== null && baseLiquidity !== void 0 ? baseLiquidity : BigInt(0),
};
}
/**
* Calculates the liquidity distribution for a batch of limit orders in a "bid-ask" or "triangular" shape.
* The core principle is that quote liquidity is lowest at the center (best bid/ask) and increases
* linearly as prices move away from the spread.
*
* @param minFeesBps - Minimum fees in basis points.
* @param startPrice - The starting price for placing liquidity (farthest bid).
* @param endPrice - The ending price for placing liquidity (farthest ask).
* @param bestAskPrice - The current best ask price, which defines the center of the spread.
* @param pricePrecision - The precision factor for prices (e.g., 10^18).
* @param sizePrecision - The precision factor for position sizes (e.g., 10^6).
* @param quoteAssetDecimals - The number of decimals for the quote asset.
* @param baseAssetDecimals - The number of decimals for the base asset.
* @param tickSize - The minimum price increment.
* @param minSize - The minimum position size allowed.
* @param quoteLiquidity - The total liquidity for one side (e.g., asks), denominated in the quote asset.
* @param baseLiquidity - The total liquidity for one side (e.g., asks), denominated in the base asset.
* @returns A promise resolving to an object with bid and ask positions and total liquidity.
*/
static async getBidAskBatchLPDetails(minFeesBps, startPrice, endPrice, bestAskPrice, pricePrecision, sizePrecision, quoteAssetDecimals, baseAssetDecimals, tickSize, minSize, quoteLiquidity, // In quote asset decimals
baseLiquidity) {
// Ensure that at least one form of liquidity is provided.
if (quoteLiquidity === undefined && baseLiquidity === undefined) {
throw new Error('Either quoteLiquidity or baseLiquidity must be provided.');
}
// Align the starting price with the nearest tick.
startPrice = startPrice - (startPrice % tickSize);
const bids = [];
const asks = [];
let currentPrice = startPrice;
const maxPrice = Math.min(Number(bestAskPrice), Number(endPrice));
// #############################################################
// # 1. Generate Bid & Ask Position Grids
// #############################################################
// Bids are created from the farthest price (startPrice) inwards to the center.
while (currentPrice < maxPrice) {
let nextPrice = (currentPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice === currentPrice)
nextPrice = currentPrice + tickSize;
nextPrice = nextPrice - (nextPrice % tickSize);
var flipPrice = (nextPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == nextPrice) {
flipPrice = nextPrice + tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
bids.push({ price: currentPrice, liquidity: BigInt(0), flipPrice });
currentPrice = nextPrice;
}
// Asks are created from the center outwards to the farthest price (endPrice).
while (currentPrice < endPrice) {
let nextPrice = (currentPrice * (FEE_DENOMINATOR + minFeesBps)) / FEE_DENOMINATOR;
if (nextPrice === currentPrice)
nextPrice = currentPrice + tickSize;
nextPrice = nextPrice - (nextPrice % tickSize);
// Prevent asks from exceeding endPrice
if (nextPrice > endPrice) {
break;
}
var flipPrice = (startPrice * (FEE_DENOMINATOR - minFeesBps)) / FEE_DENOMINATOR;
if (flipPrice == startPrice) {
flipPrice = startPrice - tickSize;
}
flipPrice = flipPrice - (flipPrice % tickSize);
asks.push({ price: nextPrice, liquidity: BigInt(0), flipPrice });
currentPrice = nextPrice;
}
const numBids = BigInt(bids.length);
const numAsks = BigInt(asks.length);
// #############################################################
// # 2. Distribute Liquidity
// #############################################################
if (quoteLiquidity !== undefined) {
// Scenario A: Total Quote Liquidity is provided.
// It's assumed this quote amount is for EACH side of the book.
let totalBaseLiquidity = BigInt(0);
// The sum of an arithmetic series 1 + 2 + ... + N is N*(N+1)/2.
// This is the total number of "quote units" to be distributed.
// We use it to find the value of a single unit (the amount for the closest position).
const quoteUnitForBids = numBids > 0 ? (BigInt(2) * quoteLiquidity) / (numBids * (numBids + BigInt(1))) : BigInt(0);
const quoteUnitForAsks = numAsks > 0 ? (BigInt(2) * quoteLiquidity) / (numAsks * (numAsks + BigInt(1))) : BigInt(0);
// Distribute liquidity across bids.
for (let i = 0; i < bids.length; i++) {
const bid = bids[i];
// bids[0] is the farthest, bids[numBids-1] is the closest.
// The closest bid gets 1 unit, the next gets 2, ..., the farthest gets numBids units.
const quoteMultiplier = numBids - BigInt(i);
const quoteForThisBid = quoteUnitForBids * quoteMultiplier;
bid.liquidity =
(quoteForThisBid * pricePrecision * sizePrecision) / (BigInt(10) ** quoteAssetDecimals * bid.price);
if (bid.liquidity < minSize)
throw new Error('Calculated bid liquidity is less than minSize.');
}
// Distribute liquidity across asks.
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
// asks[0] is the closest, asks[numAsks-1] is the farthest.
// The closest ask gets 1 unit, the next gets 2, etc.
const quoteMultiplier = BigInt(i + 1);
const quoteForThisAsk = quoteUnitForAsks * quoteMultiplier;
ask.liquidity =
(quoteForThisAsk * pricePrecision * sizePrecision) / (BigInt(10) ** quoteAssetDecimals * ask.price);
if (ask.liquidity < minSize)
throw new Error('Calculated ask liquidity is less than minSize.');
// We only need to calculate the resulting base liquidity from one side (asks).
totalBaseLiquidity +=
(quoteForThisAsk * BigInt(10) ** baseAssetDecimals * pricePrecision) /
(BigInt(10) ** quoteAssetDecimals * ask.price);
}
baseLiquidity = totalBaseLiquidity;
}
else if (baseLiquidity !== undefined) {
// Scenario B: Total Base Liquidity is provided (assumed for the asks).
// This is the corrected logic.
if (numAsks === BigInt(0)) {
throw new Error('Cannot provide baseLiquidity when there are no asks to place it in.');
}
// The invariant: quote_i = i * quote_1 (where i=1 is the closest ask)
// This means: (base_i * price_i) = i * (base_1 * price_1)
// So, base_i = i * base_1 * (price_1 / price_i)
// Total Base Liquidity (B) = sum(base_i) = base_1 * sum(i * (price_1 / price_i))
// We first calculate the weighted sum: sum(i * (price_1 / price_i))
const closestAskPrice = asks[0].price;
let weightedSumOfBaseRatios = BigInt(0);
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
// Calculate term `i * (price_1 / price_i)`. Multiply by pricePrecision for integer math.
const ratio = (BigInt(i + 1) * closestAskPrice * pricePrecision) / ask.price;
weightedSumOfBaseRatios += ratio;
}
// Solve for base_1: base_1 = B / (weightedSum / pricePrecision)
// base_1 (in base asset decimals) = (B * pricePrecision) / weightedSum
const baseInClosestAsk = (baseLiquidity * pricePrecision) / weightedSumOfBaseRatios;
let totalQuoteLiquidity = BigInt(0);
// Distribute liquidity across all asks based on base_1.
for (let i = 0; i < asks.length; i++) {
const ask = asks[i];
// Calculate base_i = base_1 * i * (price_1 / price_i)
const baseForThisAsk = (baseInClosestAsk * BigInt(i + 1) * closestAskPrice) / ask.price;
ask.liquidity = (baseForThisAsk * sizePrecision) / BigInt(10) ** baseAssetDecimals;
if (ask.liquidity < minSize)
throw new Error('Calculated ask liquidity is less than minSize.');
// Calculate the corresponding quote amount and add to the total.
const quoteForThisAsk = (baseForThisAsk * ask.price * BigInt(10) ** quoteAssetDecimals) /
(BigInt(10) ** baseAssetDecimals * pricePrecision);
totalQuoteLiquidity += quoteForThisAsk;
}
// Now, use the total quote liquidity from the asks to provision the bids.
if (numBids > 0) {
// The total quote in the bids should mirror the total quote in the asks.
const quoteUnitForBids = (BigInt(2) * totalQuoteLiquidity) / (numBids * (numBids + BigInt(1)));
for (let i = 0; i < bids.length; i++) {
const bid = bids[i];
// Farthest bid gets most liquidity, closest gets the least.
const quoteMultiplier = numBids - BigInt(i);
const quoteForThisBid = quoteUnitForBids * quoteMultiplier;
bid.liquidity =
(quoteForThisBid * pricePrecision * sizePrecision) /
(BigInt(10) ** quoteAssetDecimals * bid.price);
if (bid.liquidity < minSize)
throw new Error('Calculated bid liquidity is less than minSize.');
}
}
quoteLiquidity = totalQuoteLiquidity;
}
// #############################################################
// # 3. Finalize and Return
// #############################################################
// Sort bids descending (highest price first) and asks ascending (lowest price first).
return {
bids: bids.sort((a, b) => Number(b.price - a.price)),
asks: asks.sort((a, b) => Number(a.price - b.price)),
quoteLiquidity: quoteLiquidity !== null && quoteLiquidity !== void 0 ? quoteLiquidity : BigInt(0),
baseLiquidity: baseLiquidity !== null && baseLiquidity !== void 0 ? baseLiquidity : BigInt(0),
};
}
}
exports.PositionViewer = PositionViewer;
//# sourceMappingURL=positionViewer.js.map