Skip to content

Commit

Permalink
Merge pull request #76 from Bella-DeFinTech/main
Browse files Browse the repository at this point in the history
Release v0.1.5
  • Loading branch information
kafeikui authored Jun 10, 2022
2 parents f340ecf + b23ee78 commit a5af615
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 6 deletions.
3 changes: 2 additions & 1 deletion SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

- [Fetching all the data of a certain pool from Ethereum](docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md)
- [Getting a pool instance with the data fetched](docs/getting-a-pool-instance-with-the-data-fetched.md)
- [Loading and streaming events into a pool](docs/loading-and-streaming-events-into-a-pool.md)

- (Advanced)For a better user experience as a state machine

Expand All @@ -46,7 +47,7 @@

- [Uniswap-v3-Strategy-Backtest](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Strategy-Backtest)

- Uniswap-v3-Risk-Analysis(for some reason, not published yet)
- Uniswap-v3-Risk-Analysis(will be available soon)

## Contributing

Expand Down
8 changes: 8 additions & 0 deletions docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ This means, there is more information added on top of the mainnet events, and th
An example of the whole process will be:

```typescript
import {
EndBlockTypeWhenRecover,
EventDataSourceType,
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";

// 1. Instantiate a SimulationDataManager
// this is for handling the internal data (snapshots, roadmaps, etc.)
let simulationDataManager: SimulationDataManager =
Expand Down
2 changes: 1 addition & 1 deletion docs/how-tuner-library-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The overall design of the simulator consists of several components (rough depend

  |\_\_ **`SimulatorRoadMapManager`** - _To take snapshots and do state-change roadmap tracking_

  |\_\_ **`InternalDBManager`** - _To persist snapshots and roadmaps using SQLite_
  |\_\_ **`SimulationDataManager`** - _To persist snapshots and roadmaps using SQLite_

There are 2 abstraction layers of the library:

Expand Down
4 changes: 4 additions & 0 deletions docs/installing-tuner.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
```bash
yarn add @bella-defintech/uniswap-v3-simulator
```

```bash
yarn upgrade @bella-defintech/uniswap-v3-simulator
```
131 changes: 131 additions & 0 deletions docs/loading-and-streaming-events-into-a-pool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
### Loading and streaming events into a pool

Suppose a quant developer is going to backtest some strategy on mainnet pool.

This includes the following steps:

1. Using mainnet data to initialize a core pool
2. Replaying events up to a certain block as a checkpoint
3. Do some interaction as the strategy asks as interpolation to real transactions
4. Replaying events until the next checkpoint
5. Repeat step3-4 until events are run out
6. Evaluate performance of the strategy

Tuner has offered an example project called `uniswap-v3-bot`. It similarly follow the process above to build a strategy platform for backtesting, dry-run and run, where a user adds a new strategy by implementing some callback interfaces(`trigger`, `cache`, `act` and `evaluate`). See [Uniswap-v3-Strategy-Backtest](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Strategy-Backtest).

When getting a core pool instance by `clientInstance.initCorePoolFromMainnet` or `clientInstance.recoverFromMainnetEventDBFile`, Tuner has already automatically replayed events from the first record to the last one in `endBlock`. For replaying following events, you can load them by `EventDBManager`, and decide how to use them on your own.

An example of streaming process will be:

```typescript
import {
ConfigurableCorePool,
EventDBManager,
EventType,
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";
import { LiquidityEvent } from "@bella-defintech/uniswap-v3-simulator/dist/entity/LiquidityEvent";
import { SwapEvent } from "@bella-defintech/uniswap-v3-simulator/dist/entity/SwapEvent";
import JSBI from "jsbi";

// the database name containing downloaded-and-pre-processed mainnet events
let mainnetEventDBFilePath =
"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db";

// build a client instance
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);

// Specify an endBlock number
// the SimulatorClient will replay events up to that block
let endBlock0 = 12374077;

// get a pool instance
let configurableCorePool: ConfigurableCorePool =
await clientInstance.recoverFromMainnetEventDBFile(
mainnetEventDBFilePath,
endBlock0
);

// get an EventDBManager instance to load events
let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);

// get and sort event by block number
let events: (LiquidityEvent | SwapEvent)[] = [];
let startBlock = 1000,
endBlock = 2000;
let mintEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.MINT,
startBlock,
endBlock
);
let burnEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.BURN,
startBlock,
endBlock
);
let swapEvents: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(
startBlock,
endBlock
);
events.push(...mintEvents);
events.push(...burnEvents);
events.push(...swapEvents);
events.sort(function (a, b) {
return a.blockNumber == b.blockNumber
? a.logIndex - b.logIndex
: a.blockNumber - b.blockNumber;
});

