Skip to content

Commit

Permalink
- Fixed bug in agent.is_holding
Browse files Browse the repository at this point in the history
- Added validation for intents with partial = False flag in validate_intents
- Added tests with LRNA to single trade tests
- Fixed various solver tests to run with find_solution_outer_approx
  • Loading branch information
poliwop committed Oct 24, 2024
1 parent c12a27a commit 04bac79
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 114 deletions.
2 changes: 2 additions & 0 deletions hydradx/model/amm/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def copy(self):
def is_holding(self, tkn, amt=None):
if amt is None:
return tkn in self.holdings and self.holdings[tkn] > 0
elif amt == 0:
return True
else:
return tkn in self.holdings and self.holdings[tkn] >= amt

Expand Down
20 changes: 15 additions & 5 deletions hydradx/model/amm/omnix.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,15 @@ def validate_intents(intents: list, intent_deltas: list):
for i in range(len(intents)):
intent = intents[i]
sell_amt = -intent_deltas[i][0]
buy_amt = intent_deltas[i][1]
if not 0 <= sell_amt <= intent['sell_quantity']:
raise Exception("amt processed is not in range")
if intent['partial'] == False and sell_amt not in [0, intent['sell_quantity']]:
raise Exception("intent should not be partially executed")
if intent['partial'] == False and 0 < buy_amt < intent['buy_quantity']:
raise Exception("intent should not be partially executed")
tolerance = 1e-6 # temporarily allowing some tolerance, only for testing purposes
if sell_amt > 0 and intent_deltas[i][1] / sell_amt < (1 - tolerance) * intent['buy_quantity'] / intent['sell_quantity']:
if sell_amt > 0 and buy_amt / sell_amt < (1 - tolerance) * intent['buy_quantity'] / intent['sell_quantity']:
raise Exception("price is not within tolerance")

