diff --git a/src/cryptocom/exchange/__init__.py b/src/cryptocom/exchange/__init__.py index 3725db4..c6d1500 100644 --- a/src/cryptocom/exchange/__init__.py +++ b/src/cryptocom/exchange/__init__.py @@ -13,4 +13,4 @@ __all__ = [ 'ApiError', 'ApiProvider' ] -__version__ = '0.5.1' +__version__ = '0.6' diff --git a/src/cryptocom/exchange/api.py b/src/cryptocom/exchange/api.py index a6467b0..31c22c0 100644 --- a/src/cryptocom/exchange/api.py +++ b/src/cryptocom/exchange/api.py @@ -22,7 +22,7 @@ class ApiProvider: """Provides HTTP-api requests and websocket requests.""" def __init__( self, *, api_key='', api_secret='', from_env=False, - auth_required=True, timeout=3, retries=20, + auth_required=True, timeout=120, retries=3, root_url='https://api.crypto.com/v2/', ws_root_url='wss://stream.crypto.com/v2/'): self.api_key = api_key @@ -103,11 +103,11 @@ class ApiProvider: f"Code: {resp.status}. Data: {data}" ) - await asyncio.sleep(0.1) + await asyncio.sleep(0.5) continue except ContentTypeError: if resp.status == 429: - await asyncio.sleep(0.1) + await asyncio.sleep(0.5) continue text = await resp.text() raise ApiError( @@ -125,7 +125,7 @@ class ApiProvider: f"Data: {data}" ) - await asyncio.sleep(0.1) + await asyncio.sleep(0.5) continue async def get(self, path, params=None, sign=False): diff --git a/src/cryptocom/exchange/private.py b/src/cryptocom/exchange/private.py index 6bd8b0d..1aa9af3 100644 --- a/src/cryptocom/exchange/private.py +++ b/src/cryptocom/exchange/private.py @@ -35,48 +35,41 @@ class Account: } async def get_orders_history( - self, pair: Pair, page: int = 0, + self, pair: Pair = None, page: int = 0, page_size: int = 200) -> List[Order]: """Return all history orders.""" - data = await self.api.post('private/get-order-history', { - 'params': { - 'instrument_name': pair.value, - 'page_size': page_size, - 'page': page - } - }) + params = {'page_size': page_size, 'page': page} + if pair: + params['instrument_name'] = pair.value + data = await self.api.post( + 'private/get-order-history', {'params': params}) return [ Order.create_from_api(order) for order in data.get('order_list') or [] ] async def get_open_orders( - self, pair: Pair, page: int = 0, + self, pair: Pair = None, page: int = 0, page_size: int = 200) -> List[Order]: """Return open orders.""" - data = await self.api.post('private/get-open-orders', { - 'params': { - 'instrument_name': pair.value, - 'page_size': page_size, - 'page': page - } - }) + params = {'page_size': page_size, 'page': page} + if pair: + params['instrument_name'] = pair.value + data = await self.api.post( + 'private/get-open-orders', {'params': params}) return [ Order.create_from_api(order) for order in data.get('order_list') or [] ] async def get_trades( - self, pair: Pair, page: int = 0, + self, pair: Pair = None, page: int = 0, page_size: int = 200) -> List[PrivateTrade]: """Return trades.""" - data = await self.api.post('private/get-trades', { - 'params': { - 'instrument_name': pair.value, - 'page_size': page_size, - 'page': page - } - }) + params = {'page_size': page_size, 'page': page} + if pair: + params['instrument_name'] = pair.value + data = await self.api.post('private/get-trades', {'params': params}) return [ PrivateTrade.create_from_api(trade) for trade in data.get('trade_list') or [] @@ -199,7 +192,7 @@ class Account: async def cancel_open_orders(self, pair: Pair) -> None: """Cancel all open orders.""" - return await self.api.post('private/cancel-all-orders', { + await self.api.post('private/cancel-all-orders', { 'params': {'instrument_name': pair.value} }) diff --git a/src/cryptocom/exchange/structs.py b/src/cryptocom/exchange/structs.py index af933d9..a922058 100644 --- a/src/cryptocom/exchange/structs.py +++ b/src/cryptocom/exchange/structs.py @@ -209,6 +209,10 @@ class Balance: class OrderType(str, enum.Enum): LIMIT = 'LIMIT' MARKET = 'MARKET' + STOP_LOSS = 'STOP_LOSS' + STOP_LIMIT = 'STOP_LIMIT' + TAKE_PROFIT = 'TAKE_PROFIT' + TAKE_PROFIT_LIMIT = 'TAKE_PROFIT_LIMIT' class OrderStatus(str, enum.Enum): @@ -217,6 +221,7 @@ class OrderStatus(str, enum.Enum): CANCELED = 'CANCELED' REJECTED = 'REJECTED' EXPIRED = 'EXPIRED' + PENDING = 'PENDING' class OrderExecType(str, enum.Enum): @@ -245,6 +250,8 @@ class Order: filled_price: float fees_coin: Coin force_type: OrderForceType + filled_value: float + trigger_price: float @cached_property def is_buy(self): @@ -256,7 +263,7 @@ class Order: @cached_property def is_active(self): - return self.side == OrderStatus.ACTIVE + return self.status == OrderStatus.ACTIVE @cached_property def is_canceled(self): @@ -274,6 +281,10 @@ class Order: def is_filled(self): return self.status == OrderStatus.FILLED + @cached_property + def is_pending(self): + return self.status == OrderStatus.PENDING + @cached_property def volume(self): return self.price * self.quantity @@ -310,7 +321,9 @@ class Order: filled_quantity=data['cumulative_quantity'], filled_price=data['avg_price'], fees_coin=fees_coin, - force_type=OrderForceType(data['time_in_force']) + force_type=OrderForceType(data['time_in_force']), + filled_value=data['cumulative_value'], + trigger_price=data.get('trigger_price') ) diff --git a/tests/test_private.py b/tests/test_private.py index 781d43b..af51685 100644 --- a/tests/test_private.py +++ b/tests/test_private.py @@ -8,8 +8,8 @@ import cryptocom.exchange as cro @pytest.mark.asyncio async def test_account_get_balance(account: cro.Account): balances = await account.get_balance() - assert balances[cro.Coin.CRO].available > 0.5 - assert balances[cro.Coin.USDT].available > 0.5 + assert balances[cro.Coin.CRO].available > 0.2 + assert balances[cro.Coin.USDT].available > 0.2 for coin in cro.Coin: assert coin.value in balances @@ -18,12 +18,13 @@ async def test_account_get_balance(account: cro.Account): async def test_no_dublicated_mass_limit_orders( exchange: cro.Exchange, account: cro.Account): buy_price = round(await exchange.get_price(cro.Pair.CRO_USDT) / 2, 4) + orders_count = 185 order_ids = await asyncio.gather(*[ account.buy_limit( cro.Pair.CRO_USDT, 0.001, round(buy_price / 1000 + i / 10000.0, 4) ) - for i in range(100) + for i in range(orders_count) ]) real_orders = await asyncio.gather(*[ @@ -31,13 +32,12 @@ async def test_no_dublicated_mass_limit_orders( for id_ in order_ids ]) for order in real_orders: - assert order.status == cro.OrderStatus.ACTIVE, order + assert order.is_active, order - # wait till open orders will be updated - await asyncio.sleep(1) open_orders = await account.get_open_orders(cro.Pair.CRO_USDT) - assert len(real_orders) == len(open_orders) == 100 - assert sorted(o.id for o in open_orders) == sorted(order_ids) + open_order_ids = sorted(o.id for o in open_orders if o.is_active) + assert len(real_orders) == len(open_order_ids) == orders_count + assert open_order_ids == sorted(order_ids) @pytest.mark.asyncio @@ -65,7 +65,7 @@ async def test_account_limit_orders( open_orders = [ order - for order in await account.get_open_orders(cro.Pair.CRO_USDT) + for order in await account.get_open_orders() if order.id in order_ids ] assert not open_orders