Skip to content

Commit

Permalink
Adding pytest testing framework
Browse files Browse the repository at this point in the history
* use `pytest carbon/tests` to run all tests
* use `pytest carbon/tests/ui_tests` to run all UI tests
* use `pytest carbon/tests/ui_tests/simulations/test_both_linked_and_single_positions.py` to run a specific test file
* see pytest docs (https://docs.pytest.org/en/7.1.x/how-to/usage.html) for details on how to setup calls to test single functions

Reverted the test case to adhere to p_start / p_end vs high / low conventions

Added pytest testing framework

* use `pytest carbon/tests` to run all tests
* use `pytest carbon/tests/ui_tests` to run all UI tests
* use `pytest carbon/tests/ui_tests/simulations/test_both_linked_and_single_positions.py` to run a specific test file
* see pytest docs (https://docs.pytest.org/en/7.1.x/how-to/usage.html) for details on how to setup calls to test single functions
  • Loading branch information
mikewcasale authored and sklbancor committed Nov 18, 2022
1 parent e326a47 commit f52ca1a
Show file tree
Hide file tree
Showing 18 changed files with 453 additions and 10 deletions.
13 changes: 4 additions & 9 deletions carbon/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,6 @@ def y0(self) -> Decimal:

@property
def n(self) -> Decimal:
"""
Placeholder...
"""
p_high: Decimal = self.p_high
p_low: Decimal = self.p_low
return (
Expand All @@ -140,12 +137,10 @@ def reverseq(self, value):
# Use the dataclass post-init method to initialize all variable sets
def __post_init__(self):

if not self.pair.has_token(self.tkn):
raise RuntimeError("token not part of pair", self.tkn, self.pair)

self.p_high = Decimal(self.p_high)
self.p_low = Decimal(self.p_low)
self.y_int = Decimal(self.y_int)
if self.p_high is not None and self.p_low is not None:
self.p_high = Decimal(self.p_high)
self.p_low = Decimal(self.p_low)
self.y_int = Decimal(self.y_int)

if self.auto_convert_variables:

Expand Down
Empty file added carbon/tests/__init__.py
Empty file.
Empty file.
Empty file.
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions carbon/tests/ui_tests/analytics/test_analytics_features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import subprocess
import sys

import pytest

from carbon import CarbonPair, CarbonSimulatorUI, analytics as al


def test_orderbook_explain():
"""
Derrived from `passed_tests` notebook demo-3-2
"""
Sim = CarbonSimulatorUI(
pair=CarbonPair("ETH", "USDC"), verbose=False, raiseonerror=True
)
CA = al.Analytics(Sim, verbose=True)
maxx = 3000
orders = tuple(
[
al.orders_nt("ETH", 100, 2000, maxx),
al.orders_nt("ETH", 100, 2500, 2700),
al.orders_nt("USDC", 1000 * 100, 1500, 500),
al.orders_nt("USDC", 1100 * 100, 1200, 1000),
]
)

for o in orders:
Sim.add_order(o.tkn, o.amt, o.p_start, o.p_end)

max_liquidity = Sim.liquidity()["ETHUSDC"]["ETH"]
src_amounts = al.linspace(max_liquidity, 20)
CA.simulate_trades(60, CA.ASK)
CA.simulate_trades(70, CA.ASK)
trg_amounts = al.vec([CA.simulate_trades(size, CA.ASK) for size in src_amounts])

OB = al.OrderBook(src_amounts, trg_amounts, "ETH", "USDC")
assert (
OB.explain()
== "Source token = ETH, target token = USDC.\nAMM sells ETH for USDC.\nPrices are quoted in USDC per ETH.\nOrder book amounts are quoted in ETH."
)


def test_orderbook_plot_token_amount_chart_text():
"""
Derrived from `passed_tests` notebook demo-3-2
"""
Sim = CarbonSimulatorUI(
pair=CarbonPair("ETH", "USDC"), verbose=False, raiseonerror=True
)
CA = al.Analytics(Sim, verbose=True)
maxx = 3000
orders = tuple(
[
al.orders_nt("ETH", 100, 2000, maxx),
al.orders_nt("ETH", 100, 2500, 2700),
al.orders_nt("USDC", 1000 * 100, 1500, 500),
al.orders_nt("USDC", 1100 * 100, 1200, 1000),
]
)

for o in orders:
Sim.add_order(o.tkn, o.amt, o.p_start, o.p_end)

max_liquidity = Sim.liquidity()["ETHUSDC"]["ETH"]
src_amounts = al.linspace(max_liquidity, 20)
CA.simulate_trades(60, CA.ASK)
CA.simulate_trades(70, CA.ASK)
trg_amounts = al.vec([CA.simulate_trades(size, CA.ASK) for size in src_amounts])

OB = al.OrderBook(src_amounts, trg_amounts, "ETH", "USDC")
result = OB.plot_token_amount_chart()
assert result == "plotted tokens received against trade size (504,750)"


# The following tests the matplotlib functionality using the `mpl` fixture
# https://github.com/matplotlib/pytest-mpl/blob/main/tests/test_pytest_mpl.py
#
# @pytest.mark.mpl_image_compare(baseline_dir='/carbon/tests/ui_tests/analytics/baseline',
# filename='orderbook_plot_token_amount_chart_1.png',
# tolerance=100)
# def test_orderbook_plot_token_amount_chart():
# """
# Derrived from `passed_tests` notebook demo-3-2
# """
# Sim = CarbonSimulatorUI(pair=CarbonPair("ETH", "USDC"), verbose=False, raiseonerror=True)
# CA = al.Analytics(Sim, verbose=True)
# maxx = 3000
# orders = tuple([
# al.orders_nt("ETH", 100, 2000, maxx),
# al.orders_nt("ETH", 100, 2500, 2700),
# al.orders_nt("USDC", 1000 * 100, 1500, 500),
# al.orders_nt("USDC", 1100 * 100, 1200, 1000),
# ])
#
# for o in orders:
# Sim.add_order(o.tkn, o.amt, o.p_start, o.p_end)
#
# max_liquidity = Sim.liquidity()["ETHUSDC"]["ETH"]
# src_amounts = al.linspace(max_liquidity, 20)
# CA.simulate_trades(60, CA.ASK)
# CA.simulate_trades(70, CA.ASK)
# trg_amounts = al.vec([
# CA.simulate_trades(size, CA.ASK) for size in src_amounts
# ])
#
# OB = al.OrderBook(src_amounts, trg_amounts, "ETH", "USDC")
#
# return OB.plot_token_amount_chart(return_fig=True)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from carbon import CarbonSimulatorUI
import pandas as pd


def test_liquidity_as_dataframe():
"""
Derrived from `passed_tests` notebook test-34
"""
Sim = CarbonSimulatorUI(verbose=True)
Sim.add_order("ETH", 10, 2000, 3000, "ETHUSDC")
Sim.add_order("ETH", 20, 2010, 3010, "ETHUSDC")
Sim.add_order("ETH", 30, 2020, 3020, "ETHUSDC")
Sim.add_order("ETH", 40, 2030, 2030, "ETHUSDC")
Sim.add_strategy("ETH", 10, 2000, 3000, 5000, 1000, 900, "ETHUSDC")
Sim.add_strategy("ETH", 20, 2010, 3010, 5100, 1010, 910, "ETHUSDC")
Sim.add_strategy("ETH", 30, 2020, 3020, 5200, 1020, 920, "ETHUSDC")
Sim.add_strategy("ETH", 40, 2030, 3030, 5300, 1030, 930, "ETHUSDC")
Sim.add_order("ETH", 10, 2000, 3000, "ETHDAI")
Sim.add_order("ETH", 20, 2010, 3010, "ETHDAI")
Sim.add_order("ETH", 30, 2020, 3020, "ETHDAI")
Sim.add_order("ETH", 40, 2030, 2030, "ETHDAI")
Sim.add_strategy("ETH", 10, 2000, 3000, 5000, 1000, 900, "ETHDAI")
Sim.add_strategy("ETH", 20, 2010, 3010, 5100, 1010, 910, "ETHDAI")
Sim.add_strategy("ETH", 30, 2020, 3020, 5200, 1020, 920, "ETHDAI")
Sim.add_strategy("ETH", 40, 2030, 3030, 5300, 1030, 930, "ETHDAI")
Sim.add_sgl_pos("ETH", 10, 3000, 2000, pair="ETHUSDC")

df = Sim.liquidity(Sim.ASDF)
dct = df.to_dict(orient="dict")["y"]
assert isinstance(df, pd.DataFrame)
assert dct == {
("ETHDAI", "DAI"): 20600.0,
("ETHDAI", "ETH"): 200.0,
("ETHUSDC", "ETH"): 210.0,
("ETHUSDC", "USDC"): 20600.0,
}

pairs = set(k[0] for k in dct)
assert pairs == {"ETHDAI", "ETHUSDC"}
assert {p: {k[1]: 1 for k in dct if k[0] == p} for p in pairs} == {
"ETHUSDC": {"ETH": 1, "USDC": 1},
"ETHDAI": {"DAI": 1, "ETH": 1},
}


def test_liquidity_as_dict():
"""
Derrived from `passed_tests` notebook test-34
"""
Sim = CarbonSimulatorUI(verbose=True)
Sim.add_order("ETH", 10, 2000, 3000, "ETHUSDC")
Sim.add_order("ETH", 20, 2010, 3010, "ETHUSDC")
Sim.add_order("ETH", 30, 2020, 3020, "ETHUSDC")
Sim.add_order("ETH", 40, 2030, 2030, "ETHUSDC")
Sim.add_strategy("ETH", 10, 2000, 3000, 5000, 1000, 900, "ETHUSDC")
Sim.add_strategy("ETH", 20, 2010, 3010, 5100, 1010, 910, "ETHUSDC")
Sim.add_strategy("ETH", 30, 2020, 3020, 5200, 1020, 920, "ETHUSDC")
Sim.add_strategy("ETH", 40, 2030, 3030, 5300, 1030, 930, "ETHUSDC")
Sim.add_order("ETH", 10, 2000, 3000, "ETHDAI")
Sim.add_order("ETH", 20, 2010, 3010, "ETHDAI")
Sim.add_order("ETH", 30, 2020, 3020, "ETHDAI")
Sim.add_order("ETH", 40, 2030, 2030, "ETHDAI")
Sim.add_strategy("ETH", 10, 2000, 3000, 5000, 1000, 900, "ETHDAI")
Sim.add_strategy("ETH", 20, 2010, 3010, 5100, 1010, 910, "ETHDAI")
Sim.add_strategy("ETH", 30, 2020, 3020, 5200, 1020, 920, "ETHDAI")
Sim.add_strategy("ETH", 40, 2030, 3030, 5300, 1030, 930, "ETHDAI")
Sim.add_sgl_pos("ETH", 10, 3000, 2000, pair="ETHUSDC")

dic = Sim.liquidity(Sim.ASDICT)
assert isinstance(dic, dict)
assert dic == {
"ETHDAI": {"DAI": 20600.0, "ETH": 200.0},
"ETHUSDC": {"ETH": 210.0, "USDC": 20600.0},
}
22 changes: 22 additions & 0 deletions carbon/tests/ui_tests/simulations/test_inverted_range_positions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from carbon import CarbonSimulatorUI


def test_inverted_range_positions_1():
"""
Derrived from `passed_tests` notebook test-13
"""
Sim = CarbonSimulatorUI(verbose=True)
Sim.add_sgl_pos("ETH", 10, 2000, 3000, pair="ETHUSDC")
result = Sim.add_sgl_pos("ETH", 10, 3000, 2000, pair="ETHUSDC")
assert result["success"] is True
assert list(Sim.state()["orders"]["p_end"].values) == [3000.0, 3000.0]


def test_inverted_range_positions_2():
"""
Derrived from `passed_tests` notebook test-13
"""
Sim = CarbonSimulatorUI(verbose=True)
result = Sim.add_linked_pos("BAT", 10, 12.0, 10, 0, 5, 7.5, pair="BATMAN")
assert result["success"] is True
assert list(Sim.state()["orders"]["p_end"].values) == [12.0, 5.0]
111 changes: 111 additions & 0 deletions carbon/tests/ui_tests/simulations/test_linked_positions_and_trades.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Derrived from `passed_tests` notebook test-2"""
from carbon import CarbonSimulatorUI


def test_add_linked_pos_concentrated_on_one_point():
"""
Derrived from `passed_tests` notebook test-2
"""
Sim = CarbonSimulatorUI(pair="USDCETH", verbose=True)
assert Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)[
"orders"
].to_dict() == {
"id": {0: 0, 1: 1},
"pair": {0: "USDCETH", 1: "USDCETH"},
"tkn": {0: "ETH", 1: "USDC"},
"y_int": {0: 100.0, 1: 0.0},
"y": {0: 100.0, 1: 0.0},
"y_unit": {0: "ETH", 1: "USDC"},
"p_start": {0: 2000.0, 1: 1000.0},
"p_end": {0: 2000.0, 1: 1000.0},
"p_marg": {0: 2000.0000000000002, 1: 1000.0000000000002},
"p_unit": {0: "ETH per USDC", 1: "ETH per USDC"},
"lid": {0: 1, 1: 0},
}


def test_amm_cannot_buy_eth_with_no_usdc():
"""
Derrived from `passed_tests` notebook test-2
"""
Sim = CarbonSimulatorUI(pair="USDCETH", verbose=True)
Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)
result = Sim.amm_buys("ETH", 10)
assert result["success"] is False
assert result["error"] == "token USDC has no non-empty liquidity positions"


def test_amm_can_sell_eth_at_curve_price():
"""
Derrived from `passed_tests` notebook test-1
"""
Sim = CarbonSimulatorUI(pair="USDCETH", verbose=True)
Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)
result = Sim.amm_sells("ETH", 10)
assert result["success"] is True
assert result["trades"].to_dict() == {
"uid": {0: "0"},
"id": {0: 0},
"subid": {0: "A"},
"note": {0: "AMM sells 10ETH buys 0USDC"},
"aggr": {0: True},
"exec": {0: True},
"limitfail": {0: None},
"amt1": {0: 10.0},
"tkn1": {0: "ETH"},
"amt2": {0: 0.005},
"tkn2": {0: "USDC"},
"pair": {0: "USDCETH"},
"routeix": {0: "[0]"},
"nroutes": {0: 1},
"price": {0: "2000.0"},
"p_unit": {0: "ETH per USDC"},
}


def test_amm_cannot_sell_with_insufficient_liquidity_1():
"""
Derrived from `passed_tests` notebook test-3
"""
Sim = CarbonSimulatorUI(pair="ETHUSDC", verbose=True)
Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)
result = Sim.amm_sells("ETH", 200)
assert result["success"] is False
assert (
result["error"]
== "Insufficient liquidity across all user positions to support this trade."
)


def test_amm_cannot_sell_with_insufficient_liquidity_2():
"""
Derrived from `passed_tests` notebook test-3
"""
Sim = CarbonSimulatorUI(pair="ETHUSDC", verbose=True)
Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)
Sim.amm_sells("ETH", 10)

# Having sold 10 ETH already, it can now not sell another 90+
result = Sim.amm_sells("ETH", 90.0001)
assert result["success"] is False
assert (
result["error"]
== "Insufficient liquidity across all user positions to support this trade."
)


def test_amm_sells_both_directions():
"""
Derrived from `passed_tests` notebook test-12
"""
Sim = CarbonSimulatorUI(pair="ETHUSDC", verbose=True)
Sim.add_linked_pos("ETH", 100, 2000, 2000, 0, 1000, 1000)

# Sell the entire ETH position. This gives us 200,000 USDC which expands the curve
result = Sim.amm_sells("ETH", 100)
assert result["success"] is True
assert result["trades"]["price"].astype(float).mean() == 2000.0

result = Sim.amm_sells("USDC", 200000)
assert result["success"] is True
assert result["trades"]["price"].astype(float).mean() == 1000.0
Loading

0 comments on commit f52ca1a

Please sign in to comment.