return True
Expand All @@ -92,10 +97,13 @@ def execute_solution(

# transfer assets in from agents whose intents are being executed
for transfer in transfers:
if transfer['tkn_sell'] not in pool_agent.holdings:
pool_agent.holdings[transfer['tkn_sell']] = 0
pool_agent.holdings[transfer['tkn_sell']] += transfer['sell_quantity']
transfer['agent'].holdings[transfer['tkn_sell']] -= transfer['sell_quantity']
if transfer['sell_quantity'] > 0:
if transfer['tkn_sell'] not in pool_agent.holdings:
pool_agent.holdings[transfer['tkn_sell']] = 0
pool_agent.holdings[transfer['tkn_sell']] += transfer['sell_quantity']
transfer['agent'].holdings[transfer['tkn_sell']] -= transfer['sell_quantity']
elif transfer['sell_quantity'] < 0:
raise Exception("sell quantity is negative")

# execute swaps against Omnipool
lrna_deltas = {tkn: 0 for tkn in deltas}
Expand All @@ -117,6 +125,8 @@ def execute_solution(
if transfer['tkn_buy'] not in transfer['agent'].holdings:
transfer['agent'].holdings[transfer['tkn_buy']] = 0
transfer['agent'].holdings[transfer['tkn_buy']] += transfer['buy_quantity']
elif transfer['buy_quantity'] < 0:
raise Exception("buy quantity is negative")

return pool_agent, lrna_deltas

Expand Down
170 changes: 61 additions & 109 deletions hydradx/tests/test_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@


def test_single_trade_settles():
agents = [Agent(holdings={'DOT': 100})]
agents = [Agent(holdings={'DOT': 100, 'LRNA': 750})]

init_intents_partial = [ # selling DOT for $7
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': True}
]
init_intents_full = [ # selling DOT for $7
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': False}
]
init_intents_partial_lrna = [
{'sell_quantity': mpf(750), 'buy_quantity': mpf(700), 'tkn_sell': 'LRNA', 'tkn_buy': 'USDT', 'agent': agents[0],
'partial': True}
]
init_intents_full_lrna = [
{'sell_quantity': mpf(750), 'buy_quantity': mpf(700), 'tkn_sell': 'LRNA', 'tkn_buy': 'USDT', 'agent': agents[0],
'partial': False}
]

liquidity = {'HDX': mpf(100000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000/7.5)} # DOT price is $7.50
lrna = {'HDX': mpf(1000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000)}
Expand All @@ -46,6 +54,18 @@ def test_single_trade_settles():
assert intent_deltas[0][0] == -init_intents_full[0]['sell_quantity']
assert intent_deltas[0][1] == init_intents_full[0]['buy_quantity']

intents = copy.deepcopy(init_intents_partial_lrna)
intent_deltas = find_solution_outer_approx(initial_state, intents)
assert validate_and_execute_solution(initial_state.copy(), intents, intent_deltas)
assert intent_deltas[0][0] == -init_intents_partial_lrna[0]['sell_quantity']
assert intent_deltas[0][1] == init_intents_partial_lrna[0]['buy_quantity']

intents = copy.deepcopy(init_intents_full_lrna)
intent_deltas = find_solution_outer_approx(initial_state, intents)
assert validate_and_execute_solution(initial_state.copy(), intents, intent_deltas)
assert intent_deltas[0][0] == -init_intents_full_lrna[0]['sell_quantity']
assert intent_deltas[0][1] == init_intents_full_lrna[0]['buy_quantity']


def test_single_trade_does_not_settle():
agents = [Agent(holdings={'DOT': 100, 'USDT': 0})]
Expand All @@ -56,6 +76,14 @@ def test_single_trade_does_not_settle():
init_intents_full = [ # selling DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(800), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': False}
]
init_intents_partial_lrna = [
{'sell_quantity': mpf(650), 'buy_quantity': mpf(700), 'tkn_sell': 'LRNA', 'tkn_buy': 'USDT', 'agent': agents[0],
'partial': True}
]
init_intents_full_lrna = [
{'sell_quantity': mpf(650), 'buy_quantity': mpf(700), 'tkn_sell': 'LRNA', 'tkn_buy': 'USDT', 'agent': agents[0],
'partial': False}
]

liquidity = {'HDX': mpf(100000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000/7.5)} # DOT price is $7.50
lrna = {'HDX': mpf(1000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000)}
Expand All @@ -81,16 +109,28 @@ def test_single_trade_does_not_settle():
assert intent_deltas[0][0] == 0
assert intent_deltas[0][1] == 0

intents = copy.deepcopy(init_intents_partial_lrna)
intent_deltas = find_solution_outer_approx(initial_state, intents)
assert validate_and_execute_solution(initial_state.copy(), intents, intent_deltas)
assert intent_deltas[0][0] == 0
assert intent_deltas[0][1] == 0

intents = copy.deepcopy(init_intents_full_lrna)
intent_deltas = find_solution_outer_approx(initial_state, intents)
assert validate_and_execute_solution(initial_state.copy(), intents, intent_deltas)
assert intent_deltas[0][0] == 0
assert intent_deltas[0][1] == 0


def test_matching_trades_execute_more():
agents = [Agent(holdings={'DOT': 1000}), Agent(holdings={'USDT': 7600})]

intent1 = { # selling DOT for $7.49
'sell_quantity': mpf(1000), 'buy_quantity': mpf(7470), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0]
'sell_quantity': mpf(1000), 'buy_quantity': mpf(7470), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': True
}

intent2 = { # buying DOT for $7.51
'sell_quantity': mpf(7530), 'buy_quantity': mpf(1000), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[1]
'sell_quantity': mpf(7530), 'buy_quantity': mpf(1000), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[1], 'partial': True
}

liquidity = {'HDX': mpf(100000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000/7.5)} # DOT price is $7.50
Expand All @@ -108,19 +148,19 @@ def test_matching_trades_execute_more():
# do the DOT sale alone
state_sale = initial_state.copy()
intents_sale = [copy.deepcopy(intent1)]
sale_deltas = find_solution2(state_sale, intents_sale)
sale_deltas = find_solution_outer_approx(state_sale, intents_sale)
assert validate_and_execute_solution(state_sale, intents_sale, sale_deltas)

# do the DOT buy alone
state_buy = initial_state.copy()
intents_buy = [copy.deepcopy(intent2)]
buy_deltas = find_solution2(state_buy, intents_buy)
buy_deltas = find_solution_outer_approx(state_buy, intents_buy)
assert validate_and_execute_solution(state_buy, intents_buy, buy_deltas)

# do both trades together
state_match = initial_state.copy()
intents_match = [copy.deepcopy(intent1), copy.deepcopy(intent2)]
match_deltas = find_solution2(state_match, intents_match)
match_deltas = find_solution_outer_approx(state_match, intents_match)
assert validate_and_execute_solution(state_match, intents_match, match_deltas)

# check that matching trades caused more execution than executing either alone
Expand All @@ -138,45 +178,10 @@ def test_convex():
]

