Closed sandeepmukh closed 2 years ago
Hi @sandeepmukh ! The code is OK to me and before you run that, it's necessary for you to synchronize the latest or at least the range you want of events data from a RPC provider. The block range of the db example events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db
is from #12370624 to #13747114 and you can check that on the table pool_config
with a SQLite console.
Please see Quick Start
on https://docs.bella.fi/ for more details.
As the use case you mentioned, I made some quick additions to implement that on the codebase for reference: https://github.com/Bella-DeFinTech/uniswap-v3-simulator/blob/e5958f12b6d68b9f93ee287c57b3fc4688493924/test/SimulatorClient.test.ts#L16
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance();
let clientInstance = new SimulatorClient(simulationDataManager);
let poolName = "events";
// case 1
// 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8
// 12374077
// case 2
// 0x92560C178cE069CC014138eD3C2F5221Ba71f58a
// 13578943
let poolAddress = "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8";
let endBlock: EndBlockTypeWhenRecover = "latestDownloaded";
// Your customed RPCProviderUrl, or use config in tuner.config.js
let RPCProviderUrl: string | undefined = undefined;
if (!exists(`${poolName}_${poolAddress}.db`)) {
await clientInstance.initCorePoolFromMainnet(
poolName,
poolAddress,
"afterDeployment",
RPCProviderUrl
);
}
let configurableCorePool =
await clientInstance.recoverFromMainnetEventDBFile(
`${poolName}_${poolAddress}.db`,
endBlock,
RPCProviderUrl
);
console.log(`tick: ${configurableCorePool.getCorePool().tickCurrent}`);
console.log(
`sqrtPriceX96: ${configurableCorePool
.getCorePool()
.sqrtPriceX96.toString()}`
);
let ticks = configurableCorePool.getCorePool().tickManager.sortedTicks;
// count: 29475 of legal available tick index between MIN_TICK and MAX_TICK
function findAvailableTicks(): number[] {
let minTick = -887220; //-887220;
let maxTick = 887220; //887220;
let ticks: number[] = [];
let currTick = minTick;
while (currTick <= maxTick) {
ticks.push(currTick);
currTick += 60;
}
return ticks;
}
let availableTicks = findAvailableTicks();
let currentLiquidityNet = ZERO;
let dataPath = "ticksLiquidityNet.data";
let dataFile = fs.createWriteStream(dataPath, { flags: "w" });
for (let tickIndex of availableTicks) {
if (ticks.has(tickIndex))
currentLiquidityNet = JSBI.add(
currentLiquidityNet,
ticks.get(tickIndex)!.liquidityNet
);
dataFile.write(tickIndex + "\t" + currentLiquidityNet.toString() + "\n");
}
await clientInstance.shutdown();
I think with the data in ticksLiquidityNet.data
you can plot the histogram as you like. Here is an example on Google Sheets https://docs.google.com/spreadsheets/d/1Nida0KR56s8lgEViqnpQb7nzMXHfAQ_OjLiCIY92DUI/edit?usp=sharing
Also, you can replace the tick index on x axis with token price. These two links will be helpful https://github.com/Bella-DeFinTech/uniswap-v3-bot/blob/83ba5e4f2437d4ea5303519df27f7db7e2659501/test/Backtest.test.ts#L428 https://github.com/Bella-DeFinTech/uniswap-v3-simulator/blob/e5958f12b6d68b9f93ee287c57b3fc4688493924/src/util/TickMath.ts#L51
Thanks for the issue and the use case is valuable. We will add it to the TickManager
later.
Thank you! This was incredibly useful. I'm running into an assertion issue with a pretty opaque assertion error while converting tickIndex
to a price. I am running this within the cache function passed to Strategy builder.
This is what I'm doing--am I doing something wrong in my conversion? This issue occurs at all negative ticks, which leads me to believe it might be an implementation issue.
let dataObj: Map<number, string> = new Map();
let currPrice: number;
for (let tickIndex of availableTicks) {
if (ticks.has(tickIndex)) {
currentLiquidityNet = JSBI.add(
currentLiquidityNet,
ticks.get(tickIndex)!.liquidityNet
);
console.log(currentLiquidityNet.toString());
}
currPrice = sqrtPriceToView(toBN(TickMath.getSqrtRatioAtTick(tickIndex))).toNumber();
dataObj.set(currPrice, currentLiquidityNet.toString());
}
Error:
Error: Assertion failed
at assert (node_modules/bn.js/lib/bn.js:6:21)
at BN.divmod (node_modules/bn.js/lib/bn.js:2429:5)
at BN.div (node_modules/bn.js/lib/bn.js:2525:17)
at Object.sqrtPriceToView (src/python_strategy/Utils.ts:48:25)
at cache (src/python_strategy/Index.ts:79:23)
at cache (test/Backtest.test.ts:118:17)
at Object.backtest (src/Strategy.ts:180:7)
at async Context.<anonymous> (test/Backtest.test.ts:413:5)
Also my understanding of the engine interface is as follows please let me know if this is incorrect:
mint(recipient: string, tickLower: number, tickUpper: number, amount: JSBI): Promise<{ amount0: JSBI; amount1: JSBI }>;
This method creates a position between the two ticks on behalf recipient with liquidity amount.
burn(owner: string, tickLower: number, tickUpper: number, amount: JSBI): Promise<{ amount0: JSBI; amount1: JSBI }>;
Burns amount liquidity from a certain position. I'm the most unclear about this, because it occasionally seems to be used to query gas and othertimes to close out a position.
collect(recipient: string, tickLower: number, tickUpper: number, amount0Requested: JSBI, amount1Requested: JSBI): Promise<{ amount0: JSBI; amount1: JSBI }>;
Collect fees from a position and credits them to recipient
. I'm not entire sure what the amount{i}Requested
parameter does, but maybe a maximum amount that the user wants to collect?
swap(zeroForOne: boolean, amountSpecified: JSBI, sqrtPriceLimitX96?: JSBI): Promise<{ amount0: JSBI; amount1: JSBI }>;
Creates a limit order in the direction specified by zeroForOne
for at most amountSpecified
volume and sqrtPriceLimitX96
price.
Finally, how would one convert from a price to a tick index? (By price I mean the output of sqrtPriceToView
, not root price.)
Hi @sandeepmukh , as you mentioned above the price is a decimal(smaller than 1) when the tick is negative. Since we use a BN lib to compute number, there should be some extra work handling the precision on the decimals. e.g. the numerator part in sqrtPriceToView
can be improved by multipling some const.
The function of the engine interface is identical to interface of UniswapV3Pool
. Your understanding is correct to me except a limit order is supposed to be delivered by creating a narrow position and waiting for the price moving through that. Besides I think source code of any UniswapV3Pool
contract may help.
Hi! Thank you for creating this super useful framework! I'm trying to create a strategy using the https://github.com/Bella-DeFinTech/uniswap-v3-bot/blob/main/test/Backtest.test.ts example. However, I want to work on a block-by-block basis rather than a daily basis, but running this code on the example DB yields an empty array. Am I doing something wrong or should I try to just using smaller date/time increments?
edit: small 10 minute date/time increments work as expected.
Also is there an easy way to query the liquidity distribution from the position manager? If not how would I go about implementing one? I'm looking to essentially recreate this histogram.