Uniswap V3 Order Book
Here we utilize UniswapPy to simulate an order book in Uniswap V3
Medium Article: How to Simulate a Uniswap V3 Order Book in Python
[1]:
from defipy import *
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
user_nm = MockAddress().apply()
eth_amount = 100
tkn_amount = 1000
fee = UniV3Utils.FeeAmount.MEDIUM
tick_spacing = UniV3Utils.TICK_SPACINGS[fee]
Simulate prices
Simulate prices using a geometric brownian motion process
[2]:
n_steps = 1000
start_price = eth_amount/tkn_amount
mu = 0.1; sigma = 0.5
n_paths = 1
b = BrownianModel(start_price)
p_arr = b.gen_gbms(mu, sigma, n_steps, n_paths)
exp_p_arr = np.median(p_arr, axis = 1)
accounts = MockAddress().apply(50)
Setup pool
[3]:
eth = ERC20("ETH", "0x09")
tkn = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = tkn, symbol="LP",
address="0x011", version = 'V3',
tick_spacing = tick_spacing,
fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")
lp = factory.deploy(exchg_data)
lwr_tick = UniV3Helper().get_price_tick(lp, -1, 10, 1000)
upr_tick = UniV3Helper().get_price_tick(lp, 1, 10, 1000)
Join().apply(lp, user_nm, eth_amount, tkn_amount, lwr_tick, upr_tick)
lp.summary()
Exchange ETH-TKN (LP)
Real Reserves: ETH = 96.70469739529014, TKN = 1000.0
Gross Liquidity: 6440.3320664241655
Simulate liquidity pool
[4]:
arb = CorrectReserves(lp, x0 = 1/exp_p_arr[0])
p_intervals = [500, 800, 1000, 1200, 1500, 1700, 2000]
lp_prices = [lp.get_price(tkn)]
lp_liquidity = [lp.total_supply]
lp_swaps = []; lp_net_deposits = [];
for k in range(1, n_steps):
p = 1/exp_p_arr[k]
arb.apply(p, lwr_tick, upr_tick)
select_tkn = EventSelectionModel().bi_select(0.5)
rnd_add_amt = TokenDeltaModel(25).delta()
rnd_swap_amt = TokenDeltaModel(15).delta()
user_add = random.choice(accounts)
user_swap = random.choice(accounts)
p_interval = random.choice(p_intervals)
lwr_tick = UniV3Helper().get_price_tick(lp, -1, lp.get_price(eth), p_interval)
upr_tick = UniV3Helper().get_price_tick(lp, 1, lp.get_price(eth), p_interval)
if(select_tkn == 0):
AddLiquidity().apply(lp, eth, user_add, rnd_add_amt, lwr_tick, upr_tick)
out = Swap().apply(lp, eth, user_swap, rnd_swap_amt)
else:
AddLiquidity().apply(lp, tkn, user_add, p*rnd_add_amt, lwr_tick, upr_tick)
out = Swap().apply(lp, tkn, user_swap, p*rnd_swap_amt)
lp_prices.append(lp.get_price(tkn))
lp_liquidity.append(lp.total_supply)
lp_swaps.append(rnd_swap_amt)
lp_net_deposits.append(rnd_add_amt)
lp.summary()
Exchange ETH-TKN (LP)
Real Reserves: ETH = 614127.7691542099, TKN = 17821512.609132715
Gross Liquidity: 35165267.83386152
Construct order book
[5]:
liquidity = {}
df_liq = pd.DataFrame(columns=['tick', 'price', 'liquidity'])
for k, pos in enumerate(lp.ticks):
price = UniV3Helper().tick_to_price(pos)
liq = lp.ticks[pos].liquidityGross/10**18
df_liq.loc[k] = [pos,price,liq]
center_pos = UniV3Helper().price_to_tick(lp.get_price(eth))
price = lp.get_price(tkn)
df_liq.loc[k+1] = [center_pos,price,0]
df_liq.sort_values(by=['price'], inplace=True)
df_liq.reset_index(drop=True, inplace=True)
side_arr = []
for tick in df_liq['tick'].values:
if (tick > center_pos):
side_arr.append('asks')
elif (tick < center_pos):
side_arr.append('bids')
else:
side_arr.append('center')
df_liq['side'] = side_arr
idx = df_liq.index[df_liq['side'] == 'center']
df_liq.drop(idx[0], inplace=True)
[6]:
df_liq
[6]:
tick | price | liquidity | side | |
---|---|---|---|---|
1 | 17940.0 | 6.012919 | 50341.631295 | bids |
2 | 18120.0 | 6.122126 | 22053.081205 | bids |
3 | 18180.0 | 6.158967 | 16493.867865 | bids |
4 | 18300.0 | 6.233316 | 154124.547473 | bids |
5 | 18420.0 | 6.308563 | 1242.862215 | bids |
... | ... | ... | ... | ... |
192 | 29640.0 | 19.372447 | 1834.850559 | asks |
193 | 29700.0 | 19.489025 | 454.070649 | asks |
194 | 29820.0 | 19.724291 | 106.278197 | asks |
195 | 29940.0 | 19.962396 | 205.903139 | asks |
196 | 30480.0 | 21.069945 | 13.774698 | asks |
196 rows × 4 columns
Review simulation output
[7]:
fig = plt.figure(figsize = (10, 5))
current_price = lp.get_price(tkn)
prices = 1/df_liq['price'].values
liquidity = df_liq['liquidity'].values
fig, (book_ax, price_ax, liq_ax) = plt.subplots(nrows=3, sharex=False, sharey=False, figsize=(12, 8))
book_ax.bar(prices, liquidity, color ='steelblue', width = 0.0005, label = 'liquidity', alpha=0.7)
book_ax.axvline(x=current_price, color = 'mediumvioletred', linewidth = 1, linestyle = 'dashdot', label = 'current price')
book_ax.set_xlabel("Price (USD)", size=10)
book_ax.set_ylabel("Liquidity", size=14)
book_ax.set_title("Uniswap V3: Liquidity distribution")
book_ax.legend()
x_val = np.arange(0,len(p_arr))
price_ax.plot(x_val[1:-1], p_arr[1:-1], color = 'r',linestyle = 'dashdot', label='market price')
price_ax.plot(x_val[1:-1], lp_prices[1:], color = 'b',linestyle = 'dashed', label='lp price')
price_ax.set_ylabel('Price (ETH/TKN)', size=14)
price_ax.set_xlabel('Time sample', size=10)
price_ax.legend()
liq_ax.plot(x_val[1:-1], lp_liquidity[1:], color = 'b',linestyle = 'dashed', label='lp liquidity')
liq_ax.set_ylabel('Liquidity', size=14)
liq_ax.set_xlabel('Time sample', size=10)
liq_ax.legend()
plt.tight_layout()
<Figure size 1000x500 with 0 Axes>