intents = [
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0]}, # selling DOT for $7
{'sell_quantity': mpf(1500), 'buy_quantity': mpf(100000), 'tkn_sell': 'USDT', 'tkn_buy': 'HDX', 'agent': agents[1]}, # buying HDX for $0.015
{'sell_quantity': mpf(400), 'buy_quantity': mpf(50), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[2]}, # buying DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(100), 'tkn_sell': 'HDX', 'tkn_buy': 'USDT', 'agent': agents[3]}, # selling HDX for $1
]

liquidity = {'HDX': mpf(100000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000/7.5)}
lrna = {'HDX': mpf(1000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000)}
initial_state = OmnipoolState(
tokens={
tkn: {'liquidity': liquidity[tkn], 'LRNA': lrna[tkn]} for tkn in lrna
},
asset_fee=mpf(0.0025),
lrna_fee=mpf(0.0005)
)
initial_state.last_fee = {tkn: mpf(0.0025) for tkn in lrna}
initial_state.last_lrna_fee = {tkn: mpf(0.0005) for tkn in lrna}

intent_deltas = find_solution(initial_state, intents)

assert validate_and_execute_solution(initial_state, intents, intent_deltas)

pprint(intent_deltas)


def test_convex2():

agents = [
Agent(holdings={'DOT': 100}),
Agent(holdings={'USDT': 1500}),
Agent(holdings={'USDT': 400}),
Agent(holdings={'HDX': 100}),
]

intents = [
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0]}, # selling DOT for $7
{'sell_quantity': mpf(1500), 'buy_quantity': mpf(100000), 'tkn_sell': 'USDT', 'tkn_buy': 'HDX', 'agent': agents[1]}, # buying HDX for $0.015
{'sell_quantity': mpf(400), 'buy_quantity': mpf(50), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[2]}, # buying DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(100), 'tkn_sell': 'HDX', 'tkn_buy': 'USDT', 'agent': agents[3]}, # selling HDX for $1
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': True}, # selling DOT for $7
{'sell_quantity': mpf(1500), 'buy_quantity': mpf(100000), 'tkn_sell': 'USDT', 'tkn_buy': 'HDX', 'agent': agents[1], 'partial': True}, # buying HDX for $0.015
{'sell_quantity': mpf(400), 'buy_quantity': mpf(50), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[2], 'partial': True}, # buying DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(100), 'tkn_sell': 'HDX', 'tkn_buy': 'USDT', 'agent': agents[3], 'partial': True}, # selling HDX for $1
]

liquidity = {'HDX': mpf(100000000), 'USDT': mpf(10000000), 'DOT': mpf(10000000/7.5)}
Expand All @@ -191,7 +196,7 @@ def test_convex2():
initial_state.last_fee = {tkn: mpf(0.0025) for tkn in lrna}
initial_state.last_lrna_fee = {tkn: mpf(0.0005) for tkn in lrna}

intent_deltas = find_solution2(initial_state, intents)
intent_deltas = find_solution_outer_approx(initial_state, intents)

assert validate_and_execute_solution(initial_state, intents, intent_deltas)

Expand All @@ -209,11 +214,11 @@ def test_with_lrna_intent():
]

