Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed max_liquidity bug, and tests that failed to catch it #165

Merged
merged 4 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions hydradx/model/amm/arbitrage_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ def process_next_swap(
if amt != 0:
init_amt = test_agent.holdings[numeraire]
text_dex.swap(test_agent, tkn_buy=tkn, tkn_sell=numeraire, buy_quantity=amt)
amt_in = init_amt - test_agent.holdings[numeraire]
numeraire_holding = test_agent.holdings[numeraire]
amt_in = init_amt - numeraire_holding
test_cex.swap(test_agent, tkn_sell=ob_tkn_pair[0], tkn_buy=ob_tkn_pair[1], sell_quantity=amt)
numeraire_diff = test_agent.holdings[numeraire] - numeraire_holding
swap = {
'dex': {
'trade': 'buy',
Expand All @@ -57,7 +59,7 @@ def process_next_swap(
if tkn in max_liquidity_cex:
max_liquidity_cex[tkn] -= amt
if numeraire in max_liquidity_cex:
max_liquidity_cex[numeraire] += amt_in
max_liquidity_cex[numeraire] += numeraire_diff
if tkn in max_liquidity_dex:
max_liquidity_dex[tkn] += amt
if numeraire in max_liquidity_dex:
Expand All @@ -73,8 +75,10 @@ def process_next_swap(
if amt != 0:
init_amt = test_agent.holdings[numeraire]
text_dex.swap(test_agent, tkn_buy=numeraire, tkn_sell=tkn, sell_quantity=amt)
amt_out = test_agent.holdings[numeraire] - init_amt
numeraire_holding = test_agent.holdings[numeraire]
amt_out = numeraire_holding - init_amt
test_cex.swap(test_agent, tkn_sell=ob_tkn_pair[1], tkn_buy=ob_tkn_pair[0], buy_quantity=amt)
numeraire_diff = test_agent.holdings[numeraire] - numeraire_holding
swap = {
'dex': {
'trade': 'sell',
Expand All @@ -94,7 +98,7 @@ def process_next_swap(
if tkn in max_liquidity_cex:
max_liquidity_cex[tkn] += amt
if numeraire in max_liquidity_cex:
max_liquidity_cex[numeraire] -= amt_out
max_liquidity_cex[numeraire] += numeraire_diff
if tkn in max_liquidity_dex:
max_liquidity_dex[tkn] -= amt
if numeraire in max_liquidity_dex:
Expand Down
40 changes: 29 additions & 11 deletions hydradx/tests/test_arbitrage_agent.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import copy
from datetime import timedelta
import os
import pytest
from hypothesis import given, strategies as st, settings, Phase
from mpmath import mp, mpf
mp.dps = 50

from hydradx.model.amm.agents import Agent
from hydradx.model.amm.arbitrage_agent import calculate_profit, calculate_arb_amount_bid, calculate_arb_amount_ask, \
Expand Down Expand Up @@ -459,7 +462,8 @@ def test_process_next_swap(
trade_fee=cex_fee
)

agent = Agent(holdings={'USDT': 1000000000, 'DOT': 1000000000, 'HDX': 1000000000}, unique_id='bot')
holdings = {'USDT': mpf(1000000000), 'DOT': mpf(1000000000), 'HDX': mpf(1000000000)}
agent = Agent(holdings=holdings, unique_id='bot')

test_state = op_state.copy()
test_agent = agent.copy()
Expand All @@ -475,7 +479,25 @@ def test_process_next_swap(
tkn_pair = ('DOT', 'USDT')

swap = process_next_swap(test_state, test_agent, test_cex, tkn_pair, tkn_pair, buffer, max_liquidity['dex'], max_liquidity['kraken'], iters)

if swap:

diff_dex = {
'DOT': test_state.liquidity['DOT'] - op_state.liquidity['DOT'],
'USDT': test_state.liquidity['USDT'] - op_state.liquidity['USDT']
}

diff_agent = {
'DOT': test_agent.holdings['DOT'] - agent.holdings['DOT'],
'USDT': test_agent.holdings['USDT'] - agent.holdings['USDT']
}

diff_cex = {
'DOT': -diff_agent['DOT'] - diff_dex['DOT'],
'USDT': -diff_agent['USDT'] - diff_dex['USDT'],
'HDX': 0
}

cex_swap, dex_swap = swap['cex'], swap['dex']
dex_spot = op_state.price(op_state, 'DOT', 'USDT')
if cex_swap['buy_asset'] != dex_swap['sell_asset'] or cex_swap['sell_asset'] != dex_swap['buy_asset']:
Expand All @@ -501,26 +523,22 @@ def test_process_next_swap(
swap['exchange'] = 'exchange_name'

arb_swaps = [swap]

initial_agent = Agent(holdings={'USDT': 1000000000, 'DOT': 1000000000, 'HDX': 1000000000}, unique_id='bot')
holdings = {'USDT': mpf(1000000000), 'DOT': mpf(1000000000), 'HDX': mpf(1000000000)}
initial_agent = Agent(holdings=holdings, unique_id='bot')
agent = initial_agent.copy()

execute_arb(op_state, {'exchange_name': cex}, agent, arb_swaps)

profit = calculate_profit(initial_agent, agent)
for tkn in profit:
if profit[tkn] / initial_agent.holdings[tkn] < -1e-10:
raise

for tkn in op_state.asset_list:
if tkn in max_liquidity:
if test_state.liquidity[tkn] - op_state.liquidity[tkn] != init_max_liquidity['dex'][tkn] - \
max_liquidity['dex'][tkn]:
if tkn in max_liquidity['dex']:
if test_state.liquidity[tkn] - op_state.liquidity[tkn] != pytest.approx(init_max_liquidity['dex'][tkn] - max_liquidity['dex'][tkn], 1e-10):
raise
for tkn in cex.asset_list:
if tkn in max_liquidity:
if test_cex.liquidity[tkn] - cex.liquidity[tkn] != init_max_liquidity['cex'][tkn] - \
max_liquidity['cex'][tkn]:
if tkn in max_liquidity['kraken']:
if diff_cex[tkn] != pytest.approx(init_max_liquidity['kraken'][tkn] - max_liquidity['kraken'][tkn], 1e-10):
raise


Expand Down
41 changes: 21 additions & 20 deletions hydradx/tests/test_omnipool_amm.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,26 +124,27 @@ def test_add_liquidity(initial_state: oamm.OmnipoolState):
for i in initial_state.asset_list:
initial_state.weight_cap[i] = min(initial_state.lrna[i] / initial_state.lrna_total * 1.1, 1)

# calculate what should be the maximum allowable liquidity provision
max_amount = ((old_state.weight_cap[i] / (1 - old_state.weight_cap[i])
* old_state.lrna_total - old_state.lrna[i] / (1 - old_state.weight_cap[i]))
/ oamm.lrna_price(old_state, i))

if max_amount < 0:
raise AssertionError('This calculation makes no sense.') # but actually, it works :)

# make sure agent has enough funds
old_agent.holdings[i] = max_amount * 2
# eliminate general tvl cap, so we can test just the weight cap
old_state.tvl_cap = float('inf')

# try one just above and just below the maximum allowable amount
illegal_state, illegal_agents = oamm.simulate_add_liquidity(old_state, old_agent, max_amount * 1.0000001, i)
if not illegal_state.fail:
raise AssertionError(f'illegal transaction passed against weight limit in {i}')
legal_state, legal_agents = oamm.simulate_add_liquidity(old_state, old_agent, max_amount * 0.9999999, i)
if legal_state.fail:
raise AssertionError(f'legal transaction failed against weight limit in {i} ({new_state.fail})')
if old_state.weight_cap[i] < 1:
# calculate what should be the maximum allowable liquidity provision
max_amount = ((old_state.weight_cap[i] / (1 - old_state.weight_cap[i])
* old_state.lrna_total - old_state.lrna[i] / (1 - old_state.weight_cap[i]))
/ oamm.lrna_price(old_state, i))

if max_amount < 0:
raise AssertionError('This calculation makes no sense.') # but actually, it works :)

# make sure agent has enough funds
old_agent.holdings[i] = max_amount * 2
# eliminate general tvl cap, so we can test just the weight cap
old_state.tvl_cap = float('inf')

# try one just above and just below the maximum allowable amount
illegal_state, illegal_agents = oamm.simulate_add_liquidity(old_state, old_agent, max_amount * 1.0000001, i)
if not illegal_state.fail:
raise AssertionError(f'illegal transaction passed against weight limit in {i}')
legal_state, legal_agents = oamm.simulate_add_liquidity(old_state, old_agent, max_amount * 0.9999999, i)
if legal_state.fail:
raise AssertionError(f'legal transaction failed against weight limit in {i} ({new_state.fail})')


@settings(max_examples=1)
Expand Down