Order Book
Uniswap V3 Simulation
Section titled “Uniswap V3 Simulation”- Here we utilize UniswapPy to simulate an order book in Uniswap V3
- Medium Article: How to Simulate a Uniswap V3 Order Book in Python
- To download notebook to this tutorial, see here
📘 Notable Classes
Section titled “📘 Notable Classes”-
Class: 📘
defipy.math.model.BrownianModel- Purpose: Geometric Brownian process.
- Methods:
gen_gbms(mu: float, sigma: float, n_step: int, T: int = 1)- Parameters:
mu: asset drift (float).sigma: asset volatility (int).n_step: number of steps. (int).T: time unit (optional) (int).
- Parameters:
-
Class: 📘
defipy.math.model.TokenDeltaModel- Purpose: Random sample of Gamma distribution representing an experimental token amount.
- Methods:
delta(p: int = 1)- Parameters:
p: buy/sell probability associated (buy = 1, sell = -1, optional) (int).
- Parameters:
-
Class: 📘
defipy.analytics.simulate.CorrectReserves- Purpose: Applies
SolveDeltasto Correct x/y reserve amounts so that price reflects desired input price; in the marjority of cases, the input price would be the most recent outside market price . - Methods:
delta(price: float, lwr_tick: int = None, upr_tick: int = None)- Parameters:
price: token price (int).lwr_tick: Lower tick of the position. (optional) (int).upr_tick: Upper tick of the position. (optional) (int).
- Parameters:
- Purpose: Applies
from defipy import *import pandas as pdimport seaborn as snsimport matplotlib.pyplot as plt
user_nm = MockAddress().apply()eth_amount = 100tkn_amount = 1000
fee = UniV3Utils.FeeAmount.MEDIUMtick_spacing = UniV3Utils.TICK_SPACINGS[fee]Simulate prices
Section titled “Simulate prices”Simulate prices using a geometric brownian motion process
n_steps = 1000start_price = eth_amount/tkn_amountmu = 0.1; sigma = 0.5n_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
Section titled “Setup pool”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
Section titled “Simulate liquidity pool”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 = 153089.57969951717, TKN = 1063580.263806977
Gross Liquidity: 6723644.391607661
Construct order book
Section titled “Construct order book”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_arridx = df_liq.index[df_liq['side'] == 'center']df_liq.drop(idx[0], inplace=True)df_liq tick price liquidity side
1 15000.0 4.481353 21.875455 bids
2 15180.0 4.562744 2302.827924 bids
3 15300.0 4.617824 3.923965 bids
4 15360.0 4.645612 272.986987 bids
5 15540.0 4.729986 4858.284512 bids
.. ... ... ... ...
166 25200.0 12.427031 742.064919 asks
167 25260.0 12.501813 474.771564 asks
168 25440.0 12.728872 447.693367 asks
169 25500.0 12.805471 457.990880 asks
170 25620.0 12.960055 366.017072 asks
[170 rows x 4 columns]
Review simulation output
Section titled “Review simulation output”fig = plt.figure(figsize = (10, 5))
current_price = lp.get_price(tkn)prices = 1/df_liq['price'].valuesliquidity = 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>
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')Text(0, 0.5, 'Frequency')
Plot order book
Section titled “Plot order book”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)Text(0, 0.5, 'Liquidity')
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)Text(0, 0.5, 'Liquidity')