-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into feat/build-top-loans-chart
- Loading branch information
Showing
24 changed files
with
1,462 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,4 @@ __pycache__ | |
.vscode | ||
.env | ||
storage_credentials.json | ||
env/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
"""Tests for the EkuboOrderBook class.""" | ||
from decimal import Decimal | ||
from unittest.mock import Mock, patch, MagicMock | ||
|
||
import pandas as pd | ||
import pytest | ||
from data_handler.handlers.order_books.ekubo.main import EkuboOrderBook | ||
|
||
@pytest.fixture | ||
def mock_connector(): | ||
"""Create a mock EkuboAPIConnector.""" | ||
with patch('data_handler.handlers.order_books.ekubo.main.EkuboAPIConnector') as mock: | ||
connector = mock.return_value | ||
connector.get_pair_price.return_value = {"price": "1.5"} | ||
connector.get_pools.return_value = [{ | ||
"token0": "0x1", | ||
"token1": "0x2", | ||
"key_hash": "0xabc", | ||
"liquidity": "1000000", | ||
"lastUpdate": {"event_id": "123"}, | ||
"tick": 100, | ||
"tick_spacing": 10 | ||
}] | ||
connector.get_pool_liquidity.return_value = { | ||
"data": [ | ||
{"tick": 90, "net_liquidity_delta_diff": "100"}, | ||
{"tick": 100, "net_liquidity_delta_diff": "200"}, | ||
{"tick": 110, "net_liquidity_delta_diff": "300"} | ||
] | ||
} | ||
yield connector | ||
|
||
@pytest.fixture | ||
def order_book(mock_connector): | ||
"""Create an EkuboOrderBook instance with mocked connector.""" | ||
book = EkuboOrderBook("0x1", "0x2") | ||
# Set decimals for token precision | ||
book.token_a_decimal = 18 | ||
book.token_b_decimal = 18 | ||
return book | ||
|
||
def test_initialization(order_book): | ||
"""Test EkuboOrderBook initialization.""" | ||
assert order_book.DEX == "Ekubo" | ||
assert order_book.token_a == "0x1" | ||
assert order_book.token_b == "0x2" | ||
assert order_book.asks == [] | ||
assert order_book.bids == [] | ||
|
||
def test_set_current_price(order_book): | ||
"""Test setting current price.""" | ||
order_book.set_current_price() | ||
assert order_book.current_price == Decimal("1.5") | ||
|
||
def test_fetch_price_and_liquidity(order_book): | ||
"""Test fetching price and liquidity data.""" | ||
order_book.fetch_price_and_liquidity() | ||
assert order_book.block == "123" | ||
assert len(order_book.asks) > 0 | ||
assert len(order_book.bids) > 0 | ||
|
||
def test_calculate_order_book(order_book): | ||
"""Test order book calculation.""" | ||
# Create test data with ticks both above and below the current tick (100) | ||
liquidity_data = [ | ||
{"tick": 90, "net_liquidity_delta_diff": "100"}, | ||
{"tick": 95, "net_liquidity_delta_diff": "150"}, | ||
{"tick": 100, "net_liquidity_delta_diff": "200"}, | ||
{"tick": 105, "net_liquidity_delta_diff": "250"}, | ||
{"tick": 110, "net_liquidity_delta_diff": "300"} | ||
] | ||
|
||
# Create a pandas Series for the row | ||
row_data = { | ||
"tick": 100, | ||
"tick_spacing": 5, | ||
"liquidity": "1000000" | ||
} | ||
row = pd.Series(row_data) | ||
|
||
# Set current price for price range calculation | ||
order_book.current_price = Decimal("1.5") | ||
|
||
# Calculate order book | ||
order_book._calculate_order_book(liquidity_data, 1000000, row) | ||
|
||
# Verify that both asks and bids are populated | ||
assert len(order_book.asks) > 0, "Asks should not be empty" | ||
assert len(order_book.bids) > 0, "Bids should not be empty" | ||
|
||
# Verify the structure of asks and bids | ||
for price, supply in order_book.asks: | ||
assert isinstance(price, Decimal), "Price should be Decimal" | ||
assert isinstance(supply, Decimal), "Supply should be Decimal" | ||
assert price > 0, "Price should be positive" | ||
assert supply > 0, "Supply should be positive" | ||
|
||
for price, supply in order_book.bids: | ||
assert isinstance(price, Decimal), "Price should be Decimal" | ||
assert isinstance(supply, Decimal), "Supply should be Decimal" | ||
assert price > 0, "Price should be positive" | ||
assert supply > 0, "Supply should be positive" | ||
|
||
def test_get_pure_sqrt_ratio(order_book): | ||
"""Test square root ratio calculation.""" | ||
result = order_book._get_pure_sqrt_ratio(Decimal("1")) | ||
assert isinstance(result, Decimal) | ||
assert result > 0 | ||
|
||
def test_sort_ticks_by_asks_and_bids(): | ||
"""Test sorting ticks into asks and bids.""" | ||
liquidity_data = [ | ||
{"tick": 90}, | ||
{"tick": 100}, | ||
{"tick": 110} | ||
] | ||
current_tick = 100 | ||
asks, bids = EkuboOrderBook.sort_ticks_by_asks_and_bids(liquidity_data, current_tick) | ||
assert len(asks) == 1 | ||
assert len(bids) == 2 | ||
assert all(tick["tick"] > current_tick for tick in asks) | ||
assert all(tick["tick"] <= current_tick for tick in bids) | ||
|
||
def test_calculate_liquidity_amount(order_book): | ||
"""Test liquidity amount calculation.""" | ||
# Set token decimals for proper calculation | ||
order_book.token_a_decimal = 18 | ||
order_book.token_b_decimal = 18 | ||
|
||
tick = Decimal("100") | ||
liquidity_total = Decimal("1000000") | ||
result = order_book.calculate_liquidity_amount(tick, liquidity_total) | ||
assert isinstance(result, Decimal) | ||
assert result > 0 | ||
|
||
@pytest.mark.parametrize("tick,expected_sign", [ | ||
(1000, 1), # positive tick | ||
(-1000, -1), # negative tick | ||
(0, 0) # zero tick | ||
]) | ||
def test_tick_edge_cases(order_book, tick, expected_sign): | ||
"""Test tick calculations with edge cases.""" | ||
result = order_book._get_pure_sqrt_ratio(Decimal(tick)) | ||
assert isinstance(result, Decimal) | ||
assert result > 0 # sqrt ratio should always be positive | ||
if expected_sign > 0: | ||
assert result > 1 | ||
elif expected_sign < 0: | ||
assert result < 1 | ||
|
||
def test_empty_liquidity_data(order_book): | ||
"""Test behavior with empty liquidity data.""" | ||
row_data = { | ||
"tick": 100, | ||
"tick_spacing": 5, | ||
"liquidity": "1000000" | ||
} | ||
row = pd.Series(row_data) | ||
|
||
order_book._calculate_order_book([], 1000000, row) | ||
assert len(order_book.asks) == 0 | ||
assert len(order_book.bids) == 0 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
""" Test Cases for MySwap""" | ||
|
||
import asyncio | ||
import math | ||
from decimal import Decimal | ||
from unittest.mock import AsyncMock, Mock, patch | ||
|
||
import pytest | ||
|
||
from ...handlers.order_books.myswap.main import MySwapOrderBook | ||
|
||
MAX_MYSWAP_TICK = Decimal("1774532") | ||
|
||
|
||
@pytest.fixture | ||
def order_book(): | ||
"""Mocks""" | ||
obj = MySwapOrderBook( | ||
base_token="0x12345", quote_token="0x67890", apply_filtering=True | ||
) | ||
obj.base_token = "0x12345" | ||
obj.quote_token = "0x67890" | ||
return obj | ||
|
||
|
||
def test_order_book_initialization(order_book): | ||
"""Test that the order book is initialized correctly.""" | ||
assert order_book.base_token == "0x12345" | ||
assert order_book.quote_token == "0x67890" | ||
assert order_book.apply_filtering is True | ||
|
||
|
||
def test_price_to_tick(order_book): | ||
"""Test conversion of price to tick.""" | ||
order_book._decimals_diff = Decimal(1) | ||
price = Decimal("1.0001") | ||
tick = order_book._price_to_tick(price) | ||
expected_tick = ( | ||
round( | ||
Decimal(math.log(price / (Decimal(2**128) * order_book._decimals_diff))) | ||
/ Decimal(math.log(Decimal("1.0001"))) | ||
) | ||
+ MAX_MYSWAP_TICK | ||
) | ||
assert tick == expected_tick | ||
|
||
|
||
@patch.object(MySwapOrderBook, "_get_clean_addresses", return_value=("0x123", "0x456")) | ||
def test_filter_pools_data(mock_get_clean_addresses, order_book): | ||
"""Test that the pool data is correctly filtered.""" | ||
all_pools = { | ||
"pools": [ | ||
{"token0": {"address": "0x123"}, "token1": {"address": "0x456"}}, | ||
{"token0": {"address": "0x999"}, "token1": {"address": "0x888"}}, | ||
] | ||
} | ||
filtered = order_book._filter_pools_data(all_pools) | ||
assert len(filtered) == 1 | ||
assert filtered[0]["token0"]["address"] == "0x123" | ||
|
||
|
||
@patch.object( | ||
MySwapOrderBook, "_filter_pools_data", return_value=[{"poolkey": "test_pool"}] | ||
) | ||
@patch.object(MySwapOrderBook, "_calculate_order_book", new_callable=AsyncMock) | ||
def test_async_fetch_price_and_liquidity( | ||
mock_calculate_order_book, mock_filter_pools, order_book | ||
): | ||
"""Test the async fetching of price and liquidity.""" | ||
order_book.connector = Mock() | ||
order_book.connector.get_pools_data = Mock(return_value={"pools": []}) | ||
|
||
async def run_test(): | ||
await order_book._async_fetch_price_and_liquidity() | ||
|
||
asyncio.run(run_test()) | ||
mock_calculate_order_book.assert_awaited_once_with("test_pool") | ||
|
||
|
||
@patch.object(MySwapOrderBook, "_get_clean_addresses", return_value=("0x123", "0x456")) | ||
def test_filter_pools_data_no_match(mock_get_clean_addresses, order_book): | ||
"""Test filtering when no pools match.""" | ||
all_pools = { | ||
"pools": [ | ||
{"token0": {"address": "0x999"}, "token1": {"address": "0x888"}}, | ||
] | ||
} | ||
filtered = order_book._filter_pools_data(all_pools) | ||
assert len(filtered) == 0 | ||
|
||
|
||
def test_price_to_tick_invalid(order_book): | ||
"""Test invalid price leading to ValueError.""" | ||
order_book._decimals_diff = Decimal(1) | ||
with pytest.raises(ValueError): | ||
order_book._price_to_tick(Decimal(0)) | ||
|
||
|
||
@patch.object(MySwapOrderBook, "_filter_pools_data", return_value=[]) | ||
def test_async_fetch_price_and_liquidity_no_pools(mock_filter_pools, order_book): | ||
"""Test the async fetching when no pools are available.""" | ||
order_book.connector = Mock() | ||
order_book.connector.get_pools_data = Mock(return_value={"pools": []}) | ||
|
||
async def run_test(): | ||
await order_book._async_fetch_price_and_liquidity() | ||
|
||
asyncio.run(run_test()) | ||
mock_filter_pools.assert_called_once() |
Oops, something went wrong.