Skip to content

Commit

Permalink
Merge branch 'main' into model-stableswap-IL
Browse files Browse the repository at this point in the history
  • Loading branch information
jepidoptera committed Jul 27, 2023
2 parents dac5319 + edf0659 commit eb2b3db
Show file tree
Hide file tree
Showing 4 changed files with 645 additions and 11 deletions.
24 changes: 17 additions & 7 deletions hydradx/model/amm/omnipool_amm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def __init__(self,
remove_liquidity_volatility_threshold: float = 0,
withdrawal_fee: bool = True,
min_withdrawal_fee: float = 0.0001,
lrna_mint_pct: float = 0.0,
):
"""
tokens should be a dict in the form of [str: dict]
Expand Down Expand Up @@ -72,6 +73,7 @@ def __init__(self,
self.max_withdrawal_per_block = max_withdrawal_per_block
self.max_lp_per_block = max_lp_per_block
self.remove_liquidity_volatility_threshold = remove_liquidity_volatility_threshold
self.lrna_mint_pct = lrna_mint_pct
self.withdrawal_fee = withdrawal_fee
if withdrawal_fee:
self.min_withdrawal_fee = min_withdrawal_fee
Expand Down Expand Up @@ -422,8 +424,10 @@ def execute_swap(
)
# lrna_fee = state.last_lrna_fee[tkn_buy]

delta_Qj = -delta_Qi * (1 - lrna_fee)
delta_Rj = state.liquidity[tkn_buy] * -delta_Qj / (state.lrna[tkn_buy] + delta_Qj) * (1 - asset_fee)
delta_Qt = -delta_Qi * (1 - lrna_fee)
delta_Qm = (state.lrna[tkn_buy] + delta_Qt) * delta_Qt * asset_fee / state.lrna[tkn_buy] * state.lrna_mint_pct
delta_Qj = delta_Qt + delta_Qm
delta_Rj = state.liquidity[tkn_buy] * -delta_Qt / (state.lrna[tkn_buy] + delta_Qt) * (1 - asset_fee)
delta_L = min(-delta_Qi * lrna_fee, -state.lrna_imbalance)
delta_QH = -lrna_fee * delta_Qi - delta_L

Expand Down Expand Up @@ -491,11 +495,14 @@ def execute_lrna_swap(
return state.fail_transaction('insufficient lrna in pool', agent)
delta_ra = -state.liquidity[tkn] * delta_qa / (-delta_qa + state.lrna[tkn]) * (1 - asset_fee)

delta_qm = asset_fee * (-delta_qa) / state.lrna[tkn] * (state.lrna[tkn] - delta_qa) * state.lrna_mint_pct
delta_q = delta_qm - delta_qa

if modify_imbalance:
q = state.lrna_total
state.lrna_imbalance += delta_qa * (q + state.lrna_imbalance) / (q - delta_qa) + delta_qa
state.lrna_imbalance += -delta_q * (q + state.lrna_imbalance) / (q + delta_q) - delta_q

state.lrna[tkn] += -delta_qa
state.lrna[tkn] += delta_q
state.liquidity[tkn] += -delta_ra

elif delta_ra > 0:
Expand All @@ -504,13 +511,16 @@ def execute_lrna_swap(
return state.fail_transaction('insufficient assets in pool', agent)
elif delta_ra > agent.holdings[tkn]:
return state.fail_transaction('agent has insufficient assets', agent)
delta_qa = -state.lrna[tkn] * delta_ra / (state.liquidity[tkn] * (1 - asset_fee) - delta_ra)
denom = (state.liquidity[tkn] * (1 - asset_fee) - delta_ra)
delta_qa = -state.lrna[tkn] * delta_ra / denom
delta_qm = - asset_fee * (1 - asset_fee) * (state.liquidity[tkn] / denom) * delta_qa * state.lrna_mint_pct
delta_q = -delta_qa + delta_qm

if modify_imbalance:
q = state.lrna_total
state.lrna_imbalance += delta_qa * (q + state.lrna_imbalance) / (q - delta_qa) + delta_qa
state.lrna_imbalance -= delta_q * (q + state.lrna_imbalance) / (q + delta_q) + delta_q

state.lrna[tkn] += -delta_qa
state.lrna[tkn] += delta_q
state.liquidity[tkn] += -delta_ra

# buying LRNA
Expand Down
328 changes: 328 additions & 0 deletions hydradx/notebooks/Omnipool/LP_fees_analysis.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion hydradx/tests/test_omnipool_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_omnipool_arbitrager_feeless(omnipool: oamm.OmnipoolState, market: list,

# Trading should be profitable
if old_value > new_value:
if new_value != pytest.approx(old_value, rel=1e-15):
if new_value != pytest.approx(old_value, rel=1e-12):
raise


Expand Down
302 changes: 299 additions & 3 deletions hydradx/tests/test_omnipool_amm.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,10 @@ def test_swap_lrna(initial_state: oamm.OmnipoolState):
):
raise AssertionError(f'LRNA imbalance is wrong.')

if (new_state.liquidity[i] + new_agent.holdings[i] != pytest.approx(old_state.liquidity[i] + old_agent.holdings[i])
or new_state.lrna[i] + new_agent.holdings['LRNA']
!= pytest.approx(old_state.lrna[i] + old_agent.holdings['LRNA'])):
if new_state.liquidity[i] + new_agent.holdings[i] != pytest.approx(old_state.liquidity[i] + old_agent.holdings[i]):
raise AssertionError('System-wide asset total is wrong.')
if new_state.lrna[i] + new_agent.holdings['LRNA'] < old_state.lrna[i] + old_agent.holdings['LRNA']:
raise AssertionError('System-wide LRNA decreased.')

# try swapping into LRNA and back to see if that's equivalent
reverse_state, reverse_agent = oamm.swap_lrna(
Expand All @@ -400,6 +400,302 @@ def test_swap_lrna(initial_state: oamm.OmnipoolState):
raise AssertionError('Agent holdings are wrong.')


@given(st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=0.0001, max_value=0.01),
st.floats(min_value=0.0001, max_value=0.01))
def test_lrna_swap_buy_with_lrna_mint(
hdx_liquidity: float,
dot_liquidity: float,
usd_liquidity: float,
hdx_lrna: float,
dot_lrna: float,
usd_lrna: float,
asset_fee: float,
lrna_fee: float
):
asset_dict = {
'HDX': {'liquidity': hdx_liquidity, 'LRNA': hdx_lrna},
'DOT': {'liquidity': dot_liquidity, 'LRNA': dot_lrna},
'USD': {'liquidity': usd_liquidity, 'LRNA': usd_lrna},
}

initial_state = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=1.0
)


old_agent = Agent(
holdings={token: 10000 for token in initial_state.asset_list + ['LRNA']}
)

i = 'DOT'

delta_ra = 1000
delta_ra_feeless = delta_ra / (1 - asset_fee)

feeless_state = initial_state.copy()
feeless_state.asset_fee = 0
for asset in feeless_state.asset_list:
feeless_state.last_fee[asset] = 0

# Test with trader buying asset i
swap_state, swap_agent = oamm.swap_lrna(initial_state, old_agent, delta_ra, 0, i)
feeless_swap_state, feeless_swap_agent = oamm.swap_lrna(feeless_state, old_agent, delta_ra_feeless, 0, i)
feeless_spot_price = feeless_swap_state.price(feeless_swap_state, i)
spot_price = swap_state.price(swap_state, i)
if feeless_swap_state.fail == '' and swap_state.fail == '':
if feeless_spot_price != pytest.approx(spot_price, rel=1e-16):
raise AssertionError('Spot price is wrong.')


@given(st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=0.0001, max_value=0.01),
st.floats(min_value=0.0001, max_value=0.01))
def test_lrna_swap_sell_with_lrna_mint(
hdx_liquidity: float,
dot_liquidity: float,
usd_liquidity: float,
hdx_lrna: float,
dot_lrna: float,
usd_lrna: float,
asset_fee: float,
lrna_fee: float
):
asset_dict = {
'HDX': {'liquidity': hdx_liquidity, 'LRNA': hdx_lrna},
'DOT': {'liquidity': dot_liquidity, 'LRNA': dot_lrna},
'USD': {'liquidity': usd_liquidity, 'LRNA': usd_lrna},
}

initial_state = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=1.0
)

old_agent = Agent(
holdings={token: 10000 for token in initial_state.asset_list + ['LRNA']}
)

i = 'DOT'

delta_qa = -1000

feeless_state = initial_state.copy()
feeless_state.asset_fee = 0
for asset in feeless_state.asset_list:
feeless_state.last_fee[asset] = 0

# Test with trader buying asset i
swap_state, swap_agent = oamm.swap_lrna(initial_state, old_agent, 0, delta_qa, i)
feeless_swap_state, feeless_swap_agent = oamm.swap_lrna(feeless_state, old_agent, 0, delta_qa, i)
feeless_spot_price = feeless_swap_state.price(feeless_swap_state, i)
spot_price = swap_state.price(swap_state, i)
if feeless_swap_state.fail == '' and swap_state.fail == '':
if feeless_spot_price != pytest.approx(spot_price, rel=1e-16):
raise AssertionError('Spot price is wrong.')


@given(st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=0.0001, max_value=0.01),
st.floats(min_value=0.0001, max_value=0.01), )
def test_sell_with_lrna_mint(
hdx_liquidity: float,
dot_liquidity: float,
usd_liquidity: float,
hdx_lrna: float,
dot_lrna: float,
usd_lrna: float,
asset_fee: float,
lrna_fee: float,
):
asset_dict = {
'HDX': {'liquidity': hdx_liquidity, 'LRNA': hdx_lrna},
'DOT': {'liquidity': dot_liquidity, 'LRNA': dot_lrna},
'USD': {'liquidity': usd_liquidity, 'LRNA': usd_lrna},
}

initial_state = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=1.0
)

old_agent = Agent(
holdings={token: 10000 for token in initial_state.asset_list + ['LRNA']}
)

i = 'DOT'
j = 'USD'

delta_ri = 1000

feeless_state = initial_state.copy()
feeless_state.asset_fee = 0
for asset in feeless_state.asset_list:
feeless_state.last_fee[asset] = 0

# Test with trader buying asset i
swap_state, swap_agent = oamm.swap(initial_state, old_agent, j, i, 0, delta_ri)
feeless_swap_state, feeless_swap_agent = oamm.swap(feeless_state, old_agent, j, i, 0, delta_ri)
feeless_spot_price = feeless_swap_state.price(feeless_swap_state, j)
spot_price = swap_state.price(swap_state, j)
if feeless_swap_state.fail == '' and swap_state.fail == '':
if feeless_spot_price != pytest.approx(spot_price, rel=1e-16):
raise AssertionError('Spot price is wrong.')


@given(st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=0.0001, max_value=0.01), )
def test_buy_with_lrna_mint(
hdx_liquidity: float,
dot_liquidity: float,
usd_liquidity: float,
hdx_lrna: float,
dot_lrna: float,
usd_lrna: float,
asset_fee: float
):
asset_dict = {
'HDX': {'liquidity': hdx_liquidity, 'LRNA': hdx_lrna},
'DOT': {'liquidity': dot_liquidity, 'LRNA': dot_lrna},
'USD': {'liquidity': usd_liquidity, 'LRNA': usd_lrna},
}

initial_state = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=0.0,
lrna_mint_pct=1.0
)

old_agent = Agent(
holdings={token: 10000 for token in initial_state.asset_list + ['LRNA']}
)

i = 'DOT'
j = 'USD'

delta_rj = 1000
delta_rj_feeless = delta_rj / (1 - asset_fee)

feeless_state = initial_state.copy()
feeless_state.asset_fee = 0
for asset in feeless_state.asset_list:
feeless_state.last_fee[asset] = 0

# Test with trader buying asset i
swap_state, swap_agent = oamm.swap(initial_state, old_agent, j, i, delta_rj, 0)
feeless_swap_state, feeless_swap_agent = oamm.swap(feeless_state, old_agent, j, i, delta_rj_feeless, 0)
feeless_spot_price = feeless_swap_state.price(feeless_swap_state, j)
spot_price = swap_state.price(swap_state, j)
if feeless_swap_state.fail == '' and swap_state.fail == '':
if feeless_spot_price != pytest.approx(spot_price, rel=1e-16):
raise AssertionError('Spot price is wrong.')


@given(st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=10000, max_value=10000000),
st.floats(min_value=0.0001, max_value=0.01),
st.floats(min_value=0.0001, max_value=0.01), )
def test_sell_with_partial_lrna_mint(
hdx_liquidity: float,
dot_liquidity: float,
usd_liquidity: float,
hdx_lrna: float,
dot_lrna: float,
usd_lrna: float,
asset_fee: float,
lrna_fee: float,
):
asset_dict = {
'HDX': {'liquidity': hdx_liquidity, 'LRNA': hdx_lrna},
'DOT': {'liquidity': dot_liquidity, 'LRNA': dot_lrna},
'USD': {'liquidity': usd_liquidity, 'LRNA': usd_lrna},
}

initial_state_0 = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=0.0
)

initial_state_50 = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=0.5
)

initial_state_100 = oamm.OmnipoolState(
tokens=asset_dict,
tvl_cap=float('inf'),
asset_fee=asset_fee,
lrna_fee=lrna_fee,
lrna_mint_pct=1.0
)

old_agent = Agent(
holdings={token: 10000 for token in initial_state_0.asset_list + ['LRNA']}
)

i = 'DOT'
j = 'USD'

delta_ri = 1000

# Test with trader buying asset i
swap_state_100, swap_agent_100 = oamm.swap(initial_state_100, copy.deepcopy(old_agent), j, i, 0, delta_ri)
swap_state_50, swap_agent_50 = oamm.swap(initial_state_50, copy.deepcopy(old_agent), j, i, 0, delta_ri)
swap_state_0, swap_agent_0 = oamm.swap(initial_state_0, copy.deepcopy(old_agent), j, i, 0, delta_ri)

spot_price_100 = swap_state_100.price(swap_state_100, j)
spot_price_50 = swap_state_50.price(swap_state_50, j)
spot_price_0 = swap_state_0.price(swap_state_0, j)

if swap_state_100.fail == '' and swap_state_50.fail == '' and swap_state_0.fail == '':
if spot_price_100 <= spot_price_50:
raise AssertionError('Spot price is wrong.')
if spot_price_50 <= spot_price_0:
raise AssertionError('Spot price is wrong.')


@given(omnipool_reasonable_config(token_count=3, lrna_fee=0.0005, asset_fee=0.0025, imbalance=-1000))
def test_lrna_buy_nonzero_fee_nonzero_imbalance(initial_state: oamm.OmnipoolState):
old_state = initial_state
Expand Down

0 comments on commit eb2b3db

Please sign in to comment.