[8]:
fig, ax = plt.subplots(1, 2, figsize=(12,5))
sns.distplot(lp_net_deposits, hist=True, kde=False, bins=int(30), color = 'darkblue',
hist_kws={'edgecolor':'black'}, kde_kws={'linewidth': 2}, ax=ax[0])
ax[0].set_title('Histogram: Net Deposit Volume')
ax[0].set_xlabel('Volume ETH')
ax[0].set_ylabel('Frequency')
sns.distplot(lp_swaps, hist=True, kde=False, bins=int(30), color = 'darkblue',
hist_kws={'edgecolor':'black'}, kde_kws={'linewidth': 2}, ax=ax[1])
ax[1].set_title('Histogram: Net Swap Volume')
ax[1].set_xlabel('Volume ETH')
ax[1].set_ylabel('Frequency')
[8]:
Text(0, 0.5, 'Frequency')

Plot order book
[9]:
fig, ax = plt.subplots(figsize = (10, 5))
ax.set_title(f"ETH/TKN Order Book - Scatterplot")
sns.scatterplot(x="price", y="liquidity", hue="side",
data=df_liq, ax=ax, palette=["green", "red"])
ax.set_xlabel("Price (TKN/ETH)", fontsize = 14)
ax.set_ylabel("Liquidity", fontsize = 14)
[9]:
Text(0, 0.5, 'Liquidity')

[10]:
fig, ax = plt.subplots(figsize = (10, 5))
ax.set_title(f"ETH/TKN Order Book - Depth Chart")
sns.ecdfplot(x="price", weights="liquidity", stat="count",
complementary=True, data=df_liq.query("side == 'bids'"),
color="green", ax=ax)
sns.ecdfplot(x="price", weights="liquidity", stat="count",
data=df_liq.query("side == 'asks'"), color="red",
ax=ax)
ax.set_xlabel("Price (TKN/ETH)", fontsize = 14)
ax.set_ylabel("Liquidity", fontsize = 14)
[10]:
Text(0, 0.5, 'Liquidity')