intents = [
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0]}, # selling DOT for $7
{'sell_quantity': mpf(1500), 'buy_quantity': mpf(100000), 'tkn_sell': 'USDT', 'tkn_buy': 'HDX', 'agent': agents[1]}, # buying HDX for $0.015
{'sell_quantity': mpf(400), 'buy_quantity': mpf(50), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[2]}, # buying DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(100), 'tkn_sell': 'HDX', 'tkn_buy': 'USDT', 'agent': agents[3]}, # selling HDX for $1
{'sell_quantity': mpf(1000), 'buy_quantity': mpf(100), 'tkn_sell': 'LRNA', 'tkn_buy': 'DOT', 'agent': agents[4]}, # buying DOT for $10
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': True}, # selling DOT for $7
{'sell_quantity': mpf(1500), 'buy_quantity': mpf(100000), 'tkn_sell': 'USDT', 'tkn_buy': 'HDX', 'agent': agents[1], 'partial': True}, # buying HDX for $0.015
{'sell_quantity': mpf(400), 'buy_quantity': mpf(50), 'tkn_sell': 'USDT', 'tkn_buy': 'DOT', 'agent': agents[2], 'partial': True}, # buying DOT for $8
{'sell_quantity': mpf(100), 'buy_quantity': mpf(100), 'tkn_sell': 'HDX', 'tkn_buy': 'USDT', 'agent': agents[3], 'partial': True}, # selling HDX for $1
{'sell_quantity': mpf(1000), 'buy_quantity': mpf(100), 'tkn_sell': 'LRNA', 'tkn_buy': 'DOT', 'agent': agents[4], 'partial': True}, # buying DOT for $10
{'sell_quantity': mpf(100), 'buy_quantity': mpf(700), 'tkn_sell': 'DOT', 'tkn_buy': 'USDT', 'agent': agents[0], 'partial': False}, # selling DOT for $7
]

Expand All @@ -229,7 +234,7 @@ def test_with_lrna_intent():
initial_state.last_fee = {tkn: mpf(0.0025) for tkn in lrna}
initial_state.last_lrna_fee = {tkn: mpf(0.0005) for tkn in lrna}

intent_deltas = find_solution2(initial_state, intents)
intent_deltas = find_solution_outer_approx(initial_state, intents)

assert validate_and_execute_solution(initial_state, intents, intent_deltas)

