From 6b8854422982530674db3df97131527374e7a553 Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Tue, 19 Nov 2024 14:56:54 +0100 Subject: [PATCH 1/3] feat: add adjust kandel --- src/lib/kandel/params.ts | 214 ++++++++++++++++++++++----------------- src/lib/tick.test.ts | 7 ++ src/lib/tick.ts | 6 +- 3 files changed, 131 insertions(+), 96 deletions(-) diff --git a/src/lib/kandel/params.ts b/src/lib/kandel/params.ts index 123f8a0..72a47b1 100644 --- a/src/lib/kandel/params.ts +++ b/src/lib/kandel/params.ts @@ -4,60 +4,85 @@ import { type Logic, type MarketParams, minVolume, -} from '../../index.js' +} from "../../index.js"; import { humanPriceToRawPrice, rawPriceToHumanPrice, -} from '../human-readable.js' -import { getDefaultLimitOrderGasreq } from '../limit-order.js' -import { priceFromTick, tickFromPrice } from '../tick.js' +} from "../human-readable.js"; +import { getDefaultLimitOrderGasreq } from "../limit-order.js"; +import { priceFromTick, tickFromPrice } from "../tick.js"; import { type Distribution, createGeometricDistribution, -} from './distribution.js' +} from "./distribution.js"; export type RawKandelPositionParams = { - minPrice: number - maxPrice: number - midPrice: number - pricePoints: bigint - market: MarketParams -} + minPrice: number; + maxPrice: number; + midPrice: number; + pricePoints: bigint; + market: MarketParams; + adjust?: boolean | undefined; +}; export type PositionKandelParams = { - baseQuoteTickIndex0: bigint - baseQuoteTickOffset: bigint - firstAskIndex: bigint - pricePoints: bigint + baseQuoteTickIndex0: bigint; + baseQuoteTickOffset: bigint; + firstAskIndex: bigint; + pricePoints: bigint; +}; + +function getTick(price: number, tickSpacing: bigint, adjust: boolean) { + const lowerTick = tickFromPrice(price, tickSpacing, false); + if (!adjust) return lowerTick; + const upperTick = tickFromPrice(price, tickSpacing, true); + + if (upperTick === lowerTick) return lowerTick; + + const lowerPrice = priceFromTick(lowerTick); + const upperPrice = priceFromTick(upperTick); + + const lowerDiff = Math.abs(price - lowerPrice); + const upperDiff = Math.abs(price - upperPrice); + + return lowerDiff < upperDiff ? lowerTick : upperTick; } export function getKandelPositionRawParams( - params: RawKandelPositionParams, + params: RawKandelPositionParams ): PositionKandelParams { - const { market, pricePoints } = params - const baseQuoteTickIndex0 = tickFromPrice( - humanPriceToRawPrice(params.minPrice, market), + const { market, pricePoints } = params; + const minPriceRaw = humanPriceToRawPrice(params.minPrice, market); + const midPriceRaw = humanPriceToRawPrice(params.midPrice, market); + const maxPriceRaw = humanPriceToRawPrice(params.maxPrice, market); + + const baseQuoteTickIndex0 = getTick( + minPriceRaw, market.tickSpacing, - ) - const midTick = tickFromPrice( - humanPriceToRawPrice(params.midPrice, market), + params.adjust ?? false + ); + const midTick = getTick( + midPriceRaw, market.tickSpacing, - ) - const maxTick = tickFromPrice( - humanPriceToRawPrice(params.maxPrice, market), + params.adjust ?? false + ); + const maxTick = getTick( + maxPriceRaw, market.tickSpacing, - ) + params.adjust ?? false + ); + let baseQuoteTickOffset = ((maxTick - baseQuoteTickIndex0) / (pricePoints - 1n) / market.tickSpacing) * - market.tickSpacing - if (baseQuoteTickOffset === 0n) baseQuoteTickOffset = market.tickSpacing - let firstAskIndex = 0n + market.tickSpacing; + if (baseQuoteTickOffset === 0n) baseQuoteTickOffset = market.tickSpacing; + let firstAskIndex = 0n; for (; firstAskIndex < pricePoints; ++firstAskIndex) { if (baseQuoteTickIndex0 + firstAskIndex * baseQuoteTickOffset >= midTick) - break + break; } return { @@ -65,74 +90,74 @@ export function getKandelPositionRawParams( baseQuoteTickOffset, firstAskIndex, pricePoints, - } + }; } export type RawKandelParams = RawKandelPositionParams & { - baseAmount: bigint - quoteAmount: bigint - stepSize: bigint - gasreq: bigint - factor: number - asksLocalConfig: LocalConfig - bidsLocalConfig: LocalConfig - marketConfig: GlobalConfig - deposit?: boolean | undefined -} + baseAmount: bigint; + quoteAmount: bigint; + stepSize: bigint; + gasreq: bigint; + factor: number; + asksLocalConfig: LocalConfig; + bidsLocalConfig: LocalConfig; + marketConfig: GlobalConfig; + deposit?: boolean | undefined; +}; export type KandelParams = PositionKandelParams & { - stepSize: bigint - askGives: bigint - bidGives: bigint - gasreq: bigint - baseAmount?: bigint | undefined - quoteAmount?: bigint | undefined -} + stepSize: bigint; + askGives: bigint; + bidGives: bigint; + gasreq: bigint; + baseAmount?: bigint | undefined; + quoteAmount?: bigint | undefined; +}; export type ValidateParamsResult = { - params: KandelParams - rawParams: RawKandelParams - minBaseAmount: bigint - minQuoteAmount: bigint - minProvision: bigint - distribution: Distribution - isValid: boolean -} + params: KandelParams; + rawParams: RawKandelParams; + minBaseAmount: bigint; + minQuoteAmount: bigint; + minProvision: bigint; + distribution: Distribution; + isValid: boolean; +}; export function countBidsAndAsks(distribution: Distribution) { - let nBids = 0n - let nAsks = 0n + let nBids = 0n; + let nAsks = 0n; for (let i = 0; i < distribution.asks.length; i++) { - if (distribution.asks[i]!.gives !== 0n) nAsks++ + if (distribution.asks[i]!.gives !== 0n) nAsks++; } for (let i = 0; i < distribution.bids.length; i++) { - if (distribution.bids[i]!.gives !== 0n) nBids++ + if (distribution.bids[i]!.gives !== 0n) nBids++; } return { nBids, nAsks, - } + }; } export function changeGives( distribution: Distribution, bidGives: bigint, - askGives: bigint, + askGives: bigint ): Distribution { for (let i = 0; i < distribution.asks.length; i++) { if (distribution.asks[i]!.gives !== 0n) - distribution.asks[i]!.gives = askGives + distribution.asks[i]!.gives = askGives; if (distribution.bids[i]!.gives !== 0n) - distribution.bids[i]!.gives = bidGives + distribution.bids[i]!.gives = bidGives; } - return distribution + return distribution; } export function validateKandelParams( - params: RawKandelParams, + params: RawKandelParams ): ValidateParamsResult { const { baseQuoteTickIndex0, baseQuoteTickOffset, firstAskIndex } = - getKandelPositionRawParams(params) + getKandelPositionRawParams(params); const { pricePoints, @@ -144,7 +169,7 @@ export function validateKandelParams( bidsLocalConfig, marketConfig, deposit = false, - } = params + } = params; let distribution = createGeometricDistribution({ baseQuoteTickIndex0, @@ -155,36 +180,36 @@ export function validateKandelParams( market, askGives: 1n, bidGives: 1n, - }) + }); - const { nBids, nAsks } = countBidsAndAsks(distribution) + const { nBids, nAsks } = countBidsAndAsks(distribution); // asks gives base and bids gives quote - const askGives = nAsks > 0 ? params.baseAmount / nAsks : 0n - const bidGives = nBids > 0 ? params.quoteAmount / nBids : 0n + const askGives = nAsks > 0 ? params.baseAmount / nAsks : 0n; + const bidGives = nBids > 0 ? params.quoteAmount / nBids : 0n; - distribution = changeGives(distribution, bidGives, askGives) + distribution = changeGives(distribution, bidGives, askGives); - const baseAmount = askGives * nAsks - const quoteAmount = bidGives * nBids + const baseAmount = askGives * nAsks; + const quoteAmount = bidGives * nBids; const minPrice = rawPriceToHumanPrice( priceFromTick(baseQuoteTickIndex0), - market, - ) + market + ); const maxPrice = rawPriceToHumanPrice( priceFromTick( - baseQuoteTickIndex0 + baseQuoteTickOffset * (pricePoints - 1n), + baseQuoteTickIndex0 + baseQuoteTickOffset * (pricePoints - 1n) ), - market, - ) + market + ); - const bigintFactor = BigInt(factor * 10_000) + const bigintFactor = BigInt(factor * 10_000); - const minAsk = (minVolume(asksLocalConfig, gasreq) * bigintFactor) / 10_000n - const minBid = (minVolume(bidsLocalConfig, gasreq) * bigintFactor) / 10_000n + const minAsk = (minVolume(asksLocalConfig, gasreq) * bigintFactor) / 10_000n; + const minBid = (minVolume(bidsLocalConfig, gasreq) * bigintFactor) / 10_000n; - const minBaseAmount = minAsk * nAsks - const minQuoteAmount = minBid * nBids + const minBaseAmount = minAsk * nAsks; + const minQuoteAmount = minBid * nBids; const minProvision = ((gasreq + asksLocalConfig.offer_gasbase) * @@ -192,10 +217,11 @@ export function validateKandelParams( (gasreq + bidsLocalConfig.offer_gasbase) * BigInt(distribution.bids.length)) * marketConfig.gasprice * - BigInt(1e6) + BigInt(1e6); const isValid = - (nAsks === 0n || askGives >= minAsk) && (nBids === 0n || bidGives >= minBid) + (nAsks === 0n || askGives >= minAsk) && + (nBids === 0n || bidGives >= minBid); return { params: { @@ -222,13 +248,13 @@ export function validateKandelParams( minProvision, isValid, distribution, - } + }; } export type GetKandelGasReqParams = { - baseLogic?: Logic | undefined - quoteLogic?: Logic | undefined -} + baseLogic?: Logic | undefined; + quoteLogic?: Logic | undefined; +}; export function getKandelGasReq(params: GetKandelGasReqParams) { return ( @@ -236,8 +262,8 @@ export function getKandelGasReq(params: GetKandelGasReqParams) { Math.max( Number(params.baseLogic?.gasreq || 0), Number(params.quoteLogic?.gasreq || 0), - Number(getDefaultLimitOrderGasreq()), - ), + Number(getDefaultLimitOrderGasreq()) + ) ) + 100_000n - ) + ); } diff --git a/src/lib/tick.test.ts b/src/lib/tick.test.ts index 673297b..acf09b7 100644 --- a/src/lib/tick.test.ts +++ b/src/lib/tick.test.ts @@ -3,6 +3,7 @@ import { MAX_SAFE_VOLUME, inboundFromOutbound, outboundFromInbound, + tickFromPrice, tickFromVolumes, } from './tick.js' @@ -44,6 +45,12 @@ describe('ticks', () => { assertEq(tickFromVolumes(1000000n * 10n ** 18n, 999999n * 10n ** 18n), 0n) }) + test('tickFromPrice', () => { + assertEq(tickFromPrice(1), 0n) + assertEq(tickFromPrice(1.0001), 1n) + assertEq(tickFromPrice(0.9998, 1n, true), -2n) + }) + test('outboundFromInbound and inboundFromOutbound', () => { testPrice(1n, 1n) testPrice(2n, 1n) diff --git a/src/lib/tick.ts b/src/lib/tick.ts index ff26484..f352a15 100644 --- a/src/lib/tick.ts +++ b/src/lib/tick.ts @@ -31,10 +31,12 @@ export function tickInRange(tick: bigint): boolean { * Computes the tick value corresponding to the price of the asset. * @param price the price of the asset * @param tickSpacing the tick spacing of the market @default 1n + * @param roundUp round up the result @default false * @returns A Tick instance corresponding to the price of the asset. */ -export function tickFromPrice(price: number, tickSpacing = 1n): bigint { - const rawTick = BigInt(Math.floor(Math.log(price) / Math.log(1.0001))) +export function tickFromPrice(price: number, tickSpacing = 1n, roundUp = false): bigint { + const roundMethod = roundUp ? Math.ceil : Math.floor + const rawTick = BigInt(roundMethod(Math.log(price) / Math.log(1.0001))) const bin = rawTick / tickSpacing + (rawTick % tickSpacing > 0n ? 1n : 0n) return bin * tickSpacing } From 9536f9662f3ab31f59a5397b2bd56f29088a0fee Mon Sep 17 00:00:00 2001 From: Maxence Raballand Date: Tue, 19 Nov 2024 14:58:09 +0100 Subject: [PATCH 2/3] chore: changeset --- .changeset/healthy-ads-shake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/healthy-ads-shake.md diff --git a/.changeset/healthy-ads-shake.md b/.changeset/healthy-ads-shake.md new file mode 100644 index 0000000..613e1b9 --- /dev/null +++ b/.changeset/healthy-ads-shake.md @@ -0,0 +1,5 @@ +--- +"@mangrovedao/mgv": patch +--- + +Add adjust params for kandel From 19a243a746a04d03a6f4c9b7a78425ea571e516c Mon Sep 17 00:00:00 2001 From: maxencerb Date: Tue, 19 Nov 2024 14:03:15 +0000 Subject: [PATCH 3/3] chore: format --- src/lib/kandel/params.ts | 211 +++++++++++++++++++-------------------- src/lib/tick.ts | 6 +- 2 files changed, 110 insertions(+), 107 deletions(-) diff --git a/src/lib/kandel/params.ts b/src/lib/kandel/params.ts index 72a47b1..b8cd2d4 100644 --- a/src/lib/kandel/params.ts +++ b/src/lib/kandel/params.ts @@ -4,85 +4,85 @@ import { type Logic, type MarketParams, minVolume, -} from "../../index.js"; +} from '../../index.js' import { humanPriceToRawPrice, rawPriceToHumanPrice, -} from "../human-readable.js"; -import { getDefaultLimitOrderGasreq } from "../limit-order.js"; -import { priceFromTick, tickFromPrice } from "../tick.js"; +} from '../human-readable.js' +import { getDefaultLimitOrderGasreq } from '../limit-order.js' +import { priceFromTick, tickFromPrice } from '../tick.js' import { type Distribution, createGeometricDistribution, -} from "./distribution.js"; +} from './distribution.js' export type RawKandelPositionParams = { - minPrice: number; - maxPrice: number; - midPrice: number; - pricePoints: bigint; - market: MarketParams; - adjust?: boolean | undefined; -}; + minPrice: number + maxPrice: number + midPrice: number + pricePoints: bigint + market: MarketParams + adjust?: boolean | undefined +} export type PositionKandelParams = { - baseQuoteTickIndex0: bigint; - baseQuoteTickOffset: bigint; - firstAskIndex: bigint; - pricePoints: bigint; -}; + baseQuoteTickIndex0: bigint + baseQuoteTickOffset: bigint + firstAskIndex: bigint + pricePoints: bigint +} function getTick(price: number, tickSpacing: bigint, adjust: boolean) { - const lowerTick = tickFromPrice(price, tickSpacing, false); - if (!adjust) return lowerTick; - const upperTick = tickFromPrice(price, tickSpacing, true); + const lowerTick = tickFromPrice(price, tickSpacing, false) + if (!adjust) return lowerTick + const upperTick = tickFromPrice(price, tickSpacing, true) - if (upperTick === lowerTick) return lowerTick; + if (upperTick === lowerTick) return lowerTick - const lowerPrice = priceFromTick(lowerTick); - const upperPrice = priceFromTick(upperTick); + const lowerPrice = priceFromTick(lowerTick) + const upperPrice = priceFromTick(upperTick) - const lowerDiff = Math.abs(price - lowerPrice); - const upperDiff = Math.abs(price - upperPrice); + const lowerDiff = Math.abs(price - lowerPrice) + const upperDiff = Math.abs(price - upperPrice) - return lowerDiff < upperDiff ? lowerTick : upperTick; + return lowerDiff < upperDiff ? lowerTick : upperTick } export function getKandelPositionRawParams( - params: RawKandelPositionParams + params: RawKandelPositionParams, ): PositionKandelParams { - const { market, pricePoints } = params; - const minPriceRaw = humanPriceToRawPrice(params.minPrice, market); - const midPriceRaw = humanPriceToRawPrice(params.midPrice, market); - const maxPriceRaw = humanPriceToRawPrice(params.maxPrice, market); + const { market, pricePoints } = params + const minPriceRaw = humanPriceToRawPrice(params.minPrice, market) + const midPriceRaw = humanPriceToRawPrice(params.midPrice, market) + const maxPriceRaw = humanPriceToRawPrice(params.maxPrice, market) const baseQuoteTickIndex0 = getTick( minPriceRaw, market.tickSpacing, - params.adjust ?? false - ); + params.adjust ?? false, + ) const midTick = getTick( midPriceRaw, market.tickSpacing, - params.adjust ?? false - ); + params.adjust ?? false, + ) const maxTick = getTick( maxPriceRaw, market.tickSpacing, - params.adjust ?? false - ); + params.adjust ?? false, + ) let baseQuoteTickOffset = ((maxTick - baseQuoteTickIndex0) / (pricePoints - 1n) / market.tickSpacing) * - market.tickSpacing; - if (baseQuoteTickOffset === 0n) baseQuoteTickOffset = market.tickSpacing; - let firstAskIndex = 0n; + market.tickSpacing + if (baseQuoteTickOffset === 0n) baseQuoteTickOffset = market.tickSpacing + let firstAskIndex = 0n for (; firstAskIndex < pricePoints; ++firstAskIndex) { if (baseQuoteTickIndex0 + firstAskIndex * baseQuoteTickOffset >= midTick) - break; + break } return { @@ -90,74 +90,74 @@ export function getKandelPositionRawParams( baseQuoteTickOffset, firstAskIndex, pricePoints, - }; + } } export type RawKandelParams = RawKandelPositionParams & { - baseAmount: bigint; - quoteAmount: bigint; - stepSize: bigint; - gasreq: bigint; - factor: number; - asksLocalConfig: LocalConfig; - bidsLocalConfig: LocalConfig; - marketConfig: GlobalConfig; - deposit?: boolean | undefined; -}; + baseAmount: bigint + quoteAmount: bigint + stepSize: bigint + gasreq: bigint + factor: number + asksLocalConfig: LocalConfig + bidsLocalConfig: LocalConfig + marketConfig: GlobalConfig + deposit?: boolean | undefined +} export type KandelParams = PositionKandelParams & { - stepSize: bigint; - askGives: bigint; - bidGives: bigint; - gasreq: bigint; - baseAmount?: bigint | undefined; - quoteAmount?: bigint | undefined; -}; + stepSize: bigint + askGives: bigint + bidGives: bigint + gasreq: bigint + baseAmount?: bigint | undefined + quoteAmount?: bigint | undefined +} export type ValidateParamsResult = { - params: KandelParams; - rawParams: RawKandelParams; - minBaseAmount: bigint; - minQuoteAmount: bigint; - minProvision: bigint; - distribution: Distribution; - isValid: boolean; -}; + params: KandelParams + rawParams: RawKandelParams + minBaseAmount: bigint + minQuoteAmount: bigint + minProvision: bigint + distribution: Distribution + isValid: boolean +} export function countBidsAndAsks(distribution: Distribution) { - let nBids = 0n; - let nAsks = 0n; + let nBids = 0n + let nAsks = 0n for (let i = 0; i < distribution.asks.length; i++) { - if (distribution.asks[i]!.gives !== 0n) nAsks++; + if (distribution.asks[i]!.gives !== 0n) nAsks++ } for (let i = 0; i < distribution.bids.length; i++) { - if (distribution.bids[i]!.gives !== 0n) nBids++; + if (distribution.bids[i]!.gives !== 0n) nBids++ } return { nBids, nAsks, - }; + } } export function changeGives( distribution: Distribution, bidGives: bigint, - askGives: bigint + askGives: bigint, ): Distribution { for (let i = 0; i < distribution.asks.length; i++) { if (distribution.asks[i]!.gives !== 0n) - distribution.asks[i]!.gives = askGives; + distribution.asks[i]!.gives = askGives if (distribution.bids[i]!.gives !== 0n) - distribution.bids[i]!.gives = bidGives; + distribution.bids[i]!.gives = bidGives } - return distribution; + return distribution } export function validateKandelParams( - params: RawKandelParams + params: RawKandelParams, ): ValidateParamsResult { const { baseQuoteTickIndex0, baseQuoteTickOffset, firstAskIndex } = - getKandelPositionRawParams(params); + getKandelPositionRawParams(params) const { pricePoints, @@ -169,7 +169,7 @@ export function validateKandelParams( bidsLocalConfig, marketConfig, deposit = false, - } = params; + } = params let distribution = createGeometricDistribution({ baseQuoteTickIndex0, @@ -180,36 +180,36 @@ export function validateKandelParams( market, askGives: 1n, bidGives: 1n, - }); + }) - const { nBids, nAsks } = countBidsAndAsks(distribution); + const { nBids, nAsks } = countBidsAndAsks(distribution) // asks gives base and bids gives quote - const askGives = nAsks > 0 ? params.baseAmount / nAsks : 0n; - const bidGives = nBids > 0 ? params.quoteAmount / nBids : 0n; + const askGives = nAsks > 0 ? params.baseAmount / nAsks : 0n + const bidGives = nBids > 0 ? params.quoteAmount / nBids : 0n - distribution = changeGives(distribution, bidGives, askGives); + distribution = changeGives(distribution, bidGives, askGives) - const baseAmount = askGives * nAsks; - const quoteAmount = bidGives * nBids; + const baseAmount = askGives * nAsks + const quoteAmount = bidGives * nBids const minPrice = rawPriceToHumanPrice( priceFromTick(baseQuoteTickIndex0), - market - ); + market, + ) const maxPrice = rawPriceToHumanPrice( priceFromTick( - baseQuoteTickIndex0 + baseQuoteTickOffset * (pricePoints - 1n) + baseQuoteTickIndex0 + baseQuoteTickOffset * (pricePoints - 1n), ), - market - ); + market, + ) - const bigintFactor = BigInt(factor * 10_000); + const bigintFactor = BigInt(factor * 10_000) - const minAsk = (minVolume(asksLocalConfig, gasreq) * bigintFactor) / 10_000n; - const minBid = (minVolume(bidsLocalConfig, gasreq) * bigintFactor) / 10_000n; + const minAsk = (minVolume(asksLocalConfig, gasreq) * bigintFactor) / 10_000n + const minBid = (minVolume(bidsLocalConfig, gasreq) * bigintFactor) / 10_000n - const minBaseAmount = minAsk * nAsks; - const minQuoteAmount = minBid * nBids; + const minBaseAmount = minAsk * nAsks + const minQuoteAmount = minBid * nBids const minProvision = ((gasreq + asksLocalConfig.offer_gasbase) * @@ -217,11 +217,10 @@ export function validateKandelParams( (gasreq + bidsLocalConfig.offer_gasbase) * BigInt(distribution.bids.length)) * marketConfig.gasprice * - BigInt(1e6); + BigInt(1e6) const isValid = - (nAsks === 0n || askGives >= minAsk) && - (nBids === 0n || bidGives >= minBid); + (nAsks === 0n || askGives >= minAsk) && (nBids === 0n || bidGives >= minBid) return { params: { @@ -248,13 +247,13 @@ export function validateKandelParams( minProvision, isValid, distribution, - }; + } } export type GetKandelGasReqParams = { - baseLogic?: Logic | undefined; - quoteLogic?: Logic | undefined; -}; + baseLogic?: Logic | undefined + quoteLogic?: Logic | undefined +} export function getKandelGasReq(params: GetKandelGasReqParams) { return ( @@ -262,8 +261,8 @@ export function getKandelGasReq(params: GetKandelGasReqParams) { Math.max( Number(params.baseLogic?.gasreq || 0), Number(params.quoteLogic?.gasreq || 0), - Number(getDefaultLimitOrderGasreq()) - ) + Number(getDefaultLimitOrderGasreq()), + ), ) + 100_000n - ); + ) } diff --git a/src/lib/tick.ts b/src/lib/tick.ts index f352a15..fff2f58 100644 --- a/src/lib/tick.ts +++ b/src/lib/tick.ts @@ -34,7 +34,11 @@ export function tickInRange(tick: bigint): boolean { * @param roundUp round up the result @default false * @returns A Tick instance corresponding to the price of the asset. */ -export function tickFromPrice(price: number, tickSpacing = 1n, roundUp = false): bigint { +export function tickFromPrice( + price: number, + tickSpacing = 1n, + roundUp = false, +): bigint { const roundMethod = roundUp ? Math.ceil : Math.floor const rawTick = BigInt(roundMethod(Math.log(price) / Math.log(1.0001))) const bin = rawTick / tickSpacing + (rawTick % tickSpacing > 0n ? 1n : 0n)