Bella-DeFinTech / uniswap-v3-simulator

the "Tuner", a Uniswap V3 Simulator
https://docs.bella.fi/
Other
144 stars 45 forks source link

Getting Events by Block Number not working #64

Closed sandeepmukh closed 2 years ago

sandeepmukh commented 2 years ago

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?

      let swapEventBlock: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(
        13755817,
        13755817+1000000
      );
      console.log(swapEventBlock);

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.

kafeikui commented 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.

sandeepmukh commented 2 years ago

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:

Finally, how would one convert from a price to a tick index? (By price I mean the output of sqrtPriceToView, not root price.)

kafeikui commented 2 years ago

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.