Expand All @@ -245,8 +250,8 @@ def test_solver_with_real_omnipool():
intents = [
# {'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149711278057), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[0]},
# {'sell_quantity': mpf(1.149711278057), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX', 'agent': agents[1]},
{'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[0]},
{'sell_quantity': mpf(1.150), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX', 'agent': agents[1]},
{'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[0], 'partial': True},
{'sell_quantity': mpf(1.150), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX', 'agent': agents[1], 'partial': True},
# {'sell_quantity': mpf(100), 'buy_quantity': mpf(1.25359), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU',
# 'agent': agents[0]},
# {'sell_quantity': mpf(1.25361), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX',
Expand Down Expand Up @@ -276,11 +281,9 @@ def test_solver_with_real_omnipool():
initial_state.last_fee = {tkn: mpf(0.0025) for tkn in lrna}
initial_state.last_lrna_fee = {tkn: mpf(0.0005) for tkn in lrna}

intent_deltas = find_solution3(initial_state, intents)
intent_deltas = find_solution_outer_approx(initial_state, intents)

assert validate_and_execute_solution(initial_state.copy(), copy.deepcopy(intents), intent_deltas)
intent_deltas2 = [[-mpf(100), mpf(1.149)], [0, 0]]
assert validate_and_execute_solution(initial_state.copy(), copy.deepcopy(intents), intent_deltas2)

pprint(intent_deltas)

Expand Down Expand Up @@ -331,7 +334,7 @@ def test_solver_with_real_omnipool_one_full():
full_intent_indicators = [1]
# full_intent_indicators = []

amm_deltas, sell_deltas = _find_solution_unrounded3(initial_state, partial_intents, full_intents, I=full_intent_indicators)
amm_deltas, sell_deltas, _, _ = _find_solution_unrounded3(initial_state, partial_intents, full_intents, I=full_intent_indicators)
for i in full_intents:
if full_intent_indicators.pop(0) == 1:
sell_deltas.append(-i['sell_quantity'])
Expand All @@ -345,57 +348,6 @@ def test_solver_with_real_omnipool_one_full():
pprint(intent_deltas)


def test_MIP_solver_with_real_omnipool():
agents = [
Agent(holdings={'HDX': 100}),
Agent(holdings={'HDX': 100}),
]

intents = [
# {'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149711278057), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[0]},
# {'sell_quantity': mpf(1.149711278057), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX', 'agent': agents[1]},
{'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[0], 'partial': False},
{'sell_quantity': mpf(100), 'buy_quantity': mpf(1.149), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX', 'agent': agents[1], 'partial': True},
# {'sell_quantity': mpf(100), 'buy_quantity': mpf(200.0), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU', 'agent': agents[1],
# 'partial': True},
# {'sell_quantity': mpf(100), 'buy_quantity': mpf(1.25359), 'tkn_sell': 'HDX', 'tkn_buy': 'CRU',
# 'agent': agents[0]},
# {'sell_quantity': mpf(1.25361), 'buy_quantity': mpf(100), 'tkn_sell': 'CRU', 'tkn_buy': 'HDX',
# 'agent': agents[1]}
]

liquidity = {'4-Pool': mpf(1392263.9295618401), 'HDX': mpf(140474254.46393022), 'KILT': mpf(1941765.8700688032),
'WETH': mpf(897.820372708098), '2-Pool': mpf(80.37640742108785), 'GLMR': mpf(7389788.325282889),
'BNC': mpf(5294190.655262755), 'RING': mpf(30608622.54045291), 'vASTR': mpf(1709768.9093601815),
'vDOT': mpf(851755.7840315843), 'CFG': mpf(3497639.0397717496), 'CRU': mpf(337868.26827475097),
'2-Pool': mpf(14626788.977583803), 'DOT': mpf(2369965.4990946855), 'PHA': mpf(6002455.470581388),
'ZTG': mpf(9707643.829161936), 'INTR': mpf(52756928.48950746), 'ASTR': mpf(31837859.71273387), }
lrna = {'4-Pool': mpf(50483.454258911326), 'HDX': mpf(24725.8021660851), 'KILT': mpf(10802.301353604526),
'WETH': mpf(82979.9927924809), '2-Pool': mpf(197326.54331209575), 'GLMR': mpf(44400.11377262768),
'BNC': mpf(35968.10763198863), 'RING': mpf(1996.48438233777), 'vASTR': mpf(4292.819030020081),
'vDOT': mpf(182410.99000727307), 'CFG': mpf(41595.57689216696), 'CRU': mpf(4744.442135139952),
'2-Pool': mpf(523282.70722423657), 'DOT': mpf(363516.4838824808), 'PHA': mpf(24099.247547699764),
'ZTG': mpf(4208.90365804613), 'INTR': mpf(19516.483401186168), 'ASTR': mpf(68571.5237579274), }

initial_state = OmnipoolState(
tokens={
tkn: {'liquidity': liquidity[tkn], 'LRNA': lrna[tkn]} for tkn in lrna
},
asset_fee=mpf(0.0025),
lrna_fee=mpf(0.0005)
)
initial_state.last_fee = {tkn: mpf(0.0025) for tkn in lrna}
initial_state.last_lrna_fee = {tkn: mpf(0.0005) for tkn in lrna}

intent_deltas = _solve_inclusion_problem(initial_state, intents)

assert validate_and_execute_solution(initial_state.copy(), copy.deepcopy(intents), intent_deltas)
intent_deltas2 = [[-mpf(100), mpf(1.149)], [0, 0]]
assert validate_and_execute_solution(initial_state.copy(), copy.deepcopy(intents), intent_deltas2)

pprint(intent_deltas)


def test_full_solver():
agents = [
Agent(holdings={'HDX': 100}),
Expand Down

0 comments on commit 04bac79

Please sign in to comment.