// replay events
for (let index = 0; index < events.length; index++) {
// avoid stack overflow for the possible recovering operation
if (index % 1000 == 0) {
configurableCorePool.takeSnapshot("");
}

let event = events[index];
switch (event.type) {
case EventType.MINT:
await configurableCorePool.mint(
event.recipient,
event.tickLower,
event.tickUpper,
event.liquidity
);
break;
case EventType.BURN:
await configurableCorePool.burn(
event.msgSender,
event.tickLower,
event.tickUpper,
event.liquidity
);
break;
case EventType.SWAP:
let zeroForOne: boolean = JSBI.greaterThan(event.amount0, JSBI.BigInt(0))
? true
: false;
await configurableCorePool.swap(
zeroForOne,
event.amountSpecified
//,event.sqrt_price_x96
);
break;
default:
// @ts-ignore: ExhaustiveCheck
const exhaustiveCheck: never = event;
}
}
```

Note: Tuner doesn't export `LiquidityEvent` | `SwapEvent` directly but you can sitll use them. For external data(event logs), we recommend you to implement your `EventDBManager` to load and manage event logs, according to your context.
8 changes: 7 additions & 1 deletion docs/quick-start.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
### Quick Start

```typescript
import {
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";

// 1. Instantiate a SimulationDataManager
// this is for handling the internal data (snapshots, roadmaps, etc.)
let simulationDataManager: SimulationDataManager =
Expand All @@ -26,7 +32,7 @@ let endBlock = 12374077;
// 3. This method helps you:
// Download event data of a certain Uniswap V3 pool from mainnet
// Pre-process the data to figure out the inputs of swap events
await clientInstance.initCorePoolFromMainnetPool(
await clientInstance.initCorePoolFromMainnet(
poolName,
poolAddress,
"afterDeployment"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bella-defintech/uniswap-v3-simulator",
"version": "0.1.4",
"version": "0.1.5",
"description": "the 'Tuner', a Uniswap V3 Simulator",
"keywords": [
"uniswap",
Expand Down
2 changes: 1 addition & 1 deletion src/manager/TickManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class TickManager {
): { nextTick: number; initialized: boolean } {
const sortedTicks = this.getSortedTicks();
let compressed = Math.floor(tick / tickSpacing); // matches rounding in the code
if (tick < 0 && tick % tickSpacing != 0) compressed--;
// if (tick < 0 && tick % tickSpacing != 0) compressed--;
if (lte) {
const wordPos = compressed >> 8;
const minimum = (wordPos << 8) * tickSpacing;
Expand Down
2 changes: 1 addition & 1 deletion test/TestSubgraph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ chai.use(chaiAsPromised);
describe("Test Uniswap v3 Subgraph", function () {
const APIURL = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3";

it.only("can download events", async function () {
it("can download events", async function () {
const query = gql`
query {
pool(id: "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8") {
Expand Down
104 changes: 104 additions & 0 deletions test/TickManager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Tick } from "../src/model/Tick";
import { TickMath } from "../src/util/TickMath";
import { expect } from "./shared/expect";
import { TickManager } from "../src/manager/TickManager";

describe("TickManager", () => {
describe("#getNextInitializedTick", () => {
let highTick: Tick;
let lowTick: Tick;
let midTick: Tick;
let ticks: Tick[];
let tickManager: TickManager = new TickManager();

beforeEach(() => {
lowTick = new Tick(TickMath.MIN_TICK + 1);
midTick = new Tick(0);
highTick = new Tick(TickMath.MAX_TICK - 1);

ticks = [lowTick, midTick, highTick];

for (let tick of ticks) {
tickManager.set(tick);
}
});

it("lte = true", () => {
expect(tickManager.getNextInitializedTick(-257, 1, true)).to.eql({
nextTick: -512,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-256, 1, true)).to.eql({
nextTick: -256,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-1, 1, true)).to.eql({
nextTick: -256,
initialized: false,
});
expect(tickManager.getNextInitializedTick(0, 1, true)).to.eql({
nextTick: 0,
initialized: true,
});
expect(tickManager.getNextInitializedTick(1, 1, true)).to.eql({
nextTick: 0,
initialized: true,
});
expect(tickManager.getNextInitializedTick(255, 1, true)).to.eql({
nextTick: 0,
initialized: true,
});
expect(tickManager.getNextInitializedTick(256, 1, true)).to.eql({
nextTick: 256,
initialized: false,
});
expect(tickManager.getNextInitializedTick(257, 1, true)).to.eql({
nextTick: 256,
initialized: false,
});
});

it("lte = false", () => {
expect(tickManager.getNextInitializedTick(-215041, 60, false)).to.eql({
nextTick: -199740,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-257, 1, false)).to.eql({
nextTick: -1,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-256, 1, false)).to.eql({
nextTick: -1,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-2, 1, false)).to.eql({
nextTick: -1,
initialized: false,
});
expect(tickManager.getNextInitializedTick(-1, 1, false)).to.eql({
nextTick: 0,
initialized: true,
});
expect(tickManager.getNextInitializedTick(0, 1, false)).to.eql({
nextTick: 255,
initialized: false,
});
expect(tickManager.getNextInitializedTick(1, 1, false)).to.eql({
nextTick: 255,
initialized: false,
});
expect(tickManager.getNextInitializedTick(254, 1, false)).to.eql({
nextTick: 255,
initialized: false,
});
expect(tickManager.getNextInitializedTick(255, 1, false)).to.eql({
nextTick: 511,
initialized: false,
});
expect(tickManager.getNextInitializedTick(256, 1, false)).to.eql({
nextTick: 511,
initialized: false,
});
});
});
});

0 comments on commit a5af615

Please sign in